UNPKG

@zeix/ui-element

Version:

UIElement - minimal reactive framework based on Web Components

176 lines (145 loc) 5.09 kB
import { readFile, writeFile, readdir } from "fs/promises"; import { join } from "path"; import matter from "gray-matter"; import { marked } from "marked"; import { PAGES_DIR, INCLUDES_DIR, LAYOUT_FILE, MENU_FILE, OUTPUT_DIR, } from "./config"; import { transformCodeBlocks } from "./transform-code-blocks"; import { replaceAsync } from "./replace-async"; import { generateMenu } from "./generate-menu"; import { generateSitemap } from "./generate-sitemap"; marked.setOptions({ gfm: true, // Enables tables, task lists, and strikethroughs breaks: true, // Allows line breaks without needing double spaces }); /* const PAGE_LIST_FILE = './docs-src/.file-pages.json'; let lastPageList: string[] = []; // Load last known page list const loadPageList = async () => { try { lastPageList = JSON.parse(await readFile(PAGE_LIST_FILE, 'utf8')); } catch { lastPageList = []; } }; // Save the new page list const savePageList = async (pageList: string[]) => { await writeFile(PAGE_LIST_FILE, JSON.stringify(pageList, null, 2), 'utf8'); }; */ // Function to load HTML includes const loadIncludes = async (html: string): Promise<string> => { return await replaceAsync( html, /{{ include '(.+?)' }}/g, async (_, filename) => { const includePath = join(INCLUDES_DIR, filename); try { // console.log(`📄 Loading include: ${filename}`); return await readFile(includePath, "utf8"); } catch { console.warn(`⚠️ Warning: Missing include file: ${filename}`); return ""; } }, ); }; const processMarkdownFile = async (filename: string) => { const filePath = join(PAGES_DIR, filename); const mdContent = await readFile(filePath, "utf8"); console.log(`📂 Processing: ${filename}`); // Parse frontmatter and Markdown content const { data: frontmatter, content } = matter(mdContent); // console.log(`📝 Frontmatter:`, frontmatter); // Convert Markdown content to HTML and process code blocks const { processedMarkdown, codeBlockMap } = await transformCodeBlocks(content); // Override headings to add permalinks const renderer = { heading({ tokens, depth }) { const text = this.parser.parseInline(tokens); const slug = text .toLowerCase() .replace(/[^\w\- ]+/g, "") .trim() .replace(/\s+/g, "-"); return ` <h${depth} id="${slug}"> <a name="${slug}" class="anchor" href="#${slug}"> <span class="permalink">🔗</span> </a> ${text} </h${depth}>`; }, }; marked.use({ renderer }); let htmlContent = await marked.parse(processedMarkdown); // console.log(`📜 Converted Markdown to HTML:`, htmlContent); // Replace placeholders with actual Shiki code blocks codeBlockMap.forEach((code, key) => { htmlContent = htmlContent.replace( new RegExp(`(<p>\\s*${key}\\s*</p>)`, "g"), code, ); }); // Load layout template let layout = await readFile(LAYOUT_FILE, "utf8"); // console.log(`📄 Layout before processing:`, layout); // Use regex to match the correct <li> by href and add class="active" let menuHtml = await readFile(MENU_FILE, "utf8"); const url = filename.replace(".md", ".html"); menuHtml = menuHtml.replace( new RegExp(`(<a href="${url}")`, "g"), '$1 class="active"', ); layout = layout.replace("{{ include 'menu.html' }}", menuHtml); // 1️⃣ Process includes FIRST layout = await loadIncludes(layout); // console.log(`📎 After Includes Processing:`, layout); // 2️⃣ Replace {{ content }} SECOND layout = layout.replace("{{ content }}", htmlContent); // console.log(`✅ After Content Injection:`, layout); // 3️⃣ Replace frontmatter placeholders LAST layout = layout.replace(/{{ (.*?) }}/g, (_, key) => { // console.log(`🔍 Replacing: {{ ${key} }} →`, frontmatter[key] || ''); if (key === "url") return url; return frontmatter[key] || ""; }); // Save output file await writeFile(join(OUTPUT_DIR, url), layout, "utf8"); console.log(`✅ Generated: ${url}`); return { filename: url, title: frontmatter.title || "Untitled", emoji: frontmatter.emoji || "📄", description: frontmatter.description || "", url, }; }; // Main function const run = async () => { // console.log('🔄 Checking for page list changes...'); const files = await readdir(PAGES_DIR); const mdFiles = files.filter((file) => file.endsWith(".md")); /* // Check if page list has changed if (JSON.stringify(mdFiles) !== JSON.stringify(lastPageList)) { console.log(`📄 Page list changed! Rebuilding menu and sitemap.`); await savePageList(mdFiles); } else { console.log(`⚡ No page list changes detected.`); } */ // Process all Markdown files const pages = await Promise.all(mdFiles.map(processMarkdownFile)); // Only regenerate menu and sitemap if the page list changed // if (JSON.stringify(mdFiles) !== JSON.stringify(lastPageList)) { await generateMenu(pages); await generateSitemap(pages); // } console.log("✨ All pages generated!"); }; // await loadPageList(); run();