webpack 5.94.0 → 5.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +1 -1
  2. package/lib/AsyncDependenciesBlock.js +1 -1
  3. package/lib/BannerPlugin.js +2 -1
  4. package/lib/Chunk.js +30 -0
  5. package/lib/ChunkGraph.js +11 -6
  6. package/lib/ChunkGroup.js +2 -2
  7. package/lib/CleanPlugin.js +4 -5
  8. package/lib/CodeGenerationResults.js +6 -5
  9. package/lib/Compilation.js +71 -48
  10. package/lib/Compiler.js +7 -5
  11. package/lib/ConcatenationScope.js +7 -20
  12. package/lib/ContextModule.js +7 -8
  13. package/lib/CssModule.js +25 -21
  14. package/lib/DefinePlugin.js +14 -8
  15. package/lib/DelegatedModule.js +3 -3
  16. package/lib/DllModule.js +4 -4
  17. package/lib/DynamicEntryPlugin.js +29 -22
  18. package/lib/EnvironmentPlugin.js +3 -2
  19. package/lib/EvalDevToolModulePlugin.js +5 -2
  20. package/lib/EvalSourceMapDevToolPlugin.js +5 -2
  21. package/lib/ExternalModule.js +118 -99
  22. package/lib/ExternalModuleFactoryPlugin.js +33 -9
  23. package/lib/FileSystemInfo.js +12 -8
  24. package/lib/Generator.js +5 -4
  25. package/lib/HotModuleReplacementPlugin.js +8 -6
  26. package/lib/IgnorePlugin.js +19 -1
  27. package/lib/LoaderOptionsPlugin.js +3 -1
  28. package/lib/Module.js +9 -8
  29. package/lib/ModuleSourceTypesConstants.js +100 -0
  30. package/lib/NormalModule.js +27 -13
  31. package/lib/NormalModuleFactory.js +38 -22
  32. package/lib/OptionsApply.js +12 -1
  33. package/lib/ProgressPlugin.js +50 -10
  34. package/lib/RawModule.js +3 -4
  35. package/lib/RuntimeModule.js +3 -4
  36. package/lib/RuntimePlugin.js +11 -4
  37. package/lib/RuntimeTemplate.js +13 -42
  38. package/lib/SourceMapDevToolPlugin.js +10 -7
  39. package/lib/TemplatedPathPlugin.js +9 -3
  40. package/lib/Watching.js +2 -2
  41. package/lib/WebpackOptionsApply.js +42 -21
  42. package/lib/asset/AssetGenerator.js +347 -194
  43. package/lib/asset/AssetModulesPlugin.js +2 -1
  44. package/lib/asset/AssetSourceGenerator.js +82 -27
  45. package/lib/asset/RawDataUrlModule.js +5 -4
  46. package/lib/buildChunkGraph.js +79 -62
  47. package/lib/cache/PackFileCacheStrategy.js +69 -31
  48. package/lib/cache/ResolverCachePlugin.js +248 -173
  49. package/lib/config/defaults.js +135 -126
  50. package/lib/container/ContainerEntryModule.js +3 -4
  51. package/lib/container/ContainerPlugin.js +8 -0
  52. package/lib/container/FallbackModule.js +2 -2
  53. package/lib/container/HoistContainerReferencesPlugin.js +250 -0
  54. package/lib/container/ModuleFederationPlugin.js +38 -1
  55. package/lib/container/RemoteModule.js +4 -2
  56. package/lib/container/RemoteRuntimeModule.js +4 -2
  57. package/lib/css/CssExportsGenerator.js +16 -12
  58. package/lib/css/CssGenerator.js +22 -16
  59. package/lib/css/CssLoadingRuntimeModule.js +7 -6
  60. package/lib/css/CssModulesPlugin.js +122 -77
  61. package/lib/css/CssParser.js +655 -526
  62. package/lib/css/walkCssTokens.js +1168 -338
  63. package/lib/debug/ProfilingPlugin.js +5 -0
  64. package/lib/dependencies/CommonJsExportsParserPlugin.js +5 -2
  65. package/lib/dependencies/CommonJsImportsParserPlugin.js +3 -6
  66. package/lib/dependencies/ContextDependency.js +6 -1
  67. package/lib/dependencies/ContextElementDependency.js +33 -6
  68. package/lib/dependencies/CssExportDependency.js +3 -3
  69. package/lib/dependencies/CssLocalIdentifierDependency.js +26 -17
  70. package/lib/dependencies/CssUrlDependency.js +33 -3
  71. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +3 -3
  72. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +39 -14
  73. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +15 -82
  74. package/lib/dependencies/HarmonyImportSpecifierDependency.js +5 -2
  75. package/lib/dependencies/ImportParserPlugin.js +9 -7
  76. package/lib/dependencies/LoaderPlugin.js +19 -0
  77. package/lib/dependencies/SystemPlugin.js +2 -1
  78. package/lib/dependencies/URLPlugin.js +7 -1
  79. package/lib/dependencies/WorkerPlugin.js +1 -1
  80. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +4 -2
  81. package/lib/hmr/HotModuleReplacement.runtime.js +1 -0
  82. package/lib/hmr/JavascriptHotModuleReplacement.runtime.js +1 -0
  83. package/lib/hmr/LazyCompilationPlugin.js +16 -4
  84. package/lib/hmr/lazyCompilationBackend.js +1 -7
  85. package/lib/index.js +35 -6
  86. package/lib/javascript/EnableChunkLoadingPlugin.js +2 -2
  87. package/lib/javascript/JavascriptGenerator.js +8 -8
  88. package/lib/javascript/JavascriptModulesPlugin.js +166 -88
  89. package/lib/javascript/JavascriptParser.js +338 -117
  90. package/lib/json/JsonGenerator.js +5 -5
  91. package/lib/library/EnableLibraryPlugin.js +2 -2
  92. package/lib/library/ExportPropertyLibraryPlugin.js +1 -1
  93. package/lib/library/UmdLibraryPlugin.js +16 -8
  94. package/lib/logging/Logger.js +11 -11
  95. package/lib/logging/createConsoleLogger.js +14 -14
  96. package/lib/logging/truncateArgs.js +1 -1
  97. package/lib/node/NodeWatchFileSystem.js +3 -1
  98. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +20 -18
  99. package/lib/node/ReadFileCompileWasmPlugin.js +1 -2
  100. package/lib/node/nodeConsole.js +11 -8
  101. package/lib/optimize/AggressiveSplittingPlugin.js +21 -7
  102. package/lib/optimize/ConcatenatedModule.js +44 -148
  103. package/lib/optimize/FlagIncludedChunksPlugin.js +6 -0
  104. package/lib/optimize/InnerGraphPlugin.js +57 -16
  105. package/lib/optimize/LimitChunkCountPlugin.js +2 -4
  106. package/lib/optimize/MergeDuplicateChunksPlugin.js +2 -2
  107. package/lib/optimize/ModuleConcatenationPlugin.js +4 -2
  108. package/lib/optimize/RealContentHashPlugin.js +1 -1
  109. package/lib/optimize/SideEffectsFlagPlugin.js +6 -3
  110. package/lib/rules/RuleSetCompiler.js +2 -2
  111. package/lib/runtime/GetChunkFilenameRuntimeModule.js +2 -2
  112. package/lib/schemes/DataUriPlugin.js +1 -1
  113. package/lib/serialization/BinaryMiddleware.js +32 -19
  114. package/lib/serialization/ObjectMiddleware.js +23 -9
  115. package/lib/serialization/SerializerMiddleware.js +3 -2
  116. package/lib/serialization/types.js +2 -2
  117. package/lib/sharing/ConsumeSharedModule.js +2 -3
  118. package/lib/sharing/ConsumeSharedRuntimeModule.js +3 -1
  119. package/lib/sharing/ProvideSharedModule.js +2 -3
  120. package/lib/stats/DefaultStatsFactoryPlugin.js +22 -20
  121. package/lib/stats/StatsFactory.js +12 -12
  122. package/lib/stats/StatsPrinter.js +7 -7
  123. package/lib/util/AsyncQueue.js +17 -1
  124. package/lib/util/IterableHelpers.js +1 -1
  125. package/lib/util/LazySet.js +12 -0
  126. package/lib/util/SetHelpers.js +1 -1
  127. package/lib/util/cleverMerge.js +48 -24
  128. package/lib/util/concatenate.js +227 -0
  129. package/lib/util/create-schema-validation.js +22 -9
  130. package/lib/util/deprecation.js +86 -28
  131. package/lib/util/fs.js +10 -10
  132. package/lib/util/hash/wasm-hash.js +12 -1
  133. package/lib/util/magicComment.js +21 -0
  134. package/lib/util/makeSerializable.js +24 -1
  135. package/lib/util/memoize.js +2 -1
  136. package/lib/util/runtime.js +10 -1
  137. package/lib/util/semver.js +130 -23
  138. package/lib/wasm/EnableWasmLoadingPlugin.js +2 -2
  139. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +3 -3
  140. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +5 -5
  141. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +5 -5
  142. package/lib/wasm-sync/WebAssemblyGenerator.js +8 -9
  143. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +5 -5
  144. package/lib/web/FetchCompileAsyncWasmPlugin.js +1 -2
  145. package/lib/web/FetchCompileWasmPlugin.js +1 -2
  146. package/lib/web/JsonpChunkLoadingRuntimeModule.js +6 -6
  147. package/package.json +19 -20
  148. package/schemas/WebpackOptions.check.js +1 -1
  149. package/schemas/WebpackOptions.json +12 -2
  150. package/types.d.ts +817 -269
  151. package/lib/util/mergeScope.js +0 -76
@@ -7,22 +7,21 @@
7
7
 
8
8
  /**
9
9
  * @typedef {object} CssTokenCallbacks
10
- * @property {function(string, number): boolean=} isSelector
11
- * @property {function(string, number, number, number, number): number=} url
12
- * @property {function(string, number, number): number=} string
13
- * @property {function(string, number, number): number=} leftParenthesis
14
- * @property {function(string, number, number): number=} rightParenthesis
15
- * @property {function(string, number, number): number=} pseudoFunction
16
- * @property {function(string, number, number): number=} function
17
- * @property {function(string, number, number): number=} pseudoClass
18
- * @property {function(string, number, number): number=} atKeyword
19
- * @property {function(string, number, number): number=} class
20
- * @property {function(string, number, number): number=} identifier
21
- * @property {function(string, number, number): number=} id
22
- * @property {function(string, number, number): number=} leftCurlyBracket
23
- * @property {function(string, number, number): number=} rightCurlyBracket
24
- * @property {function(string, number, number): number=} semicolon
25
- * @property {function(string, number, number): number=} comma
10
+ * @property {(function(string, number, number, number, number): number)=} url
11
+ * @property {(function(string, number, number): number)=} comment
12
+ * @property {(function(string, number, number): number)=} string
13
+ * @property {(function(string, number, number): number)=} leftParenthesis
14
+ * @property {(function(string, number, number): number)=} rightParenthesis
15
+ * @property {(function(string, number, number): number)=} function
16
+ * @property {(function(string, number, number): number)=} colon
17
+ * @property {(function(string, number, number): number)=} atKeyword
18
+ * @property {(function(string, number, number): number)=} delim
19
+ * @property {(function(string, number, number): number)=} identifier
20
+ * @property {(function(string, number, number, boolean): number)=} hash
21
+ * @property {(function(string, number, number): number)=} leftCurlyBracket
22
+ * @property {(function(string, number, number): number)=} rightCurlyBracket
23
+ * @property {(function(string, number, number): number)=} semicolon
24
+ * @property {(function(string, number, number): number)=} comma
26
25
  */
27
26
 
28
27
  /** @typedef {function(string, number, CssTokenCallbacks): number} CharHandler */
@@ -59,12 +58,14 @@ const CC_AT_SIGN = "@".charCodeAt(0);
59
58
 
60
59
  const CC_LOW_LINE = "_".charCodeAt(0);
61
60
  const CC_LOWER_A = "a".charCodeAt(0);
62
- const CC_LOWER_U = "u".charCodeAt(0);
61
+ const CC_LOWER_F = "f".charCodeAt(0);
63
62
  const CC_LOWER_E = "e".charCodeAt(0);
63
+ const CC_LOWER_U = "u".charCodeAt(0);
64
64
  const CC_LOWER_Z = "z".charCodeAt(0);
65
65
  const CC_UPPER_A = "A".charCodeAt(0);
66
+ const CC_UPPER_F = "F".charCodeAt(0);
66
67
  const CC_UPPER_E = "E".charCodeAt(0);
67
- const CC_UPPER_U = "U".charCodeAt(0);
68
+ const CC_UPPER_U = "E".charCodeAt(0);
68
69
  const CC_UPPER_Z = "Z".charCodeAt(0);
69
70
  const CC_0 = "0".charCodeAt(0);
70
71
  const CC_9 = "9".charCodeAt(0);
@@ -85,12 +86,12 @@ const _isNewLine = cc =>
85
86
 
86
87
  /** @type {CharHandler} */
87
88
  const consumeSpace = (input, pos, _callbacks) => {
88
- /** @type {number} */
89
- let cc;
90
- do {
89
+ // Consume as much whitespace as possible.
90
+ while (_isWhiteSpace(input.charCodeAt(pos))) {
91
91
  pos++;
92
- cc = input.charCodeAt(pos);
93
- } while (_isWhiteSpace(cc));
92
+ }
93
+
94
+ // Return a <whitespace-token>.
94
95
  return pos;
95
96
  };
96
97
 
@@ -127,313 +128,560 @@ const isIdentStartCodePoint = cc =>
127
128
  cc >= 0x80;
128
129
 
129
130
  /** @type {CharHandler} */
130
- const consumeDelimToken = (input, pos, _callbacks) => pos + 1;
131
+ const consumeDelimToken = (input, pos, _callbacks) =>
132
+ // Return a <delim-token> with its value set to the current input code point.
133
+ pos;
131
134
 
132
135
  /** @type {CharHandler} */
133
- const consumeComments = (input, pos, _callbacks) => {
134
- // If the next two input code point are U+002F SOLIDUS (/) followed by a U+002A
135
- // ASTERISK (*), consume them and all following code points up to and including
136
- // the first U+002A ASTERISK (*) followed by a U+002F SOLIDUS (/), or up to an
137
- // EOF code point. Return to the start of this step.
138
- //
139
- // If the preceding paragraph ended by consuming an EOF code point, this is a parse error.
140
- // But we are silent on errors.
141
- if (
136
+ const consumeComments = (input, pos, callbacks) => {
137
+ // This section describes how to consume comments from a stream of code points. It returns nothing.
138
+ // If the next two input code point are U+002F SOLIDUS (/) followed by a U+002A ASTERISK (*),
139
+ // consume them and all following code points up to and including the first U+002A ASTERISK (*)
140
+ // followed by a U+002F SOLIDUS (/), or up to an EOF code point.
141
+ // Return to the start of this step.
142
+ while (
142
143
  input.charCodeAt(pos) === CC_SOLIDUS &&
143
144
  input.charCodeAt(pos + 1) === CC_ASTERISK
144
145
  ) {
145
- pos += 1;
146
- while (pos < input.length) {
146
+ const start = pos;
147
+ pos += 2;
148
+
149
+ for (;;) {
150
+ if (pos === input.length) {
151
+ // If the preceding paragraph ended by consuming an EOF code point, this is a parse error.
152
+ return pos;
153
+ }
154
+
147
155
  if (
148
156
  input.charCodeAt(pos) === CC_ASTERISK &&
149
157
  input.charCodeAt(pos + 1) === CC_SOLIDUS
150
158
  ) {
151
159
  pos += 2;
160
+
161
+ if (callbacks.comment) {
162
+ pos = callbacks.comment(input, start, pos);
163
+ }
164
+
152
165
  break;
153
166
  }
167
+
154
168
  pos++;
155
169
  }
156
170
  }
157
- return pos;
158
- };
159
171
 
160
- /** @type {function(number): CharHandler} */
161
- const consumeString = quoteCc => (input, pos, callbacks) => {
162
- const start = pos;
163
- pos = _consumeString(input, pos, quoteCc);
164
- if (callbacks.string !== undefined) {
165
- pos = callbacks.string(input, start, pos);
166
- }
167
172
  return pos;
168
173
  };
169
174
 
175
+ /**
176
+ * @param {number} cc char code
177
+ * @returns {boolean} true, if cc is a hex digit
178
+ */
179
+ const _isHexDigit = cc =>
180
+ _isDigit(cc) ||
181
+ (cc >= CC_UPPER_A && cc <= CC_UPPER_F) ||
182
+ (cc >= CC_LOWER_A && cc <= CC_LOWER_F);
183
+
170
184
  /**
171
185
  * @param {string} input input
172
186
  * @param {number} pos position
173
- * @param {number} quoteCc quote char code
174
- * @returns {number} new position
187
+ * @returns {number} position
175
188
  */
176
- const _consumeString = (input, pos, quoteCc) => {
189
+ const _consumeAnEscapedCodePoint = (input, pos) => {
190
+ // This section describes how to consume an escaped code point.
191
+ // It assumes that the U+005C REVERSE SOLIDUS (\) has already been consumed and that the next input code point has already been verified to be part of a valid escape.
192
+ // It will return a code point.
193
+
194
+ // Consume the next input code point.
195
+ const cc = input.charCodeAt(pos);
177
196
  pos++;
197
+
198
+ // EOF
199
+ // This is a parse error. Return U+FFFD REPLACEMENT CHARACTER (�).
200
+ if (pos === input.length) {
201
+ return pos;
202
+ }
203
+
204
+ // hex digit
205
+ // Consume as many hex digits as possible, but no more than 5.
206
+ // Note that this means 1-6 hex digits have been consumed in total.
207
+ // If the next input code point is whitespace, consume it as well.
208
+ // Interpret the hex digits as a hexadecimal number.
209
+ // If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point, return U+FFFD REPLACEMENT CHARACTER (�).
210
+ // Otherwise, return the code point with that value.
211
+ if (_isHexDigit(cc)) {
212
+ for (let i = 0; i < 5; i++) {
213
+ if (_isHexDigit(input.charCodeAt(pos))) {
214
+ pos++;
215
+ }
216
+ }
217
+
218
+ if (_isWhiteSpace(input.charCodeAt(pos))) {
219
+ pos++;
220
+ }
221
+
222
+ return pos;
223
+ }
224
+
225
+ // anything else
226
+ // Return the current input code point.
227
+ return pos;
228
+ };
229
+
230
+ /** @type {CharHandler} */
231
+ const consumeAStringToken = (input, pos, callbacks) => {
232
+ // This section describes how to consume a string token from a stream of code points.
233
+ // It returns either a <string-token> or <bad-string-token>.
234
+ //
235
+ // This algorithm may be called with an ending code point, which denotes the code point that ends the string.
236
+ // If an ending code point is not specified, the current input code point is used.
237
+ const start = pos - 1;
238
+ const endingCodePoint = input.charCodeAt(pos - 1);
239
+
240
+ // Initially create a <string-token> with its value set to the empty string.
241
+
242
+ // Repeatedly consume the next input code point from the stream:
178
243
  for (;;) {
179
- if (pos === input.length) return pos;
244
+ // EOF
245
+ // This is a parse error. Return the <string-token>.
246
+ if (pos === input.length) {
247
+ if (callbacks.string !== undefined) {
248
+ return callbacks.string(input, start, pos);
249
+ }
250
+
251
+ return pos;
252
+ }
253
+
180
254
  const cc = input.charCodeAt(pos);
181
- if (cc === quoteCc) return pos + 1;
182
- if (_isNewLine(cc)) {
255
+ pos++;
256
+
257
+ // ending code point
258
+ // Return the <string-token>.
259
+ if (cc === endingCodePoint) {
260
+ if (callbacks.string !== undefined) {
261
+ return callbacks.string(input, start, pos);
262
+ }
263
+
264
+ return pos;
265
+ }
266
+ // newline
267
+ // This is a parse error.
268
+ // Reconsume the current input code point, create a <bad-string-token>, and return it.
269
+ else if (_isNewLine(cc)) {
270
+ pos--;
183
271
  // bad string
184
272
  return pos;
185
273
  }
186
- if (cc === CC_REVERSE_SOLIDUS) {
187
- // we don't need to fully parse the escaped code point
188
- // just skip over a potential new line
189
- pos++;
190
- if (pos === input.length) return pos;
191
- pos++;
192
- } else {
193
- pos++;
274
+ // U+005C REVERSE SOLIDUS (\)
275
+ else if (cc === CC_REVERSE_SOLIDUS) {
276
+ // If the next input code point is EOF, do nothing.
277
+ if (pos === input.length) {
278
+ return pos;
279
+ }
280
+ // Otherwise, if the next input code point is a newline, consume it.
281
+ else if (_isNewLine(input.charCodeAt(pos))) {
282
+ pos++;
283
+ }
284
+ // Otherwise, (the stream starts with a valid escape) consume an escaped code point and append the returned code point to the <string-token>’s value.
285
+ else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
286
+ pos = _consumeAnEscapedCodePoint(input, pos);
287
+ }
288
+ }
289
+ // anything else
290
+ // Append the current input code point to the <string-token>’s value.
291
+ else {
292
+ // Append
194
293
  }
195
294
  }
196
295
  };
197
296
 
198
297
  /**
199
298
  * @param {number} cc char code
200
- * @returns {boolean} is identifier start code
299
+ * @param {number} q char code
300
+ * @returns {boolean} is non-ASCII code point
201
301
  */
202
- const _isIdentifierStartCode = cc =>
203
- cc === CC_LOW_LINE ||
204
- (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
205
- (cc >= CC_UPPER_A && cc <= CC_UPPER_Z) ||
302
+ const isNonASCIICodePoint = (cc, q) =>
303
+ // Simplify
206
304
  cc > 0x80;
305
+ /**
306
+ * @param {number} cc char code
307
+ * @returns {boolean} is letter
308
+ */
309
+ const isLetter = cc =>
310
+ (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
311
+ (cc >= CC_UPPER_A && cc <= CC_UPPER_Z);
312
+
313
+ /**
314
+ * @param {number} cc char code
315
+ * @param {number} q char code
316
+ * @returns {boolean} is identifier start code
317
+ */
318
+ const _isIdentStartCodePoint = (cc, q) =>
319
+ isLetter(cc) || isNonASCIICodePoint(cc, q) || cc === CC_LOW_LINE;
320
+
321
+ /**
322
+ * @param {number} cc char code
323
+ * @param {number} q char code
324
+ * @returns {boolean} is identifier code
325
+ */
326
+ const _isIdentCodePoint = (cc, q) =>
327
+ _isIdentStartCodePoint(cc, q) || _isDigit(cc) || cc === CC_HYPHEN_MINUS;
328
+ /**
329
+ * @param {number} cc char code
330
+ * @returns {boolean} is digit
331
+ */
332
+ const _isDigit = cc => cc >= CC_0 && cc <= CC_9;
207
333
 
208
334
  /**
209
- * @param {number} first first code point
210
- * @param {number} second second code point
335
+ * @param {string} input input
336
+ * @param {number} pos position
337
+ * @param {number=} f first code point
338
+ * @param {number=} s second code point
211
339
  * @returns {boolean} true if two code points are a valid escape
212
340
  */
213
- const _isTwoCodePointsAreValidEscape = (first, second) => {
341
+ const _ifTwoCodePointsAreValidEscape = (input, pos, f, s) => {
342
+ // This section describes how to check if two code points are a valid escape.
343
+ // The algorithm described here can be called explicitly with two code points, or can be called with the input stream itself.
344
+ // In the latter case, the two code points in question are the current input code point and the next input code point, in that order.
345
+
346
+ // Note: This algorithm will not consume any additional code point.
347
+ const first = f || input.charCodeAt(pos - 1);
348
+ const second = s || input.charCodeAt(pos);
349
+
350
+ // If the first code point is not U+005C REVERSE SOLIDUS (\), return false.
214
351
  if (first !== CC_REVERSE_SOLIDUS) return false;
352
+ // Otherwise, if the second code point is a newline, return false.
215
353
  if (_isNewLine(second)) return false;
354
+ // Otherwise, return true.
216
355
  return true;
217
356
  };
218
357
 
219
358
  /**
220
- * @param {number} cc char code
221
- * @returns {boolean} is digit
359
+ * @param {string} input input
360
+ * @param {number} pos position
361
+ * @param {number=} f first
362
+ * @param {number=} s second
363
+ * @param {number=} t third
364
+ * @returns {boolean} true, if input at pos starts an identifier
222
365
  */
223
- const _isDigit = cc => cc >= CC_0 && cc <= CC_9;
366
+ const _ifThreeCodePointsWouldStartAnIdentSequence = (input, pos, f, s, t) => {
367
+ // This section describes how to check if three code points would start an ident sequence.
368
+ // The algorithm described here can be called explicitly with three code points, or can be called with the input stream itself.
369
+ // In the latter case, the three code points in question are the current input code point and the next two input code points, in that order.
370
+
371
+ // Note: This algorithm will not consume any additional code points.
372
+
373
+ const first = f || input.charCodeAt(pos - 1);
374
+ const second = s || input.charCodeAt(pos);
375
+ const third = t || input.charCodeAt(pos + 1);
376
+
377
+ // Look at the first code point:
378
+
379
+ // U+002D HYPHEN-MINUS
380
+ if (first === CC_HYPHEN_MINUS) {
381
+ // If the second code point is an ident-start code point or a U+002D HYPHEN-MINUS
382
+ // or a U+002D HYPHEN-MINUS, or the second and third code points are a valid escape, return true.
383
+ if (
384
+ _isIdentStartCodePoint(second, pos) ||
385
+ second === CC_HYPHEN_MINUS ||
386
+ _ifTwoCodePointsAreValidEscape(input, pos, second, third)
387
+ ) {
388
+ return true;
389
+ }
390
+ return false;
391
+ }
392
+ // ident-start code point
393
+ else if (_isIdentStartCodePoint(first, pos - 1)) {
394
+ return true;
395
+ }
396
+ // U+005C REVERSE SOLIDUS (\)
397
+ // If the first and second code points are a valid escape, return true. Otherwise, return false.
398
+ else if (first === CC_REVERSE_SOLIDUS) {
399
+ if (_ifTwoCodePointsAreValidEscape(input, pos, first, second)) {
400
+ return true;
401
+ }
402
+
403
+ return false;
404
+ }
405
+ // anything else
406
+ // Return false.
407
+ return false;
408
+ };
224
409
 
225
410
  /**
226
411
  * @param {string} input input
227
412
  * @param {number} pos position
413
+ * @param {number=} f first
414
+ * @param {number=} s second
415
+ * @param {number=} t third
228
416
  * @returns {boolean} true, if input at pos starts an identifier
229
417
  */
230
- const _startsIdentifier = (input, pos) => {
231
- const cc = input.charCodeAt(pos);
232
- if (cc === CC_HYPHEN_MINUS) {
233
- if (pos === input.length) return false;
234
- const cc = input.charCodeAt(pos + 1);
235
- if (cc === CC_HYPHEN_MINUS) return true;
236
- if (cc === CC_REVERSE_SOLIDUS) {
237
- const cc = input.charCodeAt(pos + 2);
238
- return !_isNewLine(cc);
418
+ const _ifThreeCodePointsWouldStartANumber = (input, pos, f, s, t) => {
419
+ // This section describes how to check if three code points would start a number.
420
+ // The algorithm described here can be called explicitly with three code points, or can be called with the input stream itself.
421
+ // In the latter case, the three code points in question are the current input code point and the next two input code points, in that order.
422
+
423
+ // Note: This algorithm will not consume any additional code points.
424
+
425
+ const first = f || input.charCodeAt(pos - 1);
426
+ const second = s || input.charCodeAt(pos);
427
+ const third = t || input.charCodeAt(pos);
428
+
429
+ // Look at the first code point:
430
+
431
+ // U+002B PLUS SIGN (+)
432
+ // U+002D HYPHEN-MINUS (-)
433
+ //
434
+ // If the second code point is a digit, return true.
435
+ // Otherwise, if the second code point is a U+002E FULL STOP (.) and the third code point is a digit, return true.
436
+ // Otherwise, return false.
437
+ if (first === CC_PLUS_SIGN || first === CC_HYPHEN_MINUS) {
438
+ if (_isDigit(second)) {
439
+ return true;
440
+ } else if (second === CC_FULL_STOP && _isDigit(third)) {
441
+ return true;
442
+ }
443
+
444
+ return false;
445
+ }
446
+ // U+002E FULL STOP (.)
447
+ // If the second code point is a digit, return true. Otherwise, return false.
448
+ else if (first === CC_FULL_STOP) {
449
+ if (_isDigit(second)) {
450
+ return true;
239
451
  }
240
- return _isIdentifierStartCode(cc);
452
+
453
+ return false;
241
454
  }
242
- if (cc === CC_REVERSE_SOLIDUS) {
243
- const cc = input.charCodeAt(pos + 1);
244
- return !_isNewLine(cc);
455
+ // digit
456
+ // Return true.
457
+ else if (_isDigit(first)) {
458
+ return true;
245
459
  }
246
- return _isIdentifierStartCode(cc);
460
+
461
+ // anything else
462
+ // Return false.
463
+ return false;
247
464
  };
248
465
 
249
466
  /** @type {CharHandler} */
250
467
  const consumeNumberSign = (input, pos, callbacks) => {
251
- const start = pos;
252
- pos++;
253
- if (pos === input.length) return pos;
468
+ // If the next input code point is an ident code point or the next two input code points are a valid escape, then:
469
+ // - Create a <hash-token>.
470
+ // - If the next 3 input code points would start an ident sequence, set the <hash-token>’s type flag to "id".
471
+ // - Consume an ident sequence, and set the <hash-token>’s value to the returned string.
472
+ // - Return the <hash-token>.
473
+ const start = pos - 1;
474
+ const first = input.charCodeAt(pos);
475
+ const second = input.charCodeAt(pos + 1);
476
+
254
477
  if (
255
- callbacks.isSelector &&
256
- callbacks.isSelector(input, pos) &&
257
- _startsIdentifier(input, pos)
478
+ _isIdentCodePoint(first, pos - 1) ||
479
+ _ifTwoCodePointsAreValidEscape(input, pos, first, second)
258
480
  ) {
259
- pos = _consumeIdentifier(input, pos, callbacks);
260
- if (callbacks.id !== undefined) {
261
- return callbacks.id(input, start, pos);
481
+ const third = input.charCodeAt(pos + 2);
482
+ let isId = false;
483
+
484
+ if (
485
+ _ifThreeCodePointsWouldStartAnIdentSequence(
486
+ input,
487
+ pos,
488
+ first,
489
+ second,
490
+ third
491
+ )
492
+ ) {
493
+ isId = true;
494
+ }
495
+
496
+ pos = _consumeAnIdentSequence(input, pos, callbacks);
497
+
498
+ if (callbacks.hash !== undefined) {
499
+ return callbacks.hash(input, start, pos, isId);
262
500
  }
501
+
502
+ return pos;
263
503
  }
504
+
505
+ // Otherwise, return a <delim-token> with its value set to the current input code point.
264
506
  return pos;
265
507
  };
266
508
 
267
509
  /** @type {CharHandler} */
268
- const consumeMinus = (input, pos, callbacks) => {
269
- const start = pos;
270
- pos++;
271
- if (pos === input.length) return pos;
272
- const cc = input.charCodeAt(pos);
510
+ const consumeHyphenMinus = (input, pos, callbacks) => {
273
511
  // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
274
- if (cc === CC_FULL_STOP || _isDigit(cc)) {
275
- return consumeNumericToken(input, pos, callbacks);
276
- } else if (cc === CC_HYPHEN_MINUS) {
277
- pos++;
278
- if (pos === input.length) return pos;
279
- const cc = input.charCodeAt(pos);
280
- if (cc === CC_GREATER_THAN_SIGN) {
281
- return pos + 1;
282
- }
283
- pos = _consumeIdentifier(input, pos, callbacks);
284
- if (callbacks.identifier !== undefined) {
285
- return callbacks.identifier(input, start, pos);
286
- }
287
- } else if (cc === CC_REVERSE_SOLIDUS) {
288
- if (pos + 1 === input.length) return pos;
289
- const cc = input.charCodeAt(pos + 1);
290
- if (_isNewLine(cc)) return pos;
291
- pos = _consumeIdentifier(input, pos, callbacks);
292
- if (callbacks.identifier !== undefined) {
293
- return callbacks.identifier(input, start, pos);
294
- }
295
- } else if (_isIdentifierStartCode(cc)) {
296
- pos = consumeOtherIdentifier(input, pos - 1, callbacks);
512
+ if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
513
+ pos--;
514
+ return consumeANumericToken(input, pos, callbacks);
515
+ }
516
+ // Otherwise, if the next 2 input code points are U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->), consume them and return a <CDC-token>.
517
+ else if (
518
+ input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
519
+ input.charCodeAt(pos + 1) === CC_GREATER_THAN_SIGN
520
+ ) {
521
+ return pos + 2;
522
+ }
523
+ // Otherwise, if the input stream starts with an ident sequence, reconsume the current input code point, consume an ident-like token, and return it.
524
+ else if (_ifThreeCodePointsWouldStartAnIdentSequence(input, pos)) {
525
+ pos--;
526
+ return consumeAnIdentLikeToken(input, pos, callbacks);
297
527
  }
528
+
529
+ // Otherwise, return a <delim-token> with its value set to the current input code point.
298
530
  return pos;
299
531
  };
300
532
 
301
533
  /** @type {CharHandler} */
302
- const consumeDot = (input, pos, callbacks) => {
303
- const start = pos;
304
- pos++;
305
- if (pos === input.length) return pos;
306
- const cc = input.charCodeAt(pos);
307
- if (_isDigit(cc)) return consumeNumericToken(input, pos - 2, callbacks);
308
- if (
309
- (callbacks.isSelector && !callbacks.isSelector(input, pos)) ||
310
- !_startsIdentifier(input, pos)
311
- )
312
- return pos;
313
- pos = _consumeIdentifier(input, pos, callbacks);
314
- if (callbacks.class !== undefined) return callbacks.class(input, start, pos);
534
+ const consumeFullStop = (input, pos, callbacks) => {
535
+ const start = pos - 1;
536
+
537
+ // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
538
+ if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
539
+ pos--;
540
+ return consumeANumericToken(input, pos, callbacks);
541
+ }
542
+
543
+ // Otherwise, return a <delim-token> with its value set to the current input code point.
544
+ if (callbacks.delim !== undefined) {
545
+ return callbacks.delim(input, start, pos);
546
+ }
547
+
315
548
  return pos;
316
549
  };
317
550
 
318
551
  /** @type {CharHandler} */
319
- const consumeNumericToken = (input, pos, callbacks) => {
320
- pos = _consumeNumber(input, pos, callbacks);
321
- if (pos === input.length) return pos;
322
- if (_startsIdentifier(input, pos))
323
- return _consumeIdentifier(input, pos, callbacks);
324
- const cc = input.charCodeAt(pos);
325
- if (cc === CC_PERCENTAGE) return pos + 1;
552
+ const consumePlusSign = (input, pos, callbacks) => {
553
+ // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
554
+ if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
555
+ pos--;
556
+ return consumeANumericToken(input, pos, callbacks);
557
+ }
558
+
559
+ // Otherwise, return a <delim-token> with its value set to the current input code point.
326
560
  return pos;
327
561
  };
328
562
 
329
563
  /** @type {CharHandler} */
330
- const consumeOtherIdentifier = (input, pos, callbacks) => {
331
- const start = pos;
332
- pos = _consumeIdentifier(input, pos, callbacks);
333
- if (pos !== input.length && input.charCodeAt(pos) === CC_LEFT_PARENTHESIS) {
564
+ const _consumeANumber = (input, pos) => {
565
+ // This section describes how to consume a number from a stream of code points.
566
+ // It returns a numeric value, and a type which is either "integer" or "number".
567
+
568
+ // Execute the following steps in order:
569
+ // Initially set type to "integer". Let repr be the empty string.
570
+
571
+ // If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), consume it and append it to repr.
572
+ if (
573
+ input.charCodeAt(pos) === CC_HYPHEN_MINUS ||
574
+ input.charCodeAt(pos) === CC_PLUS_SIGN
575
+ ) {
334
576
  pos++;
335
- if (callbacks.function !== undefined) {
336
- return callbacks.function(input, start, pos);
337
- }
338
- } else if (callbacks.identifier !== undefined) {
339
- return callbacks.identifier(input, start, pos);
340
577
  }
341
- return pos;
342
- };
343
578
 
344
- /** @type {CharHandler} */
345
- const consumePotentialUrl = (input, pos, callbacks) => {
346
- const start = pos;
347
- pos = _consumeIdentifier(input, pos, callbacks);
348
- const nextPos = pos + 1;
579
+ // While the next input code point is a digit, consume it and append it to repr.
580
+ while (_isDigit(input.charCodeAt(pos))) {
581
+ pos++;
582
+ }
583
+
584
+ // If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then:
585
+ // 1. Consume the next input code point and append it to number part.
586
+ // 2. While the next input code point is a digit, consume it and append it to number part.
587
+ // 3. Set type to "number".
349
588
  if (
350
- pos === start + 3 &&
351
- input.slice(start, nextPos).toLowerCase() === "url("
589
+ input.charCodeAt(pos) === CC_FULL_STOP &&
590
+ _isDigit(input.charCodeAt(pos + 1))
352
591
  ) {
353
592
  pos++;
354
- let cc = input.charCodeAt(pos);
355
- while (_isWhiteSpace(cc)) {
593
+
594
+ while (_isDigit(input.charCodeAt(pos))) {
356
595
  pos++;
357
- if (pos === input.length) return pos;
358
- cc = input.charCodeAt(pos);
359
596
  }
360
- if (cc === CC_QUOTATION_MARK || cc === CC_APOSTROPHE) {
361
- if (callbacks.function !== undefined) {
362
- return callbacks.function(input, start, nextPos);
363
- }
364
- return nextPos;
365
- }
366
- const contentStart = pos;
367
- /** @type {number} */
368
- let contentEnd;
369
- for (;;) {
370
- if (cc === CC_REVERSE_SOLIDUS) {
371
- pos++;
372
- if (pos === input.length) return pos;
373
- pos++;
374
- } else if (_isWhiteSpace(cc)) {
375
- contentEnd = pos;
376
- do {
377
- pos++;
378
- if (pos === input.length) return pos;
379
- cc = input.charCodeAt(pos);
380
- } while (_isWhiteSpace(cc));
381
- if (cc !== CC_RIGHT_PARENTHESIS) return pos;
382
- pos++;
383
- if (callbacks.url !== undefined) {
384
- return callbacks.url(input, start, pos, contentStart, contentEnd);
385
- }
386
- return pos;
387
- } else if (cc === CC_RIGHT_PARENTHESIS) {
388
- contentEnd = pos;
389
- pos++;
390
- if (callbacks.url !== undefined) {
391
- return callbacks.url(input, start, pos, contentStart, contentEnd);
392
- }
393
- return pos;
394
- } else if (cc === CC_LEFT_PARENTHESIS) {
395
- return pos;
396
- } else {
397
- pos++;
398
- }
399
- if (pos === input.length) return pos;
400
- cc = input.charCodeAt(pos);
597
+ }
598
+
599
+ // If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e), optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+), followed by a digit, then:
600
+ // 1. Consume the next input code point.
601
+ // 2. If the next input code point is "+" or "-", consume it and append it to exponent part.
602
+ // 3. While the next input code point is a digit, consume it and append it to exponent part.
603
+ // 4. Set type to "number".
604
+ if (
605
+ (input.charCodeAt(pos) === CC_LOWER_E ||
606
+ input.charCodeAt(pos) === CC_UPPER_E) &&
607
+ (((input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS ||
608
+ input.charCodeAt(pos + 1) === CC_PLUS_SIGN) &&
609
+ _isDigit(input.charCodeAt(pos + 2))) ||
610
+ _isDigit(input.charCodeAt(pos + 1)))
611
+ ) {
612
+ pos++;
613
+
614
+ if (
615
+ input.charCodeAt(pos) === CC_PLUS_SIGN ||
616
+ input.charCodeAt(pos) === CC_HYPHEN_MINUS
617
+ ) {
618
+ pos++;
401
619
  }
402
- } else {
403
- if (callbacks.identifier !== undefined) {
404
- return callbacks.identifier(input, start, pos);
620
+
621
+ while (_isDigit(input.charCodeAt(pos))) {
622
+ pos++;
405
623
  }
406
- return pos;
407
624
  }
625
+
626
+ // Let value be the result of interpreting number part as a base-10 number.
627
+
628
+ // If exponent part is non-empty, interpret it as a base-10 integer, then raise 10 to the power of the result, multiply it by value, and set value to that result.
629
+
630
+ // Return value and type.
631
+ return pos;
408
632
  };
409
633
 
410
634
  /** @type {CharHandler} */
411
- const consumePotentialPseudo = (input, pos, callbacks) => {
412
- const start = pos;
413
- pos++;
635
+ const consumeANumericToken = (input, pos, callbacks) => {
636
+ // This section describes how to consume a numeric token from a stream of code points.
637
+ // It returns either a <number-token>, <percentage-token>, or <dimension-token>.
638
+
639
+ // Consume a number and let number be the result.
640
+ pos = _consumeANumber(input, pos, callbacks);
641
+
642
+ // If the next 3 input code points would start an ident sequence, then:
643
+ //
644
+ // - Create a <dimension-token> with the same value and type flag as number, and a unit set initially to the empty string.
645
+ // - Consume an ident sequence. Set the <dimension-token>’s unit to the returned value.
646
+ // - Return the <dimension-token>.
647
+
648
+ const first = input.charCodeAt(pos);
649
+ const second = input.charCodeAt(pos + 1);
650
+ const third = input.charCodeAt(pos + 2);
651
+
414
652
  if (
415
- (callbacks.isSelector && !callbacks.isSelector(input, pos)) ||
416
- !_startsIdentifier(input, pos)
417
- )
418
- return pos;
419
- pos = _consumeIdentifier(input, pos, callbacks);
420
- const cc = input.charCodeAt(pos);
421
- if (cc === CC_LEFT_PARENTHESIS) {
422
- pos++;
423
- if (callbacks.pseudoFunction !== undefined) {
424
- return callbacks.pseudoFunction(input, start, pos);
425
- }
426
- return pos;
653
+ _ifThreeCodePointsWouldStartAnIdentSequence(
654
+ input,
655
+ pos,
656
+ first,
657
+ second,
658
+ third
659
+ )
660
+ ) {
661
+ return _consumeAnIdentSequence(input, pos, callbacks);
427
662
  }
428
- if (callbacks.pseudoClass !== undefined) {
429
- return callbacks.pseudoClass(input, start, pos);
663
+ // Otherwise, if the next input code point is U+0025 PERCENTAGE SIGN (%), consume it.
664
+ // Create a <percentage-token> with the same value as number, and return it.
665
+ else if (first === CC_PERCENTAGE) {
666
+ return pos + 1;
667
+ }
668
+
669
+ // Otherwise, create a <number-token> with the same value and type flag as number, and return it.
670
+ return pos;
671
+ };
672
+
673
+ /** @type {CharHandler} */
674
+ const consumeColon = (input, pos, callbacks) => {
675
+ // Return a <colon-token>.
676
+ if (callbacks.colon !== undefined) {
677
+ return callbacks.colon(input, pos - 1, pos);
430
678
  }
431
679
  return pos;
432
680
  };
433
681
 
434
682
  /** @type {CharHandler} */
435
683
  const consumeLeftParenthesis = (input, pos, callbacks) => {
436
- pos++;
684
+ // Return a <(-token>.
437
685
  if (callbacks.leftParenthesis !== undefined) {
438
686
  return callbacks.leftParenthesis(input, pos - 1, pos);
439
687
  }
@@ -442,16 +690,26 @@ const consumeLeftParenthesis = (input, pos, callbacks) => {
442
690
 
443
691
  /** @type {CharHandler} */
444
692
  const consumeRightParenthesis = (input, pos, callbacks) => {
445
- pos++;
693
+ // Return a <)-token>.
446
694
  if (callbacks.rightParenthesis !== undefined) {
447
695
  return callbacks.rightParenthesis(input, pos - 1, pos);
448
696
  }
449
697
  return pos;
450
698
  };
451
699
 
700
+ /** @type {CharHandler} */
701
+ const consumeLeftSquareBracket = (input, pos, callbacks) =>
702
+ // Return a <]-token>.
703
+ pos;
704
+
705
+ /** @type {CharHandler} */
706
+ const consumeRightSquareBracket = (input, pos, callbacks) =>
707
+ // Return a <]-token>.
708
+ pos;
709
+
452
710
  /** @type {CharHandler} */
453
711
  const consumeLeftCurlyBracket = (input, pos, callbacks) => {
454
- pos++;
712
+ // Return a <{-token>.
455
713
  if (callbacks.leftCurlyBracket !== undefined) {
456
714
  return callbacks.leftCurlyBracket(input, pos - 1, pos);
457
715
  }
@@ -460,7 +718,7 @@ const consumeLeftCurlyBracket = (input, pos, callbacks) => {
460
718
 
461
719
  /** @type {CharHandler} */
462
720
  const consumeRightCurlyBracket = (input, pos, callbacks) => {
463
- pos++;
721
+ // Return a <}-token>.
464
722
  if (callbacks.rightCurlyBracket !== undefined) {
465
723
  return callbacks.rightCurlyBracket(input, pos - 1, pos);
466
724
  }
@@ -469,7 +727,7 @@ const consumeRightCurlyBracket = (input, pos, callbacks) => {
469
727
 
470
728
  /** @type {CharHandler} */
471
729
  const consumeSemicolon = (input, pos, callbacks) => {
472
- pos++;
730
+ // Return a <semicolon-token>.
473
731
  if (callbacks.semicolon !== undefined) {
474
732
  return callbacks.semicolon(input, pos - 1, pos);
475
733
  }
@@ -478,7 +736,7 @@ const consumeSemicolon = (input, pos, callbacks) => {
478
736
 
479
737
  /** @type {CharHandler} */
480
738
  const consumeComma = (input, pos, callbacks) => {
481
- pos++;
739
+ // Return a <comma-token>.
482
740
  if (callbacks.comma !== undefined) {
483
741
  return callbacks.comma(input, pos - 1, pos);
484
742
  }
@@ -486,117 +744,319 @@ const consumeComma = (input, pos, callbacks) => {
486
744
  };
487
745
 
488
746
  /** @type {CharHandler} */
489
- const _consumeIdentifier = (input, pos) => {
747
+ const _consumeAnIdentSequence = (input, pos) => {
748
+ // This section describes how to consume an ident sequence from a stream of code points.
749
+ // It returns a string containing the largest name that can be formed from adjacent code points in the stream, starting from the first.
750
+
751
+ // Note: This algorithm does not do the verification of the first few code points that are necessary to ensure the returned code points would constitute an <ident-token>.
752
+ // If that is the intended use, ensure that the stream starts with an ident sequence before calling this algorithm.
753
+
754
+ // Let result initially be an empty string.
755
+
756
+ // Repeatedly consume the next input code point from the stream:
490
757
  for (;;) {
491
758
  const cc = input.charCodeAt(pos);
492
- if (cc === CC_REVERSE_SOLIDUS) {
493
- pos++;
494
- if (pos === input.length) return pos;
495
- pos++;
496
- } else if (
497
- _isIdentifierStartCode(cc) ||
498
- _isDigit(cc) ||
499
- cc === CC_HYPHEN_MINUS
500
- ) {
501
- pos++;
502
- } else {
503
- return pos;
759
+ pos++;
760
+
761
+ // ident code point
762
+ // Append the code point to result.
763
+ if (_isIdentCodePoint(cc, pos - 1)) {
764
+ // Nothing
504
765
  }
505
- }
766
+ // the stream starts with a valid escape
767
+ // Consume an escaped code point. Append the returned code point to result.
768
+ else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
769
+ pos = _consumeAnEscapedCodePoint(input, pos);
770
+ }
771
+ // anything else
772
+ // Reconsume the current input code point. Return result.
773
+ else {
774
+ return pos - 1;
775
+ }
776
+ }
506
777
  };
507
778
 
508
- /** @type {CharHandler} */
509
- const _consumeNumber = (input, pos) => {
510
- pos++;
511
- if (pos === input.length) return pos;
512
- let cc = input.charCodeAt(pos);
513
- while (_isDigit(cc)) {
779
+ /**
780
+ * @param {number} cc char code
781
+ * @returns {boolean} true, when cc is the non-printable code point, otherwise false
782
+ */
783
+ const _isNonPrintableCodePoint = cc =>
784
+ (cc >= 0x00 && cc <= 0x08) ||
785
+ cc === 0x0b ||
786
+ (cc >= 0x0e && cc <= 0x1f) ||
787
+ cc === 0x7f;
788
+
789
+ /**
790
+ * @param {string} input input
791
+ * @param {number} pos position
792
+ * @returns {number} position
793
+ */
794
+ const consumeTheRemnantsOfABadUrl = (input, pos) => {
795
+ // This section describes how to consume the remnants of a bad url from a stream of code points,
796
+ // "cleaning up" after the tokenizer realizes that it’s in the middle of a <bad-url-token> rather than a <url-token>.
797
+ // It returns nothing; its sole use is to consume enough of the input stream to reach a recovery point where normal tokenizing can resume.
798
+
799
+ // Repeatedly consume the next input code point from the stream:
800
+ for (;;) {
801
+ // EOF
802
+ // Return.
803
+ if (pos === input.length) {
804
+ return pos;
805
+ }
806
+
807
+ const cc = input.charCodeAt(pos);
514
808
  pos++;
515
- if (pos === input.length) return pos;
516
- cc = input.charCodeAt(pos);
517
- }
518
- if (cc === CC_FULL_STOP && pos + 1 !== input.length) {
519
- const next = input.charCodeAt(pos + 1);
520
- if (_isDigit(next)) {
521
- pos += 2;
522
- cc = input.charCodeAt(pos);
523
- while (_isDigit(cc)) {
524
- pos++;
525
- if (pos === input.length) return pos;
526
- cc = input.charCodeAt(pos);
527
- }
809
+
810
+ // U+0029 RIGHT PARENTHESIS ())
811
+ // Return.
812
+ if (cc === CC_RIGHT_PARENTHESIS) {
813
+ return pos;
814
+ }
815
+ // the input stream starts with a valid escape
816
+ // Consume an escaped code point.
817
+ // This allows an escaped right parenthesis ("\)") to be encountered without ending the <bad-url-token>.
818
+ // This is otherwise identical to the "anything else" clause.
819
+ else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
820
+ pos = _consumeAnEscapedCodePoint(input, pos);
821
+ }
822
+ // anything else
823
+ // Do nothing.
824
+ else {
825
+ // Do nothing.
528
826
  }
529
827
  }
530
- if (cc === CC_LOWER_E || cc === CC_UPPER_E) {
531
- if (pos + 1 !== input.length) {
532
- const next = input.charCodeAt(pos + 2);
533
- if (_isDigit(next)) {
534
- pos += 2;
535
- } else if (
536
- (next === CC_HYPHEN_MINUS || next === CC_PLUS_SIGN) &&
537
- pos + 2 !== input.length
538
- ) {
539
- const next = input.charCodeAt(pos + 2);
540
- if (_isDigit(next)) {
541
- pos += 3;
542
- } else {
543
- return pos;
828
+ };
829
+
830
+ /**
831
+ * @param {string} input input
832
+ * @param {number} pos position
833
+ * @param {number} fnStart start
834
+ * @param {CssTokenCallbacks} callbacks callbacks
835
+ * @returns {pos} pos
836
+ */
837
+ const consumeAUrlToken = (input, pos, fnStart, callbacks) => {
838
+ // This section describes how to consume a url token from a stream of code points.
839
+ // It returns either a <url-token> or a <bad-url-token>.
840
+
841
+ // Note: This algorithm assumes that the initial "url(" has already been consumed.
842
+ // This algorithm also assumes that it’s being called to consume an "unquoted" value, like url(foo).
843
+ // A quoted value, like url("foo"), is parsed as a <function-token>.
844
+ // Consume an ident-like token automatically handles this distinction; this algorithm shouldn’t be called directly otherwise.
845
+
846
+ // Initially create a <url-token> with its value set to the empty string.
847
+
848
+ // Consume as much whitespace as possible.
849
+ while (_isWhiteSpace(input.charCodeAt(pos))) {
850
+ pos++;
851
+ }
852
+
853
+ const contentStart = pos;
854
+
855
+ // Repeatedly consume the next input code point from the stream:
856
+ for (;;) {
857
+ // EOF
858
+ // This is a parse error. Return the <url-token>.
859
+ if (pos === input.length) {
860
+ if (callbacks.url !== undefined) {
861
+ return callbacks.url(input, fnStart, pos, contentStart, pos - 1);
862
+ }
863
+
864
+ return pos;
865
+ }
866
+
867
+ const cc = input.charCodeAt(pos);
868
+ pos++;
869
+
870
+ // U+0029 RIGHT PARENTHESIS ())
871
+ // Return the <url-token>.
872
+ if (cc === CC_RIGHT_PARENTHESIS) {
873
+ if (callbacks.url !== undefined) {
874
+ return callbacks.url(input, fnStart, pos, contentStart, pos - 1);
875
+ }
876
+
877
+ return pos;
878
+ }
879
+ // whitespace
880
+ // Consume as much whitespace as possible.
881
+ // If the next input code point is U+0029 RIGHT PARENTHESIS ()) or EOF, consume it and return the <url-token>
882
+ // (if EOF was encountered, this is a parse error); otherwise, consume the remnants of a bad url, create a <bad-url-token>, and return it.
883
+ else if (_isWhiteSpace(cc)) {
884
+ const end = pos - 1;
885
+
886
+ while (_isWhiteSpace(input.charCodeAt(pos))) {
887
+ pos++;
888
+ }
889
+
890
+ if (pos === input.length) {
891
+ if (callbacks.url !== undefined) {
892
+ return callbacks.url(input, fnStart, pos, contentStart, end);
544
893
  }
545
- } else {
894
+
546
895
  return pos;
547
896
  }
897
+
898
+ if (input.charCodeAt(pos) === CC_RIGHT_PARENTHESIS) {
899
+ pos++;
900
+
901
+ if (callbacks.url !== undefined) {
902
+ return callbacks.url(input, fnStart, pos, contentStart, end);
903
+ }
904
+
905
+ return pos;
906
+ }
907
+
908
+ // Don't handle bad urls
909
+ return consumeTheRemnantsOfABadUrl(input, pos);
910
+ }
911
+ // U+0022 QUOTATION MARK (")
912
+ // U+0027 APOSTROPHE (')
913
+ // U+0028 LEFT PARENTHESIS (()
914
+ // non-printable code point
915
+ // This is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
916
+ else if (
917
+ cc === CC_QUOTATION_MARK ||
918
+ cc === CC_APOSTROPHE ||
919
+ cc === CC_LEFT_PARENTHESIS ||
920
+ _isNonPrintableCodePoint(cc)
921
+ ) {
922
+ // Don't handle bad urls
923
+ return consumeTheRemnantsOfABadUrl(input, pos);
924
+ }
925
+ // // U+005C REVERSE SOLIDUS (\)
926
+ // // If the stream starts with a valid escape, consume an escaped code point and append the returned code point to the <url-token>’s value.
927
+ // // Otherwise, this is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
928
+ else if (cc === CC_REVERSE_SOLIDUS) {
929
+ if (_ifTwoCodePointsAreValidEscape(input, pos)) {
930
+ pos = _consumeAnEscapedCodePoint(input, pos);
931
+ } else {
932
+ // Don't handle bad urls
933
+ return consumeTheRemnantsOfABadUrl(input, pos);
934
+ }
935
+ }
936
+ // anything else
937
+ // Append the current input code point to the <url-token>’s value.
938
+ else {
939
+ // Nothing
548
940
  }
549
- } else {
550
- return pos;
551
941
  }
552
- cc = input.charCodeAt(pos);
553
- while (_isDigit(cc)) {
942
+ };
943
+
944
+ /** @type {CharHandler} */
945
+ const consumeAnIdentLikeToken = (input, pos, callbacks) => {
946
+ const start = pos;
947
+ // This section describes how to consume an ident-like token from a stream of code points.
948
+ // It returns an <ident-token>, <function-token>, <url-token>, or <bad-url-token>.
949
+ pos = _consumeAnIdentSequence(input, pos, callbacks);
950
+
951
+ // If string’s value is an ASCII case-insensitive match for "url", and the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
952
+ // While the next two input code points are whitespace, consume the next input code point.
953
+ // If the next one or two input code points are U+0022 QUOTATION MARK ("), U+0027 APOSTROPHE ('), or whitespace followed by U+0022 QUOTATION MARK (") or U+0027 APOSTROPHE ('), then create a <function-token> with its value set to string and return it.
954
+ // Otherwise, consume a url token, and return it.
955
+ if (
956
+ input.slice(start, pos).toLowerCase() === "url" &&
957
+ input.charCodeAt(pos) === CC_LEFT_PARENTHESIS
958
+ ) {
554
959
  pos++;
555
- if (pos === input.length) return pos;
556
- cc = input.charCodeAt(pos);
960
+ const end = pos;
961
+
962
+ while (
963
+ _isWhiteSpace(input.charCodeAt(pos)) &&
964
+ _isWhiteSpace(input.charCodeAt(pos + 1))
965
+ ) {
966
+ pos++;
967
+ }
968
+
969
+ if (
970
+ input.charCodeAt(pos) === CC_QUOTATION_MARK ||
971
+ input.charCodeAt(pos) === CC_APOSTROPHE ||
972
+ (_isWhiteSpace(input.charCodeAt(pos)) &&
973
+ (input.charCodeAt(pos + 1) === CC_QUOTATION_MARK ||
974
+ input.charCodeAt(pos + 1) === CC_APOSTROPHE))
975
+ ) {
976
+ if (callbacks.function !== undefined) {
977
+ return callbacks.function(input, start, end);
978
+ }
979
+
980
+ return pos;
981
+ }
982
+
983
+ return consumeAUrlToken(input, pos, start, callbacks);
557
984
  }
985
+
986
+ // Otherwise, if the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
987
+ // Create a <function-token> with its value set to string and return it.
988
+ if (input.charCodeAt(pos) === CC_LEFT_PARENTHESIS) {
989
+ pos++;
990
+
991
+ if (callbacks.function !== undefined) {
992
+ return callbacks.function(input, start, pos);
993
+ }
994
+
995
+ return pos;
996
+ }
997
+
998
+ // Otherwise, create an <ident-token> with its value set to string and return it.
999
+ if (callbacks.identifier !== undefined) {
1000
+ return callbacks.identifier(input, start, pos);
1001
+ }
1002
+
558
1003
  return pos;
559
1004
  };
560
1005
 
561
1006
  /** @type {CharHandler} */
562
1007
  const consumeLessThan = (input, pos, _callbacks) => {
563
- if (input.slice(pos + 1, pos + 4) === "!--") return pos + 4;
564
- return pos + 1;
1008
+ // If the next 3 input code points are U+0021 EXCLAMATION MARK U+002D HYPHEN-MINUS U+002D HYPHEN-MINUS (!--), consume them and return a <CDO-token>.
1009
+ if (input.slice(pos, pos + 3) === "!--") {
1010
+ return pos + 3;
1011
+ }
1012
+
1013
+ // Otherwise, return a <delim-token> with its value set to the current input code point.
1014
+ return pos;
565
1015
  };
566
1016
 
567
1017
  /** @type {CharHandler} */
568
- const consumeAt = (input, pos, callbacks) => {
569
- const start = pos;
570
- pos++;
571
- if (pos === input.length) return pos;
572
- if (_startsIdentifier(input, pos)) {
573
- pos = _consumeIdentifier(input, pos, callbacks);
1018
+ const consumeCommercialAt = (input, pos, callbacks) => {
1019
+ const start = pos - 1;
1020
+
1021
+ // If the next 3 input code points would start an ident sequence, consume an ident sequence, create an <at-keyword-token> with its value set to the returned value, and return it.
1022
+ if (
1023
+ _ifThreeCodePointsWouldStartAnIdentSequence(
1024
+ input,
1025
+ pos,
1026
+ input.charCodeAt(pos),
1027
+ input.charCodeAt(pos + 1),
1028
+ input.charCodeAt(pos + 2)
1029
+ )
1030
+ ) {
1031
+ pos = _consumeAnIdentSequence(input, pos, callbacks);
1032
+
574
1033
  if (callbacks.atKeyword !== undefined) {
575
1034
  pos = callbacks.atKeyword(input, start, pos);
576
1035
  }
1036
+
1037
+ return pos;
577
1038
  }
1039
+
1040
+ // Otherwise, return a <delim-token> with its value set to the current input code point.
578
1041
  return pos;
579
1042
  };
580
1043
 
581
1044
  /** @type {CharHandler} */
582
1045
  const consumeReverseSolidus = (input, pos, callbacks) => {
583
- const start = pos;
584
- pos++;
585
- if (pos === input.length) return pos;
586
1046
  // If the input stream starts with a valid escape, reconsume the current input code point, consume an ident-like token, and return it.
587
- if (
588
- _isTwoCodePointsAreValidEscape(
589
- input.charCodeAt(start),
590
- input.charCodeAt(pos)
591
- )
592
- ) {
593
- return consumeOtherIdentifier(input, pos - 1, callbacks);
1047
+ if (_ifTwoCodePointsAreValidEscape(input, pos)) {
1048
+ pos--;
1049
+ return consumeAnIdentLikeToken(input, pos, callbacks);
594
1050
  }
1051
+
595
1052
  // Otherwise, this is a parse error. Return a <delim-token> with its value set to the current input code point.
596
1053
  return pos;
597
1054
  };
598
1055
 
599
- const CHAR_MAP = Array.from({ length: 0x80 }, (_, cc) => {
1056
+ /** @type {CharHandler} */
1057
+ const consumeAToken = (input, pos, callbacks) => {
1058
+ const cc = input.charCodeAt(pos - 1);
1059
+
600
1060
  // https://drafts.csswg.org/css-syntax/#consume-token
601
1061
  switch (cc) {
602
1062
  // whitespace
@@ -605,77 +1065,94 @@ const CHAR_MAP = Array.from({ length: 0x80 }, (_, cc) => {
605
1065
  case CC_FORM_FEED:
606
1066
  case CC_TAB:
607
1067
  case CC_SPACE:
608
- return consumeSpace;
1068
+ return consumeSpace(input, pos, callbacks);
609
1069
  // U+0022 QUOTATION MARK (")
610
1070
  case CC_QUOTATION_MARK:
611
- return consumeString(cc);
1071
+ return consumeAStringToken(input, pos, callbacks);
612
1072
  // U+0023 NUMBER SIGN (#)
613
1073
  case CC_NUMBER_SIGN:
614
- return consumeNumberSign;
1074
+ return consumeNumberSign(input, pos, callbacks);
615
1075
  // U+0027 APOSTROPHE (')
616
1076
  case CC_APOSTROPHE:
617
- return consumeString(cc);
1077
+ return consumeAStringToken(input, pos, callbacks);
618
1078
  // U+0028 LEFT PARENTHESIS (()
619
1079
  case CC_LEFT_PARENTHESIS:
620
- return consumeLeftParenthesis;
1080
+ return consumeLeftParenthesis(input, pos, callbacks);
621
1081
  // U+0029 RIGHT PARENTHESIS ())
622
1082
  case CC_RIGHT_PARENTHESIS:
623
- return consumeRightParenthesis;
1083
+ return consumeRightParenthesis(input, pos, callbacks);
624
1084
  // U+002B PLUS SIGN (+)
625
1085
  case CC_PLUS_SIGN:
626
- return consumeNumericToken;
1086
+ return consumePlusSign(input, pos, callbacks);
627
1087
  // U+002C COMMA (,)
628
1088
  case CC_COMMA:
629
- return consumeComma;
1089
+ return consumeComma(input, pos, callbacks);
630
1090
  // U+002D HYPHEN-MINUS (-)
631
1091
  case CC_HYPHEN_MINUS:
632
- return consumeMinus;
1092
+ return consumeHyphenMinus(input, pos, callbacks);
633
1093
  // U+002E FULL STOP (.)
634
1094
  case CC_FULL_STOP:
635
- return consumeDot;
1095
+ return consumeFullStop(input, pos, callbacks);
636
1096
  // U+003A COLON (:)
637
1097
  case CC_COLON:
638
- return consumePotentialPseudo;
1098
+ return consumeColon(input, pos, callbacks);
639
1099
  // U+003B SEMICOLON (;)
640
1100
  case CC_SEMICOLON:
641
- return consumeSemicolon;
1101
+ return consumeSemicolon(input, pos, callbacks);
642
1102
  // U+003C LESS-THAN SIGN (<)
643
1103
  case CC_LESS_THAN_SIGN:
644
- return consumeLessThan;
1104
+ return consumeLessThan(input, pos, callbacks);
645
1105
  // U+0040 COMMERCIAL AT (@)
646
1106
  case CC_AT_SIGN:
647
- return consumeAt;
1107
+ return consumeCommercialAt(input, pos, callbacks);
648
1108
  // U+005B LEFT SQUARE BRACKET ([)
649
1109
  case CC_LEFT_SQUARE:
650
- return consumeDelimToken;
1110
+ return consumeLeftSquareBracket(input, pos, callbacks);
651
1111
  // U+005C REVERSE SOLIDUS (\)
652
1112
  case CC_REVERSE_SOLIDUS:
653
- return consumeReverseSolidus;
1113
+ return consumeReverseSolidus(input, pos, callbacks);
654
1114
  // U+005D RIGHT SQUARE BRACKET (])
655
1115
  case CC_RIGHT_SQUARE:
656
- return consumeDelimToken;
1116
+ return consumeRightSquareBracket(input, pos, callbacks);
657
1117
  // U+007B LEFT CURLY BRACKET ({)
658
1118
  case CC_LEFT_CURLY:
659
- return consumeLeftCurlyBracket;
1119
+ return consumeLeftCurlyBracket(input, pos, callbacks);
660
1120
  // U+007D RIGHT CURLY BRACKET (})
661
1121
  case CC_RIGHT_CURLY:
662
- return consumeRightCurlyBracket;
663
- // Optimization
664
- case CC_LOWER_U:
665
- case CC_UPPER_U:
666
- return consumePotentialUrl;
1122
+ return consumeRightCurlyBracket(input, pos, callbacks);
667
1123
  default:
668
1124
  // digit
669
- if (_isDigit(cc)) return consumeNumericToken;
1125
+ // Reconsume the current input code point, consume a numeric token, and return it.
1126
+ if (_isDigit(cc)) {
1127
+ pos--;
1128
+ return consumeANumericToken(input, pos, callbacks);
1129
+ } else if (cc === CC_LOWER_U || cc === CC_UPPER_U) {
1130
+ // If unicode ranges allowed is true and the input stream would start a unicode-range,
1131
+ // reconsume the current input code point, consume a unicode-range token, and return it.
1132
+ // Skip now
1133
+ // if (_ifThreeCodePointsWouldStartAUnicodeRange(input, pos)) {
1134
+ // pos--;
1135
+ // return consumeAUnicodeRangeToken(input, pos, callbacks);
1136
+ // }
1137
+
1138
+ // Otherwise, reconsume the current input code point, consume an ident-like token, and return it.
1139
+ pos--;
1140
+ return consumeAnIdentLikeToken(input, pos, callbacks);
1141
+ }
670
1142
  // ident-start code point
671
- if (isIdentStartCodePoint(cc)) {
672
- return consumeOtherIdentifier;
1143
+ // Reconsume the current input code point, consume an ident-like token, and return it.
1144
+ else if (isIdentStartCodePoint(cc)) {
1145
+ pos--;
1146
+ return consumeAnIdentLikeToken(input, pos, callbacks);
673
1147
  }
1148
+
674
1149
  // EOF, but we don't have it
1150
+
675
1151
  // anything else
676
- return consumeDelimToken;
1152
+ // Return a <delim-token> with its value set to the current input code point.
1153
+ return consumeDelimToken(input, pos, callbacks);
677
1154
  }
678
- });
1155
+ };
679
1156
 
680
1157
  /**
681
1158
  * @param {string} input input css
@@ -689,14 +1166,9 @@ module.exports = (input, callbacks) => {
689
1166
  // Consume comments.
690
1167
  pos = consumeComments(input, pos, callbacks);
691
1168
 
692
- const cc = input.charCodeAt(pos);
693
-
694
1169
  // Consume the next input code point.
695
- if (cc < 0x80) {
696
- pos = CHAR_MAP[cc](input, pos, callbacks);
697
- } else {
698
- pos++;
699
- }
1170
+ pos++;
1171
+ pos = consumeAToken(input, pos, callbacks);
700
1172
  }
701
1173
  };
702
1174
 
@@ -752,6 +1224,23 @@ module.exports.eatWhitespaceAndComments = (input, pos) => {
752
1224
  return pos;
753
1225
  };
754
1226
 
1227
+ /**
1228
+ * @param {string} input input
1229
+ * @param {number} pos position
1230
+ * @returns {number} position after whitespace and comments
1231
+ */
1232
+ module.exports.eatComments = (input, pos) => {
1233
+ for (;;) {
1234
+ const originalPos = pos;
1235
+ pos = consumeComments(input, pos, {});
1236
+ if (originalPos === pos) {
1237
+ break;
1238
+ }
1239
+ }
1240
+
1241
+ return pos;
1242
+ };
1243
+
755
1244
  /**
756
1245
  * @param {string} input input
757
1246
  * @param {number} pos position
@@ -773,3 +1262,344 @@ module.exports.eatWhiteLine = (input, pos) => {
773
1262
 
774
1263
  return pos;
775
1264
  };
1265
+
1266
+ /**
1267
+ * @param {string} input input
1268
+ * @param {number} pos position
1269
+ * @returns {[number, number] | undefined} positions of ident sequence
1270
+ */
1271
+ module.exports.skipCommentsAndEatIdentSequence = (input, pos) => {
1272
+ pos = module.exports.eatComments(input, pos);
1273
+
1274
+ const start = pos;
1275
+
1276
+ if (
1277
+ _ifThreeCodePointsWouldStartAnIdentSequence(
1278
+ input,
1279
+ pos,
1280
+ input.charCodeAt(pos),
1281
+ input.charCodeAt(pos + 1),
1282
+ input.charCodeAt(pos + 2)
1283
+ )
1284
+ ) {
1285
+ return [start, _consumeAnIdentSequence(input, pos, {})];
1286
+ }
1287
+
1288
+ return undefined;
1289
+ };
1290
+
1291
+ /**
1292
+ * @param {string} input input
1293
+ * @param {number} pos position
1294
+ * @returns {[number, number] | undefined} positions of ident sequence
1295
+ */
1296
+ module.exports.eatString = (input, pos) => {
1297
+ pos = module.exports.eatWhitespaceAndComments(input, pos);
1298
+
1299
+ const start = pos;
1300
+
1301
+ if (
1302
+ input.charCodeAt(pos) === CC_QUOTATION_MARK ||
1303
+ input.charCodeAt(pos) === CC_APOSTROPHE
1304
+ ) {
1305
+ return [start, consumeAStringToken(input, pos + 1, {})];
1306
+ }
1307
+
1308
+ return undefined;
1309
+ };
1310
+
1311
+ /**
1312
+ * @param {string} input input
1313
+ * @param {number} pos position
1314
+ * @param {CssTokenCallbacks} cbs callbacks
1315
+ * @returns {[number, number][]} positions of ident sequence
1316
+ */
1317
+ module.exports.eatImageSetStrings = (input, pos, cbs) => {
1318
+ /** @type {[number, number][]} */
1319
+ const result = [];
1320
+
1321
+ let isFirst = true;
1322
+ let needStop = false;
1323
+ // We already in `func(` token
1324
+ let balanced = 1;
1325
+
1326
+ /** @type {CssTokenCallbacks} */
1327
+ const callbacks = {
1328
+ ...cbs,
1329
+ string: (_input, start, end) => {
1330
+ if (isFirst && balanced === 1) {
1331
+ result.push([start, end]);
1332
+ isFirst = false;
1333
+ }
1334
+
1335
+ return end;
1336
+ },
1337
+ comma: (_input, _start, end) => {
1338
+ if (balanced === 1) {
1339
+ isFirst = true;
1340
+ }
1341
+
1342
+ return end;
1343
+ },
1344
+ leftParenthesis: (input, start, end) => {
1345
+ balanced++;
1346
+
1347
+ return end;
1348
+ },
1349
+ function: (_input, start, end) => {
1350
+ balanced++;
1351
+
1352
+ return end;
1353
+ },
1354
+ rightParenthesis: (_input, _start, end) => {
1355
+ balanced--;
1356
+
1357
+ if (balanced === 0) {
1358
+ needStop = true;
1359
+ }
1360
+
1361
+ return end;
1362
+ }
1363
+ };
1364
+
1365
+ while (pos < input.length) {
1366
+ // Consume comments.
1367
+ pos = consumeComments(input, pos, callbacks);
1368
+
1369
+ // Consume the next input code point.
1370
+ pos++;
1371
+ pos = consumeAToken(input, pos, callbacks);
1372
+
1373
+ if (needStop) {
1374
+ break;
1375
+ }
1376
+ }
1377
+
1378
+ return result;
1379
+ };
1380
+
1381
+ /**
1382
+ * @param {string} input input
1383
+ * @param {number} pos position
1384
+ * @param {CssTokenCallbacks} cbs callbacks
1385
+ * @returns {[[number, number, number, number] | undefined, [number, number] | undefined, [number, number] | undefined, [number, number] | undefined]} positions of top level tokens
1386
+ */
1387
+ module.exports.eatImportTokens = (input, pos, cbs) => {
1388
+ const result =
1389
+ /** @type {[[number, number, number, number] | undefined, [number, number] | undefined, [number, number] | undefined, [number, number] | undefined]} */
1390
+ (new Array(4));
1391
+
1392
+ /** @type {0 | 1 | 2 | undefined} */
1393
+ let scope;
1394
+ let needStop = false;
1395
+ let balanced = 0;
1396
+
1397
+ /** @type {CssTokenCallbacks} */
1398
+ const callbacks = {
1399
+ ...cbs,
1400
+ url: (_input, start, end, contentStart, contentEnd) => {
1401
+ if (
1402
+ result[0] === undefined &&
1403
+ balanced === 0 &&
1404
+ result[1] === undefined &&
1405
+ result[2] === undefined &&
1406
+ result[3] === undefined
1407
+ ) {
1408
+ result[0] = [start, end, contentStart, contentEnd];
1409
+ scope = undefined;
1410
+ }
1411
+
1412
+ return end;
1413
+ },
1414
+ string: (_input, start, end) => {
1415
+ if (
1416
+ balanced === 0 &&
1417
+ result[0] === undefined &&
1418
+ result[1] === undefined &&
1419
+ result[2] === undefined &&
1420
+ result[3] === undefined
1421
+ ) {
1422
+ result[0] = [start, end, start + 1, end - 1];
1423
+ scope = undefined;
1424
+ } else if (result[0] !== undefined && scope === 0) {
1425
+ result[0][2] = start + 1;
1426
+ result[0][3] = end - 1;
1427
+ }
1428
+
1429
+ return end;
1430
+ },
1431
+ leftParenthesis: (_input, _start, end) => {
1432
+ balanced++;
1433
+
1434
+ return end;
1435
+ },
1436
+ rightParenthesis: (_input, _start, end) => {
1437
+ balanced--;
1438
+
1439
+ if (balanced === 0 && scope !== undefined) {
1440
+ /** @type {[number, number]} */
1441
+ (result[scope])[1] = end;
1442
+ scope = undefined;
1443
+ }
1444
+
1445
+ return end;
1446
+ },
1447
+ function: (input, start, end) => {
1448
+ if (balanced === 0) {
1449
+ const name = input
1450
+ .slice(start, end - 1)
1451
+ .replace(/\\/g, "")
1452
+ .toLowerCase();
1453
+
1454
+ if (
1455
+ name === "url" &&
1456
+ result[0] === undefined &&
1457
+ result[1] === undefined &&
1458
+ result[2] === undefined &&
1459
+ result[3] === undefined
1460
+ ) {
1461
+ scope = 0;
1462
+ result[scope] = [start, end + 1, end + 1, end + 1];
1463
+ } else if (
1464
+ name === "layer" &&
1465
+ result[1] === undefined &&
1466
+ result[2] === undefined
1467
+ ) {
1468
+ scope = 1;
1469
+ result[scope] = [start, end];
1470
+ } else if (name === "supports" && result[2] === undefined) {
1471
+ scope = 2;
1472
+ result[scope] = [start, end];
1473
+ } else {
1474
+ scope = undefined;
1475
+ }
1476
+ }
1477
+
1478
+ balanced++;
1479
+
1480
+ return end;
1481
+ },
1482
+ identifier: (input, start, end) => {
1483
+ if (
1484
+ balanced === 0 &&
1485
+ result[1] === undefined &&
1486
+ result[2] === undefined
1487
+ ) {
1488
+ const name = input.slice(start, end).replace(/\\/g, "").toLowerCase();
1489
+
1490
+ if (name === "layer") {
1491
+ result[1] = [start, end];
1492
+ scope = undefined;
1493
+ }
1494
+ }
1495
+
1496
+ return end;
1497
+ },
1498
+ semicolon: (_input, start, end) => {
1499
+ if (balanced === 0) {
1500
+ needStop = true;
1501
+ result[3] = [start, end];
1502
+ }
1503
+
1504
+ return end;
1505
+ }
1506
+ };
1507
+
1508
+ while (pos < input.length) {
1509
+ // Consume comments.
1510
+ pos = consumeComments(input, pos, callbacks);
1511
+
1512
+ // Consume the next input code point.
1513
+ pos++;
1514
+ pos = consumeAToken(input, pos, callbacks);
1515
+
1516
+ if (needStop) {
1517
+ break;
1518
+ }
1519
+ }
1520
+
1521
+ return result;
1522
+ };
1523
+
1524
+ /**
1525
+ * @param {string} input input
1526
+ * @param {number} pos position
1527
+ * @returns {[number, number] | undefined} positions of ident sequence
1528
+ */
1529
+ module.exports.eatIdentSequence = (input, pos) => {
1530
+ pos = module.exports.eatWhitespaceAndComments(input, pos);
1531
+
1532
+ const start = pos;
1533
+
1534
+ if (
1535
+ _ifThreeCodePointsWouldStartAnIdentSequence(
1536
+ input,
1537
+ pos,
1538
+ input.charCodeAt(pos),
1539
+ input.charCodeAt(pos + 1),
1540
+ input.charCodeAt(pos + 2)
1541
+ )
1542
+ ) {
1543
+ return [start, _consumeAnIdentSequence(input, pos, {})];
1544
+ }
1545
+
1546
+ return undefined;
1547
+ };
1548
+
1549
+ /**
1550
+ * @param {string} input input
1551
+ * @param {number} pos position
1552
+ * @returns {[number, number, boolean] | undefined} positions of ident sequence or string
1553
+ */
1554
+ module.exports.eatIdentSequenceOrString = (input, pos) => {
1555
+ pos = module.exports.eatWhitespaceAndComments(input, pos);
1556
+
1557
+ const start = pos;
1558
+
1559
+ if (
1560
+ input.charCodeAt(pos) === CC_QUOTATION_MARK ||
1561
+ input.charCodeAt(pos) === CC_APOSTROPHE
1562
+ ) {
1563
+ return [start, consumeAStringToken(input, pos + 1, {}), false];
1564
+ } else if (
1565
+ _ifThreeCodePointsWouldStartAnIdentSequence(
1566
+ input,
1567
+ pos,
1568
+ input.charCodeAt(pos),
1569
+ input.charCodeAt(pos + 1),
1570
+ input.charCodeAt(pos + 2)
1571
+ )
1572
+ ) {
1573
+ return [start, _consumeAnIdentSequence(input, pos, {}), true];
1574
+ }
1575
+
1576
+ return undefined;
1577
+ };
1578
+
1579
+ /**
1580
+ * @param {string} chars characters
1581
+ * @returns {(input: string, pos: number) => number} function to eat characters
1582
+ */
1583
+ module.exports.eatUntil = chars => {
1584
+ const charCodes = Array.from({ length: chars.length }, (_, i) =>
1585
+ chars.charCodeAt(i)
1586
+ );
1587
+ const arr = Array.from(
1588
+ { length: charCodes.reduce((a, b) => Math.max(a, b), 0) + 1 },
1589
+ () => false
1590
+ );
1591
+ for (const cc of charCodes) {
1592
+ arr[cc] = true;
1593
+ }
1594
+
1595
+ return (input, pos) => {
1596
+ for (;;) {
1597
+ const cc = input.charCodeAt(pos);
1598
+ if (cc < arr.length && arr[cc]) {
1599
+ return pos;
1600
+ }
1601
+ pos++;
1602
+ if (pos === input.length) return pos;
1603
+ }
1604
+ };
1605
+ };