var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { if (typeof path === "string" && /^\.\.?\//.test(path)) { return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); }); } return path; }; import { marked } from 'marked'; import { Eta } from 'eta'; import { usageTracker } from "./usageTracker.js"; import { extractRawBlocks } from "./extractRawBlocks.js"; import path from 'path'; // Configure marked to preserve HTML in code blocks marked.setOptions({ breaks: false, gfm: true, }); // Configure marked renderer for consistent heading IDs const renderer = new marked.Renderer(); renderer.heading = function (args) { const text = args.text; const level = args.depth; /* node:coverage disable */ const tokens = args.tokens || []; /* node:coverage enable */ const id = text .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''); const innerHTML = this.parser.parseInline(tokens); return `${innerHTML}\n`; }; marked.use({ renderer }); // Helper functions to generate asset paths function getStylePath(doc) { return path.join(doc.path, 'style.css'); } function getScriptPath(doc) { return path.join(doc.path, 'script.js'); } // Helper to extract headings from HTML content function extractHeadingsFromHtml(html) { const headings = []; const headingRegex = /]*>(.*?)<\/h\1>/gi; let match; while ((match = headingRegex.exec(html)) !== null) { const level = parseInt(match[1]); const existingId = match[2]; const htmlContent = match[3]; // Strip HTML tags to get plain text const text = htmlContent.replace(/<[^>]+>/g, '').trim(); // Use existing id or generate one const id = existingId || text .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''); headings.push({ level, text, id }); } return headings; } // Helper to inject IDs into heading tags that don't have them function injectHeadingIds(html, headings) { let result = html; let headingIndex = 0; result = result.replace(/]*)>(.*?)<\/h\1>/gi, (match, level, existingId, attrs, content) => { if (existingId) { // Already has an ID, keep it as is headingIndex++; return match; } // Get the corresponding heading from our extracted list const heading = headings[headingIndex]; headingIndex++; /* node:coverage disable */ if (heading) { // Inject the ID return `${content}`; } return match; /* node:coverage enable */ }); return result; } // Helper to generate error HTML function createErrorHtml(type, errorMessage) { return `
${type} Error: ${errorMessage}
`; } // Build nested TOC structure function buildNestedToc(headings) { const root = []; const stack = []; for (const heading of headings) { const item = { ...heading, children: [] }; while (stack.length > 0 && stack[stack.length - 1].level >= heading.level) { stack.pop(); } if (stack.length === 0) { root.push(item); } else { stack[stack.length - 1].children.push(item); } stack.push(item); } return root; } function hasMarkdown(content) { const trimmedContent = content.trim(); // Check if any line starts with common markdown syntax const lines = content.split('\n'); const hasMarkdownLine = lines.some(line => { const trimmed = line.trim(); return (/^#{1,6}\s/.test(trimmed) || // Headers /^\s*[-*+]\s/.test(line) || // Unordered lists /^\s*\d+\.\s/.test(line) || // Ordered lists /^```/.test(trimmed) || // Code blocks (/^\s*>/.test(line) && /\S/.test(line.replace(/^\s*>/, '')) && !/^\s*>\s*<.*/.test(line)) // Blockquotes with text, not starting with '<' ); }); // If we find markdown syntax, it's markdown (even if it has some HTML) if (hasMarkdownLine) { return true; } // Only treat as HTML (not markdown) if it looks like a complete HTML document // with structural tags like DOCTYPE, , , or const htmlDocPattern = /<(!DOCTYPE html|html|head|body)[>\s]/i; if (htmlDocPattern.test(trimmedContent)) { return false; } // Default to not markdown if no clear indicators return false; } // Helper to evaluate a module string and extract exports async function evaluateModule(code) { const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(code)}`; return import(__rewriteRelativeImportExtension(url)); } export async function baseRender(options) { const { doc, context = {}, type = 'content' } = options; const data = doc.data || {}; const createdAt = doc.createdAt || new Date(); const updatedAt = doc.updatedAt || new Date(); const title = doc.title; const path = doc.path; const meta = { createdAt: new Date(createdAt), updatedAt: new Date(updatedAt), }; const _etaVariables = { title, path, meta, data, server: null, ...context, html: '', markdown: '', style: { content: doc.style || '', inlineTag: doc.style ? `` : '', href: getStylePath(doc), tag: doc.style ? `` : '', }, script: { content: doc.script || '', inlineTag: doc.script ? `` : '', href: getScriptPath(doc), tag: doc.script ? `` : '', }, }; // Create tracked variables first so we can track all template variable access const [etaVariables, getUsageSnapshot] = usageTracker(_etaVariables); // Process title first so server function can access rendered title const eta = new Eta({ autoEscape: false, autoTrim: false, useWith: true, }); _etaVariables.title = await eta.renderStringAsync(_etaVariables.title, etaVariables); // Evaluate server-side JavaScript if present if (doc.server) { try { const moduleExports = await evaluateModule(doc.server.trim()); if (moduleExports.default) { // Call the default export if it's a function if (typeof moduleExports.default === 'function') { const result = await moduleExports.default(etaVariables); _etaVariables.server = result; } else { _etaVariables.server = moduleExports.default; } } else { // No default export, use all exports _etaVariables.server = moduleExports; } } catch (error) { console.error('Server function evaluation error:', error); // Fail gracefully: keep server as null and continue rendering content. // This allows templates like `<%= server ? ... : "No server data" %>`. _etaVariables.server = null; } } if (!doc.content) { const capturedUsage = getUsageSnapshot().usage; return { ..._etaVariables, variableUsage: capturedUsage }; } // Check if content is markdown const isMd = hasMarkdown(doc.content); // First Eta pass - process everything except meta.toc and meta.headings let processedContent = doc.content; let templateError = ''; // Extract <%raw%>...<%endraw%> blocks before Eta processing const firstRaw = extractRawBlocks(processedContent); try { processedContent = firstRaw.restore(await eta.renderStringAsync(firstRaw.content, etaVariables)); } catch (error) { templateError = error instanceof Error ? error.message : `Unknown ${type} error`; // Capture usage before spreading to avoid tracking the spread itself const capturedUsage = getUsageSnapshot().usage; // Return error HTML instead of broken content const renderdDoc = { ..._etaVariables, markdown: doc.content, html: createErrorHtml(type, templateError), variableUsage: capturedUsage, }; return renderdDoc; } // Parse markdown/HTML to extract headings after first Eta pass const intermediateHtml = isMd ? await marked(processedContent) : processedContent; const processedHeadings = extractHeadingsFromHtml(intermediateHtml); const nestedToc = buildNestedToc(processedHeadings); // Create enhanced meta with TOC and headings const enhancedMeta = { ...meta, toc: nestedToc, headings: processedHeadings, }; // Check if content references meta.toc or meta.headings - only do second pass if needed const needsSecondPass = /meta\.(toc|headings)/.test(processedContent); if (needsSecondPass) { // Second Eta pass - now with complete meta (toc and headings available) const secondRaw = extractRawBlocks(processedContent); try { processedContent = secondRaw.restore(await eta.renderStringAsync(secondRaw.content, { ...etaVariables, meta: enhancedMeta, })); } catch (error) { const errorMsg = error instanceof Error ? error.message : `Unknown ${type} error`; // Capture usage before spreading to avoid tracking the spread itself const capturedUsage = getUsageSnapshot().usage; // Return error HTML instead of broken content const renderdDoc = { ..._etaVariables, meta: enhancedMeta, markdown: processedContent, html: createErrorHtml(type, errorMsg), variableUsage: capturedUsage, }; return renderdDoc; } } // Render final HTML (markdown if needed) let html = !isMd ? processedContent : `
${await marked(processedContent)}
`; // For non-markdown content, inject heading IDs if (!isMd) { html = injectHeadingIds(html, processedHeadings); } // Capture usage before spreading to avoid tracking the spread itself const capturedUsage = getUsageSnapshot().usage; const renderDoc = { ..._etaVariables, meta: enhancedMeta, markdown: processedContent, html, variableUsage: capturedUsage, }; return renderDoc; } //# sourceMappingURL=base.js.map