@@ -31,6 +31,7 @@ const topologicalSort = require("../util/topologicalSort");
3131const {
3232 NodeType,
3333 SourceProcessor,
34+ equalsLowerCase,
3435 unescapeIdentifier
3536} = require ( "./walkCssTokens" ) ;
3637
@@ -58,7 +59,6 @@ const {
5859
5960/** @typedef {[number, number] } Range */
6061/** @typedef {{ line: number, column: number } } Position */
61- /** @typedef {{ value: string, range: Range, loc: { start: Position, end: Position } } } Comment */
6262
6363const CC_COLON = ":" . charCodeAt ( 0 ) ;
6464const CC_SEMICOLON = ";" . charCodeAt ( 0 ) ;
@@ -69,6 +69,37 @@ const CC_CARRIAGE_RETURN = "\r".charCodeAt(0);
6969const CC_FORM_FEED = "\f" . charCodeAt ( 0 ) ;
7070const CC_LEFT_CURLY = "{" . charCodeAt ( 0 ) ;
7171
72+ // A parsed CSS comment. `loc` is computed on demand — only magic-comment error
73+ // warnings read it, so comment-heavy CSS skips the per-comment line/col work.
74+ class Comment {
75+ /**
76+ * @param {string } value comment body (without the surrounding delimiters)
77+ * @param {Range } range `[start, end]` byte range including delimiters
78+ * @param {LocConverter } locConverter shared loc converter
79+ */
80+ constructor ( value , range , locConverter ) {
81+ this . value = value ;
82+ this . range = range ;
83+ this . _locConverter = locConverter ;
84+ }
85+
86+ /**
87+ * @returns {{ start: Position, end: Position } } source location
88+ */
89+ get loc ( ) {
90+ const lc = this . _locConverter ;
91+ // `LocConverter#get` mutates and returns itself, so snapshot between calls.
92+ const s = lc . get ( this . range [ 0 ] ) ;
93+ const sl = s . line ;
94+ const sc = s . column ;
95+ const e = lc . get ( this . range [ 1 ] ) ;
96+ return {
97+ start : { line : sl , column : sc } ,
98+ end : { line : e . line , column : e . column }
99+ } ;
100+ }
101+ }
102+
72103// Newlines (CSS Syntax 3 §3.3) — listed explicitly since there's no preprocessing stage.
73104const STRING_MULTILINE = / \\ [ \n \r \f ] / g;
74105// https://www.w3.org/TR/css-syntax-3/#whitespace
@@ -862,17 +893,9 @@ class CssParser extends Parser {
862893 */
863894 const comment = ( input , start , end ) => {
864895 if ( ! this . comments ) this . comments = [ ] ;
865- const { line : sl , column : sc } = locConverter . get ( start ) ;
866- const { line : el , column : ec } = locConverter . get ( end ) ;
867- const value = input . slice ( start + 2 , end - 2 ) ;
868- this . comments . push ( {
869- value,
870- range : [ start , end ] ,
871- loc : {
872- start : { line : sl , column : sc } ,
873- end : { line : el , column : ec }
874- }
875- } ) ;
896+ this . comments . push (
897+ new Comment ( input . slice ( start + 2 , end - 2 ) , [ start , end ] , locConverter )
898+ ) ;
876899 return end ;
877900 } ;
878901
@@ -1371,7 +1394,7 @@ class CssParser extends Parser {
13711394 if (
13721395 j >= fn . value . length ||
13731396 fn . value [ j ] . type !== NodeType . Ident ||
1374- /** @type {Token } */ ( fn . value [ j ] ) . value . toLowerCase ( ) !== "from"
1397+ ! equalsLowerCase ( /** @type {Token } */ ( fn . value [ j ] ) . value , "from" )
13751398 ) {
13761399 emitDashedIdentExport ( identStart , identEnd ) ;
13771400 return ;
@@ -1521,8 +1544,10 @@ class CssParser extends Parser {
15211544 const next = values [ i + 1 ] ;
15221545 if ( ! next ) continue ;
15231546 if ( next . type === NodeType . Ident ) {
1524- const id = /** @type {Token } */ ( next ) . value . toLowerCase ( ) ;
1525- if ( id === "local" || id === "global" ) {
1547+ const raw = /** @type {Token } */ ( next ) . value ;
1548+ const isLocal = equalsLowerCase ( raw , "local" ) ;
1549+ if ( isLocal || equalsLowerCase ( raw , "global" ) ) {
1550+ const id = isLocal ? "local" : "global" ;
15261551 // Bare `:local` / `:global`: switch the segment (and top-level persistent) mode and strip the marker. The next sibling token is whitespace when any whitespace separates the marker from the next selector (comments aren't AST nodes); strip the marker plus that whitespace, but only when it's adjacent (a comment between would end the run).
15271552 const afterMarker = values [ i + 2 ] ;
15281553 const afterIsWhitespace = Boolean (
@@ -1557,10 +1582,13 @@ class CssParser extends Parser {
15571582 continue ;
15581583 }
15591584 } else if ( next . type === NodeType . Function ) {
1560- const fname = /** @type {FunctionNode } */ ( next ) . name
1561- . replace ( / \\ / g, "" )
1562- . toLowerCase ( ) ;
1563- if ( fname === "local" || fname === "global" ) {
1585+ const rawName = /** @type {FunctionNode } */ ( next ) . name . replace (
1586+ / \\ / g,
1587+ ""
1588+ ) ;
1589+ const isLocal = equalsLowerCase ( rawName , "local" ) ;
1590+ if ( isLocal || equalsLowerCase ( rawName , "global" ) ) {
1591+ const fname = isLocal ? "local" : "global" ;
15641592 // `:local(…)` / `:global(…)`: scope mode to the args and strip the wrapper with two source-level strip deps (leading `:name(` + whitespace, then trailing `)` — `:local` also eats whitespace before `)`).
15651593 const fn = /** @type {FunctionNode } */ ( next ) ;
15661594 if ( isModules ) {
@@ -1653,7 +1681,7 @@ class CssParser extends Parser {
16531681 const attrName = unescapeIdentifier (
16541682 source . slice ( attrNameNode . start , attrNameNode . end )
16551683 ) ;
1656- if ( attrName . toLowerCase ( ) !== "class" ) continue ;
1684+ if ( ! equalsLowerCase ( attrName , "class" ) ) continue ;
16571685 ai ++ ;
16581686 while (
16591687 ai < attrParts . length &&
@@ -1803,9 +1831,7 @@ class CssParser extends Parser {
18031831 const at = /** @type {AtRule & { urlRecovery?: boolean } } */ (
18041832 currentStructural
18051833 ) ;
1806- return (
1807- `@${ at . name . toLowerCase ( ) } ` !== "@import" || Boolean ( at . urlRecovery )
1808- ) ;
1834+ return ! equalsLowerCase ( at . name , "import" ) || Boolean ( at . urlRecovery ) ;
18091835 }
18101836 return true ;
18111837 } ;
@@ -1969,14 +1995,15 @@ class CssParser extends Parser {
19691995 urlNode = cv ;
19701996 continue ;
19711997 }
1972- if ( cv . type === NodeType . Function ) {
1973- const fname = /** @type {FunctionNode } */ ( cv ) . name
1974- . replace ( / \\ / g, "" )
1975- . toLowerCase ( ) ;
1976- if ( fname === "url" ) {
1977- urlNode = cv ;
1978- continue ;
1979- }
1998+ if (
1999+ cv . type === NodeType . Function &&
2000+ equalsLowerCase (
2001+ /** @type {FunctionNode } */ ( cv ) . name . replace ( / \\ / g, "" ) ,
2002+ "url"
2003+ )
2004+ ) {
2005+ urlNode = cv ;
2006+ continue ;
19802007 }
19812008 if ( cv . type === NodeType . Ident ) {
19822009 // CSS Modules: bare ident is a `@value` reference.
@@ -1988,30 +2015,34 @@ class CssParser extends Parser {
19882015
19892016 if ( ! layerNode && ! supportsNode ) {
19902017 if ( cv . type === NodeType . Ident ) {
1991- const ident = /** @type {Token } */ ( cv ) . value
1992- . replace ( / \\ / g, "" )
1993- . toLowerCase ( ) ;
1994- if ( ident === "layer" ) {
1995- layerNode = cv ;
1996- continue ;
1997- }
1998- } else if ( cv . type === NodeType . Function ) {
1999- const fname = /** @type {FunctionNode } */ ( cv ) . name
2000- . replace ( / \\ / g, "" )
2001- . toLowerCase ( ) ;
2002- if ( fname === "layer" ) {
2018+ if (
2019+ equalsLowerCase (
2020+ /** @type {Token } */ ( cv ) . value . replace ( / \\ / g, "" ) ,
2021+ "layer"
2022+ )
2023+ ) {
20032024 layerNode = cv ;
20042025 continue ;
20052026 }
2027+ } else if (
2028+ cv . type === NodeType . Function &&
2029+ equalsLowerCase (
2030+ /** @type {FunctionNode } */ ( cv ) . name . replace ( / \\ / g, "" ) ,
2031+ "layer"
2032+ )
2033+ ) {
2034+ layerNode = cv ;
2035+ continue ;
20062036 }
20072037 }
20082038
20092039 if (
20102040 ! supportsNode &&
20112041 cv . type === NodeType . Function &&
2012- /** @type {FunctionNode } */ ( cv ) . name
2013- . replace ( / \\ / g, "" )
2014- . toLowerCase ( ) === "supports"
2042+ equalsLowerCase (
2043+ /** @type {FunctionNode } */ ( cv ) . name . replace ( / \\ / g, "" ) ,
2044+ "supports"
2045+ )
20152046 ) {
20162047 supportsNode = /** @type {FunctionNode } */ ( cv ) ;
20172048 continue ;
@@ -2398,7 +2429,7 @@ class CssParser extends Parser {
23982429 // `@scope (.x) to (.y)` — walk the prelude as a selector list.
23992430 if (
24002431 isModules &&
2401- `@ ${ at . name . toLowerCase ( ) } ` === "@ scope" &&
2432+ equalsLowerCase ( at . name , " scope") &&
24022433 at . prelude . length > 0
24032434 ) {
24042435 walkSelectorList (
@@ -2467,10 +2498,14 @@ class CssParser extends Parser {
24672498 break ;
24682499 }
24692500 if ( cv . type === NodeType . Function ) {
2470- const fname = /** @type {FunctionNode } */ ( cv ) . name
2471- . replace ( / \\ / g, "" )
2472- . toLowerCase ( ) ;
2473- if ( fname === "local" ) pure . markLocal ( ) ;
2501+ if (
2502+ equalsLowerCase (
2503+ /** @type {FunctionNode } */ ( cv ) . name . replace ( / \\ / g, "" ) ,
2504+ "local"
2505+ )
2506+ ) {
2507+ pure . markLocal ( ) ;
2508+ }
24742509 break ;
24752510 }
24762511 }
@@ -2533,17 +2568,18 @@ class CssParser extends Parser {
25332568 rule . prelude [ firstIdx ] . type === NodeType . Colon
25342569 ) {
25352570 const second = rule . prelude [ firstIdx + 1 ] ;
2536- const name =
2571+ const rawName =
25372572 second . type === NodeType . Ident
2538- ? /** @type {Token } */ ( second ) . value . toLowerCase ( )
2573+ ? /** @type {Token } */ ( second ) . value
25392574 : second . type === NodeType . Function
2540- ? /** @type {FunctionNode } */ ( second ) . name . toLowerCase ( )
2575+ ? /** @type {FunctionNode } */ ( second ) . name
25412576 : "" ;
2542- if ( name === "import" || name === "export" ) {
2577+ const isImport = equalsLowerCase ( rawName , "import" ) ;
2578+ if ( isImport || equalsLowerCase ( rawName , "export" ) ) {
25432579 if ( topLevel ) {
25442580 const startColon = rule . prelude [ firstIdx ] . start ;
25452581 const endAfterBody = processImportOrExport (
2546- name === "import" ? 0 : 1 ,
2582+ isImport ? 0 : 1 ,
25472583 second ,
25482584 rule
25492585 ) ;
@@ -2721,7 +2757,7 @@ class CssParser extends Parser {
27212757 }
27222758 if (
27232759 cv . type === NodeType . Ident &&
2724- /** @type {Token } */ ( cv ) . value . toLowerCase ( ) === "global"
2760+ equalsLowerCase ( /** @type {Token } */ ( cv ) . value , "global" )
27252761 ) {
27262762 fromSource = { kind : "global" } ;
27272763 phase = "done" ;
@@ -2740,7 +2776,7 @@ class CssParser extends Parser {
27402776 if ( cv . type === NodeType . Ident ) {
27412777 const identValue = /** @type {Token } */ ( cv ) . value ;
27422778 if (
2743- identValue . toLowerCase ( ) === "from" &&
2779+ equalsLowerCase ( identValue , "from" ) &&
27442780 classNames . length > 0 &&
27452781 nextNonWhitespace ( group , i + 1 ) < group . length
27462782 ) {
@@ -2757,8 +2793,10 @@ class CssParser extends Parser {
27572793
27582794 if ( cv . type === NodeType . Function ) {
27592795 const fn = /** @type {FunctionNode } */ ( cv ) ;
2760- const fname = fn . name . replace ( / \\ / g, "" ) . toLowerCase ( ) ;
2761- const isGlobal = fname === "global" ;
2796+ const isGlobal = equalsLowerCase (
2797+ fn . name . replace ( / \\ / g, "" ) ,
2798+ "global"
2799+ ) ;
27622800 for ( const inner of fn . value ) {
27632801 if ( inner . type === NodeType . Ident ) {
27642802 classNames . push ( {
@@ -3321,7 +3359,7 @@ class CssParser extends Parser {
33213359 if (
33223360 j < siblings . length &&
33233361 siblings [ j ] . type === NodeType . Ident &&
3324- /** @type {Token } */ ( siblings [ j ] ) . value . toLowerCase ( ) === "from"
3362+ equalsLowerCase ( /** @type {Token } */ ( siblings [ j ] ) . value , "from" )
33253363 ) {
33263364 const fromIdent = siblings [ j ] ;
33273365 const sourceNode = siblings [ nextNonWhitespace ( siblings , j + 1 ) ] ;
0 commit comments