Skip to content

Commit 1efa811

Browse files
authored
Support @starting-style (#421)
* support parsing `@starting-style` at rules * add tests * Apply suggestions from code review * Apply suggestions from code review
1 parent 1114606 commit 1efa811

File tree

4 files changed

+57
-1
lines changed

4 files changed

+57
-1
lines changed

src/parser/cssNodes.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ export enum NodeType {
101101
PropertyAtRule,
102102
Container,
103103
ModuleConfig,
104-
SelectorList
104+
SelectorList,
105+
StartingStyleAtRule,
105106
}
106107

107108
export enum ReferenceType {
@@ -1225,6 +1226,17 @@ export class PropertyAtRule extends BodyDeclaration {
12251226

12261227
}
12271228

1229+
export class StartingStyleAtRule extends BodyDeclaration {
1230+
1231+
constructor(offset: number, length: number) {
1232+
super(offset, length);
1233+
}
1234+
1235+
get type(): NodeType {
1236+
return NodeType.StartingStyleAtRule;
1237+
}
1238+
}
1239+
12281240
export class Document extends BodyDeclaration {
12291241

12301242
constructor(offset: number, length: number) {

src/parser/cssParser.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ export class Parser {
326326
|| this._parseNamespace()
327327
|| this._parseDocument()
328328
|| this._parseContainer(isNested)
329+
|| this._parseStartingStyleAtRule(isNested)
329330
|| this._parseUnknownAtRule();
330331
}
331332

@@ -366,6 +367,7 @@ export class Parser {
366367
|| this._parseSupports(true)
367368
|| this._parseLayer(true)
368369
|| this._parseContainer(true)
370+
|| this._parseStartingStyleAtRule(true)
369371
|| this._parseUnknownAtRule();
370372
}
371373

@@ -901,6 +903,30 @@ export class Parser {
901903
return this._parseBody(node, this._parseDeclaration.bind(this));
902904
}
903905

906+
_parseStartingStyleAtRule(isNested = false) {
907+
if (!this.peekKeyword("@starting-style")) {
908+
return null;
909+
}
910+
911+
const node = this.create(nodes.StartingStyleAtRule);
912+
this.consumeToken() // @starting-style
913+
914+
return this._parseBody(node, this._parseStartingStyleDeclaration.bind(this, isNested));
915+
}
916+
917+
// this method is the same as ._parseContainerDeclaration()
918+
// which is the same as ._parseMediaDeclaration(),
919+
// _parseSupportsDeclaration, and ._parseLayerDeclaration()
920+
_parseStartingStyleDeclaration(isNested = false) {
921+
if (isNested) {
922+
// if nested, the body can contain rulesets, but also declarations
923+
return this._tryParseRuleset(true)
924+
|| this._tryToParseDeclaration()
925+
|| this._parseStylesheetStatement(true);
926+
}
927+
return this._parseStylesheetStatement(false);
928+
}
929+
904930
public _parseLayer(isNested: boolean = false): nodes.Node | null {
905931
// @layer layer-name {rules}
906932
// @layer layer-name;

src/test/css/nodes.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ suite('CSS - Nodes', () => {
135135
assertNodes(fn, '@keyframes name { from { top: 0px} to { top: 100px } }', 'keyframe,identifier,...,keyframeselector,...,declaration,...,keyframeselector,...,declaration,...');
136136
});
137137

138+
test('Starting-style', function () {
139+
function fn(input: string): nodes.Node {
140+
let parser = new Parser();
141+
let node = parser.internalParse(input, parser._parseStartingStyleAtRule)!;
142+
return node;
143+
}
144+
assertNodes(fn, '@starting-style { p { opacity: 0; } }', 'startingstyleatrule,declarations,ruleset,...,selector,...,elementnameselector,...,...,...,...,...,...,...,...,...');
145+
});
146+
138147
test('UnicodeRange', function () {
139148
function fn(input: string): nodes.Node {
140149
let parser = new Parser();

src/test/css/parser.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ suite('CSS - Parser', () => {
177177
assertNode(`@container card (inline-size > 30em) { @container style(--responsive: true) {} }`, parser, parser._parseStylesheet.bind(parser));
178178
});
179179

180+
test('@starting-style', function () {
181+
const parser = new Parser();
182+
// These assertions would still hold if @starting-style blocks were being processed as unknown at-rules
183+
// Parsing into the expected AST is instead tested in nodes.test.ts
184+
assertNode(`@starting-style { p { background-color: skyblue; } }`, parser, parser._parseStylesheet.bind(parser));
185+
assertNode(`p { @starting-style { background-color: skyblue; } }`, parser, parser._parseStylesheet.bind(parser));
186+
assertNode(`p { @starting-style { @layer {} } }`, parser, parser._parseStylesheet.bind(parser));
187+
});
188+
180189
test('@container query length units', function () {
181190
const parser = new Parser();
182191
assertNode(`@container (min-width: 700px) { .card h2 { font-size: max(1.5em, 1.23em + 2cqi); } }`, parser, parser._parseStylesheet.bind(parser));

0 commit comments

Comments
 (0)