import fs from "node:fs"; import path from "node:path"; const repoRoot = process.cwd(); const legacyHtmlRoot = path.join(repoRoot, "archive-source"); const cssRoot = path.join(repoRoot, "css"); const themeCssPath = path.join(cssRoot, "theme.css"); const siteCssPath = path.join(cssRoot, "site.css"); function read(filePath) { return fs.readFileSync(filePath, "utf8"); } function write(filePath, value) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, value); } function walkHtml(dir) { return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { return walkHtml(fullPath); } return entry.isFile() && entry.name.endsWith(".html") ? [fullPath] : []; }); } function extractStyle(html, id) { return html.match(new RegExp(`]*id=["']${id}["'][^>]*>([\\s\\S]*?)<\\/style>`))?.[1].trim() ?? ""; } function extractAnonymousShortcodeCss(html) { return ( Array.from(html.matchAll(/([\s\S]*?)<\/style>/g)) .map((match) => match[1].trim()) .find((body) => body.includes(".su-column-size-1-4")) ?? "" ); } function buildCssFiles() { const aboutHtml = read(path.join(legacyHtmlRoot, "about.html")); const indexHtml = read(path.join(legacyHtmlRoot, "index.html")); const aboutTheme = extractStyle(aboutHtml, "parabola-style-inline-css"); const indexTheme = extractStyle(indexHtml, "parabola-style-inline-css"); if (!aboutTheme || !indexTheme) { return false; } const marker = "/* Parabola Custom CSS */"; const indexExtra = indexTheme.includes(marker) ? indexTheme.slice(0, indexTheme.indexOf(marker)).replace(aboutTheme.slice(0, aboutTheme.indexOf(marker)), "").trim() : ""; const customBackground = extractStyle(aboutHtml, "custom-background-css"); const headerFix = extractStyle(aboutHtml, "site-custom-css"); const shortcodeCss = extractAnonymousShortcodeCss(aboutHtml); write( themeCssPath, [ "/* Extracted from the exported Parabola inline theme settings. */", aboutTheme, "", ].join("\n") ); write( siteCssPath, [ "/* Shared site fixes extracted from repeated inline styles. */", customBackground, "", headerFix, "", "/* Shortcodes Ultimate column rule used by exported content. */", shortcodeCss, "", "/* Homepage slider/frontpage settings from the original export. */", indexExtra, "", ] .filter((section) => section !== "") .join("\n") ); return true; } function ensureThemeLinks(html) { if (html.includes("href='/css/theme.css'") || html.includes('href="/css/theme.css"')) { return html; } const links = [ "", "", ].join("\n"); return html.replace( /(]*>\s*)/, `$1${links}\n` ); } function removeExtractedInlineStyles(html) { return html .replace(/\n?]*id=["']parabola-style-inline-css["'][^>]*>[\s\S]*?<\/style>\s*/g, "\n") .replace(/\n?]*id=["']custom-background-css["'][^>]*>[\s\S]*?<\/style>\s*/g, "\n") .replace(/\n?]*id=["']site-custom-css["'][^>]*>[\s\S]*?<\/style>\s*/g, "\n") .replace(/\n?\s*\.su-column-size-1-4\{[\s\S]*?<\/style>\s*/g, "\n"); } const wroteCssFiles = buildCssFiles(); let updated = 0; for (const filePath of walkHtml(legacyHtmlRoot)) { const original = read(filePath); const next = removeExtractedInlineStyles(ensureThemeLinks(original)); if (next !== original) { write(filePath, next); updated += 1; } } console.log( wroteCssFiles ? "Extracted repeated inline CSS to css/theme.css and css/site.css." : "Inline theme CSS was already extracted; kept existing css/theme.css and css/site.css." ); console.log(`Updated ${updated} HTML files.`);