diff --git a/src/review/content-lane/duplicates.ts b/src/review/content-lane/duplicates.ts index b7636be56e..3470ea675f 100644 --- a/src/review/content-lane/duplicates.ts +++ b/src/review/content-lane/duplicates.ts @@ -55,11 +55,11 @@ function isBlockScalarHeader(raw: string): boolean { } function unquoteYamlScalar(value: string): string { - const trimmed = value.trim(); + const trimmed = stripYamlComment(value); if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { return trimmed.slice(1, -1).trim(); } - return trimmed.replace(/\s+#.*$/, "").trim(); + return trimmed.trim(); } /** diff --git a/src/review/content-lane/source-evidence.ts b/src/review/content-lane/source-evidence.ts index 1b65971a17..a06eefb560 100644 --- a/src/review/content-lane/source-evidence.ts +++ b/src/review/content-lane/source-evidence.ts @@ -107,11 +107,11 @@ function unquoteYamlValue(value: string): string { } function unquoteYamlScalar(value: string): string { - const trimmed = value.trim(); + const trimmed = stripYamlComment(value); if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { return trimmed.slice(1, -1).trim(); } - return trimmed.replace(/\s+#.*$/, "").trim(); + return trimmed.trim(); } // A YAML block-scalar header indicator: `|` or `>` with an optional chomping (`+`/`-`) and/or a single indentation @@ -186,6 +186,10 @@ function parseSimpleFrontmatter(source: string): Record { return fields; } +export const __sourceEvidenceInternals = { + parseSimpleFrontmatter, +}; + function frontmatterBlock(source: string): string { const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(String(source || "")); return match?.[1] || ""; diff --git a/test/unit/content-lane-frontmatter-inline-comment.test.ts b/test/unit/content-lane-frontmatter-inline-comment.test.ts new file mode 100644 index 0000000000..b95e05970a --- /dev/null +++ b/test/unit/content-lane-frontmatter-inline-comment.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; +import { parseSimpleFrontmatter as parseDuplicateFrontmatter } from "../../src/review/content-lane/duplicates"; +import { __sourceEvidenceInternals } from "../../src/review/content-lane/source-evidence"; + +const quotedWithComment = '---\ntitle: "My Skill" # published 2024\n---\n'; + +describe("content-lane frontmatter scalar comments", () => { + it("removes an inline comment before unquoting in the duplicate parser", () => { + expect(parseDuplicateFrontmatter(quotedWithComment).title).toBe("My Skill"); + }); + + it("removes an inline comment before unquoting in the source-evidence parser", () => { + expect(__sourceEvidenceInternals.parseSimpleFrontmatter(quotedWithComment).title).toBe("My Skill"); + }); + + it("preserves existing scalar behavior in both parsers", () => { + const parsers = [parseDuplicateFrontmatter, __sourceEvidenceInternals.parseSimpleFrontmatter]; + + for (const parse of parsers) { + expect(parse('---\ntitle: plain # note\n---\n').title).toBe("plain"); + expect(parse('---\ntitle: "quoted"\n---\n').title).toBe("quoted"); + expect(parse("---\ntitle: unadorned\n---\n").title).toBe("unadorned"); + expect(parse("---\ntitle: 'single quoted' # note\n---\n").title).toBe("single quoted"); + } + }); +});