/**
* 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}`);