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...