@@ -99,6 +99,136 @@ const normalizeUrl = (str, isString) => {
99
99
return str ;
100
100
} ;
101
101
102
+ // eslint-disable-next-line no-useless-escape
103
+ const regexSingleEscape = / [ - , . \/ : - @ [ \] \^ ` { - ~ ] / ;
104
+ const regexExcessiveSpaces =
105
+ / ( ^ | \\ + ) ? ( \\ [ A - F 0 - 9 ] { 1 , 6 } ) \u0020 (? ! [ a - f A - F 0 - 9 \u0020 ] ) / g;
106
+
107
+ /**
108
+ * @param {string } str string
109
+ * @returns {string } escaped identifier
110
+ */
111
+ const escapeIdentifier = str => {
112
+ let output = "" ;
113
+ let counter = 0 ;
114
+
115
+ while ( counter < str . length ) {
116
+ const character = str . charAt ( counter ++ ) ;
117
+
118
+ let value ;
119
+
120
+ if ( / [ \t \n \f \r \u000B ] / . test ( character ) ) {
121
+ const codePoint = character . charCodeAt ( 0 ) ;
122
+
123
+ value = `\\${ codePoint . toString ( 16 ) . toUpperCase ( ) } ` ;
124
+ } else if ( character === "\\" || regexSingleEscape . test ( character ) ) {
125
+ value = `\\${ character } ` ;
126
+ } else {
127
+ value = character ;
128
+ }
129
+
130
+ output += value ;
131
+ }
132
+
133
+ const firstChar = str . charAt ( 0 ) ;
134
+
135
+ if ( / ^ - [ - \d ] / . test ( output ) ) {
136
+ output = `\\-${ output . slice ( 1 ) } ` ;
137
+ } else if ( / \d / . test ( firstChar ) ) {
138
+ output = `\\3${ firstChar } ${ output . slice ( 1 ) } ` ;
139
+ }
140
+
141
+ // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
142
+ // since they’re redundant. Note that this is only possible if the escape
143
+ // sequence isn’t preceded by an odd number of backslashes.
144
+ output = output . replace ( regexExcessiveSpaces , ( $0 , $1 , $2 ) => {
145
+ if ( $1 && $1 . length % 2 ) {
146
+ // It’s not safe to remove the space, so don’t.
147
+ return $0 ;
148
+ }
149
+
150
+ // Strip the space.
151
+ return ( $1 || "" ) + $2 ;
152
+ } ) ;
153
+
154
+ return output ;
155
+ } ;
156
+
157
+ const CONTAINS_ESCAPE = / \\ / ;
158
+
159
+ /**
160
+ * @param {string } str string
161
+ * @returns {[string, number] | undefined } hex
162
+ */
163
+ const gobbleHex = str => {
164
+ const lower = str . toLowerCase ( ) ;
165
+ let hex = "" ;
166
+ let spaceTerminated = false ;
167
+
168
+ for ( let i = 0 ; i < 6 && lower [ i ] !== undefined ; i ++ ) {
169
+ const code = lower . charCodeAt ( i ) ;
170
+ // check to see if we are dealing with a valid hex char [a-f|0-9]
171
+ const valid = ( code >= 97 && code <= 102 ) || ( code >= 48 && code <= 57 ) ;
172
+ // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
173
+ spaceTerminated = code === 32 ;
174
+ if ( ! valid ) break ;
175
+ hex += lower [ i ] ;
176
+ }
177
+
178
+ if ( hex . length === 0 ) return undefined ;
179
+
180
+ const codePoint = Number . parseInt ( hex , 16 ) ;
181
+ const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff ;
182
+
183
+ // Add special case for
184
+ // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
185
+ // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
186
+ if ( isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff ) {
187
+ return [ "\uFFFD" , hex . length + ( spaceTerminated ? 1 : 0 ) ] ;
188
+ }
189
+
190
+ return [
191
+ String . fromCodePoint ( codePoint ) ,
192
+ hex . length + ( spaceTerminated ? 1 : 0 )
193
+ ] ;
194
+ } ;
195
+
196
+ /**
197
+ * @param {string } str string
198
+ * @returns {string } unescaped string
199
+ */
200
+ const unescapeIdentifier = str => {
201
+ const needToProcess = CONTAINS_ESCAPE . test ( str ) ;
202
+ if ( ! needToProcess ) return str ;
203
+ let ret = "" ;
204
+ for ( let i = 0 ; i < str . length ; i ++ ) {
205
+ if ( str [ i ] === "\\" ) {
206
+ const gobbled = gobbleHex ( str . slice ( i + 1 , i + 7 ) ) ;
207
+ if ( gobbled !== undefined ) {
208
+ ret += gobbled [ 0 ] ;
209
+ i += gobbled [ 1 ] ;
210
+ continue ;
211
+ }
212
+ // Retain a pair of \\ if double escaped `\\\\`
213
+ // https://github.com/postcss/postcss-selector-parser/commit/268c9a7656fb53f543dc620aa5b73a30ec3ff20e
214
+ if ( str [ i + 1 ] === "\\" ) {
215
+ ret += "\\" ;
216
+ i += 1 ;
217
+ continue ;
218
+ }
219
+ // if \\ is at the end of the string retain it
220
+ // https://github.com/postcss/postcss-selector-parser/commit/01a6b346e3612ce1ab20219acc26abdc259ccefb
221
+ if ( str . length === i + 1 ) {
222
+ ret += str [ i ] ;
223
+ }
224
+ continue ;
225
+ }
226
+ ret += str [ i ] ;
227
+ }
228
+
229
+ return ret ;
230
+ } ;
231
+
102
232
class LocConverter {
103
233
/**
104
234
* @param {string } input input
@@ -482,17 +612,15 @@ class CssParser extends Parser {
482
612
// CSS Variable
483
613
const { line : sl , column : sc } = locConverter . get ( propertyNameStart ) ;
484
614
const { line : el , column : ec } = locConverter . get ( propertyNameEnd ) ;
485
- const name = propertyName . slice ( 2 ) ;
615
+ const name = unescapeIdentifier ( propertyName . slice ( 2 ) ) ;
486
616
const dep = new CssLocalIdentifierDependency (
487
617
name ,
488
618
[ propertyNameStart , propertyNameEnd ] ,
489
619
"--"
490
620
) ;
491
621
dep . setLoc ( sl , sc , el , ec ) ;
492
622
module . addDependency ( dep ) ;
493
- declaredCssVariables . add (
494
- CssSelfLocalIdentifierDependency . unescapeIdentifier ( name )
495
- ) ;
623
+ declaredCssVariables . add ( name ) ;
496
624
} else if (
497
625
OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY . test ( propertyName )
498
626
) {
@@ -507,9 +635,11 @@ class CssParser extends Parser {
507
635
if ( inAnimationProperty && lastIdentifier ) {
508
636
const { line : sl , column : sc } = locConverter . get ( lastIdentifier [ 0 ] ) ;
509
637
const { line : el , column : ec } = locConverter . get ( lastIdentifier [ 1 ] ) ;
510
- const name = lastIdentifier [ 2 ]
511
- ? input . slice ( lastIdentifier [ 0 ] , lastIdentifier [ 1 ] )
512
- : input . slice ( lastIdentifier [ 0 ] + 1 , lastIdentifier [ 1 ] - 1 ) ;
638
+ const name = unescapeIdentifier (
639
+ lastIdentifier [ 2 ]
640
+ ? input . slice ( lastIdentifier [ 0 ] , lastIdentifier [ 1 ] )
641
+ : input . slice ( lastIdentifier [ 0 ] + 1 , lastIdentifier [ 1 ] - 1 )
642
+ ) ;
513
643
const dep = new CssSelfLocalIdentifierDependency ( name , [
514
644
lastIdentifier [ 0 ] ,
515
645
lastIdentifier [ 1 ]
@@ -882,10 +1012,11 @@ class CssParser extends Parser {
882
1012
end
883
1013
) ;
884
1014
if ( ! ident ) return end ;
885
- const name =
1015
+ const name = unescapeIdentifier (
886
1016
ident [ 2 ] === true
887
1017
? input . slice ( ident [ 0 ] , ident [ 1 ] )
888
- : input . slice ( ident [ 0 ] + 1 , ident [ 1 ] - 1 ) ;
1018
+ : input . slice ( ident [ 0 ] + 1 , ident [ 1 ] - 1 )
1019
+ ) ;
889
1020
const { line : sl , column : sc } = locConverter . get ( ident [ 0 ] ) ;
890
1021
const { line : el , column : ec } = locConverter . get ( ident [ 1 ] ) ;
891
1022
const dep = new CssLocalIdentifierDependency ( name , [
@@ -900,10 +1031,8 @@ class CssParser extends Parser {
900
1031
if ( ! ident ) return end ;
901
1032
let name = input . slice ( ident [ 0 ] , ident [ 1 ] ) ;
902
1033
if ( ! name . startsWith ( "--" ) || name . length < 3 ) return end ;
903
- name = name . slice ( 2 ) ;
904
- declaredCssVariables . add (
905
- CssSelfLocalIdentifierDependency . unescapeIdentifier ( name )
906
- ) ;
1034
+ name = unescapeIdentifier ( name . slice ( 2 ) ) ;
1035
+ declaredCssVariables . add ( name ) ;
907
1036
const { line : sl , column : sc } = locConverter . get ( ident [ 0 ] ) ;
908
1037
const { line : el , column : ec } = locConverter . get ( ident [ 1 ] ) ;
909
1038
const dep = new CssLocalIdentifierDependency (
@@ -996,7 +1125,7 @@ class CssParser extends Parser {
996
1125
end
997
1126
) ;
998
1127
if ( ! ident ) return end ;
999
- const name = input . slice ( ident [ 0 ] , ident [ 1 ] ) ;
1128
+ const name = unescapeIdentifier ( input . slice ( ident [ 0 ] , ident [ 1 ] ) ) ;
1000
1129
const dep = new CssLocalIdentifierDependency ( name , [
1001
1130
ident [ 0 ] ,
1002
1131
ident [ 1 ]
@@ -1013,7 +1142,7 @@ class CssParser extends Parser {
1013
1142
hash : ( input , start , end , isID ) => {
1014
1143
if ( isNextRulePrelude && isLocalMode ( ) && isID ) {
1015
1144
const valueStart = start + 1 ;
1016
- const name = input . slice ( valueStart , end ) ;
1145
+ const name = unescapeIdentifier ( input . slice ( valueStart , end ) ) ;
1017
1146
const dep = new CssLocalIdentifierDependency ( name , [ valueStart , end ] ) ;
1018
1147
const { line : sl , column : sc } = locConverter . get ( start ) ;
1019
1148
const { line : el , column : ec } = locConverter . get ( end ) ;
@@ -1258,12 +1387,15 @@ class CssParser extends Parser {
1258
1387
if ( name === "var" ) {
1259
1388
const customIdent = walkCssTokens . eatIdentSequence ( input , end ) ;
1260
1389
if ( ! customIdent ) return end ;
1261
- const name = input . slice ( customIdent [ 0 ] , customIdent [ 1 ] ) ;
1390
+ let name = input . slice ( customIdent [ 0 ] , customIdent [ 1 ] ) ;
1262
1391
// A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
1263
1392
// The <custom-property-name> production corresponds to this:
1264
1393
// it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes),
1265
1394
// except -- itself, which is reserved for future use by CSS.
1266
1395
if ( ! name . startsWith ( "--" ) || name . length < 3 ) return end ;
1396
+ name = unescapeIdentifier (
1397
+ input . slice ( customIdent [ 0 ] + 2 , customIdent [ 1 ] )
1398
+ ) ;
1267
1399
const afterCustomIdent = walkCssTokens . eatWhitespaceAndComments (
1268
1400
input ,
1269
1401
customIdent [ 1 ]
@@ -1301,7 +1433,7 @@ class CssParser extends Parser {
1301
1433
} else if ( from [ 2 ] === false ) {
1302
1434
const dep = new CssIcssImportDependency (
1303
1435
path . slice ( 1 , - 1 ) ,
1304
- name . slice ( 2 ) ,
1436
+ name ,
1305
1437
[ customIdent [ 0 ] , from [ 1 ] - 1 ]
1306
1438
) ;
1307
1439
const { line : sl , column : sc } = locConverter . get (
@@ -1321,7 +1453,7 @@ class CssParser extends Parser {
1321
1453
customIdent [ 1 ]
1322
1454
) ;
1323
1455
const dep = new CssSelfLocalIdentifierDependency (
1324
- name . slice ( 2 ) ,
1456
+ name ,
1325
1457
[ customIdent [ 0 ] , customIdent [ 1 ] ] ,
1326
1458
"--" ,
1327
1459
declaredCssVariables
@@ -1466,3 +1598,5 @@ class CssParser extends Parser {
1466
1598
}
1467
1599
1468
1600
module . exports = CssParser ;
1601
+ module . exports . escapeIdentifier = escapeIdentifier ;
1602
+ module . exports . unescapeIdentifier = unescapeIdentifier ;
0 commit comments