@@ -316,6 +316,7 @@ export class Parser {
316316 public _parseStylesheetAtStatement ( isNested : boolean = false ) : nodes . Node | null {
317317 return this . _parseImport ( )
318318 || this . _parseMedia ( isNested )
319+ || this . _parseScope ( )
319320 || this . _parsePage ( )
320321 || this . _parseFontFace ( )
321322 || this . _parseKeyframe ( )
@@ -364,6 +365,7 @@ export class Parser {
364365
365366 protected _parseRuleSetDeclarationAtStatement ( ) : nodes . Node | null {
366367 return this . _parseMedia ( true )
368+ || this . _parseScope ( )
367369 || this . _parseSupports ( true )
368370 || this . _parseLayer ( true )
369371 || this . _parseContainer ( true )
@@ -398,6 +400,7 @@ export class Parser {
398400 case nodes . NodeType . MixinDeclaration :
399401 case nodes . NodeType . FunctionDeclaration :
400402 case nodes . NodeType . MixinContentDeclaration :
403+ case nodes . NodeType . Scope :
401404 return false ;
402405 case nodes . NodeType . ExtendsReference :
403406 case nodes . NodeType . MixinContentReference :
@@ -1246,6 +1249,68 @@ export class Parser {
12461249 return this . _parseRatio ( ) || this . _parseTermExpression ( ) ;
12471250 }
12481251
1252+ public _parseScope ( ) : nodes . Node | null {
1253+ // @scope [<scope-limits>]? { <block-contents> }
1254+ if ( ! this . peekKeyword ( '@scope' ) ) {
1255+ return null ;
1256+ }
1257+
1258+ const node = this . create ( nodes . Scope ) ;
1259+ // @scope
1260+ this . consumeToken ( ) ;
1261+
1262+ node . addChild ( this . _parseScopeLimits ( ) )
1263+
1264+ return this . _parseBody ( node , this . _parseScopeDeclaration . bind ( this ) ) ;
1265+ }
1266+
1267+ public _parseScopeDeclaration ( ) : nodes . Node | null {
1268+ // Treat as nested as regular declarations are implicity wrapped with :where(:scope)
1269+ // https://github.com/w3c/csswg-drafts/issues/10389
1270+ // pseudo-selectors implicitly target :scope
1271+ // https://drafts.csswg.org/css-cascade-6/#scoped-rules
1272+ const isNested = true
1273+ return this . _tryParseRuleset ( isNested )
1274+ || this . _tryToParseDeclaration ( )
1275+ || this . _parseStylesheetStatement ( isNested ) ;
1276+ }
1277+
1278+ public _parseScopeLimits ( ) : nodes . Node | null {
1279+ // [(<scope-start>)]? [to (<scope-end>)]?
1280+ const node = this . create ( nodes . ScopeLimits ) ;
1281+
1282+ // [(<scope-start>)]?
1283+ if ( this . accept ( TokenType . ParenthesisL ) ) {
1284+ // scope-start selector can start with a combinator as it defaults to :scope
1285+ // Treat as nested
1286+ if ( ! node . setScopeStart ( this . _parseSelector ( true ) ) ) {
1287+ return this . finish ( node , ParseError . SelectorExpected , [ ] , [ TokenType . ParenthesisR ] )
1288+ }
1289+
1290+ if ( ! this . accept ( TokenType . ParenthesisR ) ) {
1291+ return this . finish ( node , ParseError . RightParenthesisExpected , [ ] , [ TokenType . CurlyL ] ) ;
1292+ }
1293+ }
1294+
1295+ // [to (<scope-end>)]?
1296+ if ( this . acceptIdent ( 'to' ) ) {
1297+ if ( ! this . accept ( TokenType . ParenthesisL ) ) {
1298+ return this . finish ( node , ParseError . LeftParenthesisExpected , [ ] , [ TokenType . CurlyL ] ) ;
1299+ }
1300+ // 'to' selector can start with a combinator as it defaults to :scope
1301+ // Treat as nested
1302+ if ( ! node . setScopeEnd ( this . _parseSelector ( true ) ) ) {
1303+ return this . finish ( node , ParseError . SelectorExpected , [ ] , [ TokenType . ParenthesisR ] )
1304+ }
1305+
1306+ if ( ! this . accept ( TokenType . ParenthesisR ) ) {
1307+ return this . finish ( node , ParseError . RightParenthesisExpected , [ ] , [ TokenType . CurlyL ] ) ;
1308+ }
1309+ }
1310+
1311+ return this . finish ( node )
1312+ }
1313+
12491314 public _parseMedium ( ) : nodes . Node | null {
12501315 const node = this . create ( nodes . Node ) ;
12511316 if ( node . addChild ( this . _parseIdent ( ) ) ) {
0 commit comments