Navigation Script generate-navigation.js - So funktioniert's

Wie das Script alle Navigationen, Header, Footer und Sidebars automatisch generiert – Schritt für Schritt.

Was macht das Script?

Das generate-navigation.js Script ist das Herzstück der automatischen Navigation auf DevPanicZone. Es durchsucht alle Tutorial-Dateien und erstellt automatisch:

  • Header & Footer auf allen Seiten
  • Main Navigation (Dropdown-Menü mit allen Kategorien)
  • Breadcrumbs (Navigationspfad: Home › Kategorie › Tutorial)
  • Prev/Next Navigation (Vorheriges/Nächstes Tutorial)
  • Sidebar TOC (Inhaltsverzeichnis aus Überschriften)
  • Sidebar Tutorial-Liste (Alle Tutorials der Kategorie)
  • Tutorial-Buttons (Übersicht auf Kategorieseiten)
Warum automatisch?

Stell dir vor, du müsstest bei jedem neuen Tutorial ALLE anderen Tutorial-Seiten manuell aktualisieren! Das Script macht das automatisch mit einem einzigen Befehl: npm run build

Aufbau des Scripts

Das Script ist in logische Abschnitte unterteilt. Lass uns jeden einzelnen durchgehen!

1. Konfiguration & Setup

Ganz am Anfang werden die wichtigsten Einstellungen definiert:

JavaScript
const fs = require('fs');
const path = require('path');
const { JSDOM } = require('jsdom');

// Pfade vom scripts/ Ordner aus
const tutorialsDir = path.join(__dirname, '..', 'tutorials');
const assetsDir = path.join(__dirname, '..', 'assets');

// Blacklist: Diese Ordner werden ignoriert
const BLACKLIST_FOLDERS = ['noupload', 'node_modules', '.git', 'assets'];

Was passiert hier?

  • fs → Zum Lesen und Schreiben von Dateien
  • path → Zum Arbeiten mit Dateipfaden
  • JSDOM → Zum Parsen und Manipulieren von HTML
  • tutorialsDir → Zeigt auf den /tutorials Ordner
  • BLACKLIST_FOLDERS → Ordner, die das Script ignorieren soll
Warum __dirname, '..' ?

Das Script liegt in /scripts/generate-navigation.js . Um auf /tutorials zu kommen, muss es einen Ordner HOCH .. und dann in den tutorials Ordner.

2. Deine Konfiguration

Diese Konstanten definierst DU - sie steuern, wie die Navigationen aussehen:

JavaScript
// Kategorie-Namen für Main-Navigation
const CATEGORY_NAMES = {
    'html': 'HTML',
    'css': 'CSS',
    'javascript': 'JavaScript'
};

// Reihenfolge der Tutorials
const TUTORIAL_ORDER = {
    'tutorials/html/html-basics': [
        'html-basics.html',
        'html-grundgeruest.html'
    ]
};

// Eigene Titel für Links
const CUSTOM_TITLES = {
    'tutorials/html/html-basics/html-basics.html': 'Was ist HTML?'
};

Das bedeutet:

  • CATEGORY_NAMES → Welche Kategorien im Dropdown erscheinen
  • TUTORIAL_ORDER → In welcher Reihenfolge Tutorials erscheinen (wichtig für Prev/Next!)
  • CUSTOM_TITLES → Alternative Namen für Links (z.B. statt "html-basics" → "Was ist HTML?")

3. Latest Tutorials für die Startseite

Die "Neueste Tutorials" auf der Startseite werden manuell gepflegt :

JavaScript
const LATEST_TUTORIALS = [
    'tutorials/css/css-specials/css-organization.html',
    'tutorials/javascript/javascript-basics/js-security.html',
    'tutorials/misc/web/console-security.html',
    'tutorials/php/php-basics/php-security.html'
];
So funktioniert's
  • Die ersten 4 Einträge erscheinen auf der Startseite
  • Reihenfolge im Array = Reihenfolge auf der Startseite
  • Neues Tutorial featuren? → An Position 1 einfügen, letztes entfernen

Das Script hat fertige HTML-Templates für Header und Footer gespeichert:

JavaScript
const HEADER_TEMPLATE = `<!-- Header -->
<header class="site-header">
    <div class="container">
        <!-- Logo, Theme Toggle, Navigation... -->
    </div>
</header>`;

const FOOTER_TEMPLATE = `<footer class="site-footer">
    <div class="container">
        <!-- Copyright, Links... -->
    </div>
</footer>`;

Vorteil: Wenn du den Header ändern willst, änderst du ihn nur HIER - dann wird er auf ALLEN Seiten aktualisiert!

5. Akronyme automatisch kapitalisieren

Das Script erkennt Akronyme und kapitalisiert sie korrekt in allen Navigationstiteln:

JavaScript
const ACRONYMS = {
    'html': 'HTML',
    'css': 'CSS',
    'php': 'PHP',
    'js': 'JS',
    'javascript': 'JavaScript',
    'seo': 'SEO',
    'ftp': 'FTP',
    'devpaniczone': 'DevPanicZone'
};

function capitalizeWithAcronyms(text) {
    return text.split('-')
        .map(word => {
            const lower = word.toLowerCase();
            if (ACRONYMS[lower]) {
                return ACRONYMS[lower];
            }
            return word.charAt(0).toUpperCase() + word.slice(1);
        })
        .join(' ');
}

Beispiel: Der Ordner html-basics wird zu "HTML Basics" (nicht "Html Basics").

Hilfsfunktionen

Das Script hat kleine Helferlein, die bestimmte Aufgaben übernehmen:

findHtmlFiles() - HTML-Dateien finden

JavaScript
function findHtmlFiles(dir, fileList = []) {
    const files = fs.readdirSync(dir);
    files.forEach(file => {
        const filePath = path.join(dir, file);
        const stat = fs.statSync(filePath);
        
        if (stat.isDirectory()) {
            // Blacklist-Ordner ignorieren
            if (BLACKLIST_FOLDERS.includes(file)) {
                return;
            }
            // Rekursiv in Unterordner gehen
            findHtmlFiles(filePath, fileList);
        } else if (file.endsWith('.html')) {
            fileList.push(filePath);
        }
    });
    return fileList;
}

Was macht die Funktion?

  1. Liest alle Dateien in einem Ordner
  2. Ist es ein Ordner? → Prüfe, ob er auf der Blacklist steht
  3. Ist es keine Blacklist? → Gehe rekursiv in den Ordner (Funktion ruft sich selbst auf!)
  4. Ist es eine HTML-Datei? → Füge sie zur Liste hinzu
Was bedeutet "rekursiv"?

Die Funktion ruft sich selbst auf! Das ermöglicht es, durch ALLE Unterordner zu gehen, egal wie tief verschachtelt.

getCategoryFromPath() - Kategorie ermitteln

JavaScript
function getCategoryFromPath(filePath) {
    const relativePath = path.relative(tutorialsDir, filePath);
    const parts = relativePath.split(path.sep);
    return parts[0]; // Erste Ebene (html, css, etc.)
}

Beispiel:

  • Pfad: /tutorials/html/html-basics/html-basics.html
  • Relative Pfad: html/html-basics/html-basics.html
  • Aufteilen: ['html', 'html-basics', 'html-basics.html']
  • Erste Ebene: html ← Das ist die Kategorie!

getSubcategoryFromPath() - Unterkategorie ermitteln

JavaScript
function getSubcategoryFromPath(filePath) {
    const relativePath = path.relative(tutorialsDir, filePath);
    const parts = relativePath.split(path.sep);
    if (parts.length >= 2) {
        return 'tutorials/' + parts.slice(0, 2).join('/');
    }
    return 'tutorials/' + parts[0];
}

Beispiel:

  • Pfad: /tutorials/html/html-basics/html-basics.html
  • Parts: ['html', 'html-basics', 'html-basics.html']
  • Erste 2 Ebenen: ['html', 'html-basics']
  • Ergebnis: tutorials/html/html-basics

Warum wichtig?

Das Script braucht diese Info, um in TUTORIAL_ORDER die richtige Reihenfolge zu finden!

Jetzt wird's interessant! Diese Funktionen erstellen die eigentlichen Navigationen:

generateMainNavigation() – Dropdown mit Submenus

Die neue Version erstellt verschachtelte Submenus für jede Kategorie:

JavaScript
function generateMainNavigation() {
    const categories = Object.keys(CATEGORY_NAMES);
    const navItems = [];

    categories.forEach(category => {
        const categoryName = CATEGORY_NAMES[category];
        const subcategories = getSubcategoriesForCategory(category);

        if (Object.keys(subcategories).length > 0) {
            let navItem = `<li class="nav-item-nested">
                <a href="/tutorials/${category}/" class="nav-link">
                    ${categoryName}
                    <span class="submenu-icon">▸</span>
                </a>
                <ul class="dropdown-submenu">`;

            // Unterkategorien hinzufügen
            Object.keys(subcategories).forEach(subKey => {
                const sub = subcategories[subKey];
                navItem += `
                    <li><a href="/tutorials/${category}/#${subKey}">${sub.name}</a></li>`;
            });

            navItem += `
                </ul>
            </li>`;

            navItems.push(navItem);
        }
    });

    return navItems;
}

Was ist neu?

  • .nav-item-nested – Kategorie mit aufklappbarem Submenu
  • .dropdown-submenu – Liste der Unterkategorien
  • .submenu-icon – Pfeil-Symbol (▸) zeigt Submenu an
  • Links zu Ankern: /tutorials/html/#html-basics

Ergebnis im Dropdown:

Struktur
Tutorials ▾
├── HTML ▸
│   ├── HTML Basics
│   └── HTML Advanced
├── CSS ▸
│   ├── CSS Basics
│   └── CSS Specials
└── JavaScript ▸
    ├── JavaScript Basics
    └── JavaScript Projects

generateBreadcrumbs() - Navigationspfad

JavaScript
function generateBreadcrumbs(filePath, currentTitle) {
    const category = getCategoryFromPath(filePath);
    const categoryName = CATEGORY_NAMES[category] || category;
    const basename = path.basename(filePath);
    
    // Kategorieseite: Home › Kategorie
    if (basename === 'index.html') {
        return `<a href="/">Home</a>
                <span>›</span>
                <span>${categoryName}</span>`;
    }
    
    // Tutorial-Seite: Home › Kategorie › Tutorial
    return `<a href="/">Home</a>
            <span>›</span>
            <a href="/tutorials/${category}/">${categoryName}</a>
            <span>›</span>
            <span>${currentTitle}</span>`;
}

Unterschied:

  • Kategorieseite: Home › CSS (CSS ist nicht klickbar)
  • Tutorial-Seite: Home › CSS › Flexbox (CSS ist klickbar!)

generatePrevNextNav() – Kategorieübergreifend

Die Prev/Next-Navigation führt durch alle Tutorials einer Hauptkategorie , nicht nur innerhalb einer Unterkategorie:

JavaScript
function generatePrevNextNav(filePath) {
    const category = getCategoryFromPath(filePath);
    const currentSubcategory = getSubcategoryFromPath(filePath);
    const allTutorials = getAllTutorialsFlat(category);  // Alle Tutorials der Kategorie!
    const currentIndex = allTutorials.findIndex(t => t.filePath === filePath);

    if (currentIndex === -1) return '';

    const prev = currentIndex > 0 ? allTutorials[currentIndex - 1] : null;
    const next = currentIndex < allTutorials.length - 1 ? allTutorials[currentIndex + 1] : null;

    let navHtml = '';

    // PREV Button
    if (prev) {
        const prevSubcategory = `tutorials/${category}/${prev.subcategory}`;
        const isNewCategory = prevSubcategory !== currentSubcategory;
        const categoryClass = isNewCategory ? ' is-new' : '';

        navHtml += `<div class="tutorial-nav-prev">
            <a href="${prev.url}">
                <span class="tutorial-nav-label">← Vorheriges</span>
                <span class="tutorial-nav-link">${prev.title}</span>
                <span class="tutorial-nav-category${categoryClass}">${prev.subcategoryDisplay}</span>
            </a>
        </div>`;
    } else {
        navHtml += '<div class="tutorial-nav-prev"></div>';
    }

    // NEXT Button (analog)
    if (next) {
        const nextSubcategory = `tutorials/${category}/${next.subcategory}`;
        const isNewCategory = nextSubcategory !== currentSubcategory;
        const categoryClass = isNewCategory ? ' is-new' : '';

        navHtml += `<div class="tutorial-nav-next">
            <a href="${next.url}">
                <span class="tutorial-nav-label">Nächstes →</span>
                <span class="tutorial-nav-link">${next.title}</span>
                <span class="tutorial-nav-category${categoryClass}">${next.subcategoryDisplay}</span>
            </a>
        </div>`;
    } else {
        navHtml += '<div class="tutorial-nav-next"></div>';
    }

    return navHtml;
}

Was ist neu gegenüber der alten Version?

Alt (vor v2.2):

  • Nur innerhalb einer Unterkategorie
  • Am Ende von "HTML Basics" → Sackgasse
  • Kein Hinweis auf Kategorie-Wechsel

Neu (v2.2):

  • Durch alle Tutorials der Hauptkategorie
  • "HTML Basics" → weiter zu "HTML Advanced"
  • Badge zeigt Unterkategorie an
  • .is-new Klasse bei Kategorie-Wechsel

Beispiel-Output:

HTML
<div class="tutorial-nav-prev">
    <a href="/tutorials/html/html-basics/html-listen.html">
        <span class="tutorial-nav-label">← Vorheriges</span>
        <span class="tutorial-nav-link">HTML Listen</span>
        <span class="tutorial-nav-category">HTML Basics</span>
    </a>
</div><div class="tutorial-nav-next">
    <a href="/tutorials/html/html-advanced/html-semantik.html">
        <span class="tutorial-nav-label">Nächstes →</span>
        <span class="tutorial-nav-link">HTML Semantik</span>
        <span class="tutorial-nav-category is-new">HTML Advanced</span>
    </a>
</div>
getAllTutorialsFlat() – Die Hilfsfunktion

Diese Funktion sammelt alle Tutorials einer Hauptkategorie (z.B. "html") als flache, geordnete Liste – über alle Unterkategorien hinweg. Die Reihenfolge folgt der Definition in TUTORIAL_ORDER .

addIdsToHeadingsAndGenerateSidebar() - Sidebar TOC

Diese Funktion macht mehrere Dinge auf einmal:

  1. Findet alle <h2> und <h3> Überschriften
  2. Fügt ihnen IDs hinzu (falls keine vorhanden)
  3. Prüft ob Custom Titles in TOC_CUSTOM_TITLES definiert sind
  4. Erstellt die Sidebar-Links mit Custom oder Original-Titel
JavaScript
function addIdsToHeadingsAndGenerateSidebar(html, filename) {
    const dom = new JSDOM(html);
    const doc = dom.window.document;
    const headings = doc.querySelectorAll('h2, h3');
    
    const anchors = [];
    const usedIds = new Set();
    
    headings.forEach(heading => {
        // Überschriften mit class="no-toc" überspringen
        if (heading.classList.contains('no-toc')) return;
        
        let id = heading.id;
        
        // ID generieren wenn keine vorhanden
        if (!id) {
            id = heading.textContent.trim().toLowerCase()
                .replace(/[äöüÄÖÜß]/g, match => {
                    const map = { 
                        'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 
                        'Ä': 'Ae', 'Ö': 'Oe', 'Ü': 'Ue', 'ß': 'ss' 
                    };
                    return map[match];
                })
                .replace(/[^\w\s-]/g, '')  // Sonderzeichen entfernen
                .replace(/\s+/g, '-');      // Leerzeichen → Bindestriche
            
            // Duplikate vermeiden
            let finalId = id;
            let counter = 1;
            while (usedIds.has(finalId)) {
                finalId = `${id}-${counter}`;
                counter++;
            }
            
            // ID ins Heading einfügen!
            heading.id = finalId;
            id = finalId;
        }
        
        usedIds.add(id);
        
        const level = heading.tagName === 'H2' ? 'toc-level-1' : 'toc-level-2';
        
        // Custom Title verwenden falls vorhanden, sonst Original-Text
        let displayTitle = heading.textContent.trim();
        if (TOC_CUSTOM_TITLES[filename] && TOC_CUSTOM_TITLES[filename][id]) {
            displayTitle = TOC_CUSTOM_TITLES[filename][id];
        }
        
        // Sidebar-Link erstellen
        anchors.push(
            `<li class="${level}"><a href="#${id}">${displayTitle}</a></li>`
        );
    });
    
    return {
        html: dom.serialize(),  // HTML mit IDs!
        anchors: anchors.join('\n')
    };
}

Beispiel Normal:

  • Überschrift: <h2>Was ist CSS?</h2>
  • Wird zu: <h2 id="was-ist-css">Was ist CSS?</h2>
  • Sidebar-Link: <a href="#was-ist-css">Was ist CSS?</a>

Beispiel mit Custom Title:

  • Überschrift: <h2 id="die-top-15-tags">HTML Cheat Sheet - Die Top 15 Tags</h2>
  • Custom Title in TOC_CUSTOM_TITLES: 'die-top-15-tags': 'Top 15 Tags'
  • Sidebar-Link: <a href="#die-top-15-tags">Top 15 Tags</a> (gekürzt!)
Custom Titles für TOC

Lange Überschriften können in der Sidebar zu breit werden. Mit TOC_CUSTOM_TITLES kannst du kurze, prägnante Titel für die Sidebar definieren, während die Original-Überschrift im Haupttext erhalten bleibt:

const TOC_CUSTOM_TITLES = {
    'html-basics.html': {
        'die-top-15-tags': 'Top 15 Tags',
        'html-attribute-erklaert': 'HTML Attribute'
    },
    'css-farben.html': {
        'rgb-rgba-hex': 'Farbformate'
    }
};
Wichtig:

Die ID muss die finale ID sein (nach Slug-Generierung), also z.B. 'die-top-15-tags' nicht 'die-Top-15-Tags' .

Warum Umlaute ersetzen?

IDs wie #für-anfänger können in URLs Probleme machen. Besser: #fuer-anfaenger

generateSidebarNavList() – Gruppierte Tutorial-Liste

Die rechte Sidebar zeigt alle Tutorials der Hauptkategorie , gruppiert nach Unterkategorien:

JavaScript
function generateSidebarNavList(filePath) {
    const category = getCategoryFromPath(filePath);
    const currentUrl = getRelativeUrl(filePath);
    const allTutorials = getAllTutorialsForCategory(category);

    let html = '';

    Object.keys(allTutorials).forEach(subcategoryKey => {
        const group = allTutorials[subcategoryKey];

        // Gruppen-Überschrift
        html += `<li class="sidebar-group-title">${group.displayName}</li>\n`;

        // Tutorials dieser Gruppe
        group.tutorials.forEach(tutorial => {
            const isActive = tutorial.url === currentUrl;
            const activeClass = isActive ? ' class="active"' : '';
            html += `<li><a href="${tutorial.url}"${activeClass}>${tutorial.title}</a></li>\n`;
        });
    });

    return html.trim();
}

Ergebnis in der Sidebar:

HTML Output
<li class="sidebar-group-title">HTML Basics</li>
<li><a href="/tutorials/html/html-basics/html-basics.html">HTML Basics</a></li>
<li><a href="/tutorials/html/html-basics/html-grundgeruest.html">HTML Grundgerüst</a></li>

<li class="sidebar-group-title">HTML Advanced</li>
<li><a href="/tutorials/html/html-advanced/html-semantik.html" class="active">HTML Semantik</a></li>
<li><a href="/tutorials/html/html-advanced/html-formulare.html">HTML Formulare</a></li>
Was ist neu?
  • .sidebar-group-title – Visuelle Gruppierung nach Unterkategorie
  • Zeigt alle Tutorials der Hauptkategorie (nicht nur aktuelle Unterkategorie)
  • Bessere Übersicht beim Navigieren zwischen Unterkategorien

tutorials.json Generierung (NEU in v2.2)

Das Script generiert automatisch eine JSON-Datei mit allen Tutorial-Metadaten:

Dateipfad
/assets/data/tutorials.json

extractHeroExcerpt() – Excerpt aus Hero ziehen

JavaScript
function extractHeroExcerpt(html) {
    const dom = new JSDOM(html);
    const document = dom.window.document;

    // Versuche verschiedene Selektoren
    const selectors = [
        '.hero-subtitle',
        '.hero-text',
        '.hero p',
        '.tutorial-hero p'
    ];

    for (const selector of selectors) {
        const element = document.querySelector(selector);
        if (element) {
            let text = element.textContent.trim();
            // Kürze auf max 160 Zeichen
            if (text.length > 160) {
                text = text.substring(0, 157) + '...';
            }
            return text;
        }
    }
    return null;
}

generateLatestTutorialsJSON() – JSON erstellen

Diese Funktion sammelt alle Tutorial-Metadaten und speichert sie als JSON:

JSON Struktur
{
    "generated": "2025-01-06T12:00:00.000Z",
    "total": 42,
    "all": [...],           // Alle Tutorials
    "latest": [...],        // Die 4 aus LATEST_TUTORIALS
    "byCategory": {         // Gruppiert
        "html": [...],
        "css": [...],
        "javascript": [...]
    }
}
Automatisch bei jedem Build

Die JSON-Datei wird bei jedem npm run generate neu erstellt. Du musst sie nie manuell bearbeiten!

HTML aktualisieren

Die Hauptfunktion updateHtmlFile() koordiniert alle Updates:

JavaScript
function updateHtmlFile(filePath) {
    // HTML-Datei einlesen
    let html = fs.readFileSync(filePath, 'utf8');
    
    // Metadaten sammeln
    const category = getCategoryFromPath(filePath);
    const basename = path.basename(filePath);
    const currentTitle = CUSTOM_TITLES[basename] || extractTitle(html);

    // WICHTIG: ZUERST IDs zu Headings hinzufügen (nur für Tutorial-Seiten)
    let sidebarAnchorsHtml = '';
    if (basename !== 'index.html') {
        const result = addIdsToHeadingsAndGenerateSidebar(html, basename);
        html = result.html;  // Nutze das HTML mit IDs!
        sidebarAnchorsHtml = result.anchors;
    }

    // 1. HEADER AKTUALISIEREN
    html = updateHeader(html);

    // 2. FOOTER AKTUALISIEREN
    html = updateFooter(html);

    // 3. TUTORIAL BUTTONS (nur bei Kategorieseiten!)
    if (basename === 'index.html') {
        html = updateTutorialButtons(html, category);
    }

    // 4. Main Navigation einfügen
    const mainNavItems = generateMainNavigation();
    html = html.replace(
        /<!-- Main-Nav-Start -->.*?<!-- Main-Nav-End -->/s,
        `<!-- Main-Nav-Start -->\n${mainNavItems.join('\n')}\n<!-- Main-Nav-End -->`
    );

    // 5. Breadcrumbs (nur für Tutorial-Seiten)
    const relativePath = path.relative(path.join(__dirname, '..'), filePath);
    const isInTutorials = relativePath.includes('tutorials');
    
    if (isInTutorials) {
        const breadcrumbs = generateBreadcrumbs(filePath, currentTitle);
        html = html.replace(
            /<nav class="breadcrumbs">.*?<\/nav>/s,
            `<nav class="breadcrumbs">${breadcrumbs}</nav>`
        );
    }

    // 6. Latest Tutorials Section (nur Tutorial-Seiten, nicht index.html)
    if (basename !== 'index.html') {
        const latestSection = generateLatestTutorialsSection(category);
        // ... (ersetzt oder fügt "Mehr aus X" Section ein)
    }

    // 7. Prev/Next Navigation (nur Tutorial-Seiten)
    if (basename !== 'index.html') {
        const prevNext = generatePrevNextNav(filePath);
        html = html.replace(
            /<nav class="tutorial-nav">.*?<\/nav>/s,
            `<nav class="tutorial-nav">${prevNext}</nav>`
        );
    }

    // 8. Sidebar Anchors (TOC)
    if (basename !== 'index.html') {
        html = html.replace(
            /<!-- Sidebar-Anchor-Start -->.*?<!-- Sidebar-Anchor-End -->/s,
            `<!-- Sidebar-Anchor-Start -->\n${sidebarAnchorsHtml}\n<!-- Sidebar-Anchor-End -->`
        );
    }

    // 9. Sidebar Navigation List
    if (basename !== 'index.html') {
        const navList = generateSidebarNavList(filePath);
        html = html.replace(
            /<ul class="sidebar-nav-list">.*?<\/ul>/s,
            `<ul class="sidebar-nav-list">\n${navList}\n</ul>`
        );
    }

    // 10. Datei speichern
    fs.writeFileSync(filePath, html, 'utf8');
}
Wichtig: Die Reihenfolge ist entscheidend!
  1. Zuerst IDs zu Headings hinzufügen
  2. Dann Header/Footer ersetzen
  3. Danach alle Navigationen einfügen
  4. Zum Schluss Datei speichern
Warum IDs zuerst?

Die Sidebar-Links verweisen auf IDs #meine-ueberschrift . Diese IDs müssen also existieren, BEVOR die Sidebar generiert wird!

Regex Patterns erklärt

Das Script nutzt Regular Expressions (Regex) , um HTML zu finden und zu ersetzen. Das sieht kompliziert aus, ist aber logisch:

Pattern für Kommentar-Marker

JavaScript
/<!-- Main-Nav-Start -->.*?<!-- Main-Nav-End -->/s

Aufschlüsselung:

  • / → Start des Regex
  • <!-- Main-Nav-Start --> → Findet den Start-Kommentar
  • .*? → Findet ALLES dazwischen (aber so wenig wie möglich)
  • <!-- Main-Nav-End --> → Findet den End-Kommentar
  • /s → "Dotall" Mode - . matched auch Zeilenumbrüche

Beispiel:

HTML
<!-- Main-Nav-Start -->
<li><a href="/tutorials/html/">HTML</a></li>
<li><a href="/tutorials/css/">CSS</a></li>
<!-- Main-Nav-End -->

Das Regex findet ALLES zwischen den Markern und ersetzt es mit neuem HTML!

Pattern für HTML-Tags

JavaScript
/<nav class="breadcrumbs">.*?<\/nav>/s

Aufschlüsselung:

  • <nav class="breadcrumbs"> → Findet das öffnende Tag
  • .*? → Findet den Inhalt
  • <\/nav> → Findet das schließende Tag ( / muss escaped werden!)
  • /s → Dotall Mode

Die Hauptlogik

Ganz am Ende des Scripts steht die Funktion, die alles startet:

JavaScript
function generateAllNavigation() {
    console.log('🚀 Starte Navigation-Generierung...\n');
    
    // 1. Alle HTML-Dateien finden
    const tutorialFiles = findHtmlFiles(tutorialsDir);
    
    console.log(`📄 Gefundene Tutorial-Dateien: ${tutorialFiles.length}\n`);
    
    // 2. Jede Datei aktualisieren
    tutorialFiles.forEach(filePath => {
        const relativePath = path.relative(tutorialsDir, filePath);
        console.log(`  ✓ Aktualisiere: ${relativePath}`);
        updateHtmlFile(filePath);
    });
    
    console.log('\n✅ Navigation erfolgreich generiert!');
}

// Script ausführen!
generateAllNavigation();

Ablauf:

  1. Finde alle HTML-Dateien im /tutorials Ordner
  2. Für jede gefundene Datei: Rufe updateHtmlFile() auf
  3. Zeige Fortschritt im Terminal
  4. Fertig!
Das war's!

Mit npm run build wird das Script ausgeführt und es aktualisiert ALLE Seiten automatisch! 🎉

Best Practices & Tipps

1. Immer vor dem Committen ausführen

Gewöhne dir an, vor jedem Git-Commit npm run build auszuführen. So sind alle Navigationen garantiert aktuell!

2. Teste nach Änderungen

Nach größeren Änderungen am Script:

  • Führe npm run build aus
  • Öffne mehrere Tutorial-Seiten
  • Prüfe Prev/Next, Sidebar, Breadcrumbs
  • Teste die Links!

3. Backup vor Script-Änderungen

Wenn du das Script änderst:

  • Erstelle eine Kopie: generate-navigation-backup.js
  • Teste deine Änderungen auf einer Datei
  • Erst dann auf allen Dateien ausführen

4. Console-Logs nutzen

Füge bei Problemen Console-Logs hinzu:

JavaScript
console.log('Aktuelle Kategorie:', category);
console.log('Gefundene Tutorials:', tutorials);
console.log('Current Index:', currentIndex);

Häufige Probleme

Problem: Script findet keine Dateien

Ursache:

  • Blacklist oder Pfad falsch

Lösung:

  • Prüfe BLACKLIST_FOLDERS
  • Prüfe ob Script in /scripts/ liegt
  • Teste: console.log(tutorialsDir)

Problem: Prev/Next zeigt falsches Tutorial

Ursache:

  • Reihenfolge in TUTORIAL_ORDER falsch

Lösung:

  • Öffne generate-navigation.js
  • Finde die richtige Unterkategorie
  • Korrigiere die Reihenfolge
  • Führe npm run build aus

Problem: Sidebar-Links führen nirgends hin

Ursache:

  • IDs wurden nicht zu Headings hinzugefügt

Lösung:

  • Prüfe ob addIdsToHeadingsAndGenerateSidebar ausgeführt wird
  • Prüfe ob html = result.html verwendet wird
  • Führe npm run build nochmal aus

Problem: Dropdown fehlt eine Kategorie

Ursache:

  • Keine Tutorials in TUTORIAL_ORDER für diese Kategorie

Lösung:

  • Füge Tutorials zu TUTORIAL_ORDER hinzu
  • Oder: Entferne die Kategorie aus CATEGORY_NAMES

Zusammenfassung

Das generate-navigation.js Script ist ein mächtiges Werkzeug, das dir viel manuelle Arbeit abnimmt:

Was es macht:

  • Findet alle HTML-Dateien
  • Erstellt Header & Footer
  • Generiert alle Navigationen
  • Fügt IDs zu Überschriften hinzu
  • Aktualisiert alle Seiten

Was du tun musst:

  • Neue Tutorials in TUTORIAL_ORDER eintragen
  • Optional: Custom-Titel in CUSTOM_TITLES
  • npm run build ausführen
  • Fertig!

Mehr aus DevPanicZone!

Tutorials werden geladen...