feat(baoyu-markdown-to-html): add HTML meta tags and quote stripping for frontmatter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jim Liu 宝玉
2026-02-03 11:49:07 -06:00
parent 502d2448b2
commit 876c6332f6
2 changed files with 52 additions and 10 deletions
+14 -3
View File
@@ -126,7 +126,18 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
const { frontmatter, body } = parseFrontmatter(content); const { frontmatter, body } = parseFrontmatter(content);
let title = options?.title ?? frontmatter.title ?? ''; const stripQuotes = (s?: string): string => {
if (!s) return '';
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
return s.slice(1, -1);
}
if ((s.startsWith('\u201c') && s.endsWith('\u201d')) || (s.startsWith('\u2018') && s.endsWith('\u2019'))) {
return s.slice(1, -1);
}
return s;
};
let title = options?.title ?? stripQuotes(frontmatter.title) ?? '';
if (!title) { if (!title) {
const lines = body.split('\n'); const lines = body.split('\n');
for (const line of lines) { for (const line of lines) {
@@ -138,8 +149,8 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
} }
} }
if (!title) title = path.basename(markdownPath, path.extname(markdownPath)); if (!title) title = path.basename(markdownPath, path.extname(markdownPath));
const author = frontmatter.author || ''; const author = stripQuotes(frontmatter.author);
let summary = frontmatter.description || frontmatter.summary || ''; let summary = stripQuotes(frontmatter.description) || stripQuotes(frontmatter.summary);
if (!summary) { if (!summary) {
const lines = body.split('\n'); const lines = body.split('\n');
@@ -708,14 +708,28 @@ function normalizeThemeCss(css: string): string {
return stripOutputScope(css); return stripOutputScope(css);
} }
function buildHtmlDocument(title: string, css: string, html: string): string { interface HtmlDocumentMeta {
return [ title: string;
author?: string;
description?: string;
}
function buildHtmlDocument(meta: HtmlDocumentMeta, css: string, html: string): string {
const lines = [
"<!doctype html>", "<!doctype html>",
"<html>", "<html>",
"<head>", "<head>",
' <meta charset="utf-8" />', ' <meta charset="utf-8" />',
' <meta name="viewport" content="width=device-width, initial-scale=1" />', ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
` <title>${title}</title>`, ` <title>${meta.title}</title>`,
];
if (meta.author) {
lines.push(` <meta name="author" content="${meta.author}" />`);
}
if (meta.description) {
lines.push(` <meta name="description" content="${meta.description}" />`);
}
lines.push(
` <style>${css}</style>`, ` <style>${css}</style>`,
"</head>", "</head>",
"<body>", "<body>",
@@ -723,8 +737,9 @@ function buildHtmlDocument(title: string, css: string, html: string): string {
html, html,
" </div>", " </div>",
"</body>", "</body>",
"</html>", "</html>"
].join("\n"); );
return lines.join("\n");
} }
async function inlineCss(html: string): Promise<string> { async function inlineCss(html: string): Promise<string> {
@@ -814,6 +829,7 @@ async function main(): Promise<void> {
const markdown = fs.readFileSync(inputPath, "utf-8"); const markdown = fs.readFileSync(inputPath, "utf-8");
const renderer = initRenderer({}); const renderer = initRenderer({});
const { yamlData } = renderer.parseFrontMatterAndContent(markdown);
const { html: baseHtml, readingTime: readingTimeResult } = renderMarkdown( const { html: baseHtml, readingTime: readingTimeResult } = renderMarkdown(
markdown, markdown,
renderer renderer
@@ -823,8 +839,23 @@ async function main(): Promise<void> {
content = removeFirstHeading(content); content = removeFirstHeading(content);
} }
const title = path.basename(outputPath, ".html"); const stripQuotes = (s?: string): string | undefined => {
const html = buildHtmlDocument(title, css, content); if (!s) return s;
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
return s.slice(1, -1);
}
if ((s.startsWith('\u201c') && s.endsWith('\u201d')) || (s.startsWith('\u2018') && s.endsWith('\u2019'))) {
return s.slice(1, -1);
}
return s;
};
const meta: HtmlDocumentMeta = {
title: stripQuotes(yamlData.title) || path.basename(outputPath, ".html"),
author: stripQuotes(yamlData.author),
description: stripQuotes(yamlData.description) || stripQuotes(yamlData.summary),
};
const html = buildHtmlDocument(meta, css, content);
const inlinedHtml = normalizeInlineCss(await inlineCss(html)); const inlinedHtml = normalizeInlineCss(await inlineCss(html));
const finalHtml = modifyHtmlStructure(inlinedHtml); const finalHtml = modifyHtmlStructure(inlinedHtml);