fix: resolve REG-04 — frontmatter inline array parser now respects quoted commas (#1695)

Fixes #1694

The inline array parser used .split(',') which ignored quote boundaries,
splitting "a, b" into two items. Replaced with a quote-aware splitter
that tracks single/double quote state.

Updated REG-04 test to assert correct behavior and added coverage for
single-quoted and mixed-quote inline arrays.
This commit is contained in:
Jeremy McSpadden
2026-04-04 14:50:01 -05:00
committed by GitHub
parent 839ea22d06
commit fa57a14ec7
2 changed files with 48 additions and 15 deletions

View File

@@ -8,6 +8,38 @@ const { safeReadFile, normalizeMd, output, error } = require('./core.cjs');
// ─── Parsing engine ───────────────────────────────────────────────────────────
/**
* Split a YAML inline array body on commas, respecting quoted strings.
* e.g. '"a, b", c' → ['a, b', 'c']
*/
function splitInlineArray(body) {
const items = [];
let current = '';
let inQuote = null; // null | '"' | "'"
for (let i = 0; i < body.length; i++) {
const ch = body[i];
if (inQuote) {
if (ch === inQuote) {
inQuote = null;
} else {
current += ch;
}
} else if (ch === '"' || ch === "'") {
inQuote = ch;
} else if (ch === ',') {
const trimmed = current.trim();
if (trimmed) items.push(trimmed);
current = '';
} else {
current += ch;
}
}
const trimmed = current.trim();
if (trimmed) items.push(trimmed);
return items;
}
function extractFrontmatter(content) {
const frontmatter = {};
// Find ALL frontmatter blocks at the start of the file.
@@ -53,8 +85,8 @@ function extractFrontmatter(content) {
// Push new context for potential nested content
stack.push({ obj: current.obj[key], key: null, indent });
} else if (value.startsWith('[') && value.endsWith(']')) {
// Inline array: key: [a, b, c]
current.obj[key] = value.slice(1, -1).split(',').map(s => s.trim().replace(/^["']|["']$/g, '')).filter(Boolean);
// Inline array: key: [a, b, c] — quote-aware split (REG-04 fix)
current.obj[key] = splitInlineArray(value.slice(1, -1));
current.key = null;
} else {
// Simple key: value

View File

@@ -54,21 +54,22 @@ describe('extractFrontmatter', () => {
assert.deepStrictEqual(result.key, ['a', 'b', 'c']);
});
test('handles quoted commas in inline arrays — REG-04 known limitation', () => {
// REG-04: The split(',') on line 53 does NOT respect quotes.
// The parser WILL split on commas inside quotes, producing wrong results.
// This test documents the CURRENT (buggy) behavior.
test('handles quoted commas in inline arrays — REG-04 fixed', () => {
const content = '---\nkey: ["a, b", c]\n---\n';
const result = extractFrontmatter(content);
// Current behavior: splits on ALL commas, producing 3 items instead of 2
// Expected correct behavior would be: ["a, b", "c"]
// Actual current behavior: ["a", "b", "c"] (split ignores quotes)
assert.ok(Array.isArray(result.key), 'should produce an array');
assert.ok(result.key.length >= 2, 'should produce at least 2 items from comma split');
// The bug produces ["a", "b\"", "c"] or similar — the exact output depends on
// how the regex strips quotes after the split.
// We verify the key insight: the result has MORE items than intended (known limitation).
assert.ok(result.key.length > 2, 'REG-04: split produces more items than intended due to quoted comma bug');
assert.deepStrictEqual(result.key, ['a, b', 'c']);
});
test('handles single-quoted commas in inline arrays', () => {
const content = "---\nkey: ['x, y', z]\n---\n";
const result = extractFrontmatter(content);
assert.deepStrictEqual(result.key, ['x, y', 'z']);
});
test('handles mixed quotes in inline arrays', () => {
const content = '---\nkey: ["a, b", \'c, d\', e]\n---\n';
const result = extractFrontmatter(content);
assert.deepStrictEqual(result.key, ['a, b', 'c, d', 'e']);
});
test('returns empty object for no frontmatter', () => {