Fix: unescape YAML quote escapes in DESIGN.md frontmatter scalars (#473)

* Fix: unescape YAML quote escapes in DESIGN.md frontmatter scalars (#428)

parseScalar() stripped a double-quoted scalar's outer quotes without
processing the backslash escapes inside, so a font stack that quotes a
multi-word family the CSS way, e.g.

  fontFamily: "\"IBM Plex Sans\", system-ui, sans-serif"

reached allowedFonts as '\"ibm plex sans' and design-system-font flagged
fonts DESIGN.md declares. Also collapses the doubled-quote escape in
single-quoted scalars and keeps a lone quote literal instead of slicing
it to an empty string. Applied to both copies of the parser
(cli/engine/design-system.mjs and skill/scripts/lib/design-parser.mjs).

Co-authored-by: Cursor Agent (AI-assisted change, reviewed and directed
by a maintainer)

* Decode YAML hex and Unicode escapes in double-quoted scalars

Review follow-up: the escape scanner only handled the simple set, so
\xNN, \uNNNN, and \UNNNNNNNN sequences stayed encoded and an escaped
token like "\x23b8422e" never matched #b8422e in CSS. Decode validated
hex escapes in both parser copies; malformed or out-of-range sequences
stay literal. Regression coverage for all three forms.

Co-authored-by: Cursor Agent (AI-assisted change, reviewed and directed
by a maintainer)

* Complete the YAML 1.2 double-quote escape set

Review follow-up: the escape map omitted the escaped space (\ ) and
non-breaking space (\_) forms, so fonts declared with them kept a
literal backslash in allowedFonts and their CSS declarations were
reported as undeclared. Map the full spec 5.7 set (\a \b \v \f \e
\N \L \P included) in both parser copies instead of chasing one escape
at a time. Regression coverage for both named forms.

Co-authored-by: Cursor Agent (AI-assisted change, reviewed and directed
by a maintainer)
This commit is contained in:
Abdul Wahab
2026-08-03 14:53:36 -07:00
committed by GitHub
parent df09de3676
commit b33feacbe9
4 changed files with 242 additions and 4 deletions
+59
View File
@@ -142,6 +142,65 @@ Prose.
assert.equal(model.frontmatter.colors['brand-gold'], '#d9a531');
assert.equal(model.frontmatter.rounded['"2xl"'], undefined);
});
it('unescapes quote escapes inside quoted scalars (issue #428)', () => {
const md = `---
typography:
body:
fontFamily: "\\"IBM Plex Sans\\", system-ui, sans-serif"
name: 'It''s quiet'
empty: "
---
# Design System: Escaped
## 1. Overview
Prose.
`;
const model = parseDesignMd(md);
// YAML double-quoted scalars process backslash escapes.
assert.equal(model.frontmatter.typography.body.fontFamily, '"IBM Plex Sans", system-ui, sans-serif');
// Single-quoted scalars escape the quote by doubling it.
assert.equal(model.frontmatter.name, "It's quiet");
// A lone quote satisfies startsWith and endsWith at once; keep it literal
// instead of slicing it into an empty string.
assert.equal(model.frontmatter.empty, '"');
});
it('decodes hex, Unicode, and whitespace escapes in double-quoted scalars', () => {
const md = `---
colors:
accent: "\\x23b8422e"
typography:
accent:
fontFamily: "S\\u00f6hne, sans-serif"
label:
fontFamily: "IBM\\ Plex\\ Serif, serif"
mono:
fontFamily: "Space\\_Grotesk, sans-serif"
emoji: "\\U0001F44D"
bad-hex: "\\xZZ nope"
bad-range: "\\UFFFFFFFF nope"
---
# Design System: Hex Escapes
## 1. Overview
Prose.
`;
const model = parseDesignMd(md);
assert.equal(model.frontmatter.colors.accent, '#b8422e');
assert.equal(model.frontmatter.typography.accent.fontFamily, 'Söhne, sans-serif');
// \ (escaped space) and \_ (non-breaking space) are valid YAML escapes.
assert.equal(model.frontmatter.typography.label.fontFamily, 'IBM Plex Serif, serif');
assert.equal(model.frontmatter.typography.mono.fontFamily, 'Space\u00a0Grotesk, sans-serif');
assert.equal(model.frontmatter.emoji, '\u{1F44D}');
// Malformed or out-of-range sequences stay literal.
assert.equal(model.frontmatter['bad-hex'], '\\xZZ nope');
assert.equal(model.frontmatter['bad-range'], '\\UFFFFFFFF nope');
});
});
describe('parseDesignMd overview branch', () => {