/** * Konvertiert WordPress HTML-Exports in saubere Markdown-Dateien * mit Frontmatter fuer das Next.js-Projekt. */ import fs from "fs"; import path from "path"; import { NodeHtmlMarkdown } from "node-html-markdown"; const BACKUP_POSTS = path.resolve("backup/content/posts"); const BACKUP_PAGES = path.resolve("backup/content/pages"); const OUT_GUIDES = path.resolve("app/src/content/guides"); const OUT_PAGES = path.resolve("app/src/content/pages"); // Duplikate/veraltete Slugs (die mit Datum-Suffix) const SKIP_SLUGS = [ "guide-1-pla-perfekt-einstellen-2026-03-25", "guide-2-stringing-reduzieren-2026-03-25", "guide-1-warping-vermeiden-2026-03-26", "guide-2-petg-ohne-frust-2026-03-26", ]; // Kategorie-Zuordnung basierend auf Slug-Patterns const categorize = (slug, title) => { const s = slug.toLowerCase(); const t = title.toLowerCase(); if (s.includes("guide-orcaslicer") || s.includes("guide-cura") || s.includes("guide-bambu") || s.includes("guide-prusaslicer") || s.includes("slicer")) return "Slicer"; if (s.includes("pla") || s.includes("petg") || s.includes("tpu") || s.includes("asa") || s.includes("abs") || s.includes("nylon") || s.includes("carbon") || s.includes("resin") || s.includes("filament") || s.includes("bed-adhesion")) return "Materialien"; if (s.includes("stringing") || s.includes("warping") || s.includes("unterextrusion") || s.includes("layer-separation") || s.includes("elefantenfuss") || s.includes("verstopfte")) return "Fehlerbehebung"; if (s.includes("retraction") || s.includes("flow-rate") || s.includes("pressure-advance") || s.includes("input-shaping") || s.includes("temperaturturm") || s.includes("speed-tower") || s.includes("erste-schicht") || s.includes("druckbett-leveln")) return "Kalibrierung"; if (s.includes("adaptive") || s.includes("modifier") || s.includes("ironing") || s.includes("fuzzy") || s.includes("multi-material") || s.includes("klipper")) return "Fortgeschritten"; if (s.includes("erstes-modell") || s.includes("support") || s.includes("infill") || s.includes("duesenwechsel") || s.includes("druckzeit") || s.includes("masshaltigkeit") || s.includes("bruecken") || s.includes("nachbearbeiten") || s.includes("gridfinity") || s.includes("naht")) return "Grundlagen"; return "Allgemein"; }; // Schwierigkeitsgrad const difficulty = (slug, category) => { if (slug.includes("erstes-modell") || slug.includes("erste-schicht") || slug.includes("druckbett-leveln")) return "einsteiger"; if (category === "Fortgeschritten" || slug.includes("klipper") || slug.includes("pressure-advance") || slug.includes("input-shaping") || slug.includes("carbon") || slug.includes("nylon-pa")) return "experte"; return "fortgeschritten"; }; const nhm = new NodeHtmlMarkdown({ keepDataImages: false, useLinkReferenceDefinitions: false, }); const processFile = (filePath, outDir) => { const raw = fs.readFileSync(filePath, "utf-8"); // Frontmatter extrahieren const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); if (!fmMatch) return null; const fmBlock = fmMatch[1]; const htmlContent = fmMatch[2].trim(); // Felder parsen const titleMatch = fmBlock.match(/title:\s*"(.+?)"/); const slugMatch = fmBlock.match(/slug:\s*"(.+?)"/); const excerptMatch = fmBlock.match(/excerpt:\s*"(.*)"/); const title = titleMatch?.[1] ?? path.basename(filePath, ".html"); const slug = slugMatch?.[1] ?? path.basename(filePath, ".html"); const excerpt = excerptMatch?.[1] ?? ""; if (SKIP_SLUGS.includes(slug)) { console.log(` SKIP (Duplikat): ${slug}`); return null; } // HTML -> Markdown let markdown = nhm.translate(htmlContent); // Cleanup: Elementor-Artefakte, leere Divs, etc. markdown = markdown .replace(/\[vc_[^\]]*\]/g, "") .replace(/\[\/vc_[^\]]*\]/g, "") .replace(/\[elementor[^\]]*\]/g, "") .replace(/\n{3,}/g, "\n\n") .trim(); const cat = categorize(slug, title); const diff = difficulty(slug, cat); const frontmatter = [ "---", `title: "${title}"`, `slug: "${slug}"`, `category: "${cat}"`, `difficulty: "${diff}"`, `excerpt: "${excerpt}"`, "---", ].join("\n"); const output = `${frontmatter}\n\n${markdown}\n`; const outPath = path.join(outDir, `${slug}.md`); fs.writeFileSync(outPath, output, "utf-8"); console.log(` OK: ${slug} (${cat}, ${diff})`); return slug; }; // Main console.log("=== WordPress -> Markdown Konvertierung ===\n"); fs.mkdirSync(OUT_GUIDES, { recursive: true }); fs.mkdirSync(OUT_PAGES, { recursive: true }); // Posts -> Guides console.log("Posts -> Guides:"); const postFiles = fs.readdirSync(BACKUP_POSTS).filter((f) => f.endsWith(".html")); let postCount = 0; for (const file of postFiles) { const result = processFile(path.join(BACKUP_POSTS, file), OUT_GUIDES); if (result) postCount++; } console.log(`\n${postCount} Guides konvertiert.\n`); // Pages console.log("Pages:"); const pageFiles = fs.readdirSync(BACKUP_PAGES).filter((f) => f.endsWith(".html")); let pageCount = 0; for (const file of pageFiles) { const result = processFile(path.join(BACKUP_PAGES, file), OUT_PAGES); if (result) pageCount++; } console.log(`\n${pageCount} Pages konvertiert.`); console.log(`\nFertig! Output: ${OUT_GUIDES} und ${OUT_PAGES}`);