@depup/webpack 5.109.0-depup.0 → 5.110.1-depup.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 (641) hide show
  1. package/README.md +7 -7
  2. package/bin/webpack.js +12 -0
  3. package/changes.json +10 -10
  4. package/lib/APIPlugin.js +123 -17
  5. package/lib/AsyncDependenciesBlock.js +10 -7
  6. package/lib/AutomaticPrefetchPlugin.js +1 -1
  7. package/lib/BannerPlugin.js +10 -6
  8. package/lib/CacheFacade.js +6 -6
  9. package/lib/Chunk.js +14 -11
  10. package/lib/ChunkGraph.js +43 -32
  11. package/lib/ChunkGroup.js +7 -7
  12. package/lib/ChunkTemplate.js +35 -44
  13. package/lib/CircularModulesPlugin.js +205 -28
  14. package/lib/CleanPlugin.js +15 -16
  15. package/lib/CodeGenerationResults.js +11 -8
  16. package/lib/CompatibilityPlugin.js +38 -7
  17. package/lib/Compilation.js +709 -414
  18. package/lib/Compiler.js +69 -42
  19. package/lib/ConcatenationScope.js +117 -8
  20. package/lib/ConditionalInitFragment.js +3 -3
  21. package/lib/ConstPlugin.js +13 -11
  22. package/lib/ContextExclusionPlugin.js +1 -1
  23. package/lib/ContextModule.js +146 -54
  24. package/lib/ContextModuleFactory.js +46 -14
  25. package/lib/ContextReplacementPlugin.js +12 -14
  26. package/lib/DefinePlugin.js +168 -57
  27. package/lib/DependenciesBlock.js +9 -6
  28. package/lib/Dependency.js +36 -16
  29. package/lib/DependencyTemplate.js +11 -12
  30. package/lib/DependencyTemplates.js +3 -3
  31. package/lib/DotenvPlugin.js +50 -13
  32. package/lib/DynamicEntryPlugin.js +8 -4
  33. package/lib/EntryOptionPlugin.js +8 -4
  34. package/lib/EntryPlugin.js +2 -2
  35. package/lib/Entrypoint.js +6 -2
  36. package/lib/EnvironmentPlugin.js +2 -2
  37. package/lib/EvalDevToolModulePlugin.js +8 -4
  38. package/lib/EvalSourceMapDevToolPlugin.js +15 -8
  39. package/lib/ExportsInfo.js +123 -19
  40. package/lib/ExportsInfoApiPlugin.js +2 -4
  41. package/lib/ExternalModule.js +242 -84
  42. package/lib/ExternalModuleFactoryPlugin.js +97 -38
  43. package/lib/ExternalsPlugin.js +10 -6
  44. package/lib/FileSystemInfo.js +116 -42
  45. package/lib/FlagAllModulesAsUsedPlugin.js +2 -3
  46. package/lib/FlagDependencyExportsPlugin.js +76 -19
  47. package/lib/FlagDependencyUsagePlugin.js +63 -57
  48. package/lib/FlagEntryExportAsUsedPlugin.js +1 -1
  49. package/lib/Generator.js +36 -15
  50. package/lib/HotModuleReplacementPlugin.js +16 -44
  51. package/lib/IgnorePlugin.js +8 -4
  52. package/lib/IgnoreWarningsPlugin.js +2 -2
  53. package/lib/InitFragment.js +28 -36
  54. package/lib/JavascriptMetaInfoPlugin.js +35 -4
  55. package/lib/LazyBarrel.js +3 -4
  56. package/lib/LibraryTemplatePlugin.js +10 -6
  57. package/lib/LoaderOptionsPlugin.js +7 -3
  58. package/lib/LoaderTargetPlugin.js +1 -1
  59. package/lib/MainTemplate.js +49 -42
  60. package/lib/ManifestPlugin.js +12 -11
  61. package/lib/Module.js +62 -33
  62. package/lib/ModuleFactory.js +3 -3
  63. package/lib/ModuleFilenameHelpers.js +22 -6
  64. package/lib/ModuleGraph.js +195 -21
  65. package/lib/ModuleGraphConnection.js +6 -13
  66. package/lib/ModuleInfoHeaderPlugin.js +8 -10
  67. package/lib/ModuleTemplate.js +62 -63
  68. package/lib/MultiCompiler.js +130 -64
  69. package/lib/MultiStats.js +40 -9
  70. package/lib/MultiWatching.js +3 -3
  71. package/lib/NoEmitOnErrorsPlugin.js +1 -1
  72. package/lib/NodeStuffPlugin.js +29 -19
  73. package/lib/NormalModule.js +221 -156
  74. package/lib/NormalModuleFactory.js +563 -112
  75. package/lib/NormalModuleReplacementPlugin.js +5 -8
  76. package/lib/NullFactory.js +6 -2
  77. package/lib/OptionsApply.js +7 -3
  78. package/lib/Parser.js +7 -3
  79. package/lib/PlatformPlugin.js +2 -2
  80. package/lib/PrefetchPlugin.js +1 -1
  81. package/lib/ProgressPlugin.js +16 -13
  82. package/lib/ProvidePlugin.js +3 -5
  83. package/lib/RawModule.js +25 -17
  84. package/lib/RecordIdsPlugin.js +3 -3
  85. package/lib/RequestShortener.js +1 -1
  86. package/lib/ResolverFactory.js +2 -2
  87. package/lib/RuntimeGlobals.js +22 -10
  88. package/lib/RuntimeModule.js +29 -19
  89. package/lib/RuntimePlugin.js +346 -256
  90. package/lib/RuntimeTemplate.js +2792 -352
  91. package/lib/SelfModuleFactory.js +7 -3
  92. package/lib/SourceMapDevToolModuleOptionsPlugin.js +6 -2
  93. package/lib/SourceMapDevToolPlugin.js +30 -20
  94. package/lib/Stats.js +3 -4
  95. package/lib/Template.js +55 -25
  96. package/lib/TemplatedPathPlugin.js +96 -14
  97. package/lib/UseStrictPlugin.js +4 -6
  98. package/lib/WarnCaseSensitiveModulesPlugin.js +4 -4
  99. package/lib/WarnDeprecatedOptionPlugin.js +1 -1
  100. package/lib/WarnNoModeSetPlugin.js +1 -1
  101. package/lib/WatchIgnorePlugin.js +8 -6
  102. package/lib/Watching.js +8 -8
  103. package/lib/WebpackIsIncludedPlugin.js +2 -4
  104. package/lib/WebpackOptionsApply.js +308 -18
  105. package/lib/WebpackOptionsDefaulter.js +6 -2
  106. package/lib/asset/AssetBytesGenerator.js +12 -10
  107. package/lib/asset/AssetBytesParser.js +2 -4
  108. package/lib/asset/AssetGenerator.js +188 -95
  109. package/lib/asset/AssetModule.js +7 -3
  110. package/lib/asset/AssetModulesPlugin.js +110 -38
  111. package/lib/asset/AssetParser.js +9 -7
  112. package/lib/asset/AssetSourceGenerator.js +60 -13
  113. package/lib/asset/AssetSourceParser.js +2 -4
  114. package/lib/asset/RawDataUrlModule.js +24 -16
  115. package/lib/asset/WebManifestGenerator.js +8 -10
  116. package/lib/asset/WebManifestParser.js +24 -10
  117. package/lib/async-modules/AsyncModuleHelpers.js +2 -2
  118. package/lib/async-modules/AwaitDependenciesInitFragment.js +2 -2
  119. package/lib/async-modules/InferAsyncModulesPlugin.js +2 -2
  120. package/lib/async-modules/isGeneratorLowered.js +4 -4
  121. package/lib/buildChunkGraph.js +36 -27
  122. package/lib/bun/BunTargetPlugin.js +1 -1
  123. package/lib/cache/AddBuildDependenciesPlugin.js +1 -1
  124. package/lib/cache/AddManagedPathsPlugin.js +1 -1
  125. package/lib/cache/IdleFileCachePlugin.js +2 -2
  126. package/lib/cache/MemoryCachePlugin.js +18 -10
  127. package/lib/cache/MemoryWithGcCachePlugin.js +34 -22
  128. package/lib/cache/PackFileCacheStrategy.js +289 -28
  129. package/lib/cache/ResolverCachePlugin.js +38 -18
  130. package/lib/cache/getLazyHashedEtag.js +1 -2
  131. package/lib/cache/mergeEtags.js +1 -1
  132. package/lib/cli.js +1 -3
  133. package/lib/config/browserslistTargetHandler.js +41 -5
  134. package/lib/config/defaults.js +282 -81
  135. package/lib/config/defineConfig.js +10 -2
  136. package/lib/config/getClaimedAssetTypes.js +65 -0
  137. package/lib/config/normalization.js +36 -16
  138. package/lib/config/target.js +46 -3
  139. package/lib/container/ContainerEntryDependency.js +1 -1
  140. package/lib/container/ContainerEntryModule.js +24 -15
  141. package/lib/container/ContainerEntryModuleFactory.js +7 -3
  142. package/lib/container/ContainerPlugin.js +9 -10
  143. package/lib/container/ContainerReferencePlugin.js +6 -2
  144. package/lib/container/FallbackDependency.js +1 -1
  145. package/lib/container/FallbackModule.js +24 -17
  146. package/lib/container/FallbackModuleFactory.js +7 -3
  147. package/lib/container/HoistContainerReferencesPlugin.js +53 -66
  148. package/lib/container/ModuleFederationPlugin.js +7 -18
  149. package/lib/container/RemoteModule.js +27 -19
  150. package/lib/container/RemoteRuntimeModule.js +10 -8
  151. package/lib/container/moduleFederationHooks.js +32 -0
  152. package/lib/css/CssGenerator.js +178 -44
  153. package/lib/css/CssInjectStyleRuntimeModule.js +7 -5
  154. package/lib/css/CssLoadingRuntimeModule.js +82 -36
  155. package/lib/css/CssModule.js +10 -7
  156. package/lib/css/CssModulesPlugin.js +197 -106
  157. package/lib/css/CssParser.js +1248 -618
  158. package/lib/css/CssServerStylesRuntimeModule.js +54 -0
  159. package/lib/css/cssMinify.js +168 -0
  160. package/lib/css/data.js +5324 -0
  161. package/lib/css/syntax.js +10396 -952
  162. package/lib/debug/ProfilingPlugin.js +14 -9
  163. package/lib/deno/DenoTargetPlugin.js +1 -1
  164. package/lib/dependencies/AMDDefineDependency.js +112 -87
  165. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +30 -17
  166. package/lib/dependencies/AMDPlugin.js +9 -7
  167. package/lib/dependencies/AMDRequireArrayDependency.js +5 -5
  168. package/lib/dependencies/AMDRequireContextDependency.js +2 -2
  169. package/lib/dependencies/AMDRequireDependenciesBlock.js +1 -1
  170. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +25 -14
  171. package/lib/dependencies/AMDRequireDependency.js +8 -7
  172. package/lib/dependencies/AMDRequireItemDependency.js +1 -1
  173. package/lib/dependencies/AMDRuntimeModules.js +21 -1
  174. package/lib/dependencies/BuildTimeConstDependency.js +102 -0
  175. package/lib/dependencies/CachedConstDependency.js +11 -8
  176. package/lib/dependencies/CommonJsDependencyHelpers.js +67 -46
  177. package/lib/dependencies/CommonJsExportRequireDependency.js +140 -45
  178. package/lib/dependencies/CommonJsExportsDependency.js +53 -49
  179. package/lib/dependencies/CommonJsExportsParserPlugin.js +207 -25
  180. package/lib/dependencies/CommonJsFullRequireDependency.js +140 -26
  181. package/lib/dependencies/CommonJsImportsParserPlugin.js +100 -48
  182. package/lib/dependencies/CommonJsObjectExportDependency.js +188 -0
  183. package/lib/dependencies/CommonJsPlugin.js +35 -6
  184. package/lib/dependencies/CommonJsRequireContextDependency.js +5 -6
  185. package/lib/dependencies/CommonJsRequireDependency.js +156 -27
  186. package/lib/dependencies/CommonJsSelfReferenceDependency.js +63 -38
  187. package/lib/dependencies/ConstDependency.js +8 -9
  188. package/lib/dependencies/ContextDependency.js +12 -8
  189. package/lib/dependencies/ContextDependencyHelpers.js +14 -9
  190. package/lib/dependencies/ContextDependencyTemplateAsId.js +4 -4
  191. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +4 -4
  192. package/lib/dependencies/ContextElementDependency.js +31 -12
  193. package/lib/dependencies/CreateRequireParserPlugin.js +12 -7
  194. package/lib/dependencies/CreateScriptUrlDependency.js +4 -4
  195. package/lib/dependencies/CssIcssExportDependency.js +56 -28
  196. package/lib/dependencies/CssIcssImportDependency.js +17 -13
  197. package/lib/dependencies/CssIcssSymbolDependency.js +9 -10
  198. package/lib/dependencies/CssImportDependency.js +18 -9
  199. package/lib/dependencies/CssUrlDependency.js +17 -13
  200. package/lib/dependencies/DllEntryDependency.js +1 -1
  201. package/lib/dependencies/DynamicExports.js +2 -2
  202. package/lib/dependencies/ExportBindingInitFragment.js +36 -9
  203. package/lib/dependencies/ExportsInfoDependency.js +7 -8
  204. package/lib/dependencies/ExternalModuleDependency.js +9 -5
  205. package/lib/dependencies/ExternalModuleInitFragment.js +2 -2
  206. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +8 -5
  207. package/lib/dependencies/HarmonyAcceptDependency.js +5 -5
  208. package/lib/dependencies/HarmonyCompatibilityDependency.js +52 -17
  209. package/lib/dependencies/HarmonyDetectionParserPlugin.js +11 -7
  210. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +15 -11
  211. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +78 -12
  212. package/lib/dependencies/HarmonyExportExpressionDependency.js +23 -18
  213. package/lib/dependencies/HarmonyExportHeaderDependency.js +7 -7
  214. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +159 -71
  215. package/lib/dependencies/HarmonyExportInitFragment.js +18 -6
  216. package/lib/dependencies/HarmonyExportSpecifierDependency.js +51 -14
  217. package/lib/dependencies/HarmonyExports.js +7 -4
  218. package/lib/dependencies/HarmonyImportDependency.js +28 -20
  219. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +31 -24
  220. package/lib/dependencies/HarmonyImportGuard.js +21 -30
  221. package/lib/dependencies/HarmonyImportSideEffectDependency.js +38 -9
  222. package/lib/dependencies/HarmonyImportSpecifierDependency.js +106 -55
  223. package/lib/dependencies/HarmonyModulesPlugin.js +7 -3
  224. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -3
  225. package/lib/dependencies/HtmlEntryDependency.js +37 -29
  226. package/lib/dependencies/HtmlInlineHtmlDependency.js +7 -8
  227. package/lib/dependencies/HtmlInlineScriptDependency.js +11 -8
  228. package/lib/dependencies/HtmlInlineStyleDependency.js +7 -10
  229. package/lib/dependencies/HtmlSourceDependency.js +22 -14
  230. package/lib/dependencies/ImportContextDependency.js +2 -2
  231. package/lib/dependencies/ImportDependency.js +24 -20
  232. package/lib/dependencies/ImportEagerDependency.js +6 -9
  233. package/lib/dependencies/ImportMetaContextDependency.js +2 -2
  234. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +14 -13
  235. package/lib/dependencies/ImportMetaContextPlugin.js +7 -3
  236. package/lib/dependencies/ImportMetaGlobDependency.js +2 -2
  237. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +2 -3
  238. package/lib/dependencies/ImportMetaGlobHelpers.js +19 -22
  239. package/lib/dependencies/ImportMetaHotAcceptDependency.js +1 -1
  240. package/lib/dependencies/ImportMetaHotDeclineDependency.js +1 -1
  241. package/lib/dependencies/ImportMetaPlugin.js +154 -71
  242. package/lib/dependencies/ImportMetaResolveDependency.js +3 -4
  243. package/lib/dependencies/ImportParserPlugin.js +49 -35
  244. package/lib/dependencies/ImportPhase.js +12 -5
  245. package/lib/dependencies/ImportPlugin.js +7 -3
  246. package/lib/dependencies/ImportWeakDependency.js +6 -9
  247. package/lib/dependencies/JsonExportsDependency.js +11 -7
  248. package/lib/dependencies/LoaderDependency.js +2 -2
  249. package/lib/dependencies/LoaderImportDependency.js +2 -2
  250. package/lib/dependencies/LoaderPlugin.js +11 -7
  251. package/lib/dependencies/LocalModuleDependency.js +5 -5
  252. package/lib/dependencies/LocalModulesHelpers.js +1 -1
  253. package/lib/dependencies/ModuleDecoratorDependency.js +6 -7
  254. package/lib/dependencies/ModuleDependency.js +9 -6
  255. package/lib/dependencies/ModuleDependencyTemplateAsId.js +4 -4
  256. package/lib/dependencies/ModuleDependencyTemplateAsRequireId.js +3 -3
  257. package/lib/dependencies/ModuleHotAcceptDependency.js +1 -1
  258. package/lib/dependencies/ModuleHotDeclineDependency.js +1 -1
  259. package/lib/dependencies/ModuleInitFragmentDependency.js +24 -10
  260. package/lib/dependencies/NullDependency.js +3 -3
  261. package/lib/dependencies/ProvidedDependency.js +14 -10
  262. package/lib/dependencies/PureExpressionDependency.js +9 -10
  263. package/lib/dependencies/RequireContextDependency.js +2 -2
  264. package/lib/dependencies/RequireContextDependencyParserPlugin.js +2 -4
  265. package/lib/dependencies/RequireContextPlugin.js +8 -4
  266. package/lib/dependencies/RequireEnsureDependenciesBlock.js +2 -2
  267. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +5 -8
  268. package/lib/dependencies/RequireEnsureDependency.js +8 -7
  269. package/lib/dependencies/RequireEnsurePlugin.js +7 -3
  270. package/lib/dependencies/RequireHeaderDependency.js +51 -12
  271. package/lib/dependencies/RequireIncludeDependency.js +6 -6
  272. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +2 -3
  273. package/lib/dependencies/RequireIncludePlugin.js +7 -3
  274. package/lib/dependencies/RequireJsStuffPlugin.js +7 -3
  275. package/lib/dependencies/RequireResolveContextDependency.js +2 -2
  276. package/lib/dependencies/RequireResolveDependency.js +4 -4
  277. package/lib/dependencies/RequireResolveHeaderDependency.js +4 -4
  278. package/lib/dependencies/RuntimeRequirementsDependency.js +5 -6
  279. package/lib/dependencies/StaticExportsDependency.js +2 -2
  280. package/lib/dependencies/SystemPlugin.js +10 -6
  281. package/lib/dependencies/SystemRuntimeModule.js +10 -0
  282. package/lib/dependencies/TopLevelAwaitDependency.js +11 -7
  283. package/lib/dependencies/URLContextDependency.js +2 -2
  284. package/lib/dependencies/URLDependency.js +110 -64
  285. package/lib/dependencies/URLPlugin.js +7 -3
  286. package/lib/dependencies/UnsupportedDependency.js +4 -4
  287. package/lib/dependencies/WebAssemblyExportImportedDependency.js +3 -4
  288. package/lib/dependencies/WebAssemblyImportDependency.js +5 -5
  289. package/lib/dependencies/WebpackIsIncludedDependency.js +6 -6
  290. package/lib/dependencies/WorkerAndWorkletPlugin.js +46 -28
  291. package/lib/dependencies/WorkerDependency.js +28 -22
  292. package/lib/dependencies/WorkletDependency.js +15 -14
  293. package/lib/dependencies/getFunctionExpression.js +8 -4
  294. package/lib/dependencies/processExportInfo.js +5 -5
  295. package/lib/dll/DelegatedModule.js +34 -22
  296. package/lib/dll/DelegatedModuleFactoryPlugin.js +15 -7
  297. package/lib/dll/DelegatedPlugin.js +2 -2
  298. package/lib/dll/DllEntryPlugin.js +2 -2
  299. package/lib/dll/DllModule.js +22 -15
  300. package/lib/dll/DllModuleFactory.js +7 -3
  301. package/lib/dll/DllPlugin.js +7 -5
  302. package/lib/dll/DllReferencePlugin.js +10 -7
  303. package/lib/dll/LibManifestPlugin.js +5 -5
  304. package/lib/electron/ElectronTargetPlugin.js +53 -49
  305. package/lib/errors/AsyncChunkWaterfallWarning.js +36 -0
  306. package/lib/errors/AsyncDependencyToInitialChunkError.js +2 -2
  307. package/lib/errors/BroadContextsWarning.js +43 -0
  308. package/lib/errors/BuildCycleError.js +1 -1
  309. package/lib/errors/CacheEffectivenessWarning.js +55 -0
  310. package/lib/errors/ChunkRenderError.js +1 -1
  311. package/lib/errors/CircularDependenciesWarning.js +38 -0
  312. package/lib/errors/CodeGenerationError.js +1 -1
  313. package/lib/errors/CommentCompilationWarning.js +1 -1
  314. package/lib/errors/ConflictingResourceHintsWarning.js +35 -0
  315. package/lib/errors/DuplicateModulesWarning.js +45 -0
  316. package/lib/errors/DynamicExportsWarning.js +30 -0
  317. package/lib/errors/EmbeddedSourceMapsWarning.js +25 -0
  318. package/lib/errors/EntrypointOverlapWarning.js +45 -0
  319. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +2 -2
  320. package/lib/errors/EvalUsageWarning.js +28 -0
  321. package/lib/errors/HookWebpackError.js +5 -5
  322. package/lib/errors/HotspotsWarning.js +57 -0
  323. package/lib/errors/IgnoreErrorModuleFactory.js +7 -3
  324. package/lib/errors/InlinedAssetsWarning.js +36 -0
  325. package/lib/errors/InvalidDependenciesModuleWarning.js +1 -1
  326. package/lib/errors/LargeModulesWarning.js +45 -0
  327. package/lib/errors/LegacyJavascriptWarning.js +48 -0
  328. package/lib/errors/MissingSideEffectsWarning.js +36 -0
  329. package/lib/errors/MixedExportsWarning.js +41 -0
  330. package/lib/errors/ModuleDependencyError.js +13 -16
  331. package/lib/errors/ModuleDependencyWarning.js +5 -15
  332. package/lib/errors/ModuleHashingError.js +1 -1
  333. package/lib/errors/ModuleNotFoundError.js +2 -2
  334. package/lib/errors/ModuleParseError.js +1 -2
  335. package/lib/errors/ModuleRestoreError.js +1 -1
  336. package/lib/errors/ModuleStoreError.js +1 -1
  337. package/lib/errors/NodeStuffInWebError.js +1 -1
  338. package/lib/errors/OsDependentRuleWarning.js +31 -0
  339. package/lib/errors/PureAnnotationsWarning.js +36 -0
  340. package/lib/errors/RedundantDynamicImportWarning.js +31 -0
  341. package/lib/errors/ScopeHoistingBailoutsWarning.js +41 -0
  342. package/lib/errors/SplitChunksCappedWarning.js +41 -0
  343. package/lib/errors/TinyChunksWarning.js +40 -0
  344. package/lib/errors/TopLevelThisWarning.js +34 -0
  345. package/lib/errors/UnsplitVendorsWarning.js +42 -0
  346. package/lib/errors/UnsupportedFeatureWarning.js +1 -1
  347. package/lib/errors/UnusedAliasesWarning.js +29 -0
  348. package/lib/errors/UnusedDefinesWarning.js +29 -0
  349. package/lib/errors/UnusedExternalsWarning.js +29 -0
  350. package/lib/errors/UnusedReexportsWarning.js +42 -0
  351. package/lib/errors/UnusedRuleWarning.js +29 -0
  352. package/lib/errors/WebpackError.js +9 -5
  353. package/lib/errors/deriveStackFromNestedError.js +90 -0
  354. package/lib/esm/ExportWebpackRequireRuntimeModule.js +2 -0
  355. package/lib/esm/ModuleChunkFormatPlugin.js +37 -19
  356. package/lib/esm/ModuleChunkLoadingPlugin.js +89 -51
  357. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +170 -71
  358. package/lib/hmr/HotModuleReplacement.runtime.js +24 -5
  359. package/lib/hmr/HotModuleReplacementRuntimeModule.js +1 -1
  360. package/lib/hmr/LazyCompilationPlugin.js +45 -24
  361. package/lib/hmr/lazyCompilationBackend.js +28 -10
  362. package/lib/hmr/parserHooks.js +56 -0
  363. package/lib/html/HtmlGenerator.js +299 -79
  364. package/lib/html/HtmlModule.js +7 -3
  365. package/lib/html/HtmlModulesPlugin.js +312 -174
  366. package/lib/html/HtmlParser.js +31 -86
  367. package/lib/html/data.js +1928 -0
  368. package/lib/html/htmlMinify.js +288 -0
  369. package/lib/html/syntax.js +7819 -3331
  370. package/lib/ids/ChunkModuleIdRangePlugin.js +3 -3
  371. package/lib/ids/DeterministicChunkIdsPlugin.js +1 -1
  372. package/lib/ids/DeterministicModuleIdsPlugin.js +2 -2
  373. package/lib/ids/HashedModuleIdsPlugin.js +6 -2
  374. package/lib/ids/IdHelpers.js +31 -18
  375. package/lib/ids/NamedChunkIdsPlugin.js +1 -1
  376. package/lib/ids/NamedModuleIdsPlugin.js +1 -1
  377. package/lib/ids/NaturalChunkIdsPlugin.js +2 -2
  378. package/lib/ids/NaturalModuleIdsPlugin.js +1 -1
  379. package/lib/ids/OccurrenceChunkIdsPlugin.js +7 -3
  380. package/lib/ids/OccurrenceModuleIdsPlugin.js +7 -3
  381. package/lib/ids/SyncModuleIdsPlugin.js +4 -4
  382. package/lib/index.js +10 -0
  383. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +3 -3
  384. package/lib/javascript/BasicEvaluatedExpression.js +18 -14
  385. package/lib/javascript/ChunkFormatHelpers.js +6 -6
  386. package/lib/javascript/ChunkHelpers.js +1 -1
  387. package/lib/javascript/CommonJsChunkFormatPlugin.js +11 -5
  388. package/lib/javascript/EnableChunkLoadingPlugin.js +2 -2
  389. package/lib/javascript/JavascriptGenerator.js +65 -165
  390. package/lib/javascript/JavascriptModule.js +9 -3
  391. package/lib/javascript/JavascriptModulesPlugin.js +181 -162
  392. package/lib/javascript/JavascriptParser.js +291 -112
  393. package/lib/javascript/JavascriptParserHelpers.js +10 -7
  394. package/lib/javascript/ScopeAnalyzer.js +1094 -0
  395. package/lib/javascript/StartupHelpers.js +55 -9
  396. package/lib/javascript/syntax.js +3403 -300
  397. package/lib/json/JsonData.js +8 -4
  398. package/lib/json/JsonGenerator.js +38 -16
  399. package/lib/json/JsonModule.js +7 -3
  400. package/lib/json/JsonModulesPlugin.js +1 -1
  401. package/lib/json/JsonParser.js +5 -6
  402. package/lib/library/AbstractLibraryPlugin.js +20 -14
  403. package/lib/library/AmdLibraryPlugin.js +11 -7
  404. package/lib/library/AssignLibraryPlugin.js +19 -13
  405. package/lib/library/EnableLibraryPlugin.js +2 -2
  406. package/lib/library/ExportPropertyLibraryPlugin.js +15 -8
  407. package/lib/library/JsonpLibraryPlugin.js +11 -7
  408. package/lib/library/ModuleLibraryPlugin.js +256 -64
  409. package/lib/library/SystemLibraryPlugin.js +12 -8
  410. package/lib/library/UmdLibraryPlugin.js +62 -25
  411. package/lib/loaders/LoaderRunner.js +14 -1
  412. package/lib/loaders/loadLoader.js +8 -4
  413. package/lib/logging/createConsoleLogger.js +7 -4
  414. package/lib/node/CommonJsChunkLoadingPlugin.js +36 -22
  415. package/lib/node/NodeEnvironmentPlugin.js +17 -3
  416. package/lib/node/NodeSourcePlugin.js +1 -1
  417. package/lib/node/NodeTargetPlugin.js +4 -3
  418. package/lib/node/NodeTemplatePlugin.js +1 -1
  419. package/lib/node/NodeWatchFileSystem.js +70 -9
  420. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +35 -38
  421. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +81 -20
  422. package/lib/node/ReadFileCompileWasmPlugin.js +37 -17
  423. package/lib/node/RequireChunkLoadingRuntimeModule.js +35 -38
  424. package/lib/node/nodeConsole.js +7 -3
  425. package/lib/optimize/AggressiveMergingPlugin.js +21 -17
  426. package/lib/optimize/AggressiveSplittingPlugin.js +9 -6
  427. package/lib/optimize/ConcatenatedModule.js +996 -477
  428. package/lib/optimize/ConstExportsPlugin.js +11 -7
  429. package/lib/optimize/EnsureChunkConditionsPlugin.js +3 -3
  430. package/lib/optimize/FlagIncludedChunksPlugin.js +3 -4
  431. package/lib/optimize/InlineExports.js +9 -5
  432. package/lib/optimize/InnerGraph.js +28 -12
  433. package/lib/optimize/InnerGraphPlugin.js +23 -18
  434. package/lib/optimize/LimitChunkCountPlugin.js +7 -3
  435. package/lib/optimize/MangleExportsPlugin.js +7 -8
  436. package/lib/optimize/MergeDuplicateChunksPlugin.js +7 -3
  437. package/lib/optimize/MinChunkSizePlugin.js +7 -3
  438. package/lib/optimize/ModuleConcatenationPlugin.js +124 -59
  439. package/lib/optimize/RealContentHashPlugin.js +20 -13
  440. package/lib/optimize/RemoveEmptyChunksPlugin.js +2 -2
  441. package/lib/optimize/RemoveParentModulesPlugin.js +4 -4
  442. package/lib/optimize/RuntimeChunkPlugin.js +2 -2
  443. package/lib/optimize/SideEffectsFlagPlugin.js +262 -39
  444. package/lib/optimize/SplitChunksPlugin.js +232 -68
  445. package/lib/performance/AssetsOverSizeLimitWarning.js +10 -2
  446. package/lib/performance/AsyncChunkWaterfallsPlugin.js +136 -0
  447. package/lib/performance/BroadContextsPlugin.js +142 -0
  448. package/lib/performance/CacheEffectivenessPlugin.js +125 -0
  449. package/lib/performance/ConflictingResourceHintsPlugin.js +99 -0
  450. package/lib/performance/DuplicateModulesPlugin.js +93 -0
  451. package/lib/performance/DuplicatePackagesPlugin.js +119 -0
  452. package/lib/performance/DuplicatePackagesWarning.js +84 -0
  453. package/lib/performance/DynamicExportsPlugin.js +120 -0
  454. package/lib/performance/EmbeddedSourceMapsPlugin.js +71 -0
  455. package/lib/performance/EntrypointOverlapPlugin.js +122 -0
  456. package/lib/performance/EntrypointsOverSizeLimitWarning.js +1 -1
  457. package/lib/performance/EvalUsagePlugin.js +115 -0
  458. package/lib/performance/HotspotsPlugin.js +296 -0
  459. package/lib/performance/InlinedAssetsPlugin.js +99 -0
  460. package/lib/performance/LargeModulesPlugin.js +126 -0
  461. package/lib/performance/LegacyJavascriptPlugin.js +139 -0
  462. package/lib/performance/MissingSideEffectsPlugin.js +138 -0
  463. package/lib/performance/MixedExportsPlugin.js +136 -0
  464. package/lib/performance/OsDependentRuleWarningPlugin.js +178 -0
  465. package/lib/performance/PureAnnotationsPlugin.js +219 -0
  466. package/lib/performance/RedundantDynamicImportsPlugin.js +225 -0
  467. package/lib/performance/RuntimeInLargeChunkWarning.js +27 -0
  468. package/lib/performance/ScopeHoistingBailoutsPlugin.js +164 -0
  469. package/lib/performance/SizeLimitsPlugin.js +139 -7
  470. package/lib/performance/SplitChunksCappedPlugin.js +99 -0
  471. package/lib/performance/TinyChunksPlugin.js +128 -0
  472. package/lib/performance/TopLevelThisPlugin.js +208 -0
  473. package/lib/performance/UnsplitVendorsPlugin.js +112 -0
  474. package/lib/performance/UnusedAliasesPlugin.js +253 -0
  475. package/lib/performance/UnusedDefinesPlugin.js +87 -0
  476. package/lib/performance/UnusedExternalsPlugin.js +110 -0
  477. package/lib/performance/UnusedReexportsPlugin.js +159 -0
  478. package/lib/performance/UnusedRuleWarningPlugin.js +105 -0
  479. package/lib/performance/getModuleSize.js +22 -0
  480. package/lib/performance/getSourceModules.js +22 -0
  481. package/lib/performance/hasOnlyUnusedExports.js +40 -0
  482. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +24 -10
  483. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +63 -46
  484. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +2 -3
  485. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +2 -2
  486. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +2 -2
  487. package/lib/prefetch/ResourceHintPlugin.js +377 -136
  488. package/lib/prefetch/ResourceHintRuntimeModule.js +1 -21
  489. package/lib/prefetch/StartupAssetHintRuntimeModule.js +10 -133
  490. package/lib/prefetch/parseResourceHintOptions.js +31 -4
  491. package/lib/rules/BasicEffectRulePlugin.js +2 -2
  492. package/lib/rules/BasicMatcherRulePlugin.js +8 -4
  493. package/lib/rules/GlobMatcherRulePlugin.js +68 -0
  494. package/lib/rules/ObjectMatcherRulePlugin.js +12 -5
  495. package/lib/rules/RuleSetCompiler.js +146 -12
  496. package/lib/rules/UseEffectRulePlugin.js +18 -11
  497. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +11 -1
  498. package/lib/runtime/AsyncModuleRuntimeModule.js +1 -1
  499. package/lib/runtime/AutoPublicPathRuntimeModule.js +15 -20
  500. package/lib/runtime/BaseUriRuntimeModule.js +33 -7
  501. package/lib/runtime/ChunkNameRuntimeModule.js +10 -0
  502. package/lib/runtime/CompatGetDefaultExportRuntimeModule.js +11 -1
  503. package/lib/runtime/CompatRuntimeModule.js +5 -3
  504. package/lib/runtime/ConcatenationWrapRuntimeModule.js +90 -0
  505. package/lib/runtime/ConstructRequireRuntimeModule.js +52 -0
  506. package/lib/runtime/CreateFakeNamespaceObjectRuntimeModule.js +8 -6
  507. package/lib/runtime/CreateScriptRuntimeModule.js +11 -1
  508. package/lib/runtime/CreateScriptUrlRuntimeModule.js +11 -1
  509. package/lib/runtime/DefinePropertyGettersRuntimeModule.js +56 -38
  510. package/lib/runtime/EnsureChunkRuntimeModule.js +18 -2
  511. package/lib/runtime/GetChunkFilenameRuntimeModule.js +62 -37
  512. package/lib/runtime/GetFullHashRuntimeModule.js +12 -2
  513. package/lib/runtime/GetMainFilenameRuntimeModule.js +12 -2
  514. package/lib/runtime/GetTrustedTypesPolicyRuntimeModule.js +2 -2
  515. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +16 -3
  516. package/lib/runtime/GlobalRuntimeModule.js +18 -0
  517. package/lib/runtime/HasOwnPropertyRuntimeModule.js +12 -2
  518. package/lib/runtime/LoadScriptRuntimeModule.js +2 -2
  519. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +23 -6
  520. package/lib/runtime/MakeNamespaceObjectRuntimeModule.js +22 -6
  521. package/lib/runtime/NonceRuntimeModule.js +10 -0
  522. package/lib/runtime/OnChunksLoadedRuntimeModule.js +5 -3
  523. package/lib/runtime/PublicPathRuntimeModule.js +12 -2
  524. package/lib/runtime/RelativeUrlRuntimeModule.js +11 -1
  525. package/lib/runtime/RuntimeIdRuntimeModule.js +12 -2
  526. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +11 -1
  527. package/lib/runtime/StartupChunkDependenciesPlugin.js +18 -9
  528. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +3 -3
  529. package/lib/runtime/StartupEntrypointRuntimeModule.js +21 -4
  530. package/lib/runtime/SystemContextRuntimeModule.js +10 -0
  531. package/lib/runtime/ToBinaryRuntimeModule.js +1 -1
  532. package/lib/runtime/WorkerRuntimeModule.js +11 -1
  533. package/lib/runtime/baseUri.js +63 -0
  534. package/lib/schemes/DataUriPlugin.js +1 -1
  535. package/lib/schemes/FileUriPlugin.js +1 -1
  536. package/lib/schemes/HttpUriPlugin.js +24 -15
  537. package/lib/schemes/VirtualUrlPlugin.js +13 -7
  538. package/lib/serialization/AggregateErrorSerializer.js +1 -1
  539. package/lib/serialization/BinaryMiddleware.js +274 -942
  540. package/lib/serialization/ErrorObjectSerializer.js +1 -1
  541. package/lib/serialization/FileMiddleware.js +305 -66
  542. package/lib/serialization/ObjectMiddleware.js +139 -114
  543. package/lib/serialization/PlainObjectSerializer.js +19 -16
  544. package/lib/sharing/ConsumeSharedModule.js +30 -21
  545. package/lib/sharing/ConsumeSharedPlugin.js +18 -10
  546. package/lib/sharing/ConsumeSharedRuntimeModule.js +6 -9
  547. package/lib/sharing/ProvideSharedModule.js +25 -16
  548. package/lib/sharing/ProvideSharedModuleFactory.js +7 -3
  549. package/lib/sharing/ProvideSharedPlugin.js +8 -4
  550. package/lib/sharing/SharePlugin.js +17 -5
  551. package/lib/sharing/ShareRuntimeModule.js +4 -4
  552. package/lib/sharing/resolveMatchedConfigs.js +9 -6
  553. package/lib/sharing/utils.js +1 -3
  554. package/lib/stats/DefaultStatsFactoryPlugin.js +253 -226
  555. package/lib/stats/DefaultStatsPresetPlugin.js +17 -8
  556. package/lib/stats/DefaultStatsPrinterPlugin.js +60 -192
  557. package/lib/stats/StatsFactory.js +56 -35
  558. package/lib/stats/StatsPrinter.js +16 -12
  559. package/lib/typescript/TypeScriptPlugin.js +12 -14
  560. package/lib/url/URLParserPlugin.js +26 -13
  561. package/lib/util/AsyncQueue.js +4 -3
  562. package/lib/util/Hash.js +5 -1
  563. package/lib/util/LazySet.js +35 -22
  564. package/lib/util/LocConverter.js +36 -13
  565. package/lib/util/SourceProcessor.js +1050 -24
  566. package/lib/util/StringXor.js +1 -1
  567. package/lib/util/chainedImports.js +9 -5
  568. package/lib/util/cleverMerge.js +1 -1
  569. package/lib/util/comparators.js +45 -14
  570. package/lib/util/concatenate.js +203 -77
  571. package/lib/util/conventions.js +5 -1
  572. package/lib/util/createHash.js +2 -2
  573. package/lib/util/createHooksRegistry.js +8 -10
  574. package/lib/util/dataURL.js +225 -1
  575. package/lib/util/extractSourceMap.js +4 -3
  576. package/lib/util/findCaseMismatch.js +139 -0
  577. package/lib/util/formatLocation.js +1 -2
  578. package/lib/util/fs.js +22 -5
  579. package/lib/util/globUtils.js +298 -24
  580. package/lib/util/hash/BatchedHash.js +5 -1
  581. package/lib/util/hash/BulkUpdateHash.js +5 -1
  582. package/lib/util/hash/DebugHash.js +5 -1
  583. package/lib/util/hash/hash-digest.js +6 -2
  584. package/lib/util/identifier.js +25 -1
  585. package/lib/util/implicitTypeLoaderFallback.js +1 -1
  586. package/lib/util/internalSerializables.js +6 -0
  587. package/lib/util/lazyModule.js +56 -0
  588. package/lib/util/makeSerializable.js +7 -3
  589. package/lib/util/matchAlias.js +25 -0
  590. package/lib/util/mimeTypes.js +11 -5
  591. package/lib/util/nonNumericOnlyHash.js +4 -3
  592. package/lib/util/parseJson.js +5 -3
  593. package/lib/util/preloadModuleType.js +32 -0
  594. package/lib/util/registerExternalSerializer.js +7 -5
  595. package/lib/util/runtime.js +2 -2
  596. package/lib/util/semver.js +1 -1
  597. package/lib/util/serialization.js +28 -28
  598. package/lib/util/source.js +2 -2
  599. package/lib/util/traverseDestructuringAssignmentProperties.js +6 -2
  600. package/lib/wasm/EnableWasmLoadingPlugin.js +10 -8
  601. package/lib/wasm/wasmModuleFilename.js +54 -0
  602. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +41 -21
  603. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +43 -21
  604. package/lib/wasm-async/AsyncWasmModule.js +3 -3
  605. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +4 -5
  606. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +57 -17
  607. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +34 -18
  608. package/lib/wasm-async/AsyncWebAssemblyParser.js +9 -8
  609. package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +34 -8
  610. package/lib/wasm-sync/SyncWasmModule.js +2 -2
  611. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +18 -13
  612. package/lib/wasm-sync/WasmFinalizeExportsPlugin.js +4 -5
  613. package/lib/wasm-sync/WebAssemblyGenerator.js +18 -17
  614. package/lib/wasm-sync/WebAssemblyInInitialChunkError.js +4 -4
  615. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +7 -7
  616. package/lib/wasm-sync/WebAssemblyModulesPlugin.js +24 -11
  617. package/lib/wasm-sync/WebAssemblyParser.js +4 -6
  618. package/lib/wasm-sync/WebAssemblyUtils.js +2 -2
  619. package/lib/web/FetchCompileAsyncWasmPlugin.js +101 -19
  620. package/lib/web/FetchCompileWasmPlugin.js +46 -16
  621. package/lib/web/JsonpChunkLoadingPlugin.js +47 -23
  622. package/lib/web/JsonpChunkLoadingRuntimeModule.js +50 -20
  623. package/lib/web/JsonpTemplatePlugin.js +2 -2
  624. package/lib/webpack.js +22 -11
  625. package/lib/webworker/ImportScriptsChunkLoadingPlugin.js +60 -26
  626. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +30 -32
  627. package/lib/webworker/WebWorkerTemplatePlugin.js +1 -1
  628. package/module.d.ts +1 -0
  629. package/package.json +37 -30
  630. package/schemas/WebpackOptions.check.js +1 -1
  631. package/schemas/WebpackOptions.json +780 -91
  632. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  633. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  634. package/schemas/plugins/container/ContainerPlugin.json +10 -0
  635. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  636. package/schemas/plugins/container/ModuleFederationPlugin.json +10 -0
  637. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  638. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  639. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  640. package/types.d.ts +4013 -738
  641. package/lib/runtime/CommonJsWrapRuntimeModule.js +0 -37
@@ -15,9 +15,6 @@ const CssIcssSymbolDependency = require("../dependencies/CssIcssSymbolDependency
15
15
  const CssImportDependency = require("../dependencies/CssImportDependency");
16
16
  const CssUrlDependency = require("../dependencies/CssUrlDependency");
17
17
  const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
18
- const CommentCompilationWarning = require("../errors/CommentCompilationWarning");
19
- const ModuleDependencyWarning = require("../errors/ModuleDependencyWarning");
20
- const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
21
18
  const WebpackError = require("../errors/WebpackError");
22
19
  const ResourceHintPlugin = require("../prefetch/ResourceHintPlugin");
23
20
  const LocConverter = require("../util/LocConverter");
@@ -26,7 +23,9 @@ const {
26
23
  createMagicCommentContext,
27
24
  parseCommentOptionsInRange
28
25
  } = require("../util/magicComment");
26
+ const memoize = require("../util/memoize");
29
27
  const topologicalSort = require("../util/topologicalSort");
28
+ const { CSS_MODULES_KEYWORDS, CSS_MODULES_KEYWORD_OPTIONS } = require("./data");
30
29
  const {
31
30
  A,
32
31
  NodeType,
@@ -38,40 +37,83 @@ const {
38
37
  normalizeUrl,
39
38
  rangeEquals,
40
39
  rangeEqualsLowerCase,
40
+ skipEscape,
41
41
  toLowerCaseIfNeeded,
42
42
  unescapeIdentifier
43
43
  } = require("./syntax");
44
44
 
45
+ const getUnsupportedFeatureWarning = memoize(() =>
46
+ require("../errors/UnsupportedFeatureWarning")
47
+ );
48
+
49
+ const getModuleDependencyWarning = memoize(() =>
50
+ require("../errors/ModuleDependencyWarning")
51
+ );
52
+
53
+ const getCommentCompilationWarning = memoize(() =>
54
+ require("../errors/CommentCompilationWarning")
55
+ );
56
+
45
57
  // `SourceProcessor` drives the parse and hands already-built AST nodes to the visitors; positions are read from those nodes' ranges rather than re-scanning the source.
46
58
 
47
- /** @typedef {import("../Module").BuildInfo} BuildInfo */
48
- /** @typedef {import("../Module").BuildMeta} BuildMeta */
49
- /** @typedef {import("./CssModule").CssModuleBuildInfo} CssModuleBuildInfo */
50
- /** @typedef {import("./CssModule").CssModuleBuildMeta} CssModuleBuildMeta */
51
- /** @typedef {import("../Parser").ParserState} ParserState */
52
- /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
53
- /** @typedef {import("./syntax").AtRule} AtRule */
54
- /** @typedef {import("./syntax").Declaration} Declaration */
55
- /** @typedef {import("./syntax").FunctionNode} FunctionNode */
56
- /** @typedef {import("./syntax").Node} AstNode */
57
- /** @typedef {import("./syntax").QualifiedRule} QualifiedRule */
58
- /** @typedef {import("./syntax").Rule} Rule */
59
- /** @typedef {import("./syntax").SimpleBlock} SimpleBlock */
60
- /** @typedef {import("./syntax").Token} Token */
61
- /** @typedef {import("./syntax").UrlToken} UrlToken */
62
- /** @typedef {import("./syntax").HashToken} HashToken */
63
- /** @typedef {import("./syntax").VisitorMap} VisitorMap */
64
- /** @typedef {import("../../declarations/WebpackOptions").CssAutoOrModuleParserOptions} CssAutoOrModuleParserOptions */
65
- /** @typedef {import("../../declarations/WebpackOptions").CssModuleParserOptions} CssModuleParserOptions */
66
- /** @typedef {import("./CssModule")} CssModule */
67
- /** @typedef {import("./CssModule").Inheritance} Inheritance */
59
+ /** @import { BuildInfo, BuildMeta } from "../Module" */
60
+ /**
61
+ * @import CssModule, {
62
+ * CssModuleBuildInfo,
63
+ * CssModuleBuildMeta,
64
+ * Inheritance
65
+ * } from "./CssModule"
66
+ */
67
+ /** @import { ParserState, PreparsedAst } from "../Parser" */
68
+ /**
69
+ * @import {
70
+ * AtRule,
71
+ * Declaration,
72
+ * FunctionNode,
73
+ * Node as AstNode,
74
+ * QualifiedRule,
75
+ * Rule,
76
+ * SimpleBlock,
77
+ * Token,
78
+ * UrlToken,
79
+ * VisitorMap
80
+ * } from "./syntax"
81
+ */
82
+ /**
83
+ * @import {
84
+ * CssAutoOrModuleParserOptions
85
+ * } from "../../declarations/WebpackOptions"
86
+ */
68
87
 
69
88
  /** @typedef {[number, number]} Range */
70
89
  /** @typedef {{ line: number, column: number }} Position */
71
90
  /** @typedef {{ from: string, items: ({ localName: string, importName: string })[] }} ValueAtRuleImport */
72
91
  /** @typedef {{ localName: string, value: string }} ValueAtRuleValue */
92
+ /**
93
+ * What a `@custom-media` name resolves to. `condition` substitutes into any
94
+ * `<media-in-parens>` slot, `type` only at the start of a query, `boolean` is the
95
+ * spec's `true` / `false` (folded into the enclosing query rather than written
96
+ * out — no `<media-in-parens>` is unconditionally true on every engine).
97
+ * @typedef {{ kind: "condition" | "type", text: string } | { kind: "boolean", value: boolean } | { kind: "unsupported" }} CustomMediaValue
98
+ */
99
+ /** @typedef {CustomMediaValue | { kind: "alias", name: string } | { kind: "or", parts: ({ text: string } | { alias: string })[] }} CustomMediaDefinition */
100
+ /** @typedef {{ name: string, start: number, end: number, invalid: boolean, leading: boolean }} CustomMediaUse */
101
+ /**
102
+ * A `@media` prelude captured as its boolean shape, so a `true` / `false`
103
+ * definition found later can be folded into it. Node ids are recycled per
104
+ * top-level rule, so this holds text and offsets rather than AST nodes.
105
+ * @typedef {{ kind: "text", text: string } | { kind: "ref", text: string, use: CustomMediaUse } | { kind: "group", operand: MediaNode } | { kind: "not", operand: MediaNode } | { kind: "chain", isOr: boolean, terms: MediaNode[] } | { kind: "typed", text: string, rest: MediaNode | null }} MediaNode
106
+ */
107
+
108
+ /** @type {CustomMediaValue} */
109
+ const CUSTOM_MEDIA_UNSUPPORTED = { kind: "unsupported" };
110
+ /** @type {CustomMediaValue} */
111
+ const CUSTOM_MEDIA_TRUE = { kind: "boolean", value: true };
112
+ /** @type {CustomMediaValue} */
113
+ const CUSTOM_MEDIA_FALSE = { kind: "boolean", value: false };
73
114
 
74
115
  const CC_COLON = ":".charCodeAt(0);
116
+ const CC_FULL_STOP = ".".charCodeAt(0);
75
117
  const CC_HYPHEN_MINUS = "-".charCodeAt(0);
76
118
  const CC_SEMICOLON = ";".charCodeAt(0);
77
119
  const CC_TAB = "\t".charCodeAt(0);
@@ -82,6 +124,7 @@ const CC_FORM_FEED = "\f".charCodeAt(0);
82
124
  const CC_LEFT_CURLY = "{".charCodeAt(0);
83
125
  const CC_LOWER_V = "v".charCodeAt(0);
84
126
  const CC_UPPER_V = "V".charCodeAt(0);
127
+ const CC_REVERSE_SOLIDUS = "\\".charCodeAt(0);
85
128
 
86
129
  // A parsed CSS comment. `loc` is computed on demand — only magic-comment error
87
130
  // warnings read it, so comment-heavy CSS skips the per-comment line/col work.
@@ -102,22 +145,23 @@ const VIEW_TRANSITION_PART_PSEUDO =
102
145
  /^view-transition-(?:group|image-pair|old|new)$/i;
103
146
  const IS_MODULES = /\.modules?\.[^.]+$/i;
104
147
 
105
- // `@font-face` src extension → preload `type`; the emission guesses `as="font"`.
106
- const FONT_MIME_TYPES = new Map([
107
- ["woff2", "font/woff2"],
108
- ["woff", "font/woff"],
109
- ["ttf", "font/ttf"],
110
- ["otf", "font/otf"],
111
- ["eot", "application/vnd.ms-fontobject"]
112
- ]);
148
+ // Which formats get a preload hint is webpack's call; what each one *is* comes
149
+ // from `mime-db`, not a second copy of the mapping.
150
+ const FONT_EXTENSIONS = new Set(["woff2", "woff", "ttf", "otf", "eot"]);
151
+
152
+ // mime-db is heavy — only load it once a font src actually needs a `type`.
153
+ const getMimeTypes = memoize(() => require("../util/mimeTypes"));
113
154
 
114
155
  /**
115
156
  * @param {string} request font url (may carry a query/hash)
116
157
  * @returns {string | undefined} preload `type` for a known font extension
117
158
  */
118
159
  const fontMimeType = (request) => {
119
- const match = /\.([a-z0-9]+)(?:[?#]|$)/i.exec(request);
120
- return match ? FONT_MIME_TYPES.get(match[1].toLowerCase()) : undefined;
160
+ // Off the path only: `font?fallback=.woff2` names no font.
161
+ const resourcePath = parseResource(request).path;
162
+ const extension = path.extname(resourcePath).slice(1).toLowerCase();
163
+ if (!FONT_EXTENSIONS.has(extension)) return undefined;
164
+ return getMimeTypes().lookup(resourcePath);
121
165
  };
122
166
 
123
167
  // Skip options for a non-CSS-Modules parse: drop the selector prelude (never
@@ -166,37 +210,18 @@ const ONLY_WHITESPACE = /^\s+$/;
166
210
  // Relative request prefix (`./` or `../`) — `isSelfReferenceRequest` per `from`.
167
211
  const RELATIVE_REQUEST = /^\.{1,2}\//;
168
212
 
169
- /**
170
- * Returns matches.
171
- * @param {RegExp} regexp a regexp
172
- * @param {string} str a string
173
- * @returns {RegExpExecArray[]} matches
174
- */
175
- const matchAll = (regexp, str) => {
176
- /** @type {RegExpExecArray[]} */
177
- const result = [];
178
-
179
- /** @type {null | RegExpExecArray} */
180
- let match;
181
-
182
- while ((match = regexp.exec(str)) !== null) {
183
- result.push(match);
184
- }
185
- return result;
186
- };
187
-
188
213
  /**
189
214
  * Range-keyed index over a known-properties table: ASCII-case-folded 31-hash
190
215
  * of the name bytes → canonical key(s). Lets the Declaration visitor answer
191
216
  * "is this a known property" (and get the canonical lowercase name) without
192
217
  * slicing the property name out of the source per declaration.
193
- * @type {WeakMap<Map<string, Record<string, number>>, Map<number, string | string[]>>}
218
+ * @type {WeakMap<Map<string, Map<string, number>>, Map<number, string | string[]>>}
194
219
  */
195
220
  const KNOWN_PROPERTY_INDEX_CACHE = new WeakMap();
196
221
 
197
222
  /**
198
223
  * Gets (or builds) the hash index for a known-properties table.
199
- * @param {Map<string, Record<string, number>>} knownProperties known properties table
224
+ * @param {Map<string, Map<string, number>>} knownProperties known properties table
200
225
  * @returns {Map<number, string | string[]>} hash → canonical name(s)
201
226
  */
202
227
  const getKnownPropertyIndex = (knownProperties) => {
@@ -230,6 +255,12 @@ const knownPropertyForRange = (index, input, start, end) => {
230
255
  let h = end - start;
231
256
  for (let i = start; i < end; i++) {
232
257
  let c = input.charCodeAt(i);
258
+ // The table holds no raw-byte spelling, so re-read the unescaped one.
259
+ // Each pass shortens the name, so an escaped `\` cannot recurse forever.
260
+ if (c === CC_REVERSE_SOLIDUS) {
261
+ const name = unescapeIdentifier(input.slice(start, end));
262
+ return knownPropertyForRange(index, name, 0, name.length);
263
+ }
233
264
  if (c >= 65 && c <= 90) c |= 0x20;
234
265
  h = ((h << 5) - h + c) | 0;
235
266
  }
@@ -244,291 +275,84 @@ const knownPropertyForRange = (index, input, start, end) => {
244
275
  return undefined;
245
276
  };
246
277
 
247
- /** @type {Record<string, number>} */
248
- const PREDEFINED_COUNTER_STYLES = {
249
- decimal: 1,
250
- "decimal-leading-zero": 1,
251
- "arabic-indic": 1,
252
- armenian: 1,
253
- "upper-armenian": 1,
254
- "lower-armenian": 1,
255
- bengali: 1,
256
- cambodian: 1,
257
- khmer: 1,
258
- "cjk-decimal": 1,
259
- devanagari: 1,
260
- georgian: 1,
261
- gujarati: 1,
262
- /* cspell:disable-next-line */
263
- gurmukhi: 1,
264
- hebrew: 1,
265
- kannada: 1,
266
- lao: 1,
267
- malayalam: 1,
268
- mongolian: 1,
269
- myanmar: 1,
270
- oriya: 1,
271
- persian: 1,
272
- "lower-roman": 1,
273
- "upper-roman": 1,
274
- tamil: 1,
275
- telugu: 1,
276
- thai: 1,
277
- tibetan: 1,
278
-
279
- "lower-alpha": 1,
280
- "lower-latin": 1,
281
- "upper-alpha": 1,
282
- "upper-latin": 1,
283
- "lower-greek": 1,
284
- hiragana: 1,
285
- /* cspell:disable-next-line */
286
- "hiragana-iroha": 1,
287
- katakana: 1,
288
- /* cspell:disable-next-line */
289
- "katakana-iroha": 1,
290
-
291
- disc: 1,
292
- circle: 1,
293
- square: 1,
294
- "disclosure-open": 1,
295
- "disclosure-closed": 1,
296
-
297
- "cjk-earthly-branch": 1,
298
- "cjk-heavenly-stem": 1,
299
-
300
- "japanese-informal": 1,
301
- "japanese-formal": 1,
302
-
303
- "korean-hangul-formal": 1,
304
- /* cspell:disable-next-line */
305
- "korean-hanja-informal": 1,
306
- /* cspell:disable-next-line */
307
- "korean-hanja-formal": 1,
308
-
309
- "simp-chinese-informal": 1,
310
- "simp-chinese-formal": 1,
311
- "trad-chinese-informal": 1,
312
- "trad-chinese-formal": 1,
313
- "cjk-ideographic": 1,
314
-
315
- "ethiopic-numeric": 1
316
- };
317
-
318
- /** @type {Record<string, number>} */
319
- const GLOBAL_VALUES = {
320
- // Global values
321
- initial: Infinity,
322
- inherit: Infinity,
323
- unset: Infinity,
324
- revert: Infinity,
325
- "revert-layer": Infinity
326
- };
327
-
328
- /** @type {Record<string, number>} */
329
- const GRID_AREA_OR_COLUMN_OR_ROW = {
330
- auto: Infinity,
331
- span: Infinity,
332
- ...GLOBAL_VALUES
333
- };
334
-
335
- /** @type {Record<string, number>} */
336
- const GRID_AUTO_COLUMNS_OR_ROW = {
337
- "min-content": Infinity,
338
- "max-content": Infinity,
339
- auto: Infinity,
340
- ...GLOBAL_VALUES
341
- };
342
-
343
- /** @type {Record<string, number>} */
344
- const GRID_AUTO_FLOW = {
345
- row: 1,
346
- column: 1,
347
- dense: 1,
348
- ...GLOBAL_VALUES
349
- };
350
-
351
- /** @type {Record<string, number>} */
352
- const GRID_TEMPLATE_AREAS = {
353
- // Special
354
- none: 1,
355
- ...GLOBAL_VALUES
356
- };
357
-
358
- /** @type {Record<string, number>} */
359
- const GRID_TEMPLATE_COLUMNS_OR_ROWS = {
360
- none: 1,
361
- subgrid: 1,
362
- masonry: 1,
363
- "max-content": Infinity,
364
- "min-content": Infinity,
365
- auto: Infinity,
366
- ...GLOBAL_VALUES
367
- };
368
-
369
- /** @type {Record<string, number>} */
370
- const GRID_TEMPLATE = {
371
- ...GRID_TEMPLATE_AREAS,
372
- ...GRID_TEMPLATE_COLUMNS_OR_ROWS
373
- };
374
-
375
- /** @type {Record<string, number>} */
376
- const GRID = {
377
- "auto-flow": 1,
378
- dense: 1,
379
- ...GRID_AUTO_COLUMNS_OR_ROW,
380
- ...GRID_AUTO_FLOW,
381
- ...GRID_TEMPLATE_AREAS,
382
- ...GRID_TEMPLATE_COLUMNS_OR_ROWS
278
+ /**
279
+ * Comma-separated index of the counter-name argument in a counter-reading function, or `-1` when the name isn't one. The name is matched over the raw byte range like the other function-name probes; only an escaped name (rare) pays the unescaped slice.
280
+ * @param {string} input source
281
+ * @param {number} start name start offset
282
+ * @param {number} end name end offset
283
+ * @param {string=} escapedName unescaped name, when the raw range carries an escape
284
+ * @returns {number} argument index of the counter name, or `-1`
285
+ */
286
+ const counterFunctionNameIndex = (input, start, end, escapedName) => {
287
+ // The `target-counter(url, name, style?)` cross-reference forms (Generated Content for Paged Media) name the counter second; print engines read them.
288
+ if (escapedName !== undefined) {
289
+ if (
290
+ equalsLowerCase(escapedName, "counter") ||
291
+ equalsLowerCase(escapedName, "counters")
292
+ ) {
293
+ return 0;
294
+ }
295
+ if (
296
+ equalsLowerCase(escapedName, "target-counter") ||
297
+ equalsLowerCase(escapedName, "target-counters")
298
+ ) {
299
+ return 1;
300
+ }
301
+ return -1;
302
+ }
303
+ switch (end - start) {
304
+ case 7:
305
+ return rangeEqualsLowerCase(input, start, end, "counter") ? 0 : -1;
306
+ case 8:
307
+ return rangeEqualsLowerCase(input, start, end, "counters") ? 0 : -1;
308
+ case 14:
309
+ return rangeEqualsLowerCase(input, start, end, "target-counter") ? 1 : -1;
310
+ case 15:
311
+ return rangeEqualsLowerCase(input, start, end, "target-counters")
312
+ ? 1
313
+ : -1;
314
+ default:
315
+ return -1;
316
+ }
383
317
  };
384
318
 
385
319
  /**
386
- * Gets known properties.
320
+ * Gets the known-properties table for the enabled scoping options: the
321
+ * properties a `css/module` reads a scoped name out of, each with the keywords
322
+ * of its own grammar (see `tooling/generate-css-data.js`).
387
323
  * @param {{ animation?: boolean, container?: boolean, customIdents?: boolean, grid?: boolean }=} options options
388
- * @returns {Map<string, Record<string, number>>} list of known properties
324
+ * @returns {Map<string, Map<string, number>>} list of known properties
389
325
  */
390
326
  const buildKnownProperties = (options = {}) => {
391
- /** @type {Map<string, Record<string, number>>} */
327
+ /** @type {Map<string, Map<string, number>>} */
392
328
  const knownProperties = new Map();
393
-
394
- if (options.animation) {
395
- knownProperties.set("animation", {
396
- // animation-direction
397
- normal: 1,
398
- reverse: 1,
399
- alternate: 1,
400
- "alternate-reverse": 1,
401
- // animation-fill-mode
402
- forwards: 1,
403
- backwards: 1,
404
- both: 1,
405
- // animation-iteration-count
406
- infinite: 1,
407
- // animation-play-state
408
- paused: 1,
409
- running: 1,
410
- // animation-timing-function
411
- ease: 1,
412
- "ease-in": 1,
413
- "ease-out": 1,
414
- "ease-in-out": 1,
415
- linear: 1,
416
- "step-end": 1,
417
- "step-start": 1,
418
- // Special
419
- none: Infinity, // No matter how many times you write none, it will never be an animation name
420
- ...GLOBAL_VALUES
421
- });
422
- knownProperties.set("animation-name", {
423
- // Special
424
- none: Infinity, // No matter how many times you write none, it will never be an animation name
425
- ...GLOBAL_VALUES
426
- });
427
- }
428
-
429
- if (options.container) {
430
- knownProperties.set("container", {
431
- // container-type
432
- normal: 1,
433
- size: 1,
434
- "inline-size": 1,
435
- "scroll-state": 1,
436
- // Special
437
- none: Infinity,
438
- ...GLOBAL_VALUES
439
- });
440
- knownProperties.set("container-name", {
441
- // Special
442
- none: Infinity,
443
- ...GLOBAL_VALUES
444
- });
445
- }
446
-
447
- if (options.customIdents) {
448
- knownProperties.set("list-style", {
449
- // list-style-position
450
- inside: 1,
451
- outside: 1,
452
- // list-style-type
453
- ...PREDEFINED_COUNTER_STYLES,
454
- // Special
455
- none: Infinity,
456
- ...GLOBAL_VALUES
457
- });
458
- knownProperties.set("list-style-type", {
459
- // list-style-type
460
- ...PREDEFINED_COUNTER_STYLES,
461
- // Special
462
- none: Infinity,
463
- ...GLOBAL_VALUES
464
- });
465
- knownProperties.set("system", {
466
- cyclic: 1,
467
- numeric: 1,
468
- alphabetic: 1,
469
- symbolic: 1,
470
- additive: 1,
471
- fixed: 1,
472
- extends: 1,
473
- ...PREDEFINED_COUNTER_STYLES
474
- });
475
- knownProperties.set("fallback", {
476
- ...PREDEFINED_COUNTER_STYLES
477
- });
478
- knownProperties.set("speak-as", {
479
- auto: 1,
480
- bullets: 1,
481
- numbers: 1,
482
- words: 1,
483
- "spell-out": 1,
484
- ...PREDEFINED_COUNTER_STYLES
485
- });
486
- knownProperties.set("view-transition-name", {
487
- none: Infinity,
488
- auto: Infinity,
489
- "match-element": Infinity,
490
- ...GLOBAL_VALUES
491
- });
492
- knownProperties.set("view-transition-group", {
493
- normal: Infinity,
494
- contain: Infinity,
495
- nearest: Infinity,
496
- ...GLOBAL_VALUES
497
- });
498
- knownProperties.set("view-transition-class", {
499
- none: Infinity,
500
- ...GLOBAL_VALUES
501
- });
502
- }
503
-
504
- if (options.grid) {
505
- knownProperties.set("grid", GRID);
506
- knownProperties.set("grid-area", GRID_AREA_OR_COLUMN_OR_ROW);
507
- knownProperties.set("grid-column", GRID_AREA_OR_COLUMN_OR_ROW);
508
- knownProperties.set("grid-column-end", GRID_AREA_OR_COLUMN_OR_ROW);
509
- knownProperties.set("grid-column-start", GRID_AREA_OR_COLUMN_OR_ROW);
510
- knownProperties.set("grid-row", GRID_AREA_OR_COLUMN_OR_ROW);
511
- knownProperties.set("grid-row-end", GRID_AREA_OR_COLUMN_OR_ROW);
512
- knownProperties.set("grid-row-start", GRID_AREA_OR_COLUMN_OR_ROW);
513
- knownProperties.set("grid-template", GRID_TEMPLATE);
514
- knownProperties.set("grid-template-areas", GRID_TEMPLATE_AREAS);
515
- knownProperties.set("grid-template-columns", GRID_TEMPLATE_COLUMNS_OR_ROWS);
516
- knownProperties.set("grid-template-rows", GRID_TEMPLATE_COLUMNS_OR_ROWS);
329
+ for (const [property, option] of CSS_MODULES_KEYWORD_OPTIONS) {
330
+ if (
331
+ options[
332
+ /** @type {"animation" | "container" | "customIdents" | "grid"} */
333
+ (option)
334
+ ]
335
+ ) {
336
+ knownProperties.set(
337
+ property,
338
+ /** @type {Map<string, number>} */
339
+ (CSS_MODULES_KEYWORDS.get(property))
340
+ );
341
+ }
517
342
  }
518
-
519
343
  return knownProperties;
520
344
  };
521
345
 
522
- /** @type {Map<number, Map<string, Record<string, number>>>} */
346
+ /** @type {Map<number, Map<string, Map<string, number>>>} */
523
347
  const KNOWN_PROPERTIES_CACHE = new Map();
524
348
 
525
349
  /**
526
350
  * Memoized {@link buildKnownProperties}: the table depends only on the four
527
- * boolean options (≤16 combinations) and is read-only, while the same parser is
351
+ * boolean options (≤ 16 combinations) and is read-only, while the same parser is
528
352
  * reused across modules — so build each combination once and share it instead
529
- * of rebuilding the Map (and its value objects) per parsed module.
353
+ * of rebuilding the Map per parsed module.
530
354
  * @param {{ animation?: boolean, container?: boolean, customIdents?: boolean, grid?: boolean }=} options options
531
- * @returns {Map<string, Record<string, number>>} known properties table
355
+ * @returns {Map<string, Map<string, number>>} known properties table
532
356
  */
533
357
  const getKnownProperties = (options = {}) => {
534
358
  const key =
@@ -595,6 +419,74 @@ const isContainerKeyword = (input, start, end) => {
595
419
  }
596
420
  };
597
421
 
422
+ /**
423
+ * Whether the byte range contains a CSS escape (`\`) — function names are short, so this scan replaces a per-name slice.
424
+ * @param {string} input source
425
+ * @param {number} start start offset
426
+ * @param {number} end end offset (exclusive)
427
+ * @returns {boolean} true when the range contains a backslash
428
+ */
429
+ const rangeHasEscape = (input, start, end) => {
430
+ for (let i = start; i < end; i++) {
431
+ if (input.charCodeAt(i) === CC_REVERSE_SOLIDUS) return true;
432
+ }
433
+ return false;
434
+ };
435
+
436
+ /**
437
+ * Whether a raw ident range names a custom property — `\2d\2d x` is `--x` too,
438
+ * so a name opening with a dash or a backslash pays the unescape.
439
+ * @param {string} input source
440
+ * @param {number} start ident start offset
441
+ * @param {number} end ident end offset (exclusive)
442
+ * @returns {boolean} true when the ident names a custom property
443
+ */
444
+ const rangeIsDashedIdentifier = (input, start, end) => {
445
+ // `--x` is the shortest spelling; an escaped one is longer still.
446
+ if (end - start < 3) return false;
447
+ const first = input.charCodeAt(start);
448
+ if (first === CC_HYPHEN_MINUS) {
449
+ if (input.charCodeAt(start + 1) === CC_HYPHEN_MINUS) return true;
450
+ if (input.charCodeAt(start + 1) !== CC_REVERSE_SOLIDUS) return false;
451
+ } else if (first !== CC_REVERSE_SOLIDUS) {
452
+ return false;
453
+ }
454
+ return isDashedIdentifier(unescapeIdentifier(input.slice(start, end)));
455
+ };
456
+
457
+ /**
458
+ * The named cell tokens of a `grid-template-areas` string, in source offsets.
459
+ * A whitespace closing a hex escape does not separate; a `.` run names nothing.
460
+ * @param {string} input source
461
+ * @param {number} start content start offset (inside the quote)
462
+ * @param {number} end content end offset (exclusive)
463
+ * @returns {[number, number][]} one `[start, end]` per named cell
464
+ */
465
+ const gridAreaNames = (input, start, end) => {
466
+ /** @type {[number, number][]} */
467
+ const names = [];
468
+ let i = start;
469
+ while (i < end) {
470
+ while (i < end && isWhitespace(input.charCodeAt(i))) i++;
471
+ if (i >= end) break;
472
+ const nameStart = i;
473
+ let nullCell = true;
474
+ while (i < end) {
475
+ const cc = input.charCodeAt(i);
476
+ if (isWhitespace(cc)) break;
477
+ if (cc === CC_REVERSE_SOLIDUS) {
478
+ i = Math.min(skipEscape(input, i), end);
479
+ nullCell = false;
480
+ continue;
481
+ }
482
+ if (cc !== CC_FULL_STOP) nullCell = false;
483
+ i++;
484
+ }
485
+ if (!nullCell) names.push([nameStart, i]);
486
+ }
487
+ return names;
488
+ };
489
+
598
490
  /**
599
491
  * @param {string} input source
600
492
  * @param {number} pos position
@@ -888,10 +780,14 @@ class CssParser extends Parser {
888
780
  const { line: el, column: ec } = locConverter.get(end);
889
781
 
890
782
  state.current.addWarning(
891
- new ModuleDependencyWarning(state.module, new WebpackError(message), {
892
- start: { line: sl, column: sc },
893
- end: { line: el, column: ec }
894
- })
783
+ new (getModuleDependencyWarning())(
784
+ state.module,
785
+ new WebpackError(message),
786
+ {
787
+ start: { line: sl, column: sc },
788
+ end: { line: el, column: ec }
789
+ }
790
+ )
895
791
  );
896
792
  }
897
793
 
@@ -916,6 +812,92 @@ class CssParser extends Parser {
916
812
  state.module.addError(err);
917
813
  }
918
814
 
815
+ /**
816
+ * Arbitrate cross-kind CSS-Modules export collisions collected during
817
+ * parsing. A class always wins the shared JS export key over the
818
+ * CSS-only kinds (`@keyframes`, `@counter-style`, `@container`, custom
819
+ * property, grid identifier) — matching css-loader / lightning css /
820
+ * postcss-modules; the loser's `EXPORT_MODE` is demoted to `NONE` so
821
+ * the CSS-side rewrite is preserved but the entry no longer contributes
822
+ * to `styles.<name>`. Other pairs (`class` ↔ `id`,
823
+ * `class` ↔ `:export`, `class` ↔ `@value`, kind-vs-kind without a
824
+ * class present, …) surface as a warning so the user resolves the name
825
+ * themselves. Same-kind redeclarations are silent.
826
+ * @param {ParserState} state parser state
827
+ * @param {Map<string, { kind: string, line: number, column: number, entries: number[] }[]>} declaredExports collected declarations grouped by (name, kind)
828
+ * @param {import("../dependencies/CssIcssExportDependency").CssExportEntry[]} cssExportEntries all export entries — losers are demoted in place
829
+ * @returns {void}
830
+ */
831
+ _resolveAmbiguousExports(state, declaredExports, cssExportEntries) {
832
+ const NONE = CssIcssExportDependency.EXPORT_MODE.NONE;
833
+ for (const [name, decls] of declaredExports) {
834
+ if (decls.length <= 1) continue;
835
+ // Prefer `class` as the winner (matches css-loader / lightning css / postcss-modules); otherwise the first-declared kind wins de facto.
836
+ let winner;
837
+ for (const d of decls) {
838
+ if (d.kind === "class") {
839
+ winner = d;
840
+ break;
841
+ }
842
+ }
843
+ const reference = winner !== undefined ? winner : decls[0];
844
+ for (const loser of decls) {
845
+ if (loser === reference) continue;
846
+ const anchor = cssExportEntries[loser.entries[0]];
847
+ if (
848
+ winner !== undefined &&
849
+ (loser.kind === "custom property" ||
850
+ loser.kind === "@keyframes" ||
851
+ loser.kind === "@counter-style" ||
852
+ loser.kind === "@container" ||
853
+ loser.kind === "grid identifier")
854
+ ) {
855
+ for (const idx of loser.entries) {
856
+ cssExportEntries[idx].exportMode = NONE;
857
+ }
858
+ state.current.addWarning(
859
+ new (getModuleDependencyWarning())(
860
+ state.module,
861
+ new WebpackError(
862
+ `CSS module export "${name}" is shadowed by ${winner.kind} at line ${winner.line}:${winner.column}: the ${loser.kind} "${name}" is still scoped in the emitted CSS but is not accessible from the JavaScript export — rename one of them if both are needed`
863
+ ),
864
+ {
865
+ start: {
866
+ line: anchor.locStartLine,
867
+ column: anchor.locStartColumn
868
+ },
869
+ end: {
870
+ line: anchor.locEndLine,
871
+ column: anchor.locEndColumn
872
+ }
873
+ }
874
+ )
875
+ );
876
+ } else {
877
+ // Other collision — warn without changing behavior.
878
+ state.current.addWarning(
879
+ new (getModuleDependencyWarning())(
880
+ state.module,
881
+ new WebpackError(
882
+ `Conflicting CSS module export "${name}": already declared as ${reference.kind} at line ${reference.line}:${reference.column}, redeclared as ${loser.kind}`
883
+ ),
884
+ {
885
+ start: {
886
+ line: anchor.locStartLine,
887
+ column: anchor.locStartColumn
888
+ },
889
+ end: {
890
+ line: anchor.locEndLine,
891
+ column: anchor.locEndColumn
892
+ }
893
+ }
894
+ )
895
+ );
896
+ }
897
+ }
898
+ }
899
+ }
900
+
919
901
  /**
920
902
  * Parses the provided source and updates the parser state.
921
903
  * @param {string | Buffer | PreparsedAst} source the source to parse
@@ -975,6 +957,43 @@ class CssParser extends Parser {
975
957
 
976
958
  const module = state.module;
977
959
 
960
+ // Every CSS Modules export declaration is tracked here so `_resolveAmbiguousExports` can arbitrate cross-kind collisions in one pass at end-of-parse: a class silently wins over CSS-scoped kinds (`@keyframes`, `@counter-style`, `@container`, custom property, grid identifier) — matching css-loader / lightning css / postcss-modules — while other pairs (`class` ↔ `id`, `class` ↔ `:export`, `class` ↔ `@value`, …) surface as a warning so the user can resolve them.
961
+ /** @typedef {{ kind: string, line: number, column: number, entries: number[] }} DeclarationOfKind */
962
+ /** @type {Map<string, DeclarationOfKind[]>} */
963
+ const declaredExports = new Map();
964
+
965
+ /**
966
+ * Track a declaration site for later ambiguity resolution. Pointer
967
+ * into `cssExportEntries` is captured now because the entry is
968
+ * pushed immediately after by the calling `addCssExport(…)`.
969
+ * Property-value references (`animation: foo`) are not declarations
970
+ * and skip this hook.
971
+ * @param {string} name export name
972
+ * @param {string} kind human-readable declaration kind
973
+ * @param {number} line start line (1-based) of the declaration
974
+ * @param {number} column start column (0-based) of the declaration
975
+ * @returns {void}
976
+ */
977
+ const recordDeclaration = (name, kind, line, column) => {
978
+ let decls = declaredExports.get(name);
979
+ if (decls === undefined) {
980
+ decls = [];
981
+ declaredExports.set(name, decls);
982
+ }
983
+ let d;
984
+ for (const existing of decls) {
985
+ if (existing.kind === kind) {
986
+ d = existing;
987
+ break;
988
+ }
989
+ }
990
+ if (d === undefined) {
991
+ d = { kind, line, column, entries: [] };
992
+ decls.push(d);
993
+ }
994
+ d.entries.push(cssExportEntries.length);
995
+ };
996
+
978
997
  // All `:export`-style exports are collected into a single
979
998
  // `CssIcssExportDependency` per module (emitted at parse end) instead of one
980
999
  // `Dependency` per export — far fewer retained objects on CSS-heavy builds.
@@ -1141,7 +1160,7 @@ class CssParser extends Parser {
1141
1160
  if (errors) {
1142
1161
  for (const e of errors) {
1143
1162
  state.module.addWarning(
1144
- new CommentCompilationWarning(
1163
+ new (getCommentCompilationWarning())(
1145
1164
  `Compilation error while processing magic comment(-s): /*${e.comment.value}*/: ${e.message}`,
1146
1165
  rangeLoc(e.comment.range[0], e.comment.range[1])
1147
1166
  )
@@ -1154,7 +1173,7 @@ class CssParser extends Parser {
1154
1173
  // Loc is computed lazily here — it's only needed for this rare
1155
1174
  // warning, not on every checked `url()` / `@import`.
1156
1175
  state.module.addWarning(
1157
- new UnsupportedFeatureWarning(
1176
+ new (getUnsupportedFeatureWarning())(
1158
1177
  `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
1159
1178
  rangeLoc(warnStart, warnEnd)
1160
1179
  )
@@ -1183,10 +1202,12 @@ class CssParser extends Parser {
1183
1202
  this.options.customMedia && input.includes("@custom-media");
1184
1203
  const mayHaveCustomSelectors =
1185
1204
  this.options.customSelectors && input.includes("@custom-selector");
1186
- /** @type {Map<string, { text: string, kind: ("condition" | "type" | "unsupported") }> | undefined} */
1205
+ /** @type {Map<string, CustomMediaDefinition> | undefined} */
1187
1206
  let customMediaDefs;
1188
- /** @type {{ name: string, start: number, end: number, invalid: boolean, leading: boolean }[] | undefined} */
1189
- let customMediaUses;
1207
+ /** @type {Map<string, CustomMediaValue> | undefined} */
1208
+ let customMediaValues;
1209
+ /** @type {{ queries: MediaNode[], start: number, end: number, uses: CustomMediaUse[] }[] | undefined} */
1210
+ let customMediaQueries;
1190
1211
  /** @type {Map<string, string> | undefined} */
1191
1212
  let customSelectorDefs;
1192
1213
  /** @type {{ name: string, start: number, end: number }[] | undefined} */
@@ -1510,6 +1531,7 @@ class CssParser extends Parser {
1510
1531
  } else if (type === 1) {
1511
1532
  const { line: sl, column: sc } = locConverter.get(start);
1512
1533
  const { line: el, column: ec } = locConverter.get(end);
1534
+ recordDeclaration(name, ":export", sl, sc);
1513
1535
  addCssExport(sl, sc, el, ec, name, getReexport(value));
1514
1536
  }
1515
1537
  };
@@ -1562,8 +1584,9 @@ class CssParser extends Parser {
1562
1584
  * Process a `local(...)` / `global(...)` pseudo-function: strip the call (and a leading legacy `:`) via a presentational dep, then emit `local()`'s inner top-level idents as ICSS exports.
1563
1585
  * @param {FunctionNode} fn parsed local/global function node
1564
1586
  * @param {1 | 2} type 1 = local, 2 = global
1587
+ * @param {string=} declarationKind human-readable export kind to use for conflict tracking when the caller is a declaration site (selector or at-rule prelude). Undefined for property-value references, which are not tracked.
1565
1588
  */
1566
- const processLocalOrGlobalFunction = (fn, type) => {
1589
+ const processLocalOrGlobalFunction = (fn, type, declarationKind) => {
1567
1590
  // Replace `local(` / `global(` (and a leading `:` for the `:local(`/`:global(` selector form) with empty.
1568
1591
  const fnStart = A.start(fn);
1569
1592
  const isColon = input.charCodeAt(fnStart - 1) === CC_COLON;
@@ -1581,6 +1604,14 @@ class CssParser extends Parser {
1581
1604
  const { line: el, column: ec } = locConverter.get(A.end(cv));
1582
1605
  const isDashedIdent = isDashedIdentifier(identifier);
1583
1606
  if (isDashedIdent) identifier = identifier.slice(2);
1607
+ if (declarationKind !== undefined) {
1608
+ recordDeclaration(
1609
+ identifier,
1610
+ isDashedIdent ? "custom property" : declarationKind,
1611
+ sl,
1612
+ sc
1613
+ );
1614
+ }
1584
1615
  addCssExport(
1585
1616
  sl,
1586
1617
  sc,
@@ -1608,9 +1639,10 @@ class CssParser extends Parser {
1608
1639
  * Localize the prelude name of `@keyframes` / `@counter-style` / `@container`: `options.string` takes the first string, `options.identifier` the first ident (a `RegExp` skips matching keywords), `:local()`/`:global()` count as found; top-level `var()`/`style()` dashed idents are always ICSS-processed.
1609
1640
  * @param {AtRule} atRule parsed at-rule
1610
1641
  * @param {{ string?: boolean, identifier?: boolean | RegExp }} options which prelude value kinds count as the local name
1642
+ * @param {string} kind human-readable export kind for the surrounding at-rule (`"@keyframes"`, `"@counter-style"`, `"@container"`)
1611
1643
  * @returns {number} position after handling
1612
1644
  */
1613
- const processLocalAtRule = (atRule, options) => {
1645
+ const processLocalAtRule = (atRule, options, kind) => {
1614
1646
  let found = false;
1615
1647
  for (const cv of A.prelude(atRule)) {
1616
1648
  const cvType = A.type(cv);
@@ -1621,6 +1653,7 @@ class CssParser extends Parser {
1621
1653
  const value = A.unescaped(cv);
1622
1654
  const { line: sl, column: sc } = locConverter.get(A.start(cv));
1623
1655
  const { line: el, column: ec } = locConverter.get(A.end(cv));
1656
+ recordDeclaration(value, kind, sl, sc);
1624
1657
  addCssExport(
1625
1658
  sl,
1626
1659
  sc,
@@ -1649,6 +1682,7 @@ class CssParser extends Parser {
1649
1682
  }
1650
1683
  const { line: sl, column: sc } = locConverter.get(A.start(cv));
1651
1684
  const { line: el, column: ec } = locConverter.get(A.end(cv));
1685
+ recordDeclaration(identifier, kind, sl, sc);
1652
1686
  addCssExport(
1653
1687
  sl,
1654
1688
  sc,
@@ -1678,7 +1712,7 @@ class CssParser extends Parser {
1678
1712
  if (!found && type) {
1679
1713
  found = true;
1680
1714
  if (type === 1) pure.markLocal();
1681
- processLocalOrGlobalFunction(fn, type);
1715
+ processLocalOrGlobalFunction(fn, type, kind);
1682
1716
  continue;
1683
1717
  }
1684
1718
  if (
@@ -1692,15 +1726,29 @@ class CssParser extends Parser {
1692
1726
  }
1693
1727
  return A.end(atRule);
1694
1728
  };
1729
+ /**
1730
+ * The custom property's name, without its `--`. An escaped dash makes the
1731
+ * prefix longer than two bytes, so it comes off the unescaped value.
1732
+ * @param {number} identStart start of the `--<name>` ident
1733
+ * @param {number} identEnd end of the ident
1734
+ * @returns {string} the name after `--`
1735
+ */
1736
+ const dashedIdentName = (identStart, identEnd) =>
1737
+ source.charCodeAt(identStart) === CC_HYPHEN_MINUS &&
1738
+ source.charCodeAt(identStart + 1) === CC_HYPHEN_MINUS
1739
+ ? unescapeRange(identStart + 2, identEnd)
1740
+ : unescapeRange(identStart, identEnd).slice(2);
1741
+
1695
1742
  /**
1696
1743
  * Emit the ICSS export declaring this module exports the given custom property.
1697
1744
  * @param {number} identStart start of the `--<name>` ident
1698
1745
  * @param {number} identEnd end of the ident (exclusive)
1699
1746
  */
1700
1747
  const emitDashedIdentExport = (identStart, identEnd) => {
1701
- const identifier = unescapeRange(identStart + 2, identEnd);
1748
+ const identifier = dashedIdentName(identStart, identEnd);
1702
1749
  const { line: sl, column: sc } = locConverter.get(identStart);
1703
1750
  const { line: el, column: ec } = locConverter.get(identEnd);
1751
+ recordDeclaration(identifier, "custom property", sl, sc);
1704
1752
  addCssExport(
1705
1753
  sl,
1706
1754
  sc,
@@ -1730,7 +1778,7 @@ class CssParser extends Parser {
1730
1778
  sourceEnd,
1731
1779
  pathContent
1732
1780
  ) => {
1733
- const identifier = unescapeRange(identStart + 2, identEnd);
1781
+ const identifier = dashedIdentName(identStart, identEnd);
1734
1782
  const { line: sl, column: sc } = locConverter.get(identStart);
1735
1783
  const { line: el, column: ec } = locConverter.get(sourceEnd - 1);
1736
1784
  const localName = nextIcssImportName();
@@ -1745,6 +1793,7 @@ class CssParser extends Parser {
1745
1793
  importDep.setLoc(sl, sc, el, ec);
1746
1794
  module.addDependency(importDep);
1747
1795
 
1796
+ recordDeclaration(identifier, "custom property", sl, sc);
1748
1797
  addCssExport(
1749
1798
  sl,
1750
1799
  sc,
@@ -1797,16 +1846,7 @@ class CssParser extends Parser {
1797
1846
 
1798
1847
  const identStart = A.start(identNode);
1799
1848
  const identEnd = A.end(identNode);
1800
- // `isDashedIdentifier` on the value without slicing it: a literal `--`
1801
- // prefix of length >= 3 (escaped dashes don't match, same as the string
1802
- // form, since the raw bytes aren't `--`).
1803
- if (
1804
- identEnd - identStart < 3 ||
1805
- input.charCodeAt(identStart) !== CC_HYPHEN_MINUS ||
1806
- input.charCodeAt(identStart + 1) !== CC_HYPHEN_MINUS
1807
- ) {
1808
- return;
1809
- }
1849
+ if (!rangeIsDashedIdentifier(input, identStart, identEnd)) return;
1810
1850
 
1811
1851
  let j = identIdx + 1;
1812
1852
  while (j < fv.length && A.type(fv[j]) === NodeType.Whitespace) {
@@ -1848,26 +1888,6 @@ class CssParser extends Parser {
1848
1888
  }
1849
1889
  };
1850
1890
 
1851
- /**
1852
- * The child list of an AST node (a function/block `value` or a rule `prelude`) for sibling lookahead.
1853
- * @param {AstNode | null} parent parent node
1854
- * @returns {AstNode[] | undefined} the child list, or undefined
1855
- */
1856
- const childrenOf = (parent) => {
1857
- if (!parent) return undefined;
1858
- switch (A.type(parent)) {
1859
- case NodeType.Function:
1860
- case NodeType.SimpleBlock:
1861
- case NodeType.Declaration:
1862
- return A.children(parent);
1863
- case NodeType.AtRule:
1864
- case NodeType.QualifiedRule:
1865
- return A.prelude(parent);
1866
- default:
1867
- return undefined;
1868
- }
1869
- };
1870
-
1871
1891
  // `allowImport` mirrors `allowImportAtRule`: true until the first top-level block-bearing rule.
1872
1892
  let allowImport = true;
1873
1893
  // Persistent CSS-Modules mode for a top-level rule: set by bare `:local` / `:global`, leaks into sibling rules, reset at each top-level `}`.
@@ -1882,18 +1902,25 @@ class CssParser extends Parser {
1882
1902
  active: false,
1883
1903
  /** Should top-level dashed-ident exports be emitted at this nesting level? */
1884
1904
  emit: false,
1885
- /** LIFO save/restore of `active` + `emit` across function nesting, bit-packed (bit 0 = active, bit 1 = emit) to avoid a per-function-token object allocation. */
1905
+ /** Directly inside a `counter()`-family call whose arguments were already rewritten — suppresses the generic ICSS ident rewrite over the same ranges. */
1906
+ counterArgs: false,
1907
+ /** LIFO save/restore of `active` + `emit` + `counterArgs` across function nesting, bit-packed (bit 0 = active, bit 1 = emit, bit 2 = counterArgs) to avoid a per-function-token object allocation. */
1886
1908
  /** @type {number[]} */
1887
1909
  stack: [],
1888
1910
  /** Push the current scope; call before descending into a `Function` body. */
1889
1911
  push() {
1890
- this.stack.push((this.active ? 1 : 0) | (this.emit ? 2 : 0));
1912
+ this.stack.push(
1913
+ (this.active ? 1 : 0) |
1914
+ (this.emit ? 2 : 0) |
1915
+ (this.counterArgs ? 4 : 0)
1916
+ );
1891
1917
  },
1892
1918
  /** Pop the saved scope; call when leaving a `Function` body. */
1893
1919
  pop() {
1894
1920
  const s = /** @type {number} */ (this.stack.pop());
1895
1921
  this.active = (s & 1) !== 0;
1896
1922
  this.emit = (s & 2) !== 0;
1923
+ this.counterArgs = (s & 4) !== 0;
1897
1924
  }
1898
1925
  };
1899
1926
  // Nearest enclosing declaration / at-rule / qualified-rule, set by each structural enter; the Url / Function / Ident / Comma visitors read it (via `urlActive` / `localGlobalActive` / `icssActive`) to decide value handling from the node hierarchy instead of carrying precomputed flags.
@@ -1907,10 +1934,12 @@ class CssParser extends Parser {
1907
1934
  let currentDeclIsKnownProperty = false;
1908
1935
  /** Whether the current `composes:` declaration owns the whole value (suppresses value rewrites). */
1909
1936
  let currentDeclComposesSkip = false;
1937
+ /** Whether `counter()`-family arguments are scoped in the current declaration's value. */
1938
+ let currentDeclCounterActive = false;
1910
1939
 
1911
1940
  /**
1912
1941
  * Per-at-rule scope frames; `exit` reads `hasBlock` to pick the block-cleanup vs `suppressNextRulePrelude` branch.
1913
- * @type {{ savedAnchor: boolean, savedLocalIdentifiers: string[], name: string, hasBlock: boolean, endsWithSemicolon: boolean, fontPreloaded: boolean }[]}
1942
+ * @type {{ savedAnchor: boolean, savedLocalIdentifierCount: number, name: string, hasBlock: boolean, endsWithSemicolon: boolean, fontPreloaded: boolean }[]}
1914
1943
  */
1915
1944
  const atRuleStateStack = [];
1916
1945
 
@@ -2079,6 +2108,71 @@ class CssParser extends Parser {
2079
2108
  }
2080
2109
  };
2081
2110
 
2111
+ /**
2112
+ * Emit the ICSS export for one ident argument of a `counter()`-family call. Dashed names are left to the blanket dashed-ident scanner, and reserved names (UA counters, predefined counter styles, CSS-wide keywords) stay global.
2113
+ * @param {AstNode} node the ident node
2114
+ * @param {string} reservedFrom property whose keywords must not be localized here
2115
+ * @returns {void}
2116
+ */
2117
+ const emitCounterIdent = (node, reservedFrom) => {
2118
+ const name = A.unescaped(node);
2119
+ if (isDashedIdentifier(name)) return;
2120
+ const reserved =
2121
+ /** @type {Map<string, number>} */
2122
+ (CSS_MODULES_KEYWORDS.get(reservedFrom));
2123
+ if (reserved.has(toLowerCaseIfNeeded(name))) return;
2124
+ const start = A.start(node);
2125
+ const end = A.end(node);
2126
+ const { line: sl, column: sc } = locConverter.get(start);
2127
+ const { line: el, column: ec } = locConverter.get(end);
2128
+ addCssExport(
2129
+ sl,
2130
+ sc,
2131
+ el,
2132
+ ec,
2133
+ name,
2134
+ getReexport(name),
2135
+ [start, end],
2136
+ true,
2137
+ CssIcssExportDependency.EXPORT_MODE.ONCE
2138
+ );
2139
+ };
2140
+
2141
+ /**
2142
+ * Scope a `counter()` / `counters()` / `target-counter()` / `target-counters()` call: the counter-name argument localizes like a `counter-reset` value and a trailing `<counter-style>` ident like a `list-style-type` value, so both keep naming the scoped `counter-*` declaration / `@counter-style` rule.
2143
+ * @param {AstNode[]} cvs the call's argument component values
2144
+ * @param {number} nameIndex comma-separated index of the counter-name argument
2145
+ * @returns {void}
2146
+ */
2147
+ const walkCounterFunction = (cvs, nameIndex) => {
2148
+ let group = 0;
2149
+ let groupNodes = 0;
2150
+ /** Sole ident of the current argument, or undefined when the argument isn't a lone ident. */
2151
+ let lastIdent;
2152
+ for (const cv of cvs) {
2153
+ const type = A.type(cv);
2154
+ if (type === NodeType.Whitespace) continue;
2155
+ if (type === NodeType.Comma) {
2156
+ group++;
2157
+ groupNodes = 0;
2158
+ lastIdent = undefined;
2159
+ continue;
2160
+ }
2161
+ groupNodes++;
2162
+ const loneIdent = groupNodes === 1 && type === NodeType.Ident;
2163
+ if (group === nameIndex) {
2164
+ if (loneIdent) emitCounterIdent(cv, "counter-reset");
2165
+ lastIdent = undefined;
2166
+ } else {
2167
+ lastIdent = loneIdent ? cv : undefined;
2168
+ }
2169
+ }
2170
+ // The counter style is the last argument when it's a lone ident (`counter(n, style)` / `counters(n, sep, style)`).
2171
+ if (lastIdent !== undefined && group > nameIndex) {
2172
+ emitCounterIdent(lastIdent, "list-style-type");
2173
+ }
2174
+ };
2175
+
2082
2176
  /**
2083
2177
  * Walk component values as a selector list, emitting ID / attribute deps and recursing into `:not()`/`:is()`/`:local()`/`:global()` wrappers; `localMode` is the sub-tree mode and `topLevel` controls whether commas reset it (only outside parentheses).
2084
2178
  * @param {AstNode[]} values component values to walk
@@ -2187,6 +2281,7 @@ class CssParser extends Parser {
2187
2281
  const idName = A.unescaped(v);
2188
2282
  const { line: idSl, column: idSc } = locConverter.get(A.start(v));
2189
2283
  const { line: idEl, column: idEc } = locConverter.get(A.end(v));
2284
+ recordDeclaration(idName, "id", idSl, idSc);
2190
2285
  addCssExport(
2191
2286
  idSl,
2192
2287
  idSc,
@@ -2229,6 +2324,7 @@ class CssParser extends Parser {
2229
2324
  // Cursor reads instead of `A.loc` — this is the hottest export site.
2230
2325
  const { line: sl, column: sc } = locConverter.get(A.start(next));
2231
2326
  const { line: el, column: ec } = locConverter.get(A.end(next));
2327
+ recordDeclaration(name, "class", sl, sc);
2232
2328
  addCssExport(
2233
2329
  sl,
2234
2330
  sc,
@@ -2272,18 +2368,20 @@ class CssParser extends Parser {
2272
2368
  };
2273
2369
  /**
2274
2370
  * Per-qualified-rule scope frames; `{ bailed: true }` for inline-handled `:import` / `:export` pseudo-rules.
2275
- * @type {({ bailed: true } | { bailed: false, savedAnchor: boolean, savedLocalIdentifiers: string[], savedPrevComposesFile: string | undefined, savedComposesFiles: Set<string> | null })[]}
2371
+ * @type {({ bailed: true } | { bailed: false, savedAnchor: boolean, savedLocalIdentifierCount: number, savedPrevComposesFile: string | undefined, savedComposesFiles: Set<string> | null })[]}
2276
2372
  */
2277
2373
  const qualifiedRuleStateStack = [];
2278
2374
 
2279
2375
  /**
2280
- * Whether url() deps are emitted here (from `currentStructural`): false outside `options.url` and inside an `@import` prelude (the import target, unless url recovery is on).
2376
+ * Whether url() deps are emitted here (from `currentStructural`): off in an `@import`
2377
+ * prelude (the import target, unless recovering) and in `@namespace` (an opaque identifier).
2281
2378
  * @returns {boolean} true when url() deps should be emitted
2282
2379
  */
2283
2380
  const urlActive = () => {
2284
2381
  if (!this.options.url || !currentStructural) return false;
2285
2382
  if (A.type(currentStructural) === NodeType.AtRule) {
2286
2383
  // `currentAtRuleName` is cached on at-rule enter — no per-value slice.
2384
+ if (currentAtRuleName === "@namespace") return false;
2287
2385
  return currentAtRuleName !== "@import" || currentUrlRecovery;
2288
2386
  }
2289
2387
  return true;
@@ -2639,10 +2737,39 @@ class CssParser extends Parser {
2639
2737
  /**
2640
2738
  * Emit url() deps for a `url(...)` / `src(...)` / `image-set(...)` value function.
2641
2739
  * @param {FunctionNode} fn the function node
2642
- * @param {string} fname the unescaped function name (any case)
2740
+ * @param {string | undefined} escapedName the unescaped function name when it carries an escape, undefined for the raw byte-range path
2643
2741
  */
2644
- const emitUrlFunction = (fn, fname) => {
2645
- if (equalsLowerCase(fname, "url") || equalsLowerCase(fname, "src")) {
2742
+ const emitUrlFunction = (fn, escapedName) => {
2743
+ const fnNameStart = A.nameStart(fn);
2744
+ const fnNameEnd = A.nameEnd(fn);
2745
+ let isUrlOrSrc;
2746
+ let isImageSet = false;
2747
+ if (escapedName !== undefined) {
2748
+ isUrlOrSrc =
2749
+ equalsLowerCase(escapedName, "url") ||
2750
+ equalsLowerCase(escapedName, "src");
2751
+ if (!isUrlOrSrc) isImageSet = IMAGE_SET_FUNCTION.test(escapedName);
2752
+ } else {
2753
+ const nameLength = fnNameEnd - fnNameStart;
2754
+ isUrlOrSrc =
2755
+ nameLength === 3 &&
2756
+ (rangeEqualsLowerCase(input, fnNameStart, fnNameEnd, "url") ||
2757
+ rangeEqualsLowerCase(input, fnNameStart, fnNameEnd, "src"));
2758
+ if (!isUrlOrSrc) {
2759
+ // Suffix probe first, so only a vendor-prefixed `…-image-set` pays the slice + regex.
2760
+ isImageSet =
2761
+ nameLength >= 9 &&
2762
+ rangeEqualsLowerCase(
2763
+ input,
2764
+ fnNameEnd - 9,
2765
+ fnNameEnd,
2766
+ "image-set"
2767
+ ) &&
2768
+ (nameLength === 9 ||
2769
+ IMAGE_SET_FUNCTION.test(input.slice(fnNameStart, fnNameEnd)));
2770
+ }
2771
+ }
2772
+ if (isUrlOrSrc) {
2646
2773
  // Quoted `url("…")` / `src("…")`: first non-whitespace value must be the string token.
2647
2774
  const first = A.children(fn)[nextNonWhitespace(A.children(fn), 0)];
2648
2775
  if (!first || A.type(first) !== NodeType.String) return;
@@ -2673,9 +2800,9 @@ class CssParser extends Parser {
2673
2800
  );
2674
2801
  module.addDependency(dep);
2675
2802
  module.addCodeGenerationDependency(dep);
2676
- } else if (IMAGE_SET_FUNCTION.test(fname)) {
2803
+ } else if (isImageSet) {
2677
2804
  // `image-set(…)`: each comma segment's first string is the URL; advance the magic-comment fence per string.
2678
- lastTokenEndForComments = A.nameEnd(fn) + 1;
2805
+ lastTokenEndForComments = fnNameEnd + 1;
2679
2806
  let prevStringEnd = A.start(fn);
2680
2807
  let firstInSegment = true;
2681
2808
  for (const cv of A.children(fn)) {
@@ -2983,6 +3110,7 @@ class CssParser extends Parser {
2983
3110
  {
2984
3111
  const { line: sl, column: sc } = locConverter.get(start);
2985
3112
  const { line: el, column: ec } = locConverter.get(nameEnd);
3113
+ recordDeclaration(localName, "@value", sl, sc);
2986
3114
  addCssExport(
2987
3115
  sl,
2988
3116
  sc,
@@ -3024,10 +3152,12 @@ class CssParser extends Parser {
3024
3152
 
3025
3153
  icssDefinitions.set(localName, def);
3026
3154
 
3155
+ recordDeclaration(localName, "@value", sl, sc);
3027
3156
  addCssExport(sl, sc, el, ec, localName, getReexport(value));
3028
3157
  } else {
3029
3158
  icssDefinitions.set(localName, { value });
3030
3159
 
3160
+ recordDeclaration(localName, "@value", sl, sc);
3031
3161
  addCssExport(sl, sc, el, ec, localName, value);
3032
3162
  }
3033
3163
  }
@@ -3060,7 +3190,10 @@ class CssParser extends Parser {
3060
3190
  };
3061
3191
 
3062
3192
  /**
3063
- * Collect one `@custom-media --name <value>` definition (last-wins) and drop the rule. Classifies the value: `"condition"` (a media-in-parens, or comma list → `(a or b)`) inlines anywhere; `"type"` (a media type like `screen` / `not all` / `screen and (…)`) inlines only at a query's start; `true` / `false` / nested custom-media values are `"unsupported"` (warned on use — the ecosystem disagrees on these).
3193
+ * Collect one `@custom-media --name <value>` definition (last-wins) and drop
3194
+ * the rule. A value naming another custom media is kept as an `alias` — the
3195
+ * name it points at may be defined further down the file, and node ids are
3196
+ * recycled per top-level rule, so nothing AST-shaped can outlive the walk.
3064
3197
  * @param {AtRule} at the `@custom-media` at-rule
3065
3198
  */
3066
3199
  const collectCustomMedia = (at) => {
@@ -3081,27 +3214,27 @@ class CssParser extends Parser {
3081
3214
  if (!isDashedIdentifier(name)) return;
3082
3215
  i++;
3083
3216
 
3084
- /**
3085
- * @param {string} text resolved text
3086
- * @param {"condition" | "type" | "unsupported"} kind value kind
3087
- */
3088
- const store = (text, kind) => {
3089
- (customMediaDefs || (customMediaDefs = new Map())).set(name, {
3090
- text,
3091
- kind
3092
- });
3093
- };
3094
- const extra = { hasExtra: false };
3095
-
3096
3217
  // Value span after the name, trimmed.
3097
3218
  let vs = i;
3098
3219
  let ve = prelude.length;
3099
3220
  while (vs < ve && A.type(prelude[vs]) === NodeType.Whitespace) vs++;
3100
3221
  while (ve > vs && A.type(prelude[ve - 1]) === NodeType.Whitespace) ve--;
3101
- if (vs >= ve) {
3102
- store("", "unsupported");
3103
- return;
3104
- }
3222
+ (customMediaDefs || (customMediaDefs = new Map())).set(
3223
+ name,
3224
+ classifyCustomMedia(prelude, vs, ve)
3225
+ );
3226
+ };
3227
+
3228
+ /**
3229
+ * Classify one `@custom-media` value against the live AST.
3230
+ * @param {readonly AstNode[]} prelude the defining at-rule's prelude
3231
+ * @param {number} vs first value token
3232
+ * @param {number} ve one past the last value token
3233
+ * @returns {CustomMediaDefinition} what the value is, aliases unresolved
3234
+ */
3235
+ const classifyCustomMedia = (prelude, vs, ve) => {
3236
+ if (vs >= ve) return CUSTOM_MEDIA_UNSUPPORTED;
3237
+ const extra = { hasExtra: false };
3105
3238
 
3106
3239
  let hasComma = false;
3107
3240
  for (let k = vs; k < ve; k++) {
@@ -3114,46 +3247,52 @@ class CssParser extends Parser {
3114
3247
  if (!hasComma) {
3115
3248
  const first = prelude[vs];
3116
3249
  const valueText = input.slice(A.start(first), A.end(prelude[ve - 1]));
3117
- if (A.type(first) === NodeType.SimpleBlock) {
3118
- // A lone `(--x)` is an unresolved nested custom media; anything else is a media-in-parens.
3119
- const nested =
3120
- loneDashedIdentOfBlock(
3121
- /** @type {SimpleBlock} */ (first),
3122
- extra
3123
- ) !== undefined;
3124
- store(nested ? "" : valueText, nested ? "unsupported" : "condition");
3125
- return;
3250
+ // A `(--x)` standing alone is this value spelled as another name.
3251
+ if (ve - vs === 1 && A.type(first) === NodeType.SimpleBlock) {
3252
+ const nested = loneDashedIdentOfBlock(
3253
+ /** @type {SimpleBlock} */ (first),
3254
+ extra
3255
+ );
3256
+ if (nested !== undefined && !extra.hasExtra) {
3257
+ return { kind: "alias", name: nested };
3258
+ }
3126
3259
  }
3127
- if (A.type(first) !== NodeType.Ident) {
3128
- store("", "unsupported");
3129
- return;
3260
+ // A reference anywhere else in the value has no resolution step of its
3261
+ // own — only a `@media` prelude's uses are scanned — so writing this
3262
+ // value out would emit the reference unresolved.
3263
+ if (hasCustomMediaRef(prelude, vs, ve)) return CUSTOM_MEDIA_UNSUPPORTED;
3264
+ if (A.type(first) === NodeType.SimpleBlock) {
3265
+ return { kind: "condition", text: valueText };
3130
3266
  }
3131
- if (
3132
- rangeEqualsLowerCase(input, A.start(first), A.end(first), "true") ||
3133
- rangeEqualsLowerCase(input, A.start(first), A.end(first), "false")
3134
- ) {
3135
- store("", "unsupported");
3136
- return;
3267
+ if (A.type(first) !== NodeType.Ident) return CUSTOM_MEDIA_UNSUPPORTED;
3268
+ const s = A.start(first);
3269
+ const e = A.end(first);
3270
+ if (ve - vs === 1) {
3271
+ if (rangeEqualsLowerCase(input, s, e, "true")) {
3272
+ return CUSTOM_MEDIA_TRUE;
3273
+ }
3274
+ if (rangeEqualsLowerCase(input, s, e, "false")) {
3275
+ return CUSTOM_MEDIA_FALSE;
3276
+ }
3137
3277
  }
3138
- if (rangeEqualsLowerCase(input, A.start(first), A.end(first), "not")) {
3139
- // `not (…)` is a media condition (wrap so it stays a media-in-parens); `not <type>` is a media type.
3278
+ if (rangeEqualsLowerCase(input, s, e, "not")) {
3279
+ // `not (…)` is a media condition (wrap so it stays a media-in-parens);
3280
+ // `not <type>` is a media type.
3140
3281
  let j = vs + 1;
3141
3282
  while (j < ve && A.type(prelude[j]) === NodeType.Whitespace) j++;
3142
3283
  if (j < ve && A.type(prelude[j]) === NodeType.SimpleBlock) {
3143
- store(`(${valueText})`, "condition");
3144
- } else {
3145
- store(valueText, "type");
3284
+ return { kind: "condition", text: `(${valueText})` };
3146
3285
  }
3147
- return;
3286
+ return { kind: "type", text: valueText };
3148
3287
  }
3149
3288
  // `screen`, `only screen`, `screen and (…)`, …
3150
- store(valueText, "type");
3151
- return;
3289
+ return { kind: "type", text: valueText };
3152
3290
  }
3153
3291
 
3154
- // Comma list → `(seg1 or seg2 …)`; supported only when every segment is a media-in-parens.
3155
- /** @type {string[]} */
3156
- const segments = [];
3292
+ // Comma list → `(seg1 or seg2 …)`; supported only when every segment is a
3293
+ // media-in-parens (or a name standing for one).
3294
+ /** @type {({ text: string } | { alias: string })[]} */
3295
+ const parts = [];
3157
3296
  let ok = true;
3158
3297
  /** @type {AstNode[]} */
3159
3298
  let seg = [];
@@ -3168,21 +3307,25 @@ class CssParser extends Parser {
3168
3307
  }
3169
3308
  const f = seg[a];
3170
3309
  const text = input.slice(A.start(f), A.end(seg[b - 1]));
3310
+ if (b - a === 1 && A.type(f) === NodeType.SimpleBlock) {
3311
+ const nested = loneDashedIdentOfBlock(
3312
+ /** @type {SimpleBlock} */ (f),
3313
+ extra
3314
+ );
3315
+ if (extra.hasExtra) ok = false;
3316
+ else if (nested === undefined) parts.push({ text });
3317
+ else parts.push({ alias: nested });
3318
+ return;
3319
+ }
3171
3320
  if (
3172
- b - a === 1 &&
3173
- A.type(f) === NodeType.SimpleBlock &&
3174
- loneDashedIdentOfBlock(/** @type {SimpleBlock} */ (f), extra) ===
3175
- undefined
3176
- ) {
3177
- segments.push(text);
3178
- } else if (
3179
3321
  A.type(f) === NodeType.Ident &&
3180
- rangeEqualsLowerCase(input, A.start(f), A.end(f), "not")
3322
+ rangeEqualsLowerCase(input, A.start(f), A.end(f), "not") &&
3323
+ !hasCustomMediaRef(seg, a, b)
3181
3324
  ) {
3182
- segments.push(`(${text})`);
3183
- } else {
3184
- ok = false;
3325
+ parts.push({ text: `(${text})` });
3326
+ return;
3185
3327
  }
3328
+ ok = false;
3186
3329
  };
3187
3330
  for (let k = vs; k < ve; k++) {
3188
3331
  if (A.type(prelude[k]) === NodeType.Comma) {
@@ -3193,52 +3336,384 @@ class CssParser extends Parser {
3193
3336
  }
3194
3337
  }
3195
3338
  flush();
3196
- if (ok && segments.length > 0) {
3197
- store(
3198
- segments.length === 1 ? segments[0] : `(${segments.join(" or ")})`,
3199
- "condition"
3200
- );
3339
+ return ok ? { kind: "or", parts } : CUSTOM_MEDIA_UNSUPPORTED;
3340
+ };
3341
+
3342
+ /**
3343
+ * What a `@custom-media` name resolves to, memoized. An undefined name and a
3344
+ * definition cycle both resolve to unsupported (the use site warns).
3345
+ * @param {string} name the dashed ident
3346
+ * @param {Set<string>} resolving names currently being resolved
3347
+ * @returns {CustomMediaValue} what the name resolves to
3348
+ */
3349
+ const resolveCustomMediaValue = (name, resolving) => {
3350
+ const cached = customMediaValues && customMediaValues.get(name);
3351
+ if (cached !== undefined) return cached;
3352
+ const def = customMediaDefs && customMediaDefs.get(name);
3353
+ if (def === undefined || resolving.has(name)) {
3354
+ return CUSTOM_MEDIA_UNSUPPORTED;
3355
+ }
3356
+ resolving.add(name);
3357
+ /** @type {CustomMediaValue} */
3358
+ let value;
3359
+ if (def.kind === "alias") {
3360
+ const inner = resolveCustomMediaValue(def.name, resolving);
3361
+ // A media type cannot sit in parens, so a name standing for one is no value.
3362
+ value = inner.kind === "type" ? CUSTOM_MEDIA_UNSUPPORTED : inner;
3363
+ } else if (def.kind === "or") {
3364
+ /** @type {string[]} */
3365
+ const texts = [];
3366
+ let ok = true;
3367
+ let anyTrue = false;
3368
+ for (const part of def.parts) {
3369
+ if (!("alias" in part)) {
3370
+ texts.push(part.text);
3371
+ continue;
3372
+ }
3373
+ const inner = resolveCustomMediaValue(part.alias, resolving);
3374
+ // A media query list is an `or`, so a constant segment drops out of it.
3375
+ if (inner.kind === "boolean") anyTrue = anyTrue || inner.value;
3376
+ else if (inner.kind === "condition") texts.push(inner.text);
3377
+ else ok = false;
3378
+ }
3379
+ value = !ok
3380
+ ? CUSTOM_MEDIA_UNSUPPORTED
3381
+ : anyTrue
3382
+ ? CUSTOM_MEDIA_TRUE
3383
+ : texts.length === 0
3384
+ ? CUSTOM_MEDIA_FALSE
3385
+ : {
3386
+ kind: "condition",
3387
+ text:
3388
+ texts.length === 1 ? texts[0] : `(${texts.join(" or ")})`
3389
+ };
3201
3390
  } else {
3202
- store("", "unsupported");
3391
+ value = def;
3203
3392
  }
3393
+ resolving.delete(name);
3394
+ (customMediaValues || (customMediaValues = new Map())).set(name, value);
3395
+ return value;
3204
3396
  };
3205
3397
 
3206
3398
  /**
3207
- * Record every `(--name)` custom-media reference in a media prelude (recursing into nested groups/functions) for post-walk resolution; `invalid` flags a non-boolean-context use like `(--name: 1px)`, `leading` marks a reference that is the first term of a top-level query (the only spot a media-type value may be inlined).
3208
- * @param {readonly AstNode[]} tokens prelude (or nested block) tokens
3209
- * @param {boolean} topLevel whether `tokens` is the media prelude itself (vs. a nested group)
3399
+ * Whether a node is the media keyword `word` (media queries are ASCII
3400
+ * case-insensitive).
3401
+ * @param {AstNode} node candidate node
3402
+ * @param {string} word lower-case keyword
3403
+ * @returns {boolean} true on a match
3210
3404
  */
3211
- const scanCustomMediaUses = (tokens, topLevel) => {
3212
- const extra = { hasExtra: false };
3213
- let leading = true;
3214
- for (const t of tokens) {
3215
- const tt = A.type(t);
3216
- if (tt === NodeType.Whitespace) continue;
3217
- if (topLevel && tt === NodeType.Comma) {
3218
- leading = true;
3405
+ const isMediaKeyword = (node, word) =>
3406
+ A.type(node) === NodeType.Ident &&
3407
+ rangeEqualsLowerCase(input, A.start(node), A.end(node), word);
3408
+
3409
+ /**
3410
+ * First significant index in `tokens[from…to)`.
3411
+ * @param {readonly AstNode[]} tokens token list
3412
+ * @param {number} from first index
3413
+ * @param {number} to one past the last index
3414
+ * @returns {number} the trimmed start
3415
+ */
3416
+ const mediaTrimStart = (tokens, from, to) => {
3417
+ let a = from;
3418
+ while (a < to && A.type(tokens[a]) === NodeType.Whitespace) a++;
3419
+ return a;
3420
+ };
3421
+
3422
+ /**
3423
+ * One past the last significant index in `tokens[from…to)`.
3424
+ * @param {readonly AstNode[]} tokens token list
3425
+ * @param {number} from first index
3426
+ * @param {number} to one past the last index
3427
+ * @returns {number} the trimmed end
3428
+ */
3429
+ const mediaTrimEnd = (tokens, from, to) => {
3430
+ let b = to;
3431
+ while (b > from && A.type(tokens[b - 1]) === NodeType.Whitespace) b--;
3432
+ return b;
3433
+ };
3434
+
3435
+ /**
3436
+ * The source text spanned by `tokens[from…to)`.
3437
+ * @param {readonly AstNode[]} tokens token list
3438
+ * @param {number} from first index
3439
+ * @param {number} to one past the last index
3440
+ * @returns {string} the source slice
3441
+ */
3442
+ const mediaText = (tokens, from, to) =>
3443
+ input.slice(A.start(tokens[from]), A.end(tokens[to - 1]));
3444
+
3445
+ // One flag object for the whole parse: `loneDashedIdentOfBlock` writes it and
3446
+ // every caller reads it back before the next call.
3447
+ const mediaExtra = { hasExtra: false };
3448
+
3449
+ /**
3450
+ * Whether any `(--name)` reference is reachable inside `node`. `childAt` walks
3451
+ * the children without materializing the list.
3452
+ * @param {AstNode} node a block or function node
3453
+ * @returns {boolean} true when a reference is present
3454
+ */
3455
+ const hasCustomMediaRefIn = (node) => {
3456
+ const count = A.childCount(node);
3457
+ for (let k = 0; k < count; k++) {
3458
+ const child = A.childAt(node, k);
3459
+ const type = A.type(child);
3460
+ if (type !== NodeType.SimpleBlock && type !== NodeType.Function) {
3219
3461
  continue;
3220
3462
  }
3221
- if (tt === NodeType.SimpleBlock) {
3222
- const name = loneDashedIdentOfBlock(
3223
- /** @type {SimpleBlock} */ (t),
3224
- extra
3225
- );
3226
- if (name !== undefined) {
3227
- (customMediaUses || (customMediaUses = [])).push({
3228
- name,
3229
- start: A.start(t),
3230
- end: A.end(t),
3231
- invalid: extra.hasExtra,
3232
- leading: Boolean(topLevel && leading)
3233
- });
3234
- } else {
3235
- scanCustomMediaUses(A.children(t), false);
3463
+ if (
3464
+ type === NodeType.SimpleBlock &&
3465
+ loneDashedIdentOfBlock(
3466
+ /** @type {SimpleBlock} */ (child),
3467
+ mediaExtra
3468
+ ) !== undefined
3469
+ ) {
3470
+ return true;
3471
+ }
3472
+ if (hasCustomMediaRefIn(child)) return true;
3473
+ }
3474
+ return false;
3475
+ };
3476
+
3477
+ /**
3478
+ * Whether any `(--name)` reference is reachable in `tokens[from…to)`. Cheap
3479
+ * enough to run over every `@media` prelude so one without a reference builds
3480
+ * no tree at all.
3481
+ * @param {readonly AstNode[]} tokens token list
3482
+ * @param {number} from first index
3483
+ * @param {number} to one past the last index
3484
+ * @returns {boolean} true when a reference is present
3485
+ */
3486
+ const hasCustomMediaRef = (tokens, from, to) => {
3487
+ for (let i = from; i < to; i++) {
3488
+ const node = tokens[i];
3489
+ const type = A.type(node);
3490
+ if (type !== NodeType.SimpleBlock && type !== NodeType.Function) {
3491
+ continue;
3492
+ }
3493
+ if (
3494
+ type === NodeType.SimpleBlock &&
3495
+ loneDashedIdentOfBlock(
3496
+ /** @type {SimpleBlock} */ (node),
3497
+ mediaExtra
3498
+ ) !== undefined
3499
+ ) {
3500
+ return true;
3501
+ }
3502
+ if (hasCustomMediaRefIn(node)) return true;
3503
+ }
3504
+ return false;
3505
+ };
3506
+
3507
+ /**
3508
+ * Capture one `<media-in-parens>` — a reference to a custom media name, a
3509
+ * parenthesised sub-condition, or an opaque feature test.
3510
+ * @param {readonly AstNode[]} tokens token list
3511
+ * @param {number} from first index
3512
+ * @param {number} to one past the last index
3513
+ * @param {boolean} leading whether the term starts a top-level query
3514
+ * @param {CustomMediaUse[]} uses the prelude's use list, appended to
3515
+ * @returns {MediaNode} the captured term
3516
+ */
3517
+ const captureMediaInParens = (tokens, from, to, leading, uses) => {
3518
+ const a = mediaTrimStart(tokens, from, to);
3519
+ const b = mediaTrimEnd(tokens, a, to);
3520
+ if (a >= b) return { kind: "text", text: "" };
3521
+ const text = mediaText(tokens, a, b);
3522
+ if (b - a !== 1 || A.type(tokens[a]) !== NodeType.SimpleBlock) {
3523
+ return { kind: "text", text };
3524
+ }
3525
+ const block = /** @type {SimpleBlock} */ (tokens[a]);
3526
+ const name = loneDashedIdentOfBlock(block, mediaExtra);
3527
+ if (name !== undefined) {
3528
+ const use = {
3529
+ name,
3530
+ start: A.start(block),
3531
+ end: A.end(block),
3532
+ invalid: mediaExtra.hasExtra,
3533
+ leading
3534
+ };
3535
+ uses.push(use);
3536
+ return { kind: "ref", text, use };
3537
+ }
3538
+ const children = A.children(block);
3539
+ const inner = captureMediaCondition(children, 0, children.length, uses);
3540
+ return inner.kind === "text"
3541
+ ? { kind: "text", text }
3542
+ : { kind: "group", operand: inner };
3543
+ };
3544
+
3545
+ /**
3546
+ * Capture a `<media-condition>` — `not <in-parens>`, or an `and` / `or` chain
3547
+ * of them (CSS never mixes the two at one level).
3548
+ * @param {readonly AstNode[]} tokens token list
3549
+ * @param {number} from first index
3550
+ * @param {number} to one past the last index
3551
+ * @param {CustomMediaUse[]} uses the prelude's use list, appended to
3552
+ * @returns {MediaNode} the captured condition
3553
+ */
3554
+ const captureMediaCondition = (tokens, from, to, uses) => {
3555
+ const a = mediaTrimStart(tokens, from, to);
3556
+ const b = mediaTrimEnd(tokens, a, to);
3557
+ if (a >= b) return { kind: "text", text: "" };
3558
+ if (isMediaKeyword(tokens[a], "not")) {
3559
+ return {
3560
+ kind: "not",
3561
+ operand: captureMediaInParens(tokens, a + 1, b, false, uses)
3562
+ };
3563
+ }
3564
+ /** @type {MediaNode[]} */
3565
+ const terms = [];
3566
+ let isOr = false;
3567
+ let start = a;
3568
+ for (let i = a; i <= b; i++) {
3569
+ const atEnd = i === b;
3570
+ if (
3571
+ !atEnd &&
3572
+ !isMediaKeyword(tokens[i], "and") &&
3573
+ !isMediaKeyword(tokens[i], "or")
3574
+ ) {
3575
+ continue;
3576
+ }
3577
+ if (!atEnd && isMediaKeyword(tokens[i], "or")) isOr = true;
3578
+ terms.push(captureMediaInParens(tokens, start, i, start === a, uses));
3579
+ start = i + 1;
3580
+ }
3581
+ if (terms.length === 1) return terms[0];
3582
+ return { kind: "chain", isOr, terms };
3583
+ };
3584
+
3585
+ /**
3586
+ * Capture one `<media-query>` — a bare condition, or a media type with an
3587
+ * optional `and` chain after it.
3588
+ * @param {readonly AstNode[]} tokens token list
3589
+ * @param {number} from first index
3590
+ * @param {number} to one past the last index
3591
+ * @param {CustomMediaUse[]} uses the prelude's use list, appended to
3592
+ * @returns {MediaNode} the captured query
3593
+ */
3594
+ const captureMediaQuery = (tokens, from, to, uses) => {
3595
+ const a = mediaTrimStart(tokens, from, to);
3596
+ const b = mediaTrimEnd(tokens, a, to);
3597
+ if (a >= b) return { kind: "text", text: "" };
3598
+ const rest = mediaTrimStart(tokens, a + 1, b);
3599
+ const startsWithType =
3600
+ A.type(tokens[a]) === NodeType.Ident &&
3601
+ (!isMediaKeyword(tokens[a], "not") ||
3602
+ rest >= b ||
3603
+ A.type(tokens[rest]) !== NodeType.SimpleBlock);
3604
+ if (!startsWithType) return captureMediaCondition(tokens, a, b, uses);
3605
+ // `[not | only]? <type> [and <condition-without-or>]?` — the type itself is
3606
+ // never constant, so only the `and` chain after it can fold.
3607
+ let split = b;
3608
+ for (let i = a; i < b; i++) {
3609
+ if (isMediaKeyword(tokens[i], "and")) {
3610
+ split = i;
3611
+ break;
3612
+ }
3613
+ }
3614
+ return {
3615
+ kind: "typed",
3616
+ text: mediaText(tokens, a, mediaTrimEnd(tokens, a, split)),
3617
+ rest:
3618
+ split === b ? null : captureMediaCondition(tokens, split + 1, b, uses)
3619
+ };
3620
+ };
3621
+
3622
+ /**
3623
+ * Record one `@media` prelude, the custom-media references inside it, and the
3624
+ * boolean shape needed to fold it once the references resolve.
3625
+ * @param {AtRule} at the `@media` at-rule
3626
+ */
3627
+ const collectMediaQuery = (at) => {
3628
+ const prelude = A.prelude(at);
3629
+ const start = mediaTrimStart(prelude, 0, prelude.length);
3630
+ const end = mediaTrimEnd(prelude, start, prelude.length);
3631
+ if (start >= end || !hasCustomMediaRef(prelude, start, end)) return;
3632
+ /** @type {CustomMediaUse[]} */
3633
+ const uses = [];
3634
+ /** @type {MediaNode[]} */
3635
+ const queries = [];
3636
+ let queryStart = start;
3637
+ for (let i = start; i <= end; i++) {
3638
+ if (i !== end && A.type(prelude[i]) !== NodeType.Comma) continue;
3639
+ queries.push(captureMediaQuery(prelude, queryStart, i, uses));
3640
+ queryStart = i + 1;
3641
+ }
3642
+ if (uses.length === 0) return;
3643
+ (customMediaQueries || (customMediaQueries = [])).push({
3644
+ queries,
3645
+ start: A.start(prelude[start]),
3646
+ end: A.end(prelude[end - 1]),
3647
+ uses
3648
+ });
3649
+ };
3650
+
3651
+ /**
3652
+ * Fold a captured media node against the resolved definitions. A reference the
3653
+ * resolver could not classify keeps its source text, so it prints as authored.
3654
+ * @param {MediaNode} node the captured node
3655
+ * @param {Set<string>} resolving names currently being resolved
3656
+ * @returns {boolean | string} the constant, or the node's text
3657
+ */
3658
+ const foldMediaNode = (node, resolving) => {
3659
+ switch (node.kind) {
3660
+ case "text":
3661
+ return node.text;
3662
+ case "ref": {
3663
+ const value = resolveCustomMediaValue(node.use.name, resolving);
3664
+ if (node.use.invalid) return node.text;
3665
+ if (value.kind === "boolean") return value.value;
3666
+ if (value.kind === "condition") return value.text;
3667
+ if (value.kind === "type" && node.use.leading) return value.text;
3668
+ return node.text;
3669
+ }
3670
+ case "group": {
3671
+ const inner = foldMediaNode(node.operand, resolving);
3672
+ return typeof inner === "boolean" ? inner : `(${inner})`;
3673
+ }
3674
+ case "not": {
3675
+ const operand = foldMediaNode(node.operand, resolving);
3676
+ return typeof operand === "boolean" ? !operand : `not ${operand}`;
3677
+ }
3678
+ case "chain": {
3679
+ /** @type {string[]} */
3680
+ const kept = [];
3681
+ for (const term of node.terms) {
3682
+ const folded = foldMediaNode(term, resolving);
3683
+ if (folded === node.isOr) return node.isOr;
3684
+ if (typeof folded !== "boolean") kept.push(folded);
3236
3685
  }
3237
- } else if (tt === NodeType.Function) {
3238
- scanCustomMediaUses(A.children(t), false);
3686
+ if (kept.length === 0) return !node.isOr;
3687
+ if (kept.length === 1) return kept[0];
3688
+ return kept.join(node.isOr ? " or " : " and ");
3239
3689
  }
3240
- if (topLevel) leading = false;
3690
+ default: {
3691
+ if (node.rest === null) return node.text;
3692
+ const rest = foldMediaNode(node.rest, resolving);
3693
+ if (rest === false) return false;
3694
+ if (rest === true) return node.text;
3695
+ return `${node.text} and ${rest}`;
3696
+ }
3697
+ }
3698
+ };
3699
+
3700
+ /**
3701
+ * Fold a whole `@media` prelude. A query that is always false leaves the list;
3702
+ * one that is always true makes the list match everywhere (`all`), and a list
3703
+ * with nothing left matches nowhere (`not all`).
3704
+ * @param {MediaNode[]} queries the prelude's captured queries
3705
+ * @param {Set<string>} resolving names currently being resolved
3706
+ * @returns {string} the folded prelude
3707
+ */
3708
+ const foldMediaPrelude = (queries, resolving) => {
3709
+ /** @type {string[]} */
3710
+ const kept = [];
3711
+ for (const query of queries) {
3712
+ const folded = foldMediaNode(query, resolving);
3713
+ if (folded === true) return "all";
3714
+ if (folded !== false && folded !== "") kept.push(folded);
3241
3715
  }
3716
+ return kept.length === 0 ? "not all" : kept.join(", ");
3242
3717
  };
3243
3718
 
3244
3719
  /**
@@ -3304,55 +3779,71 @@ class CssParser extends Parser {
3304
3779
  };
3305
3780
 
3306
3781
  /**
3307
- * Rewrite the collected `@custom-media` / `@custom-selector` uses now that all (possibly later-defined) definitions are known; warns on invalid, unknown, or unsupported custom-media uses.
3782
+ * Rewrite the collected `@custom-media` / `@custom-selector` uses now that all (possibly later-defined) definitions are known; warns on invalid, unknown, or media-type-in-a-non-leading-position custom-media uses.
3308
3783
  */
3309
3784
  const resolveCustomMediaAndSelectors = () => {
3310
- if (customMediaUses) {
3311
- for (const use of customMediaUses) {
3312
- if (use.invalid) {
3313
- this._emitWarning(
3314
- state,
3315
- `Custom media query '${use.name}' must be used in a boolean context`,
3316
- locConverter,
3317
- use.start,
3318
- use.end
3319
- );
3320
- continue;
3321
- }
3322
- const def = customMediaDefs && customMediaDefs.get(use.name);
3323
- if (!def) {
3324
- this._emitWarning(
3325
- state,
3326
- `Unknown custom media query '${use.name}'`,
3327
- locConverter,
3328
- use.start,
3329
- use.end
3330
- );
3331
- continue;
3785
+ if (customMediaQueries) {
3786
+ /** @type {Set<string>} */
3787
+ const resolving = new Set();
3788
+ for (const query of customMediaQueries) {
3789
+ let hasBoolean = false;
3790
+ for (const use of query.uses) {
3791
+ if (use.invalid) {
3792
+ this._emitWarning(
3793
+ state,
3794
+ `Custom media query '${use.name}' must be used in a boolean context`,
3795
+ locConverter,
3796
+ use.start,
3797
+ use.end
3798
+ );
3799
+ continue;
3800
+ }
3801
+ const value = resolveCustomMediaValue(use.name, resolving);
3802
+ if (value.kind === "unsupported") {
3803
+ this._emitWarning(
3804
+ state,
3805
+ customMediaDefs && customMediaDefs.has(use.name)
3806
+ ? `Custom media query '${use.name}' has a value that cannot be resolved and was left as written`
3807
+ : `Unknown custom media query '${use.name}'`,
3808
+ locConverter,
3809
+ use.start,
3810
+ use.end
3811
+ );
3812
+ } else if (value.kind === "boolean") {
3813
+ hasBoolean = true;
3814
+ } else if (value.kind === "type" && !use.leading) {
3815
+ this._emitWarning(
3816
+ state,
3817
+ `Custom media query '${use.name}' resolves to a media type and can only be used at the start of a media query`,
3818
+ locConverter,
3819
+ use.start,
3820
+ use.end
3821
+ );
3822
+ }
3332
3823
  }
3333
- if (def.kind === "unsupported") {
3334
- this._emitWarning(
3335
- state,
3336
- `Custom media query '${use.name}' uses an unsupported value ('true'/'false' or a nested custom media) and was left unresolved`,
3337
- locConverter,
3338
- use.start,
3339
- use.end
3824
+ // `true` / `false` have no `<media-in-parens>` spelling, so a prelude
3825
+ // holding one is rewritten whole — every other use in it folds with it.
3826
+ if (hasBoolean) {
3827
+ module.addPresentationalDependency(
3828
+ new ConstDependency(foldMediaPrelude(query.queries, resolving), [
3829
+ query.start,
3830
+ query.end
3831
+ ])
3340
3832
  );
3341
3833
  continue;
3342
3834
  }
3343
- if (def.kind === "type" && !use.leading) {
3344
- this._emitWarning(
3345
- state,
3346
- `Custom media query '${use.name}' resolves to a media type and can only be used at the start of a media query`,
3347
- locConverter,
3348
- use.start,
3349
- use.end
3350
- );
3351
- continue;
3835
+ for (const use of query.uses) {
3836
+ if (use.invalid) continue;
3837
+ const value = resolveCustomMediaValue(use.name, resolving);
3838
+ if (
3839
+ value.kind === "condition" ||
3840
+ (value.kind === "type" && use.leading)
3841
+ ) {
3842
+ module.addPresentationalDependency(
3843
+ new ConstDependency(value.text, [use.start, use.end])
3844
+ );
3845
+ }
3352
3846
  }
3353
- module.addPresentationalDependency(
3354
- new ConstDependency(def.text, [use.start, use.end])
3355
- );
3356
3847
  }
3357
3848
  }
3358
3849
  if (customSelectorUses) {
@@ -3376,10 +3867,13 @@ class CssParser extends Parser {
3376
3867
  /** @type {Record<string, number>} */
3377
3868
  let parsedKeywords = Object.create(null);
3378
3869
  const isGridProperty = Boolean(declPropertyName.startsWith("grid"));
3379
- // Only `view-transition-*` legitimately takes a dashed-ident value; other known properties keep the historical plain-ident handling.
3870
+ // Only `view-transition-*` and `counter-*` legitimately take a dashed-ident value; other known properties keep the historical plain-ident handling.
3380
3871
  const isViewTransitionProperty = Boolean(
3381
3872
  declPropertyName.startsWith("view-transition")
3382
3873
  );
3874
+ const isCounterProperty = Boolean(
3875
+ declPropertyName.startsWith("counter-")
3876
+ );
3383
3877
  const isGridTemplate = isGridProperty
3384
3878
  ? Boolean(
3385
3879
  declPropertyName === "grid" ||
@@ -3389,7 +3883,7 @@ class CssParser extends Parser {
3389
3883
  )
3390
3884
  : false;
3391
3885
  const keywords =
3392
- /** @type {Record<string, number>} */
3886
+ /** @type {Map<string, number>} */
3393
3887
  (knownProperties.get(declPropertyName));
3394
3888
  let afterExclamation = false;
3395
3889
  /**
@@ -3406,6 +3900,8 @@ class CssParser extends Parser {
3406
3900
  isString ? start + 1 : start,
3407
3901
  isString ? end - 1 : end
3408
3902
  );
3903
+ // Only grid-line names are declaration sites here; property-value references (`animation: foo`) are usages.
3904
+ if (isGridProperty) recordDeclaration(name, "grid identifier", sl, sc);
3409
3905
  addCssExport(
3410
3906
  sl,
3411
3907
  sc,
@@ -3439,8 +3935,11 @@ class CssParser extends Parser {
3439
3935
  break;
3440
3936
  }
3441
3937
  const identifier = A.value(cv);
3442
- // `view-transition-name: --foo` — a dashed name scopes as a custom property under `dashedIdents`, not as a plain ident export.
3443
- if (isViewTransitionProperty && isDashedIdentifier(identifier)) {
3938
+ // `view-transition-name: --foo` / `counter-reset: --foo` — a dashed name scopes as a custom property under `dashedIdents`, not as a plain ident export.
3939
+ if (
3940
+ (isViewTransitionProperty || isCounterProperty) &&
3941
+ isDashedIdentifier(identifier)
3942
+ ) {
3444
3943
  if (this.options.dashedIdents) {
3445
3944
  emitDashedIdentExport(A.start(cv), A.end(cv));
3446
3945
  }
@@ -3452,10 +3951,8 @@ class CssParser extends Parser {
3452
3951
  typeof parsedKeywords[keyword] !== "undefined"
3453
3952
  ? parsedKeywords[keyword] + 1
3454
3953
  : 0;
3455
- if (
3456
- keywords[keyword] &&
3457
- parsedKeywords[keyword] < keywords[keyword]
3458
- ) {
3954
+ const limit = keywords.get(keyword);
3955
+ if (limit !== undefined && parsedKeywords[keyword] < limit) {
3459
3956
  break;
3460
3957
  }
3461
3958
  emit(A.start(cv), A.end(cv));
@@ -3473,11 +3970,15 @@ class CssParser extends Parser {
3473
3970
  declPropertyName === "grid-template" ||
3474
3971
  declPropertyName === "grid-template-areas"
3475
3972
  ) {
3476
- const areas = A.unescaped(cv);
3477
- const matches = matchAll(/\b\w+\b/g, areas);
3478
- for (const match of matches) {
3479
- const areaStart = A.start(cv) + 1 + match.index;
3480
- emit(areaStart, areaStart + match[0].length, false);
3973
+ // Raw offsets: a match index into the unescaped text would
3974
+ // slide the replaced range past every escape before it.
3975
+ const names = gridAreaNames(
3976
+ source,
3977
+ A.start(cv) + 1,
3978
+ A.end(cv) - 1
3979
+ );
3980
+ for (const [nameStart, nameEnd] of names) {
3981
+ emit(nameStart, nameEnd, false);
3481
3982
  }
3482
3983
  }
3483
3984
  break;
@@ -3497,7 +3998,12 @@ class CssParser extends Parser {
3497
3998
  break;
3498
3999
  }
3499
4000
  case NodeType.Function:
3500
- if (isGridTemplate) {
4001
+ // `repeat(…)` line names, and the `counter-reset: reversed(name)` wrapper — but not `local()`/`global()`, whose idents the value visitors own.
4002
+ if (
4003
+ isGridTemplate ||
4004
+ (isCounterProperty &&
4005
+ equalsLowerCase(A.unescapedName(cv), "reversed"))
4006
+ ) {
3501
4007
  walkExports(A.children(cv));
3502
4008
  }
3503
4009
  break;
@@ -3564,13 +4070,10 @@ class CssParser extends Parser {
3564
4070
  currentUrlRecovery = false;
3565
4071
  advanceCommentCursor(A.start(at));
3566
4072
  const savedAnchor = currentRule.hasLocalAnchor;
3567
- const savedLocalIdentifiers = currentRule.localIdentifiers;
3568
- // Only modules-mode walks mutate `localIdentifiers`; otherwise share
3569
- // the (empty) parent list instead of copying it per rule.
3570
- currentRule.localIdentifiers = isModules
3571
- ? [...savedLocalIdentifiers]
3572
- : savedLocalIdentifiers;
3573
- const name = `@${toLowerCaseIfNeeded(A.name(at))}`;
4073
+ // Rollback point: nested walks append to the shared list; the exit
4074
+ // truncates back to this length (no per-rule copy).
4075
+ const savedLocalIdentifierCount = currentRule.localIdentifiers.length;
4076
+ const name = `@${toLowerCaseIfNeeded(A.unescapedName(at))}`;
3574
4077
  switch (name) {
3575
4078
  case "@namespace": {
3576
4079
  this._emitWarning(
@@ -3614,7 +4117,7 @@ class CssParser extends Parser {
3614
4117
  break;
3615
4118
  }
3616
4119
  case "@media": {
3617
- if (mayHaveCustomMedia) scanCustomMediaUses(A.prelude(at), true);
4120
+ if (mayHaveCustomMedia) collectMediaQuery(at);
3618
4121
  break;
3619
4122
  }
3620
4123
  default: {
@@ -3626,21 +4129,33 @@ class CssParser extends Parser {
3626
4129
  this.options.animation &&
3627
4130
  OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name)
3628
4131
  ) {
3629
- processLocalAtRule(at, {
3630
- string: mode === "local",
3631
- identifier: mode === "local"
3632
- });
4132
+ processLocalAtRule(
4133
+ at,
4134
+ {
4135
+ string: mode === "local",
4136
+ identifier: mode === "local"
4137
+ },
4138
+ "@keyframes"
4139
+ );
3633
4140
  } else if (
3634
4141
  this.options.customIdents &&
3635
4142
  name === "@counter-style"
3636
4143
  ) {
3637
- processLocalAtRule(at, {
3638
- identifier: mode === "local"
3639
- });
4144
+ processLocalAtRule(
4145
+ at,
4146
+ {
4147
+ identifier: mode === "local"
4148
+ },
4149
+ "@counter-style"
4150
+ );
3640
4151
  } else if (this.options.container && name === "@container") {
3641
- processLocalAtRule(at, {
3642
- identifier: mode === "local" ? /^(none|and|or|not)$/ : false
3643
- });
4152
+ processLocalAtRule(
4153
+ at,
4154
+ {
4155
+ identifier: mode === "local" ? /^(none|and|or|not)$/ : false
4156
+ },
4157
+ "@container"
4158
+ );
3644
4159
  }
3645
4160
  }
3646
4161
  }
@@ -3648,7 +4163,7 @@ class CssParser extends Parser {
3648
4163
  // `@scope (.x) to (.y)` — walk the prelude as a selector list.
3649
4164
  if (
3650
4165
  isModules &&
3651
- equalsLowerCase(A.name(at), "scope") &&
4166
+ equalsLowerCase(A.unescapedName(at), "scope") &&
3652
4167
  A.prelude(at).length > 0
3653
4168
  ) {
3654
4169
  walkSelectorList(
@@ -3664,6 +4179,7 @@ class CssParser extends Parser {
3664
4179
  const isProcessedByLocalAtRule = isLocalHandledAtRule(name);
3665
4180
  currentStructural = at;
3666
4181
  currentAtRuleName = name;
4182
+ currentDeclCounterActive = false;
3667
4183
  dashed.active = false;
3668
4184
  // `@import` url() is the import target — only walk its prelude for url deps on malformed-import recovery.
3669
4185
  if (this.options.url && (name !== "@import" || currentUrlRecovery)) {
@@ -3704,12 +4220,13 @@ class CssParser extends Parser {
3704
4220
  );
3705
4221
  }
3706
4222
 
3707
- // pure.stack push for block-bearing at-rules.
4223
+ // pure.stack push for block-bearing at-rules (pure-off builds skip
4224
+ // the frame object — every consumer inside is a no-op then).
3708
4225
  const atDecls = A.declarations(at);
3709
4226
  const atChildRules = A.childRules(at);
3710
4227
  const atHasBlock = Boolean(atDecls || atChildRules);
3711
4228
  const atBlockStart = A.blockStart(at);
3712
- if (atHasBlock && atBlockStart !== -1) {
4229
+ if (pure.enabled && atHasBlock && atBlockStart !== -1) {
3713
4230
  const isAtRulePrelude = isPureBodyAtRule(name);
3714
4231
  if (isAtRulePrelude) pure.finalizeSelector();
3715
4232
  pure.enterBlock({
@@ -3725,7 +4242,7 @@ class CssParser extends Parser {
3725
4242
 
3726
4243
  atRuleStateStack.push({
3727
4244
  savedAnchor,
3728
- savedLocalIdentifiers,
4245
+ savedLocalIdentifierCount,
3729
4246
  name,
3730
4247
  hasBlock: atHasBlock,
3731
4248
  endsWithSemicolon: source.charCodeAt(A.end(at)) === CC_SEMICOLON,
@@ -3748,7 +4265,7 @@ class CssParser extends Parser {
3748
4265
  suppressNextRulePrelude = true;
3749
4266
  }
3750
4267
  currentRule.hasLocalAnchor = state.savedAnchor;
3751
- currentRule.localIdentifiers = state.savedLocalIdentifiers;
4268
+ currentRule.localIdentifiers.length = state.savedLocalIdentifierCount;
3752
4269
  if (path.parent === null) finishTopLevelRule(node, true);
3753
4270
  }
3754
4271
  },
@@ -3759,11 +4276,16 @@ class CssParser extends Parser {
3759
4276
  const rule = /** @type {QualifiedRule} */ (node);
3760
4277
  const topLevel = path.parent === null;
3761
4278
  advanceCommentCursor(A.start(rule));
4279
+ // One prelude materialization per rule, shared by the scans below
4280
+ // (null in plain mode, which never reads the array).
4281
+ const rulePrelude =
4282
+ isModules || mayHaveCustomSelectors ? A.prelude(rule) : null;
3762
4283
  // `:--name` custom-selector references (dashed idents never collide with the `:import`/`:export` ICSS preludes below).
3763
- if (mayHaveCustomSelectors) scanCustomSelectorUses(A.prelude(rule));
4284
+ if (mayHaveCustomSelectors && rulePrelude !== null) {
4285
+ scanCustomSelectorUses(rulePrelude);
4286
+ }
3764
4287
  // `:import(…) { … }` / `:export { … }` ICSS pseudo-rules are processed inline at top level; nested ones bail out.
3765
- if (isModules) {
3766
- const rulePrelude = A.prelude(rule);
4288
+ if (isModules && rulePrelude !== null) {
3767
4289
  const firstIdx = nextNonWhitespace(rulePrelude, 0);
3768
4290
  if (
3769
4291
  firstIdx + 1 < rulePrelude.length &&
@@ -3804,14 +4326,12 @@ class CssParser extends Parser {
3804
4326
  }
3805
4327
  }
3806
4328
  }
3807
- // Reset the anchor flag for this rule's body, inheriting (copying) the parent's identifier list so nested `composes:` sees both parent and child class names.
4329
+ // Reset the anchor flag for this rule's body; nested `composes:` sees
4330
+ // parent + own class names in the shared identifier list, rolled back
4331
+ // by length on exit (no per-rule copy).
3808
4332
  const savedAnchor = currentRule.hasLocalAnchor;
3809
- const savedLocalIdentifiers = currentRule.localIdentifiers;
4333
+ const savedLocalIdentifierCount = currentRule.localIdentifiers.length;
3810
4334
  currentRule.hasLocalAnchor = false;
3811
- // Only modules-mode walks mutate `localIdentifiers`; otherwise share the (empty) parent list instead of copying it per rule.
3812
- currentRule.localIdentifiers = isModules
3813
- ? [...savedLocalIdentifiers]
3814
- : savedLocalIdentifiers;
3815
4335
  // Composes-state reset between rules (saved / restored around this rule); composesFiles is swapped out and re-created lazily only if this rule composes.
3816
4336
  const savedPrevComposesFile = currentRule.composesPrevFile;
3817
4337
  const savedComposesFiles = currentRule.composesFiles;
@@ -3820,13 +4340,12 @@ class CssParser extends Parser {
3820
4340
  qualifiedRuleStateStack.push({
3821
4341
  bailed: false,
3822
4342
  savedAnchor,
3823
- savedLocalIdentifiers,
4343
+ savedLocalIdentifierCount,
3824
4344
  savedPrevComposesFile,
3825
4345
  savedComposesFiles
3826
4346
  });
3827
4347
  // Selectors are only CSS-Modules-relevant when `isModules` holds.
3828
- const rulePrelude = A.prelude(rule);
3829
- if (isModules) {
4348
+ if (isModules && rulePrelude !== null) {
3830
4349
  walkSelectorList(
3831
4350
  rulePrelude,
3832
4351
  /** @type {"local" | "global"} */ (
@@ -3836,22 +4355,24 @@ class CssParser extends Parser {
3836
4355
  }
3837
4356
  // A malformed declaration can leave orphan `url(...)` in the prelude — let the url visitor pick those up.
3838
4357
  currentStructural = rule;
4358
+ currentDeclCounterActive = false;
3839
4359
  dashed.active = false;
3840
4360
  dashed.emit = false;
3841
- if (this.options.url && rulePrelude.length > 0) {
3842
- lastTokenEndForComments = A.start(rulePrelude[0]);
4361
+ if (this.options.url && A.childCount(rule) > 0) {
4362
+ lastTokenEndForComments = A.start(A.childAt(rule, 0));
3843
4363
  }
3844
4364
  // Dashed-ident scoping for the deprecated `--foo: { … }` custom-property-set syntax (prelude starts with a dashed-ident).
3845
4365
  if (
3846
4366
  this.options.dashedIdents &&
3847
4367
  isModules &&
4368
+ rulePrelude !== null &&
3848
4369
  rulePrelude.length > 0
3849
4370
  ) {
3850
4371
  const first = rulePrelude[nextNonWhitespace(rulePrelude, 0)];
3851
4372
  if (
3852
4373
  first &&
3853
4374
  A.type(first) === NodeType.Ident &&
3854
- isDashedIdentifier(A.value(first))
4375
+ rangeIsDashedIdentifier(source, A.start(first), A.end(first))
3855
4376
  ) {
3856
4377
  const effectiveLocalMode = isEffectivelyLocal();
3857
4378
  if (effectiveLocalMode) {
@@ -3861,6 +4382,7 @@ class CssParser extends Parser {
3861
4382
  }
3862
4383
  }
3863
4384
  // Pure-mode: report an impure prelude (if leaf-ish) and push the inherited-context frame before walking the body.
4385
+ if (!pure.enabled) return;
3864
4386
  const ruleBlockStart = A.blockStart(rule);
3865
4387
  pure.enterBlock({
3866
4388
  isRulePrelude: true,
@@ -3879,7 +4401,7 @@ class CssParser extends Parser {
3879
4401
  if (!state || state.bailed) return;
3880
4402
  pure.exitBlock();
3881
4403
  currentRule.hasLocalAnchor = state.savedAnchor;
3882
- currentRule.localIdentifiers = state.savedLocalIdentifiers;
4404
+ currentRule.localIdentifiers.length = state.savedLocalIdentifierCount;
3883
4405
  currentRule.composesPrevFile = state.savedPrevComposesFile;
3884
4406
  currentRule.composesFiles = state.savedComposesFiles;
3885
4407
  if (path.parent === null) finishTopLevelRule(node, false);
@@ -3904,6 +4426,8 @@ class CssParser extends Parser {
3904
4426
  lastTokenEndForComments = colonPos + 1;
3905
4427
  currentDeclIsKnownProperty = false;
3906
4428
  currentDeclComposesSkip = false;
4429
+ currentDeclCounterActive = false;
4430
+ dashed.counterArgs = false;
3907
4431
  // Property-name analysis and value exports are CSS-Modules-only; a plain
3908
4432
  // stylesheet's declarations need no per-decl name slice / known-property lookup.
3909
4433
  if (!isModules) return;
@@ -3915,13 +4439,23 @@ class CssParser extends Parser {
3915
4439
  let declName;
3916
4440
  /** @type {string | undefined} */
3917
4441
  let declPropertyName;
3918
- if (source.charCodeAt(nameStart) === CC_HYPHEN_MINUS) {
3919
- // Vendor-prefixed or custom property — rare, take the string path.
3920
- declName = A.name(decl);
3921
- declPropertyName = toLowerCaseIfNeeded(
3922
- declName.replace(VENDOR_PREFIX, "")
3923
- );
3924
- currentDeclIsKnownProperty = knownProperties.has(declPropertyName);
4442
+ let declIsDashed = false;
4443
+ if (
4444
+ source.charCodeAt(nameStart) === CC_HYPHEN_MINUS ||
4445
+ source.charCodeAt(nameStart) === CC_REVERSE_SOLIDUS
4446
+ ) {
4447
+ if (rangeIsDashedIdentifier(source, nameStart, nameEnd)) {
4448
+ // Custom property: never vendor-prefixed / known / composes.
4449
+ declIsDashed = true;
4450
+ } else {
4451
+ // Vendor-prefixed, degenerate short, or an escaped name that is
4452
+ // not dashed after all — rare, string path.
4453
+ declName = A.unescapedName(decl);
4454
+ declPropertyName = toLowerCaseIfNeeded(
4455
+ declName.replace(VENDOR_PREFIX, "")
4456
+ );
4457
+ currentDeclIsKnownProperty = knownProperties.has(declPropertyName);
4458
+ }
3925
4459
  } else {
3926
4460
  declPropertyName = knownPropertyForRange(
3927
4461
  knownPropertyIndex,
@@ -3942,6 +4476,11 @@ class CssParser extends Parser {
3942
4476
  rangeEqualsLowerCase(source, nameStart, nameEnd, "compose-with"));
3943
4477
  if (currentDeclComposesSkip) emitComposesWithAnchor(decl);
3944
4478
  const skipForComposes = currentDeclComposesSkip;
4479
+ // `content: counter(name, style)` can name a scoped counter / `@counter-style` from any property, so this isn't driven by the known-property table.
4480
+ currentDeclCounterActive =
4481
+ Boolean(this.options.customIdents) &&
4482
+ effectiveLocalMode &&
4483
+ !skipForComposes;
3945
4484
  // Known-property value localization (`animation-name: foo` exports `foo`).
3946
4485
  if (effectiveLocalMode && currentDeclIsKnownProperty) {
3947
4486
  emitKnownPropertyExports(
@@ -3955,8 +4494,8 @@ class CssParser extends Parser {
3955
4494
  effectiveLocalMode &&
3956
4495
  !skipForComposes
3957
4496
  ) {
3958
- // Only the `-`-prefixed path can carry a dashed ident.
3959
- if (declName !== undefined && isDashedIdentifier(declName)) {
4497
+ // Only the `--`-prefixed path can carry a dashed ident.
4498
+ if (declIsDashed) {
3960
4499
  emitDashedIdentExport(nameStart, nameEnd);
3961
4500
  }
3962
4501
  dashed.active = true;
@@ -3972,7 +4511,7 @@ class CssParser extends Parser {
3972
4511
  declName = source.slice(nameStart, nameEnd);
3973
4512
  }
3974
4513
  if (
3975
- !(dashed.active && isDashedIdentifier(declName)) &&
4514
+ !(dashed.active && declIsDashed) &&
3976
4515
  icssDefinitions.has(declName)
3977
4516
  ) {
3978
4517
  emitICSSSymbol(declName, nameStart, nameEnd);
@@ -4052,12 +4591,34 @@ class CssParser extends Parser {
4052
4591
  enter: (path) => {
4053
4592
  const node = path.node;
4054
4593
  const fn = /** @type {FunctionNode} */ (node);
4055
- const fnameRaw = A.unescapedName(fn);
4056
- // `equalsLowerCase` keeps this visitor free of a per-function
4057
- // lowercased copy — functions are the densest value nodes.
4058
- const isLocalFn = equalsLowerCase(fnameRaw, "local");
4059
- const isGlobalFn = !isLocalFn && equalsLowerCase(fnameRaw, "global");
4060
- if (urlActive()) emitUrlFunction(fn, fnameRaw);
4594
+ const fnNameStart = A.nameStart(fn);
4595
+ const fnNameEnd = A.nameEnd(fn);
4596
+ const fnNameLength = fnNameEnd - fnNameStart;
4597
+ // Functions are the densest value nodes, so the name is matched by raw byte range; only a name carrying an escape (rare) pays the unescaped slice.
4598
+ /** @type {string | undefined} */
4599
+ let escapedName;
4600
+ let isLocalFn = false;
4601
+ let isGlobalFn = false;
4602
+ if (rangeHasEscape(input, fnNameStart, fnNameEnd)) {
4603
+ escapedName = A.unescapedName(fn);
4604
+ isLocalFn = equalsLowerCase(escapedName, "local");
4605
+ isGlobalFn = !isLocalFn && equalsLowerCase(escapedName, "global");
4606
+ } else if (fnNameLength === 5) {
4607
+ isLocalFn = rangeEqualsLowerCase(
4608
+ input,
4609
+ fnNameStart,
4610
+ fnNameEnd,
4611
+ "local"
4612
+ );
4613
+ } else if (fnNameLength === 6) {
4614
+ isGlobalFn = rangeEqualsLowerCase(
4615
+ input,
4616
+ fnNameStart,
4617
+ fnNameEnd,
4618
+ "global"
4619
+ );
4620
+ }
4621
+ if (urlActive()) emitUrlFunction(fn, escapedName);
4061
4622
  if (localGlobalActive() && (isLocalFn || isGlobalFn)) {
4062
4623
  processLocalOrGlobalFunction(fn, isLocalFn ? 1 : 2);
4063
4624
  }
@@ -4065,27 +4626,67 @@ class CssParser extends Parser {
4065
4626
  icssActive() &&
4066
4627
  !isLocalFn &&
4067
4628
  !isGlobalFn &&
4068
- !(dashed.active && isDashedIdentifier(fnameRaw)) &&
4069
- icssDefinitions.has(fnameRaw)
4629
+ icssDefinitions.size !== 0
4070
4630
  ) {
4071
- emitICSSSymbol(fnameRaw, A.nameStart(fn), A.nameEnd(fn));
4631
+ // Without an escape the raw name equals the unescaped one — only the `@value`-lookup path needs the string at all.
4632
+ const fname = escapedName === undefined ? A.name(fn) : escapedName;
4633
+ if (
4634
+ !(dashed.active && isDashedIdentifier(fname)) &&
4635
+ icssDefinitions.has(fname)
4636
+ ) {
4637
+ emitICSSSymbol(fname, fnNameStart, fnNameEnd);
4638
+ }
4639
+ }
4640
+ // `counter()` / `counters()` / `target-counter()` / `target-counters()`: rewrite the counter name and any trailing counter-style ident.
4641
+ let counterNameIndex = -1;
4642
+ if (currentDeclCounterActive && !isLocalFn && !isGlobalFn) {
4643
+ counterNameIndex = counterFunctionNameIndex(
4644
+ input,
4645
+ fnNameStart,
4646
+ fnNameEnd,
4647
+ escapedName
4648
+ );
4649
+ if (counterNameIndex !== -1) {
4650
+ walkCounterFunction(A.children(fn), counterNameIndex);
4651
+ }
4072
4652
  }
4073
4653
  // Dashed-ident scoping: handle this function, then set the child nesting level's state for the walk.
4074
4654
  dashed.push();
4655
+ dashed.counterArgs = counterNameIndex !== -1;
4075
4656
  if (dashed.active) {
4076
4657
  if (isLocalFn || isGlobalFn) {
4077
4658
  // `local()` / `global()` dashed args go through the ICSS path above, not here.
4078
4659
  dashed.active = false;
4079
4660
  } else if (
4080
- equalsLowerCase(fnameRaw, "var") ||
4081
- equalsLowerCase(fnameRaw, "style")
4661
+ escapedName === undefined
4662
+ ? (fnNameLength === 3 &&
4663
+ rangeEqualsLowerCase(
4664
+ input,
4665
+ fnNameStart,
4666
+ fnNameEnd,
4667
+ "var"
4668
+ )) ||
4669
+ (fnNameLength === 5 &&
4670
+ rangeEqualsLowerCase(
4671
+ input,
4672
+ fnNameStart,
4673
+ fnNameEnd,
4674
+ "style"
4675
+ ))
4676
+ : equalsLowerCase(escapedName, "var") ||
4677
+ equalsLowerCase(escapedName, "style")
4082
4678
  ) {
4083
4679
  // `var(--foo, …)` / `style(--foo, …)`: emit the first ident; the fallback doesn't self-emit.
4084
4680
  processDashedIdentInVarFunction(fn);
4085
4681
  dashed.emit = false;
4086
- } else if (dashed.emit && isDashedIdentifier(A.name(fn))) {
4087
- // Custom-function call `--my-func(args)` — the name is the exported dashed-ident.
4088
- emitDashedIdentExport(A.nameStart(fn), A.nameEnd(fn));
4682
+ } else if (
4683
+ dashed.emit &&
4684
+ fnNameLength >= 3 &&
4685
+ input.charCodeAt(fnNameStart) === CC_HYPHEN_MINUS &&
4686
+ input.charCodeAt(fnNameStart + 1) === CC_HYPHEN_MINUS
4687
+ ) {
4688
+ // Custom-function call `--my-func(args)` — the name is the exported dashed-ident (a literal `--` prefix, like the `A.name` string check it replaces).
4689
+ emitDashedIdentExport(fnNameStart, fnNameEnd);
4089
4690
  }
4090
4691
  }
4091
4692
  },
@@ -4099,31 +4700,50 @@ class CssParser extends Parser {
4099
4700
  // and ICSS context (any non-CSS-Modules stylesheet) a bare ident carries
4100
4701
  // no work, and idents are the most common node, so skipping the per-ident
4101
4702
  // `value` slice matters. With no `@value`/`:import` definitions the ICSS
4102
- // probe can never hit, so it doesn't warrant the slice either.
4703
+ // probe can never hit, so it doesn't warrant the slice either. The dashed
4704
+ // probe reads two char codes instead of slicing (escaped dashes don't
4705
+ // match, same as the string form — cf. `processDashedIdentInVarFunction`).
4103
4706
  const dashedActive = dashed.active;
4104
4707
  const icss = icssActive() && icssDefinitions.size !== 0;
4105
4708
  if (!dashedActive && !icss) return;
4106
- const identValue = A.value(node);
4107
- if (dashedActive && isDashedIdentifier(identValue)) {
4709
+ const identStart = A.start(node);
4710
+ const identEnd = A.end(node);
4711
+ if (
4712
+ dashedActive &&
4713
+ rangeIsDashedIdentifier(input, identStart, identEnd)
4714
+ ) {
4108
4715
  // Dashed idents are scoped here, never `@value` ICSS-rewritten.
4109
4716
  if (!dashed.emit) return;
4110
- // Resolve the `--foo from "./x.css"` / `--foo from global` import suffix via sibling lookahead.
4111
- const siblings = childrenOf(path.parent);
4112
- const i = siblings ? siblings.indexOf(node) : -1;
4113
- if (siblings && i !== -1) {
4114
- const j = nextNonWhitespace(siblings, i + 1);
4717
+ // Resolve the `--foo from "./x.css"` / `--foo from global` import suffix via sibling lookahead over the parent's child span (`path.index` avoids materializing the sibling list per dashed ident).
4718
+ const parent = path.parent;
4719
+ if (parent) {
4720
+ const count = A.childCount(parent);
4721
+ let j = path.index + 1;
4722
+ while (
4723
+ j < count &&
4724
+ A.type(A.childAt(parent, j)) === NodeType.Whitespace
4725
+ ) {
4726
+ j++;
4727
+ }
4728
+ const fromIdent = j < count ? A.childAt(parent, j) : undefined;
4115
4729
  if (
4116
- j < siblings.length &&
4117
- A.type(siblings[j]) === NodeType.Ident &&
4730
+ fromIdent &&
4731
+ A.type(fromIdent) === NodeType.Ident &&
4118
4732
  rangeEqualsLowerCase(
4119
4733
  input,
4120
- A.start(siblings[j]),
4121
- A.end(siblings[j]),
4734
+ A.start(fromIdent),
4735
+ A.end(fromIdent),
4122
4736
  "from"
4123
4737
  )
4124
4738
  ) {
4125
- const fromIdent = siblings[j];
4126
- const sourceNode = siblings[nextNonWhitespace(siblings, j + 1)];
4739
+ j++;
4740
+ while (
4741
+ j < count &&
4742
+ A.type(A.childAt(parent, j)) === NodeType.Whitespace
4743
+ ) {
4744
+ j++;
4745
+ }
4746
+ const sourceNode = j < count ? A.childAt(parent, j) : undefined;
4127
4747
  if (
4128
4748
  sourceNode &&
4129
4749
  A.type(sourceNode) === NodeType.Ident &&
@@ -4134,13 +4754,13 @@ class CssParser extends Parser {
4134
4754
  "global"
4135
4755
  )
4136
4756
  ) {
4137
- emitDashedIdentFromGlobal(A.end(node), A.end(sourceNode));
4757
+ emitDashedIdentFromGlobal(identEnd, A.end(sourceNode));
4138
4758
  return;
4139
4759
  }
4140
4760
  if (sourceNode && A.type(sourceNode) === NodeType.String) {
4141
4761
  emitDashedIdentImport(
4142
- A.start(node),
4143
- A.end(node),
4762
+ identStart,
4763
+ identEnd,
4144
4764
  A.start(fromIdent),
4145
4765
  A.end(sourceNode),
4146
4766
  input.slice(A.start(sourceNode) + 1, A.end(sourceNode) - 1)
@@ -4149,12 +4769,16 @@ class CssParser extends Parser {
4149
4769
  }
4150
4770
  }
4151
4771
  }
4152
- emitDashedIdentExport(A.start(node), A.end(node));
4772
+ emitDashedIdentExport(identStart, identEnd);
4153
4773
  return;
4154
4774
  }
4155
4775
  if (!icss) return;
4776
+ // Already rewritten as a counter / counter-style name — a second dep would overlap the same range.
4777
+ if (dashed.counterArgs) return;
4778
+ // Deferred: only the ICSS probe needs the ident's string value.
4779
+ const identValue = A.value(node);
4156
4780
  if (icssDefinitions.has(identValue)) {
4157
- emitICSSSymbol(identValue, A.start(node), A.end(node));
4781
+ emitICSSSymbol(identValue, identStart, identEnd);
4158
4782
  }
4159
4783
  }
4160
4784
  };
@@ -4164,19 +4788,22 @@ class CssParser extends Parser {
4164
4788
  // (per-source) is passed to `process`. One skip set: selector prelude +
4165
4789
  // unread value leaves (non-modules only; CSS Modules needs its selectors
4166
4790
  // and ICSS-captured values).
4167
- new SourceProcessor({
4168
- as: /** @type {"stylesheet" | "block-contents"} */ (this.options.as),
4169
- // Non-modules parses skip selector preludes, but `@custom-selector` expansion needs them, so keep them when the file may use one.
4170
- skip: isModules
4171
- ? undefined
4172
- : mayHaveCustomSelectors
4173
- ? SKIP_NON_MODULES_KEEP_SELECTORS
4174
- : SKIP_NON_MODULES
4175
- })
4791
+ new SourceProcessor()
4176
4792
  .use(/** @type {VisitorMap} */ (visitors))
4177
- .process(source, { locConverter });
4793
+ .process(source, {
4794
+ as: /** @type {"stylesheet" | "block-contents"} */ (this.options.as),
4795
+ // Non-modules parses skip selector preludes, but `@custom-selector` expansion needs them, so keep them when the file may use one.
4796
+ skip: isModules
4797
+ ? undefined
4798
+ : mayHaveCustomSelectors
4799
+ ? SKIP_NON_MODULES_KEEP_SELECTORS
4800
+ : SKIP_NON_MODULES,
4801
+ locConverter
4802
+ });
4178
4803
 
4179
- if (customMediaUses || customSelectorUses) resolveCustomMediaAndSelectors();
4804
+ if (customMediaQueries || customSelectorUses) {
4805
+ resolveCustomMediaAndSelectors();
4806
+ }
4180
4807
 
4181
4808
  /** @type {BuildInfo} */
4182
4809
  (module.buildInfo).strict = true;
@@ -4201,6 +4828,9 @@ class CssParser extends Parser {
4201
4828
  : "redirect-warn";
4202
4829
 
4203
4830
  if (cssExportEntries.length > 0) {
4831
+ if (isModules && declaredExports.size > 0) {
4832
+ this._resolveAmbiguousExports(state, declaredExports, cssExportEntries);
4833
+ }
4204
4834
  module.addDependency(new CssIcssExportDependency(cssExportEntries));
4205
4835
  }
4206
4836