* test(#2977): prove extractFrontmatter returns {} on a leading BOM Failing-first regression for #2977. extractFrontmatter's startsWith('---') byte-0 check fails on any leading byte, so a UTF-8 BOM (Windows PowerShell/Out-File, several editors) makes every frontmatter field silently disappear. Rows 1-2 assert BOM-prefixed frontmatter parses identically to no-BOM (incl. BOM+CRLF); Row 3 guards the no-frontmatter negative space; Row 4 covers STATE/PLAN/SUMMARY/UAT artifact shapes; Row 5 is the control. * fix(#2977): tolerate a leading UTF-8 BOM before the frontmatter fence extractFrontmatter's byte-0 startsWith('---') fence check failed on any leading byte, so a UTF-8 BOM (\uFEFF) — written by default by Windows PowerShell `>`/Out-File (PS 5.1) and several editors — made every frontmatter field silently disappear. STATE.md/ROADMAP.md/PLAN.md/UAT.md/SUMMARY.md all lost phase/status/name with no error, no warning. Strip a single leading BOM before the fence check. The rest of the function is unchanged — the BOM is one codepoint, and removing it restores byte-0 alignment. Negative space preserved: BOM + no-frontmatter and BOM + thematic-break Markdown both still return {} (no false diagnostic). Scope: BOM only; the generalized 'arbitrary content before the fence' fork (tolerate vs diagnose) is a separate product-intent decision, surfaced in the PR. * chore(#2977): add changeset fragment * chore(#2977): backfill changeset PR number 3076 --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -204,6 +204,18 @@ function parseYamlRegion(yaml: string): Frontmatter {
|
||||
* hold only an in-memory string; those dedup on a content digest instead.
|
||||
*/
|
||||
function extractFrontmatter(content: string, sourcePath?: string): Frontmatter {
|
||||
// #2977: tolerate a single leading UTF-8 BOM (\uFEFF), which Windows tooling
|
||||
// (PowerShell `>`/`Out-File` on PS 5.1, several editors) writes by default. Without this
|
||||
// strip, the byte-0 `startsWith('---')` fence check below fails on the BOM and the whole
|
||||
// parse collapses to {} — every frontmatter field silently disappears, and the engine
|
||||
// proceeds as though the file had no frontmatter at all. The BOM is a single codepoint;
|
||||
// stripping it here restores byte-0 alignment so the rest of the function is unchanged.
|
||||
// Scope: BOM only. Arbitrary non-BOM content before the fence (leading whitespace/blank
|
||||
// line/comment) is a separate product-intent decision (tolerate vs diagnose) left to a
|
||||
// future change — this fix does not broaden the byte-0 fence rule beyond the BOM.
|
||||
if (content.charCodeAt(0) === 0xFEFF) {
|
||||
content = content.slice(1);
|
||||
}
|
||||
// Match frontmatter only at byte 0 — a `---` block later in the document
|
||||
// body (YAML examples, horizontal rules) must never be treated as frontmatter.
|
||||
const headerEnd = content.startsWith('---\r\n') ? 5 : content.startsWith('---\n') ? 4 : -1;
|
||||
|
||||
Reference in New Issue
Block a user