webpack 5.102.1 → 5.104.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 (220) hide show
  1. package/README.md +121 -134
  2. package/hot/dev-server.js +18 -3
  3. package/hot/emitter-event-target.js +7 -0
  4. package/hot/lazy-compilation-node.js +45 -29
  5. package/hot/lazy-compilation-universal.js +18 -0
  6. package/hot/lazy-compilation-web.js +15 -5
  7. package/hot/load-http.js +7 -0
  8. package/hot/only-dev-server.js +19 -4
  9. package/lib/APIPlugin.js +6 -0
  10. package/lib/Chunk.js +1 -1
  11. package/lib/ChunkGraph.js +9 -7
  12. package/lib/ChunkGroup.js +8 -5
  13. package/lib/CleanPlugin.js +6 -3
  14. package/lib/CodeGenerationResults.js +2 -1
  15. package/lib/CompatibilityPlugin.js +28 -2
  16. package/lib/Compilation.js +58 -21
  17. package/lib/Compiler.js +3 -3
  18. package/lib/ConcatenationScope.js +0 -15
  19. package/lib/ContextModule.js +6 -3
  20. package/lib/ContextModuleFactory.js +6 -4
  21. package/lib/CssModule.js +6 -1
  22. package/lib/DefinePlugin.js +45 -14
  23. package/lib/DelegatedModule.js +7 -4
  24. package/lib/Dependency.js +8 -1
  25. package/lib/DependencyTemplate.js +1 -0
  26. package/lib/DllModule.js +6 -3
  27. package/lib/DotenvPlugin.js +462 -0
  28. package/lib/EnvironmentPlugin.js +19 -16
  29. package/lib/EvalSourceMapDevToolPlugin.js +16 -0
  30. package/lib/ExportsInfo.js +6 -2
  31. package/lib/ExternalModule.js +28 -35
  32. package/lib/ExternalModuleFactoryPlugin.js +11 -9
  33. package/lib/ExternalsPlugin.js +2 -1
  34. package/lib/FileSystemInfo.js +1 -1
  35. package/lib/Generator.js +10 -7
  36. package/lib/HookWebpackError.js +33 -4
  37. package/lib/HotModuleReplacementPlugin.js +22 -0
  38. package/lib/ManifestPlugin.js +235 -0
  39. package/lib/Module.js +27 -15
  40. package/lib/ModuleBuildError.js +1 -1
  41. package/lib/ModuleError.js +1 -1
  42. package/lib/ModuleFilenameHelpers.js +1 -1
  43. package/lib/ModuleGraph.js +29 -13
  44. package/lib/ModuleGraphConnection.js +2 -2
  45. package/lib/ModuleSourceTypeConstants.js +189 -0
  46. package/lib/ModuleTypeConstants.js +1 -4
  47. package/lib/ModuleWarning.js +1 -1
  48. package/lib/MultiCompiler.js +1 -1
  49. package/lib/NodeStuffPlugin.js +424 -116
  50. package/lib/NormalModule.js +23 -20
  51. package/lib/NormalModuleFactory.js +7 -10
  52. package/lib/Parser.js +1 -1
  53. package/lib/RawModule.js +7 -4
  54. package/lib/RuntimeGlobals.js +22 -4
  55. package/lib/RuntimeModule.js +1 -1
  56. package/lib/RuntimePlugin.js +27 -6
  57. package/lib/RuntimeTemplate.js +120 -57
  58. package/lib/SourceMapDevToolPlugin.js +26 -1
  59. package/lib/Template.js +17 -6
  60. package/lib/TemplatedPathPlugin.js +5 -6
  61. package/lib/WebpackError.js +0 -1
  62. package/lib/WebpackOptionsApply.js +67 -15
  63. package/lib/asset/AssetBytesGenerator.js +16 -12
  64. package/lib/asset/AssetGenerator.js +31 -26
  65. package/lib/asset/AssetSourceGenerator.js +16 -12
  66. package/lib/asset/RawDataUrlModule.js +6 -3
  67. package/lib/buildChunkGraph.js +4 -2
  68. package/lib/cache/PackFileCacheStrategy.js +6 -5
  69. package/lib/cli.js +2 -43
  70. package/lib/config/browserslistTargetHandler.js +24 -0
  71. package/lib/config/defaults.js +226 -61
  72. package/lib/config/normalization.js +4 -3
  73. package/lib/config/target.js +11 -0
  74. package/lib/container/ContainerEntryModule.js +6 -3
  75. package/lib/container/FallbackModule.js +6 -3
  76. package/lib/container/RemoteModule.js +1 -3
  77. package/lib/css/CssGenerator.js +304 -76
  78. package/lib/css/CssLoadingRuntimeModule.js +14 -4
  79. package/lib/css/CssMergeStyleSheetsRuntimeModule.js +56 -0
  80. package/lib/css/CssModulesPlugin.js +72 -67
  81. package/lib/css/CssParser.js +1726 -732
  82. package/lib/css/walkCssTokens.js +128 -11
  83. package/lib/dependencies/CachedConstDependency.js +24 -10
  84. package/lib/dependencies/CommonJsImportsParserPlugin.js +0 -9
  85. package/lib/dependencies/CommonJsPlugin.js +12 -0
  86. package/lib/dependencies/CommonJsRequireContextDependency.js +1 -1
  87. package/lib/dependencies/ContextDependencyHelpers.js +2 -2
  88. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +3 -1
  89. package/lib/dependencies/CssIcssExportDependency.js +389 -12
  90. package/lib/dependencies/CssIcssImportDependency.js +114 -51
  91. package/lib/dependencies/CssIcssSymbolDependency.js +31 -33
  92. package/lib/dependencies/CssImportDependency.js +17 -6
  93. package/lib/dependencies/CssUrlDependency.js +3 -2
  94. package/lib/dependencies/DynamicExports.js +7 -7
  95. package/lib/dependencies/ExternalModuleDependency.js +7 -4
  96. package/lib/dependencies/ExternalModuleInitFragment.js +3 -2
  97. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +96 -0
  98. package/lib/dependencies/HarmonyAcceptDependency.js +6 -1
  99. package/lib/dependencies/HarmonyAcceptImportDependency.js +2 -1
  100. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +12 -1
  101. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +35 -23
  102. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +13 -9
  103. package/lib/dependencies/HarmonyExports.js +4 -4
  104. package/lib/dependencies/HarmonyImportDependency.js +28 -27
  105. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +28 -69
  106. package/lib/dependencies/HarmonyImportSideEffectDependency.js +4 -3
  107. package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -8
  108. package/lib/dependencies/ImportDependency.js +8 -2
  109. package/lib/dependencies/ImportEagerDependency.js +6 -3
  110. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +1 -1
  111. package/lib/dependencies/ImportMetaPlugin.js +154 -9
  112. package/lib/dependencies/ImportParserPlugin.js +21 -23
  113. package/lib/dependencies/ImportPhase.js +121 -0
  114. package/lib/dependencies/ImportWeakDependency.js +6 -3
  115. package/lib/dependencies/LocalModulesHelpers.js +3 -3
  116. package/lib/dependencies/ModuleDependency.js +5 -1
  117. package/lib/dependencies/ModuleHotAcceptDependency.js +1 -1
  118. package/lib/dependencies/WorkerPlugin.js +2 -2
  119. package/lib/dependencies/getFunctionExpression.js +1 -1
  120. package/lib/esm/ExportWebpackRequireRuntimeModule.js +1 -8
  121. package/lib/esm/ModuleChunkFormatPlugin.js +5 -4
  122. package/lib/hmr/HotModuleReplacement.runtime.js +2 -1
  123. package/lib/hmr/LazyCompilationPlugin.js +5 -3
  124. package/lib/ids/IdHelpers.js +20 -8
  125. package/lib/index.js +6 -0
  126. package/lib/javascript/ChunkHelpers.js +16 -5
  127. package/lib/javascript/JavascriptGenerator.js +105 -104
  128. package/lib/javascript/JavascriptModulesPlugin.js +80 -37
  129. package/lib/javascript/JavascriptParser.js +161 -44
  130. package/lib/json/JsonGenerator.js +5 -4
  131. package/lib/json/JsonParser.js +9 -2
  132. package/lib/library/AbstractLibraryPlugin.js +1 -1
  133. package/lib/library/AmdLibraryPlugin.js +4 -1
  134. package/lib/library/ExportPropertyLibraryPlugin.js +4 -1
  135. package/lib/library/ModuleLibraryPlugin.js +41 -23
  136. package/lib/library/SystemLibraryPlugin.js +8 -1
  137. package/lib/library/UmdLibraryPlugin.js +2 -2
  138. package/lib/logging/Logger.js +5 -4
  139. package/lib/logging/createConsoleLogger.js +2 -2
  140. package/lib/node/NodeTargetPlugin.js +9 -1
  141. package/lib/node/ReadFileCompileWasmPlugin.js +0 -2
  142. package/lib/optimize/ConcatenatedModule.js +208 -167
  143. package/lib/optimize/ModuleConcatenationPlugin.js +5 -4
  144. package/lib/optimize/SideEffectsFlagPlugin.js +3 -2
  145. package/lib/optimize/SplitChunksPlugin.js +60 -46
  146. package/lib/rules/RuleSetCompiler.js +1 -1
  147. package/lib/runtime/AsyncModuleRuntimeModule.js +28 -18
  148. package/lib/runtime/AutoPublicPathRuntimeModule.js +8 -3
  149. package/lib/runtime/GetChunkFilenameRuntimeModule.js +3 -2
  150. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +89 -55
  151. package/lib/schemes/HttpUriPlugin.js +78 -7
  152. package/lib/serialization/AggregateErrorSerializer.js +1 -2
  153. package/lib/serialization/ObjectMiddleware.js +0 -2
  154. package/lib/serialization/SingleItemMiddleware.js +1 -1
  155. package/lib/sharing/ConsumeSharedModule.js +1 -1
  156. package/lib/sharing/ConsumeSharedPlugin.js +5 -3
  157. package/lib/sharing/ProvideSharedModule.js +1 -1
  158. package/lib/sharing/resolveMatchedConfigs.js +15 -9
  159. package/lib/sharing/utils.js +1 -1
  160. package/lib/stats/DefaultStatsFactoryPlugin.js +8 -5
  161. package/lib/stats/DefaultStatsPresetPlugin.js +1 -1
  162. package/lib/stats/DefaultStatsPrinterPlugin.js +1 -1
  163. package/lib/util/StringXor.js +1 -1
  164. package/lib/util/URLAbsoluteSpecifier.js +2 -2
  165. package/lib/util/binarySearchBounds.js +2 -2
  166. package/lib/util/comparators.js +54 -76
  167. package/lib/util/compileBooleanMatcher.js +78 -6
  168. package/lib/util/createHash.js +20 -199
  169. package/lib/util/deprecation.js +1 -1
  170. package/lib/util/deterministicGrouping.js +6 -3
  171. package/lib/util/fs.js +75 -75
  172. package/lib/util/hash/BatchedHash.js +10 -9
  173. package/lib/util/hash/BulkUpdateHash.js +138 -0
  174. package/lib/util/hash/DebugHash.js +75 -0
  175. package/lib/util/hash/hash-digest.js +216 -0
  176. package/lib/util/identifier.js +82 -17
  177. package/lib/util/internalSerializables.js +2 -6
  178. package/lib/util/runtime.js +3 -3
  179. package/lib/util/source.js +2 -2
  180. package/lib/wasm/EnableWasmLoadingPlugin.js +10 -4
  181. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +3 -2
  182. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +11 -7
  183. package/lib/wasm-sync/WebAssemblyGenerator.js +9 -6
  184. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +11 -6
  185. package/lib/wasm-sync/WebAssemblyModulesPlugin.js +6 -2
  186. package/lib/web/FetchCompileWasmPlugin.js +0 -2
  187. package/lib/webpack.js +85 -82
  188. package/module.d.ts +5 -0
  189. package/package.json +34 -28
  190. package/schemas/WebpackOptions.check.js +1 -1
  191. package/schemas/WebpackOptions.json +160 -101
  192. package/schemas/plugins/{css/CssAutoParserOptions.check.d.ts → ManifestPlugin.check.d.ts} +1 -1
  193. package/schemas/plugins/ManifestPlugin.check.js +6 -0
  194. package/schemas/plugins/ManifestPlugin.json +98 -0
  195. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  196. package/schemas/plugins/SourceMapDevToolPlugin.json +16 -3
  197. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  198. package/schemas/plugins/container/ContainerReferencePlugin.json +4 -1
  199. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  200. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  201. package/schemas/plugins/container/ModuleFederationPlugin.json +4 -1
  202. package/schemas/plugins/css/CssModuleGeneratorOptions.check.js +1 -1
  203. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  204. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  205. package/schemas/plugins/json/JsonModulesPluginParser.check.js +1 -1
  206. package/types.d.ts +771 -436
  207. package/lib/ModuleSourceTypesConstants.js +0 -123
  208. package/lib/dependencies/CssLocalIdentifierDependency.js +0 -250
  209. package/lib/dependencies/CssSelfLocalIdentifierDependency.js +0 -112
  210. package/schemas/plugins/css/CssAutoGeneratorOptions.check.d.ts +0 -7
  211. package/schemas/plugins/css/CssAutoGeneratorOptions.check.js +0 -6
  212. package/schemas/plugins/css/CssAutoGeneratorOptions.json +0 -3
  213. package/schemas/plugins/css/CssAutoParserOptions.check.js +0 -6
  214. package/schemas/plugins/css/CssAutoParserOptions.json +0 -3
  215. package/schemas/plugins/css/CssGlobalGeneratorOptions.check.d.ts +0 -7
  216. package/schemas/plugins/css/CssGlobalGeneratorOptions.check.js +0 -6
  217. package/schemas/plugins/css/CssGlobalGeneratorOptions.json +0 -3
  218. package/schemas/plugins/css/CssGlobalParserOptions.check.d.ts +0 -7
  219. package/schemas/plugins/css/CssGlobalParserOptions.check.js +0 -6
  220. package/schemas/plugins/css/CssGlobalParserOptions.json +0 -3
@@ -7,6 +7,7 @@
7
7
 
8
8
  const vm = require("vm");
9
9
  const CommentCompilationWarning = require("../CommentCompilationWarning");
10
+ const CssModule = require("../CssModule");
10
11
  const ModuleDependencyWarning = require("../ModuleDependencyWarning");
11
12
  const { CSS_MODULE_TYPE_AUTO } = require("../ModuleTypeConstants");
12
13
  const Parser = require("../Parser");
@@ -17,8 +18,6 @@ const CssIcssExportDependency = require("../dependencies/CssIcssExportDependency
17
18
  const CssIcssImportDependency = require("../dependencies/CssIcssImportDependency");
18
19
  const CssIcssSymbolDependency = require("../dependencies/CssIcssSymbolDependency");
19
20
  const CssImportDependency = require("../dependencies/CssImportDependency");
20
- const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
21
- const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
22
21
  const CssUrlDependency = require("../dependencies/CssUrlDependency");
23
22
  const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
24
23
  const binarySearchBounds = require("../util/binarySearchBounds");
@@ -34,17 +33,23 @@ const walkCssTokens = require("./walkCssTokens");
34
33
  /** @typedef {import("../Parser").ParserState} ParserState */
35
34
  /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
36
35
  /** @typedef {import("./walkCssTokens").CssTokenCallbacks} CssTokenCallbacks */
36
+ /** @typedef {import("../../declarations/WebpackOptions").CssModuleParserOptions} CssModuleParserOptions */
37
37
 
38
38
  /** @typedef {[number, number]} Range */
39
39
  /** @typedef {{ line: number, column: number }} Position */
40
40
  /** @typedef {{ value: string, range: Range, loc: { start: Position, end: Position } }} Comment */
41
41
 
42
42
  const CC_COLON = ":".charCodeAt(0);
43
- const CC_SLASH = "/".charCodeAt(0);
43
+ const CC_SEMICOLON = ";".charCodeAt(0);
44
+ const CC_COMMA = ",".charCodeAt(0);
44
45
  const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
45
46
  const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
46
47
  const CC_LOWER_F = "f".charCodeAt(0);
47
48
  const CC_UPPER_F = "F".charCodeAt(0);
49
+ const CC_RIGHT_CURLY = "}".charCodeAt(0);
50
+ const CC_HYPHEN_MINUS = "-".charCodeAt(0);
51
+ const CC_TILDE = "~".charCodeAt(0);
52
+ const CC_EQUAL = "=".charCodeAt(0);
48
53
 
49
54
  // https://www.w3.org/TR/css-syntax-3/#newline
50
55
  // We don't have `preprocessing` stage, so we need specify all of them
@@ -54,11 +59,30 @@ const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
54
59
  const UNESCAPE = /\\([0-9a-fA-F]{1,6}[ \t\n\r\f]?|[\s\S])/g;
55
60
  const IMAGE_SET_FUNCTION = /^(-\w+-)?image-set$/i;
56
61
  const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(-\w+-)?keyframes$/;
57
- const OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY =
58
- /^(-\w+-)?animation(-name)?$/i;
62
+ const COMPOSES_PROPERTY = /^(composes|compose-with)$/i;
59
63
  const IS_MODULES = /\.module(s)?\.[^.]+$/i;
60
64
  const CSS_COMMENT = /\/\*((?!\*\/).*?)\*\//g;
61
65
 
66
+ /**
67
+ * @param {RegExp} regexp a regexp
68
+ * @param {string} str a string
69
+ * @returns {RegExpExecArray[]} matches
70
+ */
71
+ const matchAll = (regexp, str) => {
72
+ /** @type {RegExpExecArray[]} */
73
+ const result = [];
74
+
75
+ let match;
76
+
77
+ // Use a while loop with exec() to find all matches
78
+ while ((match = regexp.exec(str)) !== null) {
79
+ result.push(match);
80
+ }
81
+ // Return an array to be easily iterable (note: a true spec-compliant polyfill
82
+ // returns an iterator object, but an array spread often suffices for basic use)
83
+ return result;
84
+ };
85
+
62
86
  /**
63
87
  * @param {string} str url string
64
88
  * @param {boolean} isString is url wrapped in quotes
@@ -230,6 +254,276 @@ const unescapeIdentifier = (str) => {
230
254
  return ret;
231
255
  };
232
256
 
257
+ /**
258
+ * A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
259
+ * The <custom-property-name> production corresponds to this:
260
+ * it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes),
261
+ * except -- itself, which is reserved for future use by CSS.
262
+ * @param {string} identifier identifier
263
+ * @returns {boolean} true when identifier is dashed, otherwise false
264
+ */
265
+ const isDashedIdentifier = (identifier) =>
266
+ identifier.startsWith("--") && identifier.length >= 3;
267
+
268
+ /** @type {Record<string, number>} */
269
+ const PREDEFINED_COUNTER_STYLES = {
270
+ decimal: 1,
271
+ "decimal-leading-zero": 1,
272
+ "arabic-indic": 1,
273
+ armenian: 1,
274
+ "upper-armenian": 1,
275
+ "lower-armenian": 1,
276
+ bengali: 1,
277
+ cambodian: 1,
278
+ khmer: 1,
279
+ "cjk-decimal": 1,
280
+ devanagari: 1,
281
+ georgian: 1,
282
+ gujarati: 1,
283
+ /* cspell:disable-next-line */
284
+ gurmukhi: 1,
285
+ hebrew: 1,
286
+ kannada: 1,
287
+ lao: 1,
288
+ malayalam: 1,
289
+ mongolian: 1,
290
+ myanmar: 1,
291
+ oriya: 1,
292
+ persian: 1,
293
+ "lower-roman": 1,
294
+ "upper-roman": 1,
295
+ tamil: 1,
296
+ telugu: 1,
297
+ thai: 1,
298
+ tibetan: 1,
299
+
300
+ "lower-alpha": 1,
301
+ "lower-latin": 1,
302
+ "upper-alpha": 1,
303
+ "upper-latin": 1,
304
+ "lower-greek": 1,
305
+ hiragana: 1,
306
+ /* cspell:disable-next-line */
307
+ "hiragana-iroha": 1,
308
+ katakana: 1,
309
+ /* cspell:disable-next-line */
310
+ "katakana-iroha": 1,
311
+
312
+ disc: 1,
313
+ circle: 1,
314
+ square: 1,
315
+ "disclosure-open": 1,
316
+ "disclosure-closed": 1,
317
+
318
+ "cjk-earthly-branch": 1,
319
+ "cjk-heavenly-stem": 1,
320
+
321
+ "japanese-informal": 1,
322
+ "japanese-formal": 1,
323
+
324
+ "korean-hangul-formal": 1,
325
+ /* cspell:disable-next-line */
326
+ "korean-hanja-informal": 1,
327
+ /* cspell:disable-next-line */
328
+ "korean-hanja-formal": 1,
329
+
330
+ "simp-chinese-informal": 1,
331
+ "simp-chinese-formal": 1,
332
+ "trad-chinese-informal": 1,
333
+ "trad-chinese-formal": 1,
334
+ "cjk-ideographic": 1,
335
+
336
+ "ethiopic-numeric": 1
337
+ };
338
+
339
+ /** @type {Record<string, number>} */
340
+ const GLOBAL_VALUES = {
341
+ // Global values
342
+ initial: Infinity,
343
+ inherit: Infinity,
344
+ unset: Infinity,
345
+ revert: Infinity,
346
+ "revert-layer": Infinity
347
+ };
348
+
349
+ /** @type {Record<string, number>} */
350
+ const GRID_AREA_OR_COLUMN_OR_ROW = {
351
+ auto: Infinity,
352
+ span: Infinity,
353
+ ...GLOBAL_VALUES
354
+ };
355
+
356
+ /** @type {Record<string, number>} */
357
+ const GRID_AUTO_COLUMNS_OR_ROW = {
358
+ "min-content": Infinity,
359
+ "max-content": Infinity,
360
+ auto: Infinity,
361
+ ...GLOBAL_VALUES
362
+ };
363
+
364
+ /** @type {Record<string, number>} */
365
+ const GRID_AUTO_FLOW = {
366
+ row: 1,
367
+ column: 1,
368
+ dense: 1,
369
+ ...GLOBAL_VALUES
370
+ };
371
+
372
+ /** @type {Record<string, number>} */
373
+ const GRID_TEMPLATE_ARES = {
374
+ // Special
375
+ none: 1,
376
+ ...GLOBAL_VALUES
377
+ };
378
+
379
+ /** @type {Record<string, number>} */
380
+ const GRID_TEMPLATE_COLUMNS_OR_ROWS = {
381
+ none: 1,
382
+ subgrid: 1,
383
+ masonry: 1,
384
+ "max-content": Infinity,
385
+ "min-content": Infinity,
386
+ auto: Infinity,
387
+ ...GLOBAL_VALUES
388
+ };
389
+
390
+ /** @type {Record<string, number>} */
391
+ const GRID_TEMPLATE = {
392
+ ...GRID_TEMPLATE_ARES,
393
+ ...GRID_TEMPLATE_COLUMNS_OR_ROWS
394
+ };
395
+
396
+ /** @type {Record<string, number>} */
397
+ const GRID = {
398
+ "auto-flow": 1,
399
+ dense: 1,
400
+ ...GRID_AUTO_COLUMNS_OR_ROW,
401
+ ...GRID_AUTO_FLOW,
402
+ ...GRID_TEMPLATE_ARES,
403
+ ...GRID_TEMPLATE_COLUMNS_OR_ROWS
404
+ };
405
+
406
+ /**
407
+ * @param {{ animation?: boolean, container?: boolean, customIdents?: boolean, grid?: boolean }=} options options
408
+ * @returns {Map<string, Record<string, number>>} list of known properties
409
+ */
410
+ const getKnownProperties = (options = {}) => {
411
+ /** @type {Map<string, Record<string, number>>} */
412
+ const knownProperties = new Map();
413
+
414
+ if (options.animation) {
415
+ knownProperties.set("animation", {
416
+ // animation-direction
417
+ normal: 1,
418
+ reverse: 1,
419
+ alternate: 1,
420
+ "alternate-reverse": 1,
421
+ // animation-fill-mode
422
+ forwards: 1,
423
+ backwards: 1,
424
+ both: 1,
425
+ // animation-iteration-count
426
+ infinite: 1,
427
+ // animation-play-state
428
+ paused: 1,
429
+ running: 1,
430
+ // animation-timing-function
431
+ ease: 1,
432
+ "ease-in": 1,
433
+ "ease-out": 1,
434
+ "ease-in-out": 1,
435
+ linear: 1,
436
+ "step-end": 1,
437
+ "step-start": 1,
438
+ // Special
439
+ none: Infinity, // No matter how many times you write none, it will never be an animation name
440
+ ...GLOBAL_VALUES
441
+ });
442
+ knownProperties.set("animation-name", {
443
+ // Special
444
+ none: Infinity, // No matter how many times you write none, it will never be an animation name
445
+ ...GLOBAL_VALUES
446
+ });
447
+ }
448
+
449
+ if (options.container) {
450
+ knownProperties.set("container", {
451
+ // container-type
452
+ normal: 1,
453
+ size: 1,
454
+ "inline-size": 1,
455
+ "scroll-state": 1,
456
+ // Special
457
+ none: Infinity,
458
+ ...GLOBAL_VALUES
459
+ });
460
+ knownProperties.set("container-name", {
461
+ // Special
462
+ none: Infinity,
463
+ ...GLOBAL_VALUES
464
+ });
465
+ }
466
+
467
+ if (options.customIdents) {
468
+ knownProperties.set("list-style", {
469
+ // list-style-position
470
+ inside: 1,
471
+ outside: 1,
472
+ // list-style-type
473
+ ...PREDEFINED_COUNTER_STYLES,
474
+ // Special
475
+ none: Infinity,
476
+ ...GLOBAL_VALUES
477
+ });
478
+ knownProperties.set("list-style-type", {
479
+ // list-style-type
480
+ ...PREDEFINED_COUNTER_STYLES,
481
+ // Special
482
+ none: Infinity,
483
+ ...GLOBAL_VALUES
484
+ });
485
+ knownProperties.set("system", {
486
+ cyclic: 1,
487
+ numeric: 1,
488
+ alphabetic: 1,
489
+ symbolic: 1,
490
+ additive: 1,
491
+ fixed: 1,
492
+ extends: 1,
493
+ ...PREDEFINED_COUNTER_STYLES
494
+ });
495
+ knownProperties.set("fallback", {
496
+ ...PREDEFINED_COUNTER_STYLES
497
+ });
498
+ knownProperties.set("speak-as", {
499
+ auto: 1,
500
+ bullets: 1,
501
+ numbers: 1,
502
+ words: 1,
503
+ "spell-out": 1,
504
+ ...PREDEFINED_COUNTER_STYLES
505
+ });
506
+ }
507
+
508
+ if (options.grid) {
509
+ knownProperties.set("grid", GRID);
510
+ knownProperties.set("grid-area", GRID_AREA_OR_COLUMN_OR_ROW);
511
+ knownProperties.set("grid-column", GRID_AREA_OR_COLUMN_OR_ROW);
512
+ knownProperties.set("grid-column-end", GRID_AREA_OR_COLUMN_OR_ROW);
513
+ knownProperties.set("grid-column-start", GRID_AREA_OR_COLUMN_OR_ROW);
514
+ knownProperties.set("grid-column-start", GRID_AREA_OR_COLUMN_OR_ROW);
515
+ knownProperties.set("grid-row", GRID_AREA_OR_COLUMN_OR_ROW);
516
+ knownProperties.set("grid-row-end", GRID_AREA_OR_COLUMN_OR_ROW);
517
+ knownProperties.set("grid-row-start", GRID_AREA_OR_COLUMN_OR_ROW);
518
+ knownProperties.set("grid-template", GRID_TEMPLATE);
519
+ knownProperties.set("grid-template-areas", GRID_TEMPLATE_ARES);
520
+ knownProperties.set("grid-template-columns", GRID_TEMPLATE_COLUMNS_OR_ROWS);
521
+ knownProperties.set("grid-template-rows", GRID_TEMPLATE_COLUMNS_OR_ROWS);
522
+ }
523
+
524
+ return knownProperties;
525
+ };
526
+
233
527
  class LocConverter {
234
528
  /**
235
529
  * @param {string} input input
@@ -281,33 +575,39 @@ const EMPTY_COMMENT_OPTIONS = {
281
575
  const CSS_MODE_TOP_LEVEL = 0;
282
576
  const CSS_MODE_IN_BLOCK = 1;
283
577
 
578
+ const LOCAL_MODE = 0;
579
+ const GLOBAL_MODE = 1;
580
+
284
581
  const eatUntilSemi = walkCssTokens.eatUntil(";");
285
582
  const eatUntilLeftCurly = walkCssTokens.eatUntil("{");
286
- const eatSemi = walkCssTokens.eatUntil(";");
287
583
 
288
584
  /**
289
- * @typedef {object} CssParserOptions
290
- * @property {boolean=} importOption need handle `@import`
291
- * @property {boolean=} url need handle URLs
585
+ * @typedef {object} CssParserOwnOptions
292
586
  * @property {("pure" | "global" | "local" | "auto")=} defaultMode default mode
293
- * @property {boolean=} namedExports is named exports
294
587
  */
295
588
 
589
+ /** @typedef {CssModuleParserOptions & CssParserOwnOptions} CssParserOptions */
590
+
296
591
  class CssParser extends Parser {
297
592
  /**
298
593
  * @param {CssParserOptions=} options options
299
594
  */
300
- constructor({
301
- defaultMode = "pure",
302
- importOption = true,
303
- url = true,
304
- namedExports = true
305
- } = {}) {
595
+ constructor(options = {}) {
306
596
  super();
307
- this.defaultMode = defaultMode;
308
- this.import = importOption;
309
- this.url = url;
310
- this.namedExports = namedExports;
597
+ this.defaultMode =
598
+ typeof options.defaultMode !== "undefined" ? options.defaultMode : "pure";
599
+ this.options = {
600
+ url: true,
601
+ import: true,
602
+ namedExports: true,
603
+ animation: true,
604
+ container: true,
605
+ customIdents: true,
606
+ dashedIdents: true,
607
+ function: true,
608
+ grid: true,
609
+ ...options
610
+ };
311
611
  /** @type {Comment[] | undefined} */
312
612
  this.comments = undefined;
313
613
  this.magicCommentContext = createMagicCommentContext();
@@ -355,6 +655,7 @@ class CssParser extends Parser {
355
655
  mode === "auto" &&
356
656
  module.type === CSS_MODULE_TYPE_AUTO &&
357
657
  IS_MODULES.test(
658
+ // TODO matchResource
358
659
  parseResource(/** @type {string} */ (module.getResource())).path
359
660
  )
360
661
  ) {
@@ -362,6 +663,12 @@ class CssParser extends Parser {
362
663
  }
363
664
 
364
665
  const isModules = mode === "global" || mode === "local";
666
+ const knownProperties = getKnownProperties({
667
+ animation: this.options.animation,
668
+ container: this.options.container,
669
+ customIdents: this.options.customIdents,
670
+ grid: this.options.grid
671
+ });
365
672
 
366
673
  /** @type {BuildMeta} */
367
674
  (module.buildMeta).isCSSModule = isModules;
@@ -372,7 +679,7 @@ class CssParser extends Parser {
372
679
  let scope = CSS_MODE_TOP_LEVEL;
373
680
  /** @type {boolean} */
374
681
  let allowImportAtRule = true;
375
- /** @type {[string, number, number][]} */
682
+ /** @type {[string, number, number, boolean?][]} */
376
683
  const balanced = [];
377
684
  let lastTokenEndForComments = 0;
378
685
 
@@ -380,15 +687,13 @@ class CssParser extends Parser {
380
687
  let isNextRulePrelude = isModules;
381
688
  /** @type {number} */
382
689
  let blockNestingLevel = 0;
383
- /** @type {"local" | "global" | undefined} */
690
+ /** @type {0 | 1 | undefined} */
384
691
  let modeData;
385
- /** @type {boolean} */
386
- let inAnimationProperty = false;
387
- /** @type {[number, number, boolean] | undefined} */
388
- let lastIdentifier;
389
- /** @type {Set<string>} */
390
- const declaredCssVariables = new Set();
391
- /** @typedef {{ path?: string, value: string }} IcssDefinition */
692
+
693
+ /** @type {string[]} */
694
+ let lastLocalIdentifiers = [];
695
+
696
+ /** @typedef {{ value: string, isReference?: boolean }} IcssDefinition */
392
697
  /** @type {Map<string, IcssDefinition>} */
393
698
  const icssDefinitions = new Map();
394
699
 
@@ -398,55 +703,375 @@ class CssParser extends Parser {
398
703
  * @returns {boolean} true, when next is nested syntax
399
704
  */
400
705
  const isNextNestedSyntax = (input, pos) => {
401
- pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
706
+ pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
402
707
 
403
- if (input[pos] === "}") {
708
+ if (
709
+ input.charCodeAt(pos) === CC_RIGHT_CURLY ||
710
+ (input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
711
+ input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS)
712
+ ) {
404
713
  return false;
405
714
  }
406
715
 
407
- // According spec only identifier can be used as a property name
408
- const isIdentifier = walkCssTokens.isIdentStartCodePoint(
409
- input.charCodeAt(pos)
410
- );
716
+ const identifier = walkCssTokens.eatIdentSequence(input, pos);
717
+
718
+ if (!identifier) {
719
+ return true;
720
+ }
721
+
722
+ const leftCurly = eatUntilLeftCurly(input, pos);
723
+ const content = input.slice(identifier[0], leftCurly);
411
724
 
412
- return !isIdentifier;
725
+ if (content.includes(";") || content.includes("}")) {
726
+ return false;
727
+ }
728
+
729
+ return true;
413
730
  };
414
731
  /**
415
732
  * @returns {boolean} true, when in local scope
416
733
  */
417
734
  const isLocalMode = () =>
418
- modeData === "local" || (mode === "local" && modeData === undefined);
735
+ modeData === LOCAL_MODE || (mode === "local" && modeData === undefined);
419
736
 
420
737
  /**
421
738
  * @param {string} input input
422
- * @param {number} pos start position
423
- * @param {(input: string, pos: number) => number} eater eater
424
- * @returns {[number,string]} new position and text
739
+ * @param {number} start start
740
+ * @param {number} end end
741
+ * @returns {number} end
425
742
  */
426
- const eatText = (input, pos, eater) => {
427
- let text = "";
428
- for (;;) {
429
- if (input.charCodeAt(pos) === CC_SLASH) {
430
- const newPos = walkCssTokens.eatComments(input, pos);
431
- if (pos !== newPos) {
432
- pos = newPos;
433
- if (pos === input.length) break;
434
- } else {
435
- text += "/";
436
- pos++;
437
- if (pos === input.length) break;
743
+ const comment = (input, start, end) => {
744
+ if (!this.comments) this.comments = [];
745
+ const { line: sl, column: sc } = locConverter.get(start);
746
+ const { line: el, column: ec } = locConverter.get(end);
747
+
748
+ /** @type {Comment} */
749
+ const comment = {
750
+ value: input.slice(start + 2, end - 2),
751
+ range: [start, end],
752
+ loc: {
753
+ start: { line: sl, column: sc },
754
+ end: { line: el, column: ec }
755
+ }
756
+ };
757
+ this.comments.push(comment);
758
+ return end;
759
+ };
760
+
761
+ // Vanilla CSS stuff
762
+
763
+ /**
764
+ * @param {string} input input
765
+ * @param {number} start name start position
766
+ * @param {number} end name end position
767
+ * @returns {number} position after handling
768
+ */
769
+ const processAtImport = (input, start, end) => {
770
+ const tokens = walkCssTokens.eatImportTokens(input, end, {
771
+ comment
772
+ });
773
+ if (!tokens[3]) return end;
774
+ const semi = tokens[3][1];
775
+ if (!tokens[0]) {
776
+ this._emitWarning(
777
+ state,
778
+ `Expected URL in '${input.slice(start, semi)}'`,
779
+ locConverter,
780
+ start,
781
+ semi
782
+ );
783
+ return end;
784
+ }
785
+
786
+ const urlToken = tokens[0];
787
+ const url = normalizeUrl(input.slice(urlToken[2], urlToken[3]), true);
788
+ const newline = walkCssTokens.eatWhiteLine(input, semi);
789
+ const { options, errors: commentErrors } = this.parseCommentOptions([
790
+ end,
791
+ urlToken[1]
792
+ ]);
793
+ if (commentErrors) {
794
+ for (const e of commentErrors) {
795
+ const { comment } = e;
796
+ state.module.addWarning(
797
+ new CommentCompilationWarning(
798
+ `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
799
+ comment.loc
800
+ )
801
+ );
802
+ }
803
+ }
804
+ if (options && options.webpackIgnore !== undefined) {
805
+ if (typeof options.webpackIgnore !== "boolean") {
806
+ const { line: sl, column: sc } = locConverter.get(start);
807
+ const { line: el, column: ec } = locConverter.get(newline);
808
+
809
+ state.module.addWarning(
810
+ new UnsupportedFeatureWarning(
811
+ `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
812
+ {
813
+ start: { line: sl, column: sc },
814
+ end: { line: el, column: ec }
815
+ }
816
+ )
817
+ );
818
+ } else if (options.webpackIgnore) {
819
+ return newline;
820
+ }
821
+ }
822
+ if (url.length === 0) {
823
+ const { line: sl, column: sc } = locConverter.get(start);
824
+ const { line: el, column: ec } = locConverter.get(newline);
825
+ const dep = new ConstDependency("", [start, newline]);
826
+ module.addPresentationalDependency(dep);
827
+ dep.setLoc(sl, sc, el, ec);
828
+
829
+ return newline;
830
+ }
831
+
832
+ let layer;
833
+
834
+ if (tokens[1]) {
835
+ layer = input.slice(tokens[1][0] + 6, tokens[1][1] - 1).trim();
836
+ }
837
+
838
+ let supports;
839
+
840
+ if (tokens[2]) {
841
+ supports = input.slice(tokens[2][0] + 9, tokens[2][1] - 1).trim();
842
+ }
843
+
844
+ const last = tokens[2] || tokens[1] || tokens[0];
845
+ const mediaStart = walkCssTokens.eatWhitespaceAndComments(
846
+ input,
847
+ last[1]
848
+ )[0];
849
+
850
+ let media;
851
+
852
+ if (mediaStart !== semi - 1) {
853
+ media = input.slice(mediaStart, semi - 1).trim();
854
+ }
855
+
856
+ const { line: sl, column: sc } = locConverter.get(start);
857
+ const { line: el, column: ec } = locConverter.get(newline);
858
+ const dep = new CssImportDependency(
859
+ url,
860
+ [start, newline],
861
+ mode === "local" || mode === "global" ? mode : undefined,
862
+ layer,
863
+ supports && supports.length > 0 ? supports : undefined,
864
+ media && media.length > 0 ? media : undefined
865
+ );
866
+ dep.setLoc(sl, sc, el, ec);
867
+ module.addDependency(dep);
868
+
869
+ return newline;
870
+ };
871
+
872
+ /**
873
+ * @param {string} input input
874
+ * @param {number} end end position
875
+ * @param {string} name the name of function
876
+ * @returns {number} position after handling
877
+ */
878
+ const processURLFunction = (input, end, name) => {
879
+ const string = walkCssTokens.eatString(input, end);
880
+ if (!string) return end;
881
+ const { options, errors: commentErrors } = this.parseCommentOptions([
882
+ lastTokenEndForComments,
883
+ end
884
+ ]);
885
+ if (commentErrors) {
886
+ for (const e of commentErrors) {
887
+ const { comment } = e;
888
+ state.module.addWarning(
889
+ new CommentCompilationWarning(
890
+ `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
891
+ comment.loc
892
+ )
893
+ );
894
+ }
895
+ }
896
+ if (options && options.webpackIgnore !== undefined) {
897
+ if (typeof options.webpackIgnore !== "boolean") {
898
+ const { line: sl, column: sc } = locConverter.get(string[0]);
899
+ const { line: el, column: ec } = locConverter.get(string[1]);
900
+
901
+ state.module.addWarning(
902
+ new UnsupportedFeatureWarning(
903
+ `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
904
+ {
905
+ start: { line: sl, column: sc },
906
+ end: { line: el, column: ec }
907
+ }
908
+ )
909
+ );
910
+ } else if (options.webpackIgnore) {
911
+ return end;
912
+ }
913
+ }
914
+ const value = normalizeUrl(
915
+ input.slice(string[0] + 1, string[1] - 1),
916
+ true
917
+ );
918
+ // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
919
+ if (value.length === 0) return end;
920
+ const isUrl = name === "url" || name === "src";
921
+ const dep = new CssUrlDependency(
922
+ value,
923
+ [string[0], string[1]],
924
+ isUrl ? "string" : "url"
925
+ );
926
+ const { line: sl, column: sc } = locConverter.get(string[0]);
927
+ const { line: el, column: ec } = locConverter.get(string[1]);
928
+ dep.setLoc(sl, sc, el, ec);
929
+ module.addDependency(dep);
930
+ module.addCodeGenerationDependency(dep);
931
+ return string[1];
932
+ };
933
+
934
+ /**
935
+ * @param {string} input input
936
+ * @param {number} start start position
937
+ * @param {number} end end position
938
+ * @param {number} contentStart start position
939
+ * @param {number} contentEnd end position
940
+ * @returns {number} position after handling
941
+ */
942
+ const processOldURLFunction = (
943
+ input,
944
+ start,
945
+ end,
946
+ contentStart,
947
+ contentEnd
948
+ ) => {
949
+ const { options, errors: commentErrors } = this.parseCommentOptions([
950
+ lastTokenEndForComments,
951
+ end
952
+ ]);
953
+ if (commentErrors) {
954
+ for (const e of commentErrors) {
955
+ const { comment } = e;
956
+ state.module.addWarning(
957
+ new CommentCompilationWarning(
958
+ `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
959
+ comment.loc
960
+ )
961
+ );
962
+ }
963
+ }
964
+ if (options && options.webpackIgnore !== undefined) {
965
+ if (typeof options.webpackIgnore !== "boolean") {
966
+ const { line: sl, column: sc } = locConverter.get(
967
+ lastTokenEndForComments
968
+ );
969
+ const { line: el, column: ec } = locConverter.get(end);
970
+
971
+ state.module.addWarning(
972
+ new UnsupportedFeatureWarning(
973
+ `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
974
+ {
975
+ start: { line: sl, column: sc },
976
+ end: { line: el, column: ec }
977
+ }
978
+ )
979
+ );
980
+ } else if (options.webpackIgnore) {
981
+ return end;
982
+ }
983
+ }
984
+ const value = normalizeUrl(input.slice(contentStart, contentEnd), false);
985
+ // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
986
+ if (value.length === 0) return end;
987
+ const dep = new CssUrlDependency(value, [start, end], "url");
988
+ const { line: sl, column: sc } = locConverter.get(start);
989
+ const { line: el, column: ec } = locConverter.get(end);
990
+ dep.setLoc(sl, sc, el, ec);
991
+ module.addDependency(dep);
992
+ module.addCodeGenerationDependency(dep);
993
+ return end;
994
+ };
995
+
996
+ /**
997
+ * @param {string} input input
998
+ * @param {number} start start position
999
+ * @param {number} end end position
1000
+ * @returns {number} position after handling
1001
+ */
1002
+ const processImageSetFunction = (input, start, end) => {
1003
+ lastTokenEndForComments = end;
1004
+ const values = walkCssTokens.eatImageSetStrings(input, end, {
1005
+ comment
1006
+ });
1007
+ if (values.length === 0) return end;
1008
+ for (const [index, string] of values.entries()) {
1009
+ const value = normalizeUrl(
1010
+ input.slice(string[0] + 1, string[1] - 1),
1011
+ true
1012
+ );
1013
+ if (value.length === 0) return end;
1014
+ const { options, errors: commentErrors } = this.parseCommentOptions([
1015
+ index === 0 ? start : values[index - 1][1],
1016
+ string[1]
1017
+ ]);
1018
+ if (commentErrors) {
1019
+ for (const e of commentErrors) {
1020
+ const { comment } = e;
1021
+ state.module.addWarning(
1022
+ new CommentCompilationWarning(
1023
+ `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
1024
+ comment.loc
1025
+ )
1026
+ );
438
1027
  }
439
1028
  }
440
- const newPos = eater(input, pos);
441
- if (pos !== newPos) {
442
- text += input.slice(pos, newPos);
443
- pos = newPos;
444
- } else {
445
- break;
1029
+ if (options && options.webpackIgnore !== undefined) {
1030
+ if (typeof options.webpackIgnore !== "boolean") {
1031
+ const { line: sl, column: sc } = locConverter.get(string[0]);
1032
+ const { line: el, column: ec } = locConverter.get(string[1]);
1033
+
1034
+ state.module.addWarning(
1035
+ new UnsupportedFeatureWarning(
1036
+ `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
1037
+ {
1038
+ start: { line: sl, column: sc },
1039
+ end: { line: el, column: ec }
1040
+ }
1041
+ )
1042
+ );
1043
+ } else if (options.webpackIgnore) {
1044
+ continue;
1045
+ }
1046
+ }
1047
+ const dep = new CssUrlDependency(value, [string[0], string[1]], "url");
1048
+ const { line: sl, column: sc } = locConverter.get(string[0]);
1049
+ const { line: el, column: ec } = locConverter.get(string[1]);
1050
+ dep.setLoc(sl, sc, el, ec);
1051
+ module.addDependency(dep);
1052
+ module.addCodeGenerationDependency(dep);
1053
+ }
1054
+ // Can contain `url()` inside, so let's return end to allow parse them
1055
+ return end;
1056
+ };
1057
+
1058
+ // CSS modules stuff
1059
+
1060
+ /**
1061
+ * @param {string} value value to resolve
1062
+ * @returns {string | [string, string, boolean]} resolved reexport
1063
+ */
1064
+ const getReexport = (value) => {
1065
+ const reexport = icssDefinitions.get(value);
1066
+
1067
+ if (reexport) {
1068
+ if (reexport.isReference) {
1069
+ return [value, reexport.value, true];
446
1070
  }
447
- if (pos === input.length) break;
1071
+ return [value, reexport.value, false];
448
1072
  }
449
- return [pos, text.trimEnd()];
1073
+
1074
+ return value;
450
1075
  };
451
1076
 
452
1077
  /**
@@ -455,10 +1080,10 @@ class CssParser extends Parser {
455
1080
  * @param {number} pos start position
456
1081
  * @returns {number} position after parse
457
1082
  */
458
- const parseImportOrExport = (type, input, pos) => {
459
- pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
1083
+ const processImportOrExport = (type, input, pos) => {
1084
+ pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
460
1085
  /** @type {string | undefined} */
461
- let importPath;
1086
+ let request;
462
1087
  if (type === 0) {
463
1088
  let cc = input.charCodeAt(pos);
464
1089
  if (cc !== CC_LEFT_PARENTHESIS) {
@@ -477,16 +1102,16 @@ class CssParser extends Parser {
477
1102
  if (!str) {
478
1103
  this._emitWarning(
479
1104
  state,
480
- `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected string)`,
1105
+ `Unexpected '${input[pos]}' at ${pos} during parsing of '${type ? ":import" : ":export"}' (expected string)`,
481
1106
  locConverter,
482
1107
  stringStart,
483
1108
  pos
484
1109
  );
485
1110
  return pos;
486
1111
  }
487
- importPath = input.slice(str[0] + 1, str[1] - 1);
1112
+ request = input.slice(str[0] + 1, str[1] - 1);
488
1113
  pos = str[1];
489
- pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
1114
+ pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
490
1115
  cc = input.charCodeAt(pos);
491
1116
  if (cc !== CC_RIGHT_PARENTHESIS) {
492
1117
  this._emitWarning(
@@ -499,7 +1124,7 @@ class CssParser extends Parser {
499
1124
  return pos;
500
1125
  }
501
1126
  pos++;
502
- pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
1127
+ pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
503
1128
  }
504
1129
 
505
1130
  /**
@@ -510,12 +1135,22 @@ class CssParser extends Parser {
510
1135
  */
511
1136
  const createDep = (name, value, start, end) => {
512
1137
  if (type === 0) {
513
- icssDefinitions.set(name, {
514
- path: /** @type {string} */ (importPath),
1138
+ const dep = new CssIcssImportDependency(
1139
+ /** @type {string} */
1140
+ (request),
1141
+ [0, 0],
1142
+ /** @type {"local" | "global"} */
1143
+ (mode),
515
1144
  value
516
- });
1145
+ );
1146
+ const { line: sl, column: sc } = locConverter.get(start);
1147
+ const { line: el, column: ec } = locConverter.get(end);
1148
+ dep.setLoc(sl, sc, el, ec);
1149
+ module.addDependency(dep);
1150
+
1151
+ icssDefinitions.set(name, { value, isReference: true });
517
1152
  } else if (type === 1) {
518
- const dep = new CssIcssExportDependency(name, value);
1153
+ const dep = new CssIcssExportDependency(name, getReexport(value));
519
1154
  const { line: sl, column: sc } = locConverter.get(start);
520
1155
  const { line: el, column: ec } = locConverter.get(end);
521
1156
  dep.setLoc(sl, sc, el, ec);
@@ -605,90 +1240,877 @@ class CssParser extends Parser {
605
1240
 
606
1241
  return pos;
607
1242
  };
608
- const eatPropertyName = walkCssTokens.eatUntil(":{};");
1243
+
609
1244
  /**
610
1245
  * @param {string} input input
611
- * @param {number} pos name start position
1246
+ * @param {number} start name start position
612
1247
  * @param {number} end name end position
613
1248
  * @returns {number} position after handling
614
1249
  */
615
- const processLocalDeclaration = (input, pos, end) => {
616
- modeData = undefined;
617
- pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
618
- const propertyNameStart = pos;
619
- const [propertyNameEnd, propertyName] = eatText(
620
- input,
621
- pos,
622
- eatPropertyName
623
- );
624
- if (input.charCodeAt(propertyNameEnd) !== CC_COLON) return end;
625
- pos = propertyNameEnd + 1;
626
- if (propertyName.startsWith("--") && propertyName.length >= 3) {
627
- // CSS Variable
628
- const { line: sl, column: sc } = locConverter.get(propertyNameStart);
629
- const { line: el, column: ec } = locConverter.get(propertyNameEnd);
630
- const name = unescapeIdentifier(propertyName.slice(2));
631
- const dep = new CssLocalIdentifierDependency(
632
- name,
633
- [propertyNameStart, propertyNameEnd],
634
- "--"
635
- );
636
- dep.setLoc(sl, sc, el, ec);
637
- module.addDependency(dep);
638
- declaredCssVariables.add(name);
639
- } else if (
640
- OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY.test(propertyName)
641
- ) {
642
- inAnimationProperty = true;
643
- }
644
- return pos;
645
- };
646
- /**
647
- * @param {string} input input
648
- */
649
- const processDeclarationValueDone = (input) => {
650
- if (inAnimationProperty && lastIdentifier) {
651
- const { line: sl, column: sc } = locConverter.get(lastIdentifier[0]);
652
- const { line: el, column: ec } = locConverter.get(lastIdentifier[1]);
653
- const name = unescapeIdentifier(
654
- lastIdentifier[2]
655
- ? input.slice(lastIdentifier[0], lastIdentifier[1])
656
- : input.slice(lastIdentifier[0] + 1, lastIdentifier[1] - 1)
657
- );
658
- const dep = new CssSelfLocalIdentifierDependency(name, [
659
- lastIdentifier[0],
660
- lastIdentifier[1]
661
- ]);
662
- dep.setLoc(sl, sc, el, ec);
663
- module.addDependency(dep);
664
- lastIdentifier = undefined;
665
- }
666
- };
1250
+ const processAtValue = (input, start, end) => {
1251
+ const semi = eatUntilSemi(input, end);
1252
+ const atRuleEnd = semi + 1;
1253
+ const params = input.slice(end, semi);
1254
+ let [alias, request] = params.split(/\s*from\s*/);
667
1255
 
668
- /**
669
- * @param {string} input input
670
- * @param {number} start start
671
- * @param {number} end end
672
- * @returns {number} end
673
- */
674
- const comment = (input, start, end) => {
675
- if (!this.comments) this.comments = [];
676
- const { line: sl, column: sc } = locConverter.get(start);
677
- const { line: el, column: ec } = locConverter.get(end);
1256
+ if (request) {
1257
+ const aliases = alias
1258
+ .replace(CSS_COMMENT, " ")
1259
+ .trim()
1260
+ .replace(/^\(\s*|\s*\)$/g, "")
1261
+ .split(/\s*,\s*/);
678
1262
 
679
- /** @type {Comment} */
680
- const comment = {
681
- value: input.slice(start + 2, end - 2),
682
- range: [start, end],
683
- loc: {
684
- start: { line: sl, column: sc },
685
- end: { line: el, column: ec }
1263
+ request = request.replace(CSS_COMMENT, "").trim();
1264
+
1265
+ const isExplicitImport = request[0] === "'" || request[0] === '"';
1266
+
1267
+ if (isExplicitImport) {
1268
+ request = request.slice(1, -1);
686
1269
  }
687
- };
688
- this.comments.push(comment);
1270
+
1271
+ for (const alias of aliases) {
1272
+ const [name, aliasName] = alias.split(/\s+as\s+/);
1273
+
1274
+ {
1275
+ const reexport = icssDefinitions.get(request);
1276
+
1277
+ if (reexport) {
1278
+ request = reexport.value.slice(1, -1);
1279
+ }
1280
+
1281
+ const dep = new CssIcssImportDependency(
1282
+ request,
1283
+ [0, 0],
1284
+ /** @type {"local" | "global"} */
1285
+ (mode),
1286
+ name
1287
+ );
1288
+ const { line: sl, column: sc } = locConverter.get(start);
1289
+ const { line: el, column: ec } = locConverter.get(end);
1290
+ dep.setLoc(sl, sc, el, ec);
1291
+ module.addDependency(dep);
1292
+
1293
+ icssDefinitions.set(aliasName || name, {
1294
+ value: name,
1295
+ isReference: true
1296
+ });
1297
+ }
1298
+
1299
+ {
1300
+ const dep = new CssIcssExportDependency(
1301
+ aliasName || name,
1302
+ getReexport(aliasName || name),
1303
+ undefined,
1304
+ false,
1305
+ CssIcssExportDependency.EXPORT_MODE.REPLACE
1306
+ );
1307
+ const { line: sl, column: sc } = locConverter.get(start);
1308
+ const { line: el, column: ec } = locConverter.get(end);
1309
+ dep.setLoc(sl, sc, el, ec);
1310
+ module.addDependency(dep);
1311
+ }
1312
+ }
1313
+ } else {
1314
+ const ident = walkCssTokens.eatIdentSequence(alias, 0);
1315
+
1316
+ if (!ident) {
1317
+ this._emitWarning(
1318
+ state,
1319
+ `Broken '@value' at-rule: ${input.slice(start, atRuleEnd)}'`,
1320
+ locConverter,
1321
+ start,
1322
+ atRuleEnd
1323
+ );
1324
+
1325
+ const dep = new ConstDependency("", [start, atRuleEnd]);
1326
+ module.addPresentationalDependency(dep);
1327
+ return atRuleEnd;
1328
+ }
1329
+
1330
+ const pos = walkCssTokens.eatWhitespaceAndComments(alias, ident[1])[0];
1331
+
1332
+ const name = alias.slice(ident[0], ident[1]);
1333
+ let value =
1334
+ alias.charCodeAt(pos) === CC_COLON
1335
+ ? alias.slice(pos + 1)
1336
+ : alias.slice(ident[1]);
1337
+
1338
+ if (value && !/^\s+$/.test(value.replace(CSS_COMMENT, ""))) {
1339
+ value = value.trim();
1340
+ }
1341
+
1342
+ if (icssDefinitions.has(value)) {
1343
+ const def =
1344
+ /** @type {IcssDefinition} */
1345
+ (icssDefinitions.get(value));
1346
+
1347
+ value = def.value;
1348
+ }
1349
+
1350
+ icssDefinitions.set(name, { value });
1351
+
1352
+ const dep = new CssIcssExportDependency(name, value);
1353
+ const { line: sl, column: sc } = locConverter.get(start);
1354
+ const { line: el, column: ec } = locConverter.get(end);
1355
+ dep.setLoc(sl, sc, el, ec);
1356
+ module.addDependency(dep);
1357
+ }
1358
+
1359
+ const dep = new ConstDependency("", [start, atRuleEnd]);
1360
+ module.addPresentationalDependency(dep);
1361
+ return atRuleEnd;
1362
+ };
1363
+
1364
+ /**
1365
+ * @param {string} name ICSS symbol name
1366
+ * @param {number} start start position
1367
+ * @param {number} end end position
1368
+ * @returns {number} position after handling
1369
+ */
1370
+ const processICSSSymbol = (name, start, end) => {
1371
+ const { value, isReference } =
1372
+ /** @type {IcssDefinition} */
1373
+ (icssDefinitions.get(name));
1374
+ const { line: sl, column: sc } = locConverter.get(start);
1375
+ const { line: el, column: ec } = locConverter.get(end);
1376
+ const dep = new CssIcssSymbolDependency(
1377
+ name,
1378
+ value,
1379
+ [start, end],
1380
+ isReference
1381
+ );
1382
+ dep.setLoc(sl, sc, el, ec);
1383
+ module.addDependency(dep);
1384
+ return end;
1385
+ };
1386
+
1387
+ /**
1388
+ * @param {string} input input
1389
+ * @param {1 | 2} type type of function
1390
+ * @param {number} start start position
1391
+ * @param {number} end end position
1392
+ * @returns {number} position after handling
1393
+ */
1394
+ const processLocalOrGlobalFunction = (input, type, start, end) => {
1395
+ // Replace `local(`/` or `global(` (handle legacy `:local(` or `:global(` too)
1396
+ {
1397
+ const isColon = input.charCodeAt(start - 1) === CC_COLON;
1398
+ const dep = new ConstDependency("", [isColon ? start - 1 : start, end]);
1399
+ module.addPresentationalDependency(dep);
1400
+ }
1401
+
1402
+ end = walkCssTokens.consumeUntil(
1403
+ input,
1404
+ start,
1405
+ {
1406
+ identifier(input, start, end) {
1407
+ if (type === 1) {
1408
+ let identifier = unescapeIdentifier(input.slice(start, end));
1409
+ const { line: sl, column: sc } = locConverter.get(start);
1410
+ const { line: el, column: ec } = locConverter.get(end);
1411
+ const isDashedIdent = isDashedIdentifier(identifier);
1412
+
1413
+ if (isDashedIdent) {
1414
+ identifier = identifier.slice(2);
1415
+ }
1416
+
1417
+ const dep = new CssIcssExportDependency(
1418
+ identifier,
1419
+ getReexport(identifier),
1420
+ [start, end],
1421
+ true,
1422
+ CssIcssExportDependency.EXPORT_MODE.ONCE,
1423
+ isDashedIdent
1424
+ ? CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
1425
+ : CssIcssExportDependency.EXPORT_TYPE.NORMAL
1426
+ );
1427
+
1428
+ dep.setLoc(sl, sc, el, ec);
1429
+ module.addDependency(dep);
1430
+ }
1431
+
1432
+ return end;
1433
+ }
1434
+ },
1435
+ {},
1436
+ { onlyTopLevel: true, functionValue: true }
1437
+ );
1438
+
1439
+ {
1440
+ // Replace the last `)`
1441
+ const dep = new ConstDependency("", [end, end + 1]);
1442
+ module.addPresentationalDependency(dep);
1443
+ }
1444
+
1445
+ return end;
1446
+ };
1447
+
1448
+ /**
1449
+ * @param {string} input input
1450
+ * @param {number} end name end position
1451
+ * @param {{ string?: boolean, identifier?: boolean | RegExp }} options types which allowed to handle
1452
+ * @returns {number} position after handling
1453
+ */
1454
+ const processLocalAtRule = (input, end, options) => {
1455
+ let found = false;
1456
+
1457
+ return walkCssTokens.consumeUntil(
1458
+ input,
1459
+ end,
1460
+ {
1461
+ string(_input, start, end) {
1462
+ if (!found && options.string) {
1463
+ const value = unescapeIdentifier(input.slice(start + 1, end - 1));
1464
+ const { line: sl, column: sc } = locConverter.get(start);
1465
+ const { line: el, column: ec } = locConverter.get(end);
1466
+ const dep = new CssIcssExportDependency(
1467
+ value,
1468
+ value,
1469
+ [start, end],
1470
+ true,
1471
+ CssIcssExportDependency.EXPORT_MODE.ONCE
1472
+ );
1473
+ dep.setLoc(sl, sc, el, ec);
1474
+ module.addDependency(dep);
1475
+ found = true;
1476
+ }
1477
+ return end;
1478
+ },
1479
+ identifier(input, start, end) {
1480
+ if (!found) {
1481
+ const value = input.slice(start, end);
1482
+
1483
+ if (options.identifier) {
1484
+ const identifier = unescapeIdentifier(value);
1485
+
1486
+ if (
1487
+ options.identifier instanceof RegExp &&
1488
+ options.identifier.test(identifier)
1489
+ ) {
1490
+ return end;
1491
+ }
1492
+
1493
+ const { line: sl, column: sc } = locConverter.get(start);
1494
+ const { line: el, column: ec } = locConverter.get(end);
1495
+
1496
+ const dep = new CssIcssExportDependency(
1497
+ identifier,
1498
+ getReexport(identifier),
1499
+ [start, end],
1500
+ true,
1501
+ CssIcssExportDependency.EXPORT_MODE.ONCE,
1502
+ CssIcssExportDependency.EXPORT_TYPE.NORMAL
1503
+ );
1504
+ dep.setLoc(sl, sc, el, ec);
1505
+ module.addDependency(dep);
1506
+ found = true;
1507
+ }
1508
+ }
1509
+ return end;
1510
+ }
1511
+ },
1512
+ {
1513
+ function: (input, start, end) => {
1514
+ // No need to handle `:` (COLON), because it's always a function
1515
+ const name = input
1516
+ .slice(start, end - 1)
1517
+ .replace(/\\/g, "")
1518
+ .toLowerCase();
1519
+
1520
+ const type =
1521
+ name === "local" ? 1 : name === "global" ? 2 : undefined;
1522
+
1523
+ if (!found && type) {
1524
+ found = true;
1525
+ return processLocalOrGlobalFunction(input, type, start, end);
1526
+ }
1527
+
1528
+ if (
1529
+ this.options.dashedIdents &&
1530
+ isLocalMode() &&
1531
+ (name === "var" || name === "style")
1532
+ ) {
1533
+ return processDashedIdent(input, end, end);
1534
+ }
1535
+
1536
+ return end;
1537
+ }
1538
+ },
1539
+ { onlyTopLevel: true, atRulePrelude: true }
1540
+ );
1541
+ };
1542
+ /**
1543
+ * @param {string} input input
1544
+ * @param {number} start start position
1545
+ * @param {number} end end position
1546
+ * @returns {number} position after handling
1547
+ */
1548
+ const processDashedIdent = (input, start, end) => {
1549
+ const customIdent = walkCssTokens.eatIdentSequence(input, start);
1550
+ if (!customIdent) return end;
1551
+ const identifier = unescapeIdentifier(
1552
+ input.slice(customIdent[0] + 2, customIdent[1])
1553
+ );
1554
+ const afterCustomIdent = walkCssTokens.eatWhitespaceAndComments(
1555
+ input,
1556
+ customIdent[1]
1557
+ )[0];
1558
+ if (
1559
+ input.charCodeAt(afterCustomIdent) === CC_LOWER_F ||
1560
+ input.charCodeAt(afterCustomIdent) === CC_UPPER_F
1561
+ ) {
1562
+ const fromWord = walkCssTokens.eatIdentSequence(
1563
+ input,
1564
+ afterCustomIdent
1565
+ );
1566
+ if (
1567
+ !fromWord ||
1568
+ input.slice(fromWord[0], fromWord[1]).toLowerCase() !== "from"
1569
+ ) {
1570
+ return end;
1571
+ }
1572
+ const from = walkCssTokens.eatIdentSequenceOrString(
1573
+ input,
1574
+ walkCssTokens.eatWhitespaceAndComments(input, fromWord[1])[0]
1575
+ );
1576
+ if (!from) {
1577
+ return end;
1578
+ }
1579
+ const path = input.slice(from[0], from[1]);
1580
+ if (from[2] === true && path === "global") {
1581
+ const dep = new ConstDependency("", [customIdent[1], from[1]]);
1582
+ module.addPresentationalDependency(dep);
1583
+ return end;
1584
+ } else if (from[2] === false) {
1585
+ const { line: sl, column: sc } = locConverter.get(customIdent[0]);
1586
+ const { line: el, column: ec } = locConverter.get(from[1] - 1);
1587
+ const dep = new CssIcssImportDependency(
1588
+ path.slice(1, -1),
1589
+ [customIdent[0], from[1] - 1],
1590
+ /** @type {"local" | "global"} */
1591
+ (mode),
1592
+ identifier,
1593
+ identifier,
1594
+ CssIcssExportDependency.EXPORT_MODE.NONE,
1595
+ CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
1596
+ );
1597
+
1598
+ dep.setLoc(sl, sc, el, ec);
1599
+ module.addDependency(dep);
1600
+
1601
+ {
1602
+ const dep = new ConstDependency("", [fromWord[0], from[1]]);
1603
+ module.addPresentationalDependency(dep);
1604
+ return end;
1605
+ }
1606
+ }
1607
+ } else {
1608
+ const { line: sl, column: sc } = locConverter.get(customIdent[0]);
1609
+ const { line: el, column: ec } = locConverter.get(customIdent[1]);
1610
+ const dep = new CssIcssExportDependency(
1611
+ identifier,
1612
+ getReexport(identifier),
1613
+ [customIdent[0], customIdent[1]],
1614
+ true,
1615
+ CssIcssExportDependency.EXPORT_MODE.ONCE,
1616
+ CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
1617
+ );
1618
+ dep.setLoc(sl, sc, el, ec);
1619
+ module.addDependency(dep);
1620
+ return end;
1621
+ }
1622
+
1623
+ return end;
1624
+ };
1625
+ /**
1626
+ * @param {string} input input
1627
+ * @param {number} pos name start position
1628
+ * @param {number} end name end position
1629
+ * @returns {number} position after handling
1630
+ */
1631
+ const processLocalDeclaration = (input, pos, end) => {
1632
+ pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
1633
+ const identifier = walkCssTokens.eatIdentSequence(input, pos);
1634
+
1635
+ if (!identifier) {
1636
+ return end;
1637
+ }
1638
+
1639
+ const propertyNameStart = identifier[0];
1640
+
1641
+ pos = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0];
1642
+
1643
+ if (input.charCodeAt(pos) !== CC_COLON) {
1644
+ return end;
1645
+ }
1646
+
1647
+ pos += 1;
1648
+
1649
+ // Remove prefix and lowercase
1650
+ const propertyName = input
1651
+ .slice(identifier[0], identifier[1])
1652
+ .replace(/^(-\w+-)/, "")
1653
+ .toLowerCase();
1654
+
1655
+ if (isLocalMode() && knownProperties.has(propertyName)) {
1656
+ /** @type {[number, number, boolean?][]} */
1657
+ const values = [];
1658
+ /** @type {Record<string, number>} */
1659
+ let parsedKeywords = Object.create(null);
1660
+
1661
+ const isGridProperty = Boolean(propertyName.startsWith("grid"));
1662
+ const isGridTemplate = isGridProperty
1663
+ ? Boolean(
1664
+ propertyName === "grid" ||
1665
+ propertyName === "grid-template" ||
1666
+ propertyName === "grid-template-columns" ||
1667
+ propertyName === "grid-template-rows"
1668
+ )
1669
+ : false;
1670
+
1671
+ const end = walkCssTokens.consumeUntil(
1672
+ input,
1673
+ pos,
1674
+ {
1675
+ leftSquareBracket(input, start, end) {
1676
+ let i = end;
1677
+
1678
+ while (true) {
1679
+ i = walkCssTokens.eatWhitespaceAndComments(input, i)[0];
1680
+ const name = walkCssTokens.eatIdentSequence(input, i);
1681
+
1682
+ if (!name) {
1683
+ break;
1684
+ }
1685
+
1686
+ values.push(name);
1687
+ i = name[1];
1688
+ }
1689
+
1690
+ return end;
1691
+ },
1692
+ string(_input, start, end) {
1693
+ if (
1694
+ propertyName === "animation" ||
1695
+ propertyName === "animation-name"
1696
+ ) {
1697
+ values.push([start, end, true]);
1698
+ }
1699
+
1700
+ if (
1701
+ propertyName === "grid" ||
1702
+ propertyName === "grid-template" ||
1703
+ propertyName === "grid-template-areas"
1704
+ ) {
1705
+ const areas = unescapeIdentifier(
1706
+ input.slice(start + 1, end - 1)
1707
+ );
1708
+ const matches = matchAll(/\b\w+\b/g, areas);
1709
+
1710
+ for (const match of matches) {
1711
+ const areaStart = start + 1 + match.index;
1712
+ values.push([areaStart, areaStart + match[0].length, false]);
1713
+ }
1714
+ }
1715
+
1716
+ return end;
1717
+ },
1718
+ identifier(input, start, end) {
1719
+ if (isGridTemplate) {
1720
+ return end;
1721
+ }
1722
+
1723
+ const identifier = input.slice(start, end);
1724
+ const keyword = identifier.toLowerCase();
1725
+
1726
+ parsedKeywords[keyword] =
1727
+ typeof parsedKeywords[keyword] !== "undefined"
1728
+ ? parsedKeywords[keyword] + 1
1729
+ : 0;
1730
+ const keywords =
1731
+ /** @type {Record<string, number>} */
1732
+ (knownProperties.get(propertyName));
1733
+
1734
+ if (
1735
+ keywords[keyword] &&
1736
+ parsedKeywords[keyword] < keywords[keyword]
1737
+ ) {
1738
+ return end;
1739
+ }
1740
+
1741
+ values.push([start, end]);
1742
+ return end;
1743
+ },
1744
+ comma(_input, _start, end) {
1745
+ parsedKeywords = {};
1746
+
1747
+ return end;
1748
+ }
1749
+ },
1750
+ {
1751
+ function: (input, start, end) => {
1752
+ const name = input
1753
+ .slice(start, end - 1)
1754
+ .replace(/\\/g, "")
1755
+ .toLowerCase();
1756
+
1757
+ const type =
1758
+ name === "local" ? 1 : name === "global" ? 2 : undefined;
1759
+
1760
+ if (type) {
1761
+ return processLocalOrGlobalFunction(input, type, start, end);
1762
+ }
1763
+
1764
+ if (
1765
+ this.options.dashedIdents &&
1766
+ isLocalMode() &&
1767
+ name === "var"
1768
+ ) {
1769
+ return processDashedIdent(input, end, end);
1770
+ }
1771
+
1772
+ if (this.options.url) {
1773
+ if (name === "src" || name === "url") {
1774
+ return processURLFunction(input, end, name);
1775
+ } else if (IMAGE_SET_FUNCTION.test(name)) {
1776
+ return processImageSetFunction(input, start, end);
1777
+ }
1778
+ }
1779
+
1780
+ return end;
1781
+ }
1782
+ },
1783
+ {
1784
+ onlyTopLevel: !isGridTemplate,
1785
+ declarationValue: true
1786
+ }
1787
+ );
1788
+
1789
+ if (values.length > 0) {
1790
+ for (const value of values) {
1791
+ const { line: sl, column: sc } = locConverter.get(value[0]);
1792
+ const { line: el, column: ec } = locConverter.get(value[1]);
1793
+ const [start, end, isString] = value;
1794
+ const name = unescapeIdentifier(
1795
+ isString
1796
+ ? input.slice(start + 1, end - 1)
1797
+ : input.slice(start, end)
1798
+ );
1799
+ const dep = new CssIcssExportDependency(
1800
+ name,
1801
+ getReexport(name),
1802
+ [start, end],
1803
+ true,
1804
+ CssIcssExportDependency.EXPORT_MODE.ONCE,
1805
+ isGridProperty
1806
+ ? CssIcssExportDependency.EXPORT_TYPE.GRID_CUSTOM_IDENTIFIER
1807
+ : CssIcssExportDependency.EXPORT_TYPE.NORMAL
1808
+ );
1809
+ dep.setLoc(sl, sc, el, ec);
1810
+ module.addDependency(dep);
1811
+ }
1812
+ }
1813
+
1814
+ return end;
1815
+ } else if (COMPOSES_PROPERTY.test(propertyName)) {
1816
+ if (lastLocalIdentifiers.length > 1) {
1817
+ const end = eatUntilSemi(input, pos);
1818
+ this._emitWarning(
1819
+ state,
1820
+ `Composition is only allowed when selector is single local class name not in "${lastLocalIdentifiers.join('", "')}"`,
1821
+ locConverter,
1822
+ pos,
1823
+ end
1824
+ );
1825
+
1826
+ return end;
1827
+ }
1828
+
1829
+ if (lastLocalIdentifiers.length !== 1) return pos;
1830
+
1831
+ const lastLocalIdentifier = lastLocalIdentifiers[0];
1832
+ let end = pos;
1833
+
1834
+ /** @type {Set<[number, number]>} */
1835
+ const classNames = new Set();
1836
+
1837
+ while (true) {
1838
+ pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
1839
+
1840
+ let className = walkCssTokens.eatIdentSequence(input, pos);
1841
+
1842
+ const ifFunction =
1843
+ className && input.charCodeAt(className[1]) === CC_LEFT_PARENTHESIS;
1844
+ let isGlobalFunction = false;
1845
+
1846
+ if (className && ifFunction) {
1847
+ const name = input
1848
+ .slice(className[0], className[1])
1849
+ .replace(/\\/g, "")
1850
+ .toLowerCase();
1851
+
1852
+ isGlobalFunction = name === "global";
1853
+ pos = walkCssTokens.eatWhitespaceAndComments(
1854
+ input,
1855
+ className[1] + 1
1856
+ )[0];
1857
+ className = walkCssTokens.eatIdentSequence(input, pos);
1858
+ if (className) {
1859
+ pos = walkCssTokens.eatWhitespaceAndComments(
1860
+ input,
1861
+ className[1]
1862
+ )[0];
1863
+ pos += 1;
1864
+ }
1865
+ } else if (className) {
1866
+ pos = walkCssTokens.eatWhitespaceAndComments(
1867
+ input,
1868
+ className[1]
1869
+ )[0];
1870
+ pos = className[1];
1871
+ }
1872
+
1873
+ // True when we have multiple values
1874
+ const isComma = input.charCodeAt(pos) === CC_COMMA;
1875
+ const isSemicolon = input.charCodeAt(pos) === CC_SEMICOLON;
1876
+ const isRightCurly = input.charCodeAt(pos) === CC_RIGHT_CURLY;
1877
+
1878
+ if (isComma || isSemicolon || isRightCurly) {
1879
+ if (className) {
1880
+ classNames.add(className);
1881
+ }
1882
+
1883
+ for (const className of classNames) {
1884
+ const [start, end] = className;
1885
+ const identifier = unescapeIdentifier(input.slice(start, end));
1886
+ const resolvedClassName = getReexport(identifier);
1887
+ const dep = new CssIcssExportDependency(
1888
+ lastLocalIdentifier,
1889
+ resolvedClassName,
1890
+ [start, end],
1891
+ isGlobalFunction ? false : !Array.isArray(resolvedClassName),
1892
+ isGlobalFunction
1893
+ ? CssIcssExportDependency.EXPORT_MODE.APPEND
1894
+ : CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE
1895
+ );
1896
+ const { line: sl, column: sc } = locConverter.get(start);
1897
+ const { line: el, column: ec } = locConverter.get(end);
1898
+ dep.setLoc(sl, sc, el, ec);
1899
+ module.addDependency(dep);
1900
+ }
1901
+
1902
+ classNames.clear();
1903
+
1904
+ if (isSemicolon || isRightCurly) {
1905
+ end = isSemicolon
1906
+ ? walkCssTokens.eatWhitespace(input, pos + 1)
1907
+ : pos;
1908
+ break;
1909
+ }
1910
+
1911
+ pos += 1;
1912
+ } else if (
1913
+ classNames.size > 0 &&
1914
+ className &&
1915
+ input.slice(className[0], className[1]).toLowerCase() === "from"
1916
+ ) {
1917
+ let from = walkCssTokens.eatString(input, pos);
1918
+
1919
+ if (from) {
1920
+ const request = input.slice(from[0] + 1, from[1] - 1);
1921
+
1922
+ for (const className of classNames) {
1923
+ const [start, end] = className;
1924
+ const identifier = unescapeIdentifier(input.slice(start, end));
1925
+ const dep = new CssIcssImportDependency(
1926
+ request,
1927
+ [start, end],
1928
+ /** @type {"local" | "global"} */
1929
+ (mode),
1930
+ identifier,
1931
+ /** @type {string} */
1932
+ (lastLocalIdentifier),
1933
+ CssIcssExportDependency.EXPORT_MODE.APPEND
1934
+ );
1935
+ const { line: sl, column: sc } = locConverter.get(start);
1936
+ const { line: el, column: ec } = locConverter.get(end);
1937
+ dep.setLoc(sl, sc, el, ec);
1938
+ module.addDependency(dep);
1939
+ }
1940
+
1941
+ classNames.clear();
1942
+ pos = from[1];
1943
+ } else {
1944
+ from = walkCssTokens.eatIdentSequence(input, pos);
1945
+
1946
+ if (from && input.slice(from[0], from[1]) === "global") {
1947
+ for (const className of classNames) {
1948
+ const [start, end] = className;
1949
+ const identifier = unescapeIdentifier(
1950
+ input.slice(start, end)
1951
+ );
1952
+ const dep = new CssIcssExportDependency(
1953
+ /** @type {string} */
1954
+ (lastLocalIdentifier),
1955
+ getReexport(identifier),
1956
+ [start, end],
1957
+ false,
1958
+ CssIcssExportDependency.EXPORT_MODE.APPEND
1959
+ );
1960
+ const { line: sl, column: sc } = locConverter.get(start);
1961
+ const { line: el, column: ec } = locConverter.get(end);
1962
+ dep.setLoc(sl, sc, el, ec);
1963
+ module.addDependency(dep);
1964
+ }
1965
+
1966
+ classNames.clear();
1967
+ pos = from[1];
1968
+ } else {
1969
+ const end = eatUntilSemi(input, pos);
1970
+ this._emitWarning(
1971
+ state,
1972
+ "Incorrect composition, expected global keyword or string value",
1973
+ locConverter,
1974
+ pos,
1975
+ end
1976
+ );
1977
+ return end;
1978
+ }
1979
+ }
1980
+ } else if (className) {
1981
+ classNames.add(className);
1982
+ } else {
1983
+ const end = eatUntilSemi(input, pos);
1984
+ this._emitWarning(
1985
+ state,
1986
+ "Incorrect composition, expected class named",
1987
+ locConverter,
1988
+ pos,
1989
+ end
1990
+ );
1991
+ return end;
1992
+ }
1993
+ }
1994
+
1995
+ // Remove `composes` from source code
1996
+ const dep = new ConstDependency("", [propertyNameStart, end]);
1997
+ module.addPresentationalDependency(dep);
1998
+ }
1999
+
2000
+ return pos;
2001
+ };
2002
+
2003
+ /**
2004
+ * @param {string} input input
2005
+ * @param {number} start start position
2006
+ * @param {number} end end position
2007
+ * @returns {number} position after handling
2008
+ */
2009
+ const processIdSelector = (input, start, end) => {
2010
+ const valueStart = start + 1;
2011
+ const name = unescapeIdentifier(input.slice(valueStart, end));
2012
+ const dep = new CssIcssExportDependency(
2013
+ name,
2014
+ getReexport(name),
2015
+ [valueStart, end],
2016
+ true,
2017
+ CssIcssExportDependency.EXPORT_MODE.ONCE
2018
+ );
2019
+ const { line: sl, column: sc } = locConverter.get(start);
2020
+ const { line: el, column: ec } = locConverter.get(end);
2021
+ dep.setLoc(sl, sc, el, ec);
2022
+ module.addDependency(dep);
689
2023
  return end;
690
2024
  };
691
2025
 
2026
+ /**
2027
+ * @param {string} input input
2028
+ * @param {number} start start position
2029
+ * @param {number} end end position
2030
+ * @returns {number} position after handling
2031
+ */
2032
+ const processClassSelector = (input, start, end) => {
2033
+ const ident = walkCssTokens.skipCommentsAndEatIdentSequence(input, end);
2034
+ if (!ident) return end;
2035
+ const name = unescapeIdentifier(input.slice(ident[0], ident[1]));
2036
+ lastLocalIdentifiers.push(name);
2037
+ const dep = new CssIcssExportDependency(
2038
+ name,
2039
+ getReexport(name),
2040
+ [ident[0], ident[1]],
2041
+ true,
2042
+ CssIcssExportDependency.EXPORT_MODE.ONCE
2043
+ );
2044
+ const { line: sl, column: sc } = locConverter.get(ident[0]);
2045
+ const { line: el, column: ec } = locConverter.get(ident[1]);
2046
+ dep.setLoc(sl, sc, el, ec);
2047
+ module.addDependency(dep);
2048
+ return ident[1];
2049
+ };
2050
+
2051
+ /**
2052
+ * @param {string} input input
2053
+ * @param {number} start start position
2054
+ * @param {number} end end position
2055
+ * @returns {number} position after handling
2056
+ */
2057
+ const processAttributeSelector = (input, start, end) => {
2058
+ end = walkCssTokens.eatWhitespaceAndComments(input, end)[0];
2059
+ const identifier = walkCssTokens.eatIdentSequence(input, end);
2060
+ if (!identifier) return end;
2061
+ const name = unescapeIdentifier(
2062
+ input.slice(identifier[0], identifier[1])
2063
+ );
2064
+ if (name.toLowerCase() !== "class") {
2065
+ return end;
2066
+ }
2067
+ end = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0];
2068
+
2069
+ const isTilde = input.charCodeAt(end) === CC_TILDE;
2070
+
2071
+ if (
2072
+ input.charCodeAt(end) !== CC_EQUAL &&
2073
+ input.charCodeAt(end) !== CC_TILDE
2074
+ ) {
2075
+ return end;
2076
+ }
2077
+
2078
+ end += 1;
2079
+
2080
+ if (isTilde) {
2081
+ if (input.charCodeAt(end) !== CC_EQUAL) {
2082
+ return end;
2083
+ }
2084
+
2085
+ end += 1;
2086
+ }
2087
+
2088
+ end = walkCssTokens.eatWhitespaceAndComments(input, end)[0];
2089
+ const value = walkCssTokens.eatIdentSequenceOrString(input, end);
2090
+
2091
+ if (!value) {
2092
+ return end;
2093
+ }
2094
+
2095
+ const classNameStart = value[2] ? value[0] : value[0] + 1;
2096
+ const classNameEnd = value[2] ? value[1] : value[1] - 1;
2097
+ const className = unescapeIdentifier(
2098
+ input.slice(classNameStart, classNameEnd)
2099
+ );
2100
+ const dep = new CssIcssExportDependency(
2101
+ className,
2102
+ getReexport(className),
2103
+ [classNameStart, classNameEnd],
2104
+ true,
2105
+ CssIcssExportDependency.EXPORT_MODE.NONE
2106
+ );
2107
+ const { line: sl, column: sc } = locConverter.get(classNameStart);
2108
+ const { line: el, column: ec } = locConverter.get(classNameEnd);
2109
+ dep.setLoc(sl, sc, el, ec);
2110
+ module.addDependency(dep);
2111
+ return value[2] ? classNameEnd : classNameEnd + 1;
2112
+ };
2113
+
692
2114
  walkCssTokens(source, 0, {
693
2115
  comment,
694
2116
  leftCurlyBracket: (input, start, end) => {
@@ -723,13 +2145,9 @@ class CssParser extends Parser {
723
2145
  if (isModules) {
724
2146
  isNextRulePrelude = true;
725
2147
  modeData = undefined;
2148
+ lastLocalIdentifiers = [];
726
2149
  }
727
2150
  } else if (isModules) {
728
- if (isLocalMode()) {
729
- processDeclarationValueDone(input);
730
- inAnimationProperty = false;
731
- }
732
-
733
2151
  isNextRulePrelude = isNextNestedSyntax(input, end);
734
2152
  }
735
2153
  break;
@@ -738,68 +2156,17 @@ class CssParser extends Parser {
738
2156
  return end;
739
2157
  },
740
2158
  url: (input, start, end, contentStart, contentEnd) => {
741
- if (!this.url) {
2159
+ if (!this.options.url) {
742
2160
  return end;
743
2161
  }
744
2162
 
745
- const { options, errors: commentErrors } = this.parseCommentOptions([
746
- lastTokenEndForComments,
747
- end
748
- ]);
749
- if (commentErrors) {
750
- for (const e of commentErrors) {
751
- const { comment } = e;
752
- state.module.addWarning(
753
- new CommentCompilationWarning(
754
- `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
755
- comment.loc
756
- )
757
- );
758
- }
759
- }
760
- if (options && options.webpackIgnore !== undefined) {
761
- if (typeof options.webpackIgnore !== "boolean") {
762
- const { line: sl, column: sc } = locConverter.get(
763
- lastTokenEndForComments
764
- );
765
- const { line: el, column: ec } = locConverter.get(end);
766
-
767
- state.module.addWarning(
768
- new UnsupportedFeatureWarning(
769
- `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
770
- {
771
- start: { line: sl, column: sc },
772
- end: { line: el, column: ec }
773
- }
774
- )
775
- );
776
- } else if (options.webpackIgnore) {
777
- return end;
778
- }
779
- }
780
- const value = normalizeUrl(
781
- input.slice(contentStart, contentEnd),
782
- false
2163
+ return processOldURLFunction(
2164
+ input,
2165
+ start,
2166
+ end,
2167
+ contentStart,
2168
+ contentEnd
783
2169
  );
784
- // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
785
- if (value.length === 0) return end;
786
- const dep = new CssUrlDependency(value, [start, end], "url");
787
- const { line: sl, column: sc } = locConverter.get(start);
788
- const { line: el, column: ec } = locConverter.get(end);
789
- dep.setLoc(sl, sc, el, ec);
790
- module.addDependency(dep);
791
- module.addCodeGenerationDependency(dep);
792
- return end;
793
- },
794
- string: (_input, start, end) => {
795
- switch (scope) {
796
- case CSS_MODE_IN_BLOCK: {
797
- if (inAnimationProperty && balanced.length === 0) {
798
- lastIdentifier = [start, end, false];
799
- }
800
- }
801
- }
802
- return end;
803
2170
  },
804
2171
  atKeyword: (input, start, end) => {
805
2172
  const name = input.slice(start, end).toLowerCase();
@@ -817,8 +2184,8 @@ class CssParser extends Parser {
817
2184
  return eatUntilSemi(input, start);
818
2185
  }
819
2186
  case "@import": {
820
- if (!this.import) {
821
- return eatSemi(input, end);
2187
+ if (!this.options.import) {
2188
+ return eatUntilSemi(input, end);
822
2189
  }
823
2190
 
824
2191
  if (!allowImportAtRule) {
@@ -832,234 +2199,37 @@ class CssParser extends Parser {
832
2199
  return end;
833
2200
  }
834
2201
 
835
- const tokens = walkCssTokens.eatImportTokens(input, end, {
836
- comment
837
- });
838
- if (!tokens[3]) return end;
839
- const semi = tokens[3][1];
840
- if (!tokens[0]) {
841
- this._emitWarning(
842
- state,
843
- `Expected URL in '${input.slice(start, semi)}'`,
844
- locConverter,
845
- start,
846
- semi
847
- );
848
- return end;
849
- }
850
-
851
- const urlToken = tokens[0];
852
- const url = normalizeUrl(
853
- input.slice(urlToken[2], urlToken[3]),
854
- true
855
- );
856
- const newline = walkCssTokens.eatWhiteLine(input, semi);
857
- const { options, errors: commentErrors } = this.parseCommentOptions(
858
- [end, urlToken[1]]
859
- );
860
- if (commentErrors) {
861
- for (const e of commentErrors) {
862
- const { comment } = e;
863
- state.module.addWarning(
864
- new CommentCompilationWarning(
865
- `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
866
- comment.loc
867
- )
868
- );
869
- }
870
- }
871
- if (options && options.webpackIgnore !== undefined) {
872
- if (typeof options.webpackIgnore !== "boolean") {
873
- const { line: sl, column: sc } = locConverter.get(start);
874
- const { line: el, column: ec } = locConverter.get(newline);
875
-
876
- state.module.addWarning(
877
- new UnsupportedFeatureWarning(
878
- `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
879
- {
880
- start: { line: sl, column: sc },
881
- end: { line: el, column: ec }
882
- }
883
- )
884
- );
885
- } else if (options.webpackIgnore) {
886
- return newline;
887
- }
888
- }
889
- if (url.length === 0) {
890
- const { line: sl, column: sc } = locConverter.get(start);
891
- const { line: el, column: ec } = locConverter.get(newline);
892
- const dep = new ConstDependency("", [start, newline]);
893
- module.addPresentationalDependency(dep);
894
- dep.setLoc(sl, sc, el, ec);
895
-
896
- return newline;
897
- }
898
-
899
- let layer;
900
-
901
- if (tokens[1]) {
902
- layer = input.slice(tokens[1][0] + 6, tokens[1][1] - 1).trim();
903
- }
904
-
905
- let supports;
906
-
907
- if (tokens[2]) {
908
- supports = input.slice(tokens[2][0] + 9, tokens[2][1] - 1).trim();
909
- }
910
-
911
- const last = tokens[2] || tokens[1] || tokens[0];
912
- const mediaStart = walkCssTokens.eatWhitespaceAndComments(
913
- input,
914
- last[1]
915
- );
916
-
917
- let media;
918
-
919
- if (mediaStart !== semi - 1) {
920
- media = input.slice(mediaStart, semi - 1).trim();
921
- }
922
-
923
- const { line: sl, column: sc } = locConverter.get(start);
924
- const { line: el, column: ec } = locConverter.get(newline);
925
- const dep = new CssImportDependency(
926
- url,
927
- [start, newline],
928
- layer,
929
- supports && supports.length > 0 ? supports : undefined,
930
- media && media.length > 0 ? media : undefined
931
- );
932
- dep.setLoc(sl, sc, el, ec);
933
- module.addDependency(dep);
934
-
935
- return newline;
2202
+ return processAtImport(input, start, end);
936
2203
  }
937
2204
  default: {
938
2205
  if (isModules) {
939
2206
  if (name === "@value") {
940
- const semi = eatUntilSemi(input, end);
941
- const atRuleEnd = semi + 1;
942
- const params = input.slice(end, semi);
943
- let [alias, from] = params.split(/\s*from\s*/);
944
-
945
- if (from) {
946
- const aliases = alias
947
- .replace(CSS_COMMENT, " ")
948
- .trim()
949
- .replace(/^\(|\)$/g, "")
950
- .split(/\s*,\s*/);
951
-
952
- from = from.replace(CSS_COMMENT, "").trim();
953
-
954
- const isExplicitImport = from[0] === "'" || from[0] === '"';
955
-
956
- if (isExplicitImport) {
957
- from = from.slice(1, -1);
958
- }
959
-
960
- for (const alias of aliases) {
961
- const [name, aliasName] = alias.split(/\s*as\s*/);
962
-
963
- icssDefinitions.set(aliasName || name, {
964
- value: name,
965
- path: from
966
- });
967
- }
968
- } else {
969
- const ident = walkCssTokens.eatIdentSequence(alias, 0);
970
-
971
- if (!ident) {
972
- this._emitWarning(
973
- state,
974
- `Broken '@value' at-rule: ${input.slice(
975
- start,
976
- atRuleEnd
977
- )}'`,
978
- locConverter,
979
- start,
980
- atRuleEnd
981
- );
982
-
983
- const dep = new ConstDependency("", [start, atRuleEnd]);
984
- module.addPresentationalDependency(dep);
985
- return atRuleEnd;
986
- }
987
-
988
- const pos = walkCssTokens.eatWhitespaceAndComments(
989
- alias,
990
- ident[1]
991
- );
992
-
993
- const name = alias.slice(ident[0], ident[1]);
994
- let value =
995
- alias.charCodeAt(pos) === CC_COLON
996
- ? alias.slice(pos + 1)
997
- : alias.slice(ident[1]);
998
-
999
- if (value && !/^\s+$/.test(value)) {
1000
- value = value.trim();
1001
- }
1002
-
1003
- if (icssDefinitions.has(value)) {
1004
- const def =
1005
- /** @type {IcssDefinition} */
1006
- (icssDefinitions.get(value));
1007
-
1008
- value = def.value;
1009
- }
1010
-
1011
- icssDefinitions.set(name, { value });
1012
-
1013
- const dep = new CssIcssExportDependency(name, value);
1014
- const { line: sl, column: sc } = locConverter.get(start);
1015
- const { line: el, column: ec } = locConverter.get(end);
1016
- dep.setLoc(sl, sc, el, ec);
1017
- module.addDependency(dep);
1018
- }
1019
-
1020
- const dep = new ConstDependency("", [start, atRuleEnd]);
1021
- module.addPresentationalDependency(dep);
1022
- return atRuleEnd;
2207
+ return processAtValue(input, start, end);
1023
2208
  } else if (
2209
+ this.options.animation &&
1024
2210
  OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name) &&
1025
2211
  isLocalMode()
1026
2212
  ) {
1027
- const ident = walkCssTokens.eatIdentSequenceOrString(
1028
- input,
1029
- end
1030
- );
1031
- if (!ident) return end;
1032
- const name = unescapeIdentifier(
1033
- ident[2] === true
1034
- ? input.slice(ident[0], ident[1])
1035
- : input.slice(ident[0] + 1, ident[1] - 1)
1036
- );
1037
- const { line: sl, column: sc } = locConverter.get(ident[0]);
1038
- const { line: el, column: ec } = locConverter.get(ident[1]);
1039
- const dep = new CssLocalIdentifierDependency(name, [
1040
- ident[0],
1041
- ident[1]
1042
- ]);
1043
- dep.setLoc(sl, sc, el, ec);
1044
- module.addDependency(dep);
1045
- return ident[1];
1046
- } else if (name === "@property" && isLocalMode()) {
1047
- const ident = walkCssTokens.eatIdentSequence(input, end);
1048
- if (!ident) return end;
1049
- let name = input.slice(ident[0], ident[1]);
1050
- if (!name.startsWith("--") || name.length < 3) return end;
1051
- name = unescapeIdentifier(name.slice(2));
1052
- declaredCssVariables.add(name);
1053
- const { line: sl, column: sc } = locConverter.get(ident[0]);
1054
- const { line: el, column: ec } = locConverter.get(ident[1]);
1055
- const dep = new CssLocalIdentifierDependency(
1056
- name,
1057
- [ident[0], ident[1]],
1058
- "--"
1059
- );
1060
- dep.setLoc(sl, sc, el, ec);
1061
- module.addDependency(dep);
1062
- return ident[1];
2213
+ return processLocalAtRule(input, end, {
2214
+ string: true,
2215
+ identifier: true
2216
+ });
2217
+ } else if (
2218
+ this.options.customIdents &&
2219
+ name === "@counter-style" &&
2220
+ isLocalMode()
2221
+ ) {
2222
+ return processLocalAtRule(input, end, {
2223
+ identifier: true
2224
+ });
2225
+ } else if (
2226
+ this.options.container &&
2227
+ name === "@container" &&
2228
+ isLocalMode()
2229
+ ) {
2230
+ return processLocalAtRule(input, end, {
2231
+ identifier: /^(none|and|or|not)$/
2232
+ });
1063
2233
  } else if (name === "@scope") {
1064
2234
  isNextRulePrelude = true;
1065
2235
  return end;
@@ -1074,64 +2244,31 @@ class CssParser extends Parser {
1074
2244
  },
1075
2245
  semicolon: (input, start, end) => {
1076
2246
  if (isModules && scope === CSS_MODE_IN_BLOCK) {
1077
- if (isLocalMode()) {
1078
- processDeclarationValueDone(input);
1079
- inAnimationProperty = false;
1080
- }
1081
-
1082
2247
  isNextRulePrelude = isNextNestedSyntax(input, end);
1083
2248
  }
1084
2249
  return end;
1085
2250
  },
1086
2251
  identifier: (input, start, end) => {
1087
2252
  if (isModules) {
1088
- const name = input.slice(start, end);
1089
-
1090
- if (icssDefinitions.has(name)) {
1091
- let { path, value } =
1092
- /** @type {IcssDefinition} */
1093
- (icssDefinitions.get(name));
1094
-
1095
- if (path) {
1096
- if (icssDefinitions.has(path)) {
1097
- const definition =
1098
- /** @type {IcssDefinition} */
1099
- (icssDefinitions.get(path));
1100
-
1101
- path = definition.value.slice(1, -1);
1102
- }
2253
+ const identifier = input.slice(start, end);
1103
2254
 
1104
- const dep = new CssIcssImportDependency(path, value, [
1105
- start,
1106
- end - 1
1107
- ]);
1108
- const { line: sl, column: sc } = locConverter.get(start);
1109
- const { line: el, column: ec } = locConverter.get(end - 1);
1110
- dep.setLoc(sl, sc, el, ec);
1111
- module.addDependency(dep);
1112
- } else {
1113
- const { line: sl, column: sc } = locConverter.get(start);
1114
- const { line: el, column: ec } = locConverter.get(end);
1115
- const dep = new CssIcssSymbolDependency(name, value, [
1116
- start,
1117
- end
1118
- ]);
1119
- dep.setLoc(sl, sc, el, ec);
1120
- module.addDependency(dep);
1121
- }
2255
+ if (icssDefinitions.has(identifier)) {
2256
+ return processICSSSymbol(identifier, start, end);
2257
+ }
1122
2258
 
1123
- return end;
2259
+ if (
2260
+ this.options.dashedIdents &&
2261
+ isLocalMode() &&
2262
+ isDashedIdentifier(identifier)
2263
+ ) {
2264
+ return processDashedIdent(input, start, end);
1124
2265
  }
1125
2266
 
1126
2267
  switch (scope) {
1127
2268
  case CSS_MODE_IN_BLOCK: {
1128
- if (isLocalMode()) {
2269
+ if (isModules && !isNextRulePrelude) {
1129
2270
  // Handle only top level values and not inside functions
1130
- if (inAnimationProperty && balanced.length === 0) {
1131
- lastIdentifier = [start, end, true];
1132
- } else {
1133
- return processLocalDeclaration(input, start, end);
1134
- }
2271
+ return processLocalDeclaration(input, start, end);
1135
2272
  }
1136
2273
  break;
1137
2274
  }
@@ -1142,34 +2279,14 @@ class CssParser extends Parser {
1142
2279
  },
1143
2280
  delim: (input, start, end) => {
1144
2281
  if (isNextRulePrelude && isLocalMode()) {
1145
- const ident = walkCssTokens.skipCommentsAndEatIdentSequence(
1146
- input,
1147
- end
1148
- );
1149
- if (!ident) return end;
1150
- const name = unescapeIdentifier(input.slice(ident[0], ident[1]));
1151
- const dep = new CssLocalIdentifierDependency(name, [
1152
- ident[0],
1153
- ident[1]
1154
- ]);
1155
- const { line: sl, column: sc } = locConverter.get(ident[0]);
1156
- const { line: el, column: ec } = locConverter.get(ident[1]);
1157
- dep.setLoc(sl, sc, el, ec);
1158
- module.addDependency(dep);
1159
- return ident[1];
2282
+ return processClassSelector(input, start, end);
1160
2283
  }
1161
2284
 
1162
2285
  return end;
1163
2286
  },
1164
2287
  hash: (input, start, end, isID) => {
1165
2288
  if (isNextRulePrelude && isLocalMode() && isID) {
1166
- const valueStart = start + 1;
1167
- const name = unescapeIdentifier(input.slice(valueStart, end));
1168
- const dep = new CssLocalIdentifierDependency(name, [valueStart, end]);
1169
- const { line: sl, column: sc } = locConverter.get(start);
1170
- const { line: el, column: ec } = locConverter.get(end);
1171
- dep.setLoc(sl, sc, el, ec);
1172
- module.addDependency(dep);
2289
+ return processIdSelector(input, start, end);
1173
2290
  }
1174
2291
 
1175
2292
  return end;
@@ -1186,12 +2303,12 @@ class CssParser extends Parser {
1186
2303
  switch (scope) {
1187
2304
  case CSS_MODE_TOP_LEVEL: {
1188
2305
  if (name === "import") {
1189
- const pos = parseImportOrExport(0, input, ident[1]);
2306
+ const pos = processImportOrExport(0, input, ident[1]);
1190
2307
  const dep = new ConstDependency("", [start, pos]);
1191
2308
  module.addPresentationalDependency(dep);
1192
2309
  return pos;
1193
2310
  } else if (name === "export") {
1194
- const pos = parseImportOrExport(1, input, ident[1]);
2311
+ const pos = processImportOrExport(1, input, ident[1]);
1195
2312
  const dep = new ConstDependency("", [start, pos]);
1196
2313
  module.addPresentationalDependency(dep);
1197
2314
  return pos;
@@ -1203,18 +2320,24 @@ class CssParser extends Parser {
1203
2320
  const isFn = input.charCodeAt(ident[1]) === CC_LEFT_PARENTHESIS;
1204
2321
 
1205
2322
  if (isFn && name === "local") {
1206
- const end = ident[1] + 1;
1207
- modeData = "local";
2323
+ // Eat extra whitespace
2324
+ const end = walkCssTokens.eatWhitespaceAndComments(
2325
+ input,
2326
+ ident[1] + 1
2327
+ )[0];
2328
+ modeData = LOCAL_MODE;
1208
2329
  const dep = new ConstDependency("", [start, end]);
1209
2330
  module.addPresentationalDependency(dep);
1210
- balanced.push([":local", start, end]);
2331
+ balanced.push([":local", start, end, true]);
1211
2332
  return end;
1212
2333
  } else if (name === "local") {
1213
- modeData = "local";
1214
- // Eat extra whitespace
1215
- end = walkCssTokens.eatWhitespace(input, ident[1]);
2334
+ modeData = LOCAL_MODE;
2335
+ const found = walkCssTokens.eatWhitespaceAndComments(
2336
+ input,
2337
+ ident[1]
2338
+ );
1216
2339
 
1217
- if (ident[1] === end) {
2340
+ if (!found[1]) {
1218
2341
  this._emitWarning(
1219
2342
  state,
1220
2343
  `Missing whitespace after ':local' in '${input.slice(
@@ -1227,22 +2350,30 @@ class CssParser extends Parser {
1227
2350
  );
1228
2351
  }
1229
2352
 
2353
+ end = walkCssTokens.eatWhitespace(input, ident[1]);
1230
2354
  const dep = new ConstDependency("", [start, end]);
1231
2355
  module.addPresentationalDependency(dep);
1232
2356
  return end;
1233
2357
  } else if (isFn && name === "global") {
1234
- const end = ident[1] + 1;
1235
- modeData = "global";
2358
+ // Eat extra whitespace
2359
+ const end = walkCssTokens.eatWhitespaceAndComments(
2360
+ input,
2361
+ ident[1] + 1
2362
+ )[0];
2363
+ modeData = GLOBAL_MODE;
1236
2364
  const dep = new ConstDependency("", [start, end]);
1237
2365
  module.addPresentationalDependency(dep);
1238
- balanced.push([":global", start, end]);
2366
+ balanced.push([":global", start, end, true]);
1239
2367
  return end;
1240
2368
  } else if (name === "global") {
1241
- modeData = "global";
2369
+ modeData = GLOBAL_MODE;
1242
2370
  // Eat extra whitespace
1243
- end = walkCssTokens.eatWhitespace(input, ident[1]);
2371
+ const found = walkCssTokens.eatWhitespaceAndComments(
2372
+ input,
2373
+ ident[1]
2374
+ );
1244
2375
 
1245
- if (ident[1] === end) {
2376
+ if (!found[1]) {
1246
2377
  this._emitWarning(
1247
2378
  state,
1248
2379
  `Missing whitespace after ':global' in '${input.slice(
@@ -1255,6 +2386,7 @@ class CssParser extends Parser {
1255
2386
  );
1256
2387
  }
1257
2388
 
2389
+ end = walkCssTokens.eatWhitespace(input, ident[1]);
1258
2390
  const dep = new ConstDependency("", [start, end]);
1259
2391
  module.addPresentationalDependency(dep);
1260
2392
  return end;
@@ -1279,211 +2411,31 @@ class CssParser extends Parser {
1279
2411
  switch (name) {
1280
2412
  case "src":
1281
2413
  case "url": {
1282
- if (!this.url) {
2414
+ if (!this.options.url) {
1283
2415
  return end;
1284
2416
  }
1285
2417
 
1286
- const string = walkCssTokens.eatString(input, end);
1287
- if (!string) return end;
1288
- const { options, errors: commentErrors } = this.parseCommentOptions(
1289
- [lastTokenEndForComments, end]
1290
- );
1291
- if (commentErrors) {
1292
- for (const e of commentErrors) {
1293
- const { comment } = e;
1294
- state.module.addWarning(
1295
- new CommentCompilationWarning(
1296
- `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
1297
- comment.loc
1298
- )
1299
- );
1300
- }
1301
- }
1302
- if (options && options.webpackIgnore !== undefined) {
1303
- if (typeof options.webpackIgnore !== "boolean") {
1304
- const { line: sl, column: sc } = locConverter.get(string[0]);
1305
- const { line: el, column: ec } = locConverter.get(string[1]);
1306
-
1307
- state.module.addWarning(
1308
- new UnsupportedFeatureWarning(
1309
- `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
1310
- {
1311
- start: { line: sl, column: sc },
1312
- end: { line: el, column: ec }
1313
- }
1314
- )
1315
- );
1316
- } else if (options.webpackIgnore) {
1317
- return end;
1318
- }
1319
- }
1320
- const value = normalizeUrl(
1321
- input.slice(string[0] + 1, string[1] - 1),
1322
- true
1323
- );
1324
- // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
1325
- if (value.length === 0) return end;
1326
- const isUrl = name === "url" || name === "src";
1327
- const dep = new CssUrlDependency(
1328
- value,
1329
- [string[0], string[1]],
1330
- isUrl ? "string" : "url"
1331
- );
1332
- const { line: sl, column: sc } = locConverter.get(string[0]);
1333
- const { line: el, column: ec } = locConverter.get(string[1]);
1334
- dep.setLoc(sl, sc, el, ec);
1335
- module.addDependency(dep);
1336
- module.addCodeGenerationDependency(dep);
1337
- return string[1];
2418
+ return processURLFunction(input, end, name);
1338
2419
  }
1339
2420
  default: {
1340
- if (this.url && IMAGE_SET_FUNCTION.test(name)) {
1341
- lastTokenEndForComments = end;
1342
- const values = walkCssTokens.eatImageSetStrings(input, end, {
1343
- comment
1344
- });
1345
- if (values.length === 0) return end;
1346
- for (const [index, string] of values.entries()) {
1347
- const value = normalizeUrl(
1348
- input.slice(string[0] + 1, string[1] - 1),
1349
- true
1350
- );
1351
- if (value.length === 0) return end;
1352
- const { options, errors: commentErrors } =
1353
- this.parseCommentOptions([
1354
- index === 0 ? start : values[index - 1][1],
1355
- string[1]
1356
- ]);
1357
- if (commentErrors) {
1358
- for (const e of commentErrors) {
1359
- const { comment } = e;
1360
- state.module.addWarning(
1361
- new CommentCompilationWarning(
1362
- `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
1363
- comment.loc
1364
- )
1365
- );
1366
- }
1367
- }
1368
- if (options && options.webpackIgnore !== undefined) {
1369
- if (typeof options.webpackIgnore !== "boolean") {
1370
- const { line: sl, column: sc } = locConverter.get(
1371
- string[0]
1372
- );
1373
- const { line: el, column: ec } = locConverter.get(
1374
- string[1]
1375
- );
2421
+ if (this.options.url && IMAGE_SET_FUNCTION.test(name)) {
2422
+ return processImageSetFunction(input, start, end);
2423
+ }
1376
2424
 
1377
- state.module.addWarning(
1378
- new UnsupportedFeatureWarning(
1379
- `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
1380
- {
1381
- start: { line: sl, column: sc },
1382
- end: { line: el, column: ec }
1383
- }
1384
- )
1385
- );
1386
- } else if (options.webpackIgnore) {
1387
- continue;
1388
- }
1389
- }
1390
- const dep = new CssUrlDependency(
1391
- value,
1392
- [string[0], string[1]],
1393
- "url"
1394
- );
1395
- const { line: sl, column: sc } = locConverter.get(string[0]);
1396
- const { line: el, column: ec } = locConverter.get(string[1]);
1397
- dep.setLoc(sl, sc, el, ec);
1398
- module.addDependency(dep);
1399
- module.addCodeGenerationDependency(dep);
1400
- }
1401
- // Can contain `url()` inside, so let's return end to allow parse them
1402
- return end;
1403
- } else if (isLocalMode()) {
1404
- // Don't rename animation name when we have `var()` function
1405
- if (inAnimationProperty && balanced.length === 1) {
1406
- lastIdentifier = undefined;
2425
+ if (isModules) {
2426
+ if (
2427
+ this.options.function &&
2428
+ isLocalMode() &&
2429
+ isDashedIdentifier(name)
2430
+ ) {
2431
+ return processDashedIdent(input, start, end);
1407
2432
  }
1408
2433
 
1409
- if (name === "var") {
1410
- const customIdent = walkCssTokens.eatIdentSequence(input, end);
1411
- if (!customIdent) return end;
1412
- let name = input.slice(customIdent[0], customIdent[1]);
1413
- // A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
1414
- // The <custom-property-name> production corresponds to this:
1415
- // it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes),
1416
- // except -- itself, which is reserved for future use by CSS.
1417
- if (!name.startsWith("--") || name.length < 3) return end;
1418
- name = unescapeIdentifier(
1419
- input.slice(customIdent[0] + 2, customIdent[1])
1420
- );
1421
- const afterCustomIdent = walkCssTokens.eatWhitespaceAndComments(
1422
- input,
1423
- customIdent[1]
1424
- );
1425
- if (
1426
- input.charCodeAt(afterCustomIdent) === CC_LOWER_F ||
1427
- input.charCodeAt(afterCustomIdent) === CC_UPPER_F
1428
- ) {
1429
- const fromWord = walkCssTokens.eatIdentSequence(
1430
- input,
1431
- afterCustomIdent
1432
- );
1433
- if (
1434
- !fromWord ||
1435
- input.slice(fromWord[0], fromWord[1]).toLowerCase() !==
1436
- "from"
1437
- ) {
1438
- return end;
1439
- }
1440
- const from = walkCssTokens.eatIdentSequenceOrString(
1441
- input,
1442
- walkCssTokens.eatWhitespaceAndComments(input, fromWord[1])
1443
- );
1444
- if (!from) {
1445
- return end;
1446
- }
1447
- const path = input.slice(from[0], from[1]);
1448
- if (from[2] === true && path === "global") {
1449
- const dep = new ConstDependency("", [
1450
- customIdent[1],
1451
- from[1]
1452
- ]);
1453
- module.addPresentationalDependency(dep);
1454
- return end;
1455
- } else if (from[2] === false) {
1456
- const dep = new CssIcssImportDependency(
1457
- path.slice(1, -1),
1458
- name,
1459
- [customIdent[0], from[1] - 1]
1460
- );
1461
- const { line: sl, column: sc } = locConverter.get(
1462
- customIdent[0]
1463
- );
1464
- const { line: el, column: ec } = locConverter.get(
1465
- from[1] - 1
1466
- );
1467
- dep.setLoc(sl, sc, el, ec);
1468
- module.addDependency(dep);
1469
- }
1470
- } else {
1471
- const { line: sl, column: sc } = locConverter.get(
1472
- customIdent[0]
1473
- );
1474
- const { line: el, column: ec } = locConverter.get(
1475
- customIdent[1]
1476
- );
1477
- const dep = new CssSelfLocalIdentifierDependency(
1478
- name,
1479
- [customIdent[0], customIdent[1]],
1480
- "--",
1481
- declaredCssVariables
1482
- );
1483
- dep.setLoc(sl, sc, el, ec);
1484
- module.addDependency(dep);
1485
- return end;
1486
- }
2434
+ const type =
2435
+ name === "local" ? 1 : name === "global" ? 2 : undefined;
2436
+
2437
+ if (type && !isNextRulePrelude) {
2438
+ return processLocalOrGlobalFunction(input, type, start, end);
1487
2439
  }
1488
2440
  }
1489
2441
  }
@@ -1491,6 +2443,12 @@ class CssParser extends Parser {
1491
2443
 
1492
2444
  return end;
1493
2445
  },
2446
+ leftSquareBracket: (input, start, end) => {
2447
+ if (isNextRulePrelude && isLocalMode()) {
2448
+ return processAttributeSelector(input, start, end);
2449
+ }
2450
+ return end;
2451
+ },
1494
2452
  leftParenthesis: (input, start, end) => {
1495
2453
  balanced.push(["(", start, end]);
1496
2454
 
@@ -1499,28 +2457,38 @@ class CssParser extends Parser {
1499
2457
  rightParenthesis: (input, start, end) => {
1500
2458
  const popped = balanced.pop();
1501
2459
 
1502
- if (
1503
- isModules &&
1504
- popped &&
1505
- (popped[0] === ":local" || popped[0] === ":global")
1506
- ) {
1507
- modeData = balanced[balanced.length - 1]
1508
- ? /** @type {"local" | "global"} */
1509
- (balanced[balanced.length - 1][0])
1510
- : undefined;
1511
- const dep = new ConstDependency("", [start, end]);
1512
- module.addPresentationalDependency(dep);
2460
+ if (isModules && popped) {
2461
+ const isLocal = popped[0] === ":local";
2462
+ const isGlobal = popped[0] === ":global";
2463
+ if (isLocal || isGlobal) {
2464
+ modeData = balanced[balanced.length - 1]
2465
+ ? balanced[balanced.length - 1][0] === ":local"
2466
+ ? LOCAL_MODE
2467
+ : balanced[balanced.length - 1][0] === ":global"
2468
+ ? GLOBAL_MODE
2469
+ : undefined
2470
+ : undefined;
2471
+ if (popped[3] && isLocal) {
2472
+ while (walkCssTokens.isWhiteSpace(input.charCodeAt(start - 1))) {
2473
+ start -= 1;
2474
+ }
2475
+ }
2476
+ const dep = new ConstDependency("", [start, end]);
2477
+ module.addPresentationalDependency(dep);
2478
+ } else if (isNextRulePrelude) {
2479
+ modeData = undefined;
2480
+ }
1513
2481
  }
1514
2482
 
1515
2483
  return end;
1516
2484
  },
1517
2485
  comma: (input, start, end) => {
1518
2486
  if (isModules) {
1519
- // Reset stack for `:global .class :local .class-other` selector after
1520
- modeData = undefined;
2487
+ const popped = balanced.pop();
1521
2488
 
1522
- if (scope === CSS_MODE_IN_BLOCK && isLocalMode()) {
1523
- processDeclarationValueDone(input);
2489
+ if (!popped) {
2490
+ // Reset stack for `:global .class :local .class-other` selector after
2491
+ modeData = undefined;
1524
2492
  }
1525
2493
  }
1526
2494
 
@@ -1532,17 +2500,43 @@ class CssParser extends Parser {
1532
2500
 
1533
2501
  /** @type {BuildInfo} */
1534
2502
  (module.buildInfo).strict = true;
1535
- /** @type {BuildMeta} */
1536
- (module.buildMeta).exportsType = this.namedExports
1537
- ? "namespace"
1538
- : "default";
1539
2503
 
1540
- if (!this.namedExports) {
1541
- /** @type {BuildMeta} */
1542
- (module.buildMeta).defaultObject = "redirect";
2504
+ const buildMeta = /** @type {BuildMeta} */ (state.module.buildMeta);
2505
+
2506
+ buildMeta.exportsType = this.options.namedExports ? "namespace" : "default";
2507
+ buildMeta.defaultObject = this.options.namedExports
2508
+ ? false
2509
+ : "redirect-warn";
2510
+ buildMeta.exportType = this.options.exportType;
2511
+
2512
+ if (!buildMeta.exportType) {
2513
+ // Inherit exportType from parent module to ensure consistency.
2514
+ // When a CSS file is imported with syntax like `import "./basic.css" with { type: "css" }`,
2515
+ // the parent module's exportType is set to "css-style-sheet".
2516
+ // Child modules imported via @import should inherit this exportType
2517
+ // instead of using the default "link", ensuring that the entire
2518
+ // import chain uses the same export format.
2519
+ const parent = state.compilation.moduleGraph.getIssuer(module);
2520
+ if (parent instanceof CssModule) {
2521
+ buildMeta.exportType = /** @type {BuildMeta} */ (
2522
+ parent.buildMeta
2523
+ ).exportType;
2524
+ }
2525
+ }
2526
+ if (!buildMeta.exportType) {
2527
+ buildMeta.exportType = "link";
2528
+ }
2529
+
2530
+ // TODO this.namedExports?
2531
+ if (
2532
+ buildMeta.exportType === "text" ||
2533
+ buildMeta.exportType === "css-style-sheet"
2534
+ ) {
2535
+ module.addDependency(new StaticExportsDependency(["default"], true));
2536
+ } else {
2537
+ module.addDependency(new StaticExportsDependency([], true));
1543
2538
  }
1544
2539
 
1545
- module.addDependency(new StaticExportsDependency([], true));
1546
2540
  return state;
1547
2541
  }
1548
2542
 
@@ -1584,7 +2578,7 @@ class CssParser extends Parser {
1584
2578
  if (comments.length === 0) {
1585
2579
  return EMPTY_COMMENT_OPTIONS;
1586
2580
  }
1587
- /** @type {Record<string, EXPECTED_ANY> } */
2581
+ /** @type {Record<string, EXPECTED_ANY>} */
1588
2582
  const options = {};
1589
2583
  /** @type {(Error & { comment: Comment })[]} */
1590
2584
  const errors = [];