From c6e308a56eac824db461cce045fce3cc94e4788a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 11:41:57 +0000 Subject: [PATCH 1/3] feat: parse nested feature-value blocks inside @font-feature-values --- src/parse.test.ts | 17 +++++++++++++++++ src/parse.ts | 7 ++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/parse.test.ts b/src/parse.test.ts index c67247d..04c9d6d 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -885,6 +885,23 @@ describe('Core Nodes', () => { expect(decl2.type).toBe(DECLARATION) }) + test('@font-feature-values with nested at-rules', () => { + const root = parse( + '@font-feature-values Font One { @styleset { nice-style: 12; } @swash { fancy: 1 } } a {}', + ) + const rule = root.first_child! as Atrule + expect(rule.name).toBe('font-feature-values') + expect(rule.next_sibling!.type).toBe(STYLE_RULE) + expect(rule.next_sibling!.next_sibling).toBeNull() + + const [styleset, swash] = rule.block!.children + expect(styleset.type).toBe(AT_RULE) + expect((styleset as Atrule).name).toBe('styleset') + expect(styleset.block!.first_child!.type).toBe(DECLARATION) + expect((swash as Atrule).name).toBe('swash') + expect(swash.block!.first_child!.type).toBe(DECLARATION) + }) + test('@page', () => { const source = '@page { margin: 1in; }' const root = parse(source) diff --git a/src/parse.ts b/src/parse.ts index d7a0a16..90d4201 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -57,7 +57,12 @@ export interface ParserOptions { // Static at-rule lookup sets for fast classification const DECLARATION_AT_RULES = new Set([ 'font-face', - 'font-feature-values', + 'styleset', + 'stylistic', + 'character-variant', + 'swash', + 'ornaments', + 'annotation', 'page', 'property', 'counter-style', From fb3a42b368f4768ff22b8f07e4a5cb788ef55b71 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 11:44:36 +0000 Subject: [PATCH 2/3] feat: parse nested margin at-rules inside @page --- src/parse.test.ts | 16 ++++++++++++++++ src/parse.ts | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/parse.test.ts b/src/parse.test.ts index 04c9d6d..1a2e7f5 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -902,6 +902,22 @@ describe('Core Nodes', () => { expect(swash.block!.first_child!.type).toBe(DECLARATION) }) + test('@page with nested margin at-rules', () => { + const root = parse( + '@page :first { margin: 1in; @top-left { content: "a"; } @bottom-right-corner { color: red } } a {}', + ) + const page = root.first_child! as Atrule + expect(page.name).toBe('page') + expect(page.next_sibling!.type).toBe(STYLE_RULE) + + const [margin, top_left, bottom_right] = page.block!.children + expect(margin.type).toBe(DECLARATION) + expect((top_left as Atrule).name).toBe('top-left') + expect(top_left.block!.first_child!.type).toBe(DECLARATION) + expect((bottom_right as Atrule).name).toBe('bottom-right-corner') + expect(bottom_right.block!.first_child!.type).toBe(DECLARATION) + }) + test('@page', () => { const source = '@page { margin: 1in; }' const root = parse(source) diff --git a/src/parse.ts b/src/parse.ts index 90d4201..1a45ac6 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -63,7 +63,22 @@ const DECLARATION_AT_RULES = new Set([ 'swash', 'ornaments', 'annotation', - 'page', + 'top-left-corner', + 'top-left', + 'top-center', + 'top-right', + 'top-right-corner', + 'bottom-left-corner', + 'bottom-left', + 'bottom-center', + 'bottom-right', + 'bottom-right-corner', + 'left-top', + 'left-middle', + 'left-bottom', + 'right-top', + 'right-middle', + 'right-bottom', 'property', 'counter-style', 'color-profile', From 58772c010cb9e17813bc99dec2f2aa22e5d8441e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 11:59:49 +0000 Subject: [PATCH 3/3] fix: type errors in nested at-rule tests --- src/parse.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/parse.test.ts b/src/parse.test.ts index 1a2e7f5..f6eb5b7 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -894,11 +894,11 @@ describe('Core Nodes', () => { expect(rule.next_sibling!.type).toBe(STYLE_RULE) expect(rule.next_sibling!.next_sibling).toBeNull() - const [styleset, swash] = rule.block!.children + const [styleset, swash] = rule.block!.children as Atrule[] expect(styleset.type).toBe(AT_RULE) - expect((styleset as Atrule).name).toBe('styleset') + expect(styleset.name).toBe('styleset') expect(styleset.block!.first_child!.type).toBe(DECLARATION) - expect((swash as Atrule).name).toBe('swash') + expect(swash.name).toBe('swash') expect(swash.block!.first_child!.type).toBe(DECLARATION) }) @@ -910,11 +910,11 @@ describe('Core Nodes', () => { expect(page.name).toBe('page') expect(page.next_sibling!.type).toBe(STYLE_RULE) - const [margin, top_left, bottom_right] = page.block!.children + const [margin, top_left, bottom_right] = page.block!.children as Atrule[] expect(margin.type).toBe(DECLARATION) - expect((top_left as Atrule).name).toBe('top-left') + expect(top_left.name).toBe('top-left') expect(top_left.block!.first_child!.type).toBe(DECLARATION) - expect((bottom_right as Atrule).name).toBe('bottom-right-corner') + expect(bottom_right.name).toBe('bottom-right-corner') expect(bottom_right.block!.first_child!.type).toBe(DECLARATION) })