diff --git a/src/build.ts b/src/build.ts index de974f52f..37546c740 100644 --- a/src/build.ts +++ b/src/build.ts @@ -72,7 +72,7 @@ export async function build( effects.output.write(`${faint("parse")} ${sourcePath} `); const start = performance.now(); const source = await readFile(sourcePath, "utf8"); - const page = parseMarkdown(source, options); + const page = parseMarkdown(source, {...options, build: true}); if (page.data.draft) { effects.logger.log(faint("(skipped)")); continue; diff --git a/src/frontMatter.ts b/src/frontMatter.ts index b79ab7b30..35f7f7de7 100644 --- a/src/frontMatter.ts +++ b/src/frontMatter.ts @@ -1,3 +1,4 @@ +import matter from "gray-matter"; import {normalizeTheme} from "./config.js"; export interface FrontMatter { @@ -12,6 +13,31 @@ export interface FrontMatter { sql?: {[key: string]: string}; } +interface FrontMatterException { + reason: string; + mark: {buffer: string}; +} + +export function readFrontMatter(input: string, build: boolean): {content: string; data: FrontMatter} { + try { + const {content, data} = matter(input, {}); + return {content, data: normalizeFrontMatter(data)}; + } catch (error) { + if (!build && "mark" in (error as any)) { + const { + reason, + mark: {buffer} + } = error as FrontMatterException; + return { + content: `
Invalid front matter\n${reason}
+
${String(buffer).slice(0, -1)}
\n\n${input.slice(buffer.length + 6)}`, + data: {} + }; + } + throw error; + } +} + export function normalizeFrontMatter(spec: any = {}): FrontMatter { const frontMatter: FrontMatter = {}; if (spec == null || typeof spec !== "object") return frontMatter; diff --git a/src/markdown.ts b/src/markdown.ts index 1800d85f2..e4304cbd1 100644 --- a/src/markdown.ts +++ b/src/markdown.ts @@ -1,7 +1,6 @@ /* eslint-disable import/no-named-as-default-member */ import {createHash} from "node:crypto"; import {extname} from "node:path/posix"; -import matter from "gray-matter"; import he from "he"; import MarkdownIt from "markdown-it"; import type {RuleCore} from "markdown-it/lib/parser_core.js"; @@ -11,7 +10,7 @@ import MarkdownItAnchor from "markdown-it-anchor"; import type {Config} from "./config.js"; import {mergeStyle} from "./config.js"; import type {FrontMatter} from "./frontMatter.js"; -import {normalizeFrontMatter} from "./frontMatter.js"; +import {readFrontMatter} from "./frontMatter.js"; import {rewriteHtmlPaths} from "./html.js"; import {parseInfo} from "./info.js"; import type {JavaScriptNode} from "./javascript/parse.js"; @@ -305,6 +304,7 @@ export interface ParseOptions { head?: Config["head"]; header?: Config["header"]; footer?: Config["footer"]; + build?: boolean; } export function createMarkdownIt({ @@ -327,9 +327,8 @@ export function createMarkdownIt({ } export function parseMarkdown(input: string, options: ParseOptions): MarkdownPage { - const {md, path} = options; - const {content, data: frontMatter} = matter(input, {}); - const data = normalizeFrontMatter(frontMatter); + const {md, path, build} = options; + const {content, data} = readFrontMatter(input, build); const code: MarkdownCode[] = []; const context: ParseContext = {code, startLine: 0, currentLine: 0, path}; const tokens = md.parse(content, context);