mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 14:16:28 +03:00
* 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)
230 lines
6.5 KiB
JavaScript
230 lines
6.5 KiB
JavaScript
/**
|
|
* Tests for design-parser.mjs — frontmatter + body extraction.
|
|
* Run with: node --test tests/design-parser.test.mjs
|
|
*/
|
|
|
|
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { parseDesignMd } from '../skill/scripts/lib/design-parser.mjs';
|
|
|
|
describe('parseDesignMd frontmatter branch', () => {
|
|
it('returns null frontmatter when the file has no YAML header', () => {
|
|
const md = `# Design System: Demo
|
|
|
|
## 1. Overview
|
|
|
|
Some prose.
|
|
`;
|
|
const model = parseDesignMd(md);
|
|
assert.equal(model.schemaVersion, 2);
|
|
assert.equal(model.frontmatter, null);
|
|
assert.equal(model.title, 'Design System: Demo');
|
|
});
|
|
|
|
it('parses a Stitch-shaped frontmatter and strips it from the body', () => {
|
|
const md = `---
|
|
name: Demo System
|
|
description: A quiet editorial look.
|
|
colors:
|
|
primary: "#b8422e"
|
|
neutral-bg: "#faf7f2"
|
|
typography:
|
|
display:
|
|
fontFamily: "Cormorant Garamond, Georgia, serif"
|
|
fontWeight: 300
|
|
lineHeight: 1
|
|
body:
|
|
fontFamily: "Inter, sans-serif"
|
|
rounded:
|
|
sm: "4px"
|
|
md: "8px"
|
|
components:
|
|
button-primary:
|
|
backgroundColor: "{colors.primary}"
|
|
textColor: "{colors.neutral-bg}"
|
|
rounded: "{rounded.sm}"
|
|
---
|
|
|
|
# Design System: Demo
|
|
|
|
## 1. Overview
|
|
|
|
Opening prose.
|
|
`;
|
|
const model = parseDesignMd(md);
|
|
assert.equal(model.schemaVersion, 2);
|
|
assert.equal(model.title, 'Design System: Demo');
|
|
assert.ok(model.frontmatter);
|
|
assert.equal(model.frontmatter.name, 'Demo System');
|
|
assert.equal(model.frontmatter.description, 'A quiet editorial look.');
|
|
assert.equal(model.frontmatter.colors.primary, '#b8422e');
|
|
assert.equal(model.frontmatter.colors['neutral-bg'], '#faf7f2');
|
|
assert.equal(model.frontmatter.typography.display.fontFamily, 'Cormorant Garamond, Georgia, serif');
|
|
assert.equal(model.frontmatter.typography.display.fontWeight, 300);
|
|
assert.equal(model.frontmatter.typography.display.lineHeight, 1);
|
|
assert.equal(model.frontmatter.rounded.md, '8px');
|
|
assert.equal(model.frontmatter.components['button-primary'].backgroundColor, '{colors.primary}');
|
|
});
|
|
|
|
it('recovers gracefully when frontmatter has no closing marker', () => {
|
|
// No `---` terminator: the whole file is treated as body, not partial
|
|
// frontmatter. The H1 title still resolves from the body.
|
|
const md = `---
|
|
this is not valid yaml : : :
|
|
no closing marker
|
|
# Design System: Broken
|
|
|
|
## 1. Overview
|
|
|
|
Prose.
|
|
`;
|
|
const model = parseDesignMd(md);
|
|
assert.equal(model.frontmatter, null);
|
|
assert.equal(model.title, 'Design System: Broken');
|
|
});
|
|
|
|
it('ignores line-only comments but preserves unquoted hex values', () => {
|
|
const md = `---
|
|
# Top-level comment
|
|
colors:
|
|
primary: #b8422e
|
|
# mid-block comment
|
|
accent: "#ec4899"
|
|
---
|
|
|
|
# Design System: Commented
|
|
|
|
## 1. Overview
|
|
|
|
Prose.
|
|
`;
|
|
const model = parseDesignMd(md);
|
|
assert.equal(model.frontmatter.colors.primary, '#b8422e');
|
|
assert.equal(model.frontmatter.colors.accent, '#ec4899');
|
|
});
|
|
|
|
it('strips inline comments after quoted OKLCH values', () => {
|
|
const md = `---
|
|
colors:
|
|
kinpaku-gold: "oklch(84% 0.19 80.46)" # primary accent
|
|
gold-hairline: "oklch(58% 0.065 82 / 0.32)" # default rule
|
|
---
|
|
|
|
# Design System: Kinpaku
|
|
|
|
## 1. Overview
|
|
|
|
Prose.
|
|
`;
|
|
const model = parseDesignMd(md);
|
|
assert.equal(model.frontmatter.colors['kinpaku-gold'], 'oklch(84% 0.19 80.46)');
|
|
assert.equal(model.frontmatter.colors['gold-hairline'], 'oklch(58% 0.065 82 / 0.32)');
|
|
});
|
|
|
|
it('normalizes quoted YAML keys in token maps', () => {
|
|
const md = `---
|
|
rounded:
|
|
"2xl": "80px"
|
|
'3xl': "96px"
|
|
colors:
|
|
"brand-gold": "#d9a531"
|
|
---
|
|
|
|
# Design System: Quoted Keys
|
|
|
|
## 1. Overview
|
|
|
|
Prose.
|
|
`;
|
|
const model = parseDesignMd(md);
|
|
assert.equal(model.frontmatter.rounded['2xl'], '80px');
|
|
assert.equal(model.frontmatter.rounded['3xl'], '96px');
|
|
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', () => {
|
|
it('joins wrapped Key Characteristics bullets without leaking continuations into philosophy', () => {
|
|
const md = `# Design System: Example
|
|
|
|
## Overview
|
|
|
|
**Creative North Star: "Structured clarity"**
|
|
|
|
**Key Characteristics:**
|
|
|
|
- Status remains understandable without relying on color
|
|
alone.
|
|
- Navigation controls remain visible when the viewport becomes
|
|
narrow.
|
|
`;
|
|
const overview = parseDesignMd(md).overview;
|
|
|
|
assert.deepEqual(overview.keyCharacteristics, [
|
|
'Status remains understandable without relying on color alone.',
|
|
'Navigation controls remain visible when the viewport becomes narrow.',
|
|
]);
|
|
assert.deepEqual(overview.philosophy, []);
|
|
});
|
|
});
|