webpack 5.107.2 → 5.108.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 (357) hide show
  1. package/hot/dev-server.js +2 -0
  2. package/lib/APIPlugin.js +8 -4
  3. package/lib/CacheFacade.js +7 -0
  4. package/lib/Chunk.js +4 -0
  5. package/lib/ChunkGraph.js +30 -9
  6. package/lib/CircularModulesPlugin.js +190 -0
  7. package/lib/CleanPlugin.js +2 -1
  8. package/lib/Compilation.js +396 -65
  9. package/lib/Compiler.js +25 -11
  10. package/lib/ConcatenationScope.js +10 -3
  11. package/lib/ContextExclusionPlugin.js +1 -0
  12. package/lib/ContextModule.js +92 -47
  13. package/lib/ContextReplacementPlugin.js +4 -0
  14. package/lib/DefinePlugin.js +89 -15
  15. package/lib/Dependency.js +111 -7
  16. package/lib/EntryOptionPlugin.js +39 -2
  17. package/lib/EntryPlugin.js +2 -0
  18. package/lib/ExportsInfo.js +314 -73
  19. package/lib/ExternalModule.js +82 -34
  20. package/lib/ExternalModuleFactoryPlugin.js +1 -0
  21. package/lib/ExternalsPlugin.js +1 -0
  22. package/lib/FileSystemInfo.js +173 -23
  23. package/lib/FlagAllModulesAsUsedPlugin.js +1 -5
  24. package/lib/FlagDependencyExportsPlugin.js +23 -0
  25. package/lib/FlagDependencyUsagePlugin.js +87 -5
  26. package/lib/FlagEntryExportAsUsedPlugin.js +2 -0
  27. package/lib/Generator.js +8 -0
  28. package/lib/HotModuleReplacementPlugin.js +65 -28
  29. package/lib/IgnorePlugin.js +1 -0
  30. package/lib/InitFragment.js +5 -0
  31. package/lib/JavascriptMetaInfoPlugin.js +7 -5
  32. package/lib/LazyBarrel.js +361 -0
  33. package/lib/LoaderTargetPlugin.js +1 -0
  34. package/lib/Module.js +21 -42
  35. package/lib/ModuleGraph.js +3 -2
  36. package/lib/ModuleProfile.js +27 -1
  37. package/lib/ModuleTemplate.js +2 -0
  38. package/lib/MultiCompiler.js +2 -0
  39. package/lib/MultiStats.js +1 -0
  40. package/lib/MultiWatching.js +2 -0
  41. package/lib/NodeStuffPlugin.js +22 -17
  42. package/lib/NormalModule.js +142 -80
  43. package/lib/NormalModuleReplacementPlugin.js +2 -0
  44. package/lib/PrefetchPlugin.js +2 -0
  45. package/lib/ProgressPlugin.js +8 -0
  46. package/lib/ProvidePlugin.js +1 -0
  47. package/lib/RawModule.js +20 -16
  48. package/lib/RecordIdsPlugin.js +1 -0
  49. package/lib/RuntimeGlobals.js +5 -0
  50. package/lib/RuntimePlugin.js +58 -26
  51. package/lib/RuntimeTemplate.js +278 -21
  52. package/lib/SelfModuleFactory.js +1 -0
  53. package/lib/SourceMapDevToolPlugin.js +3 -5
  54. package/lib/Stats.js +1 -0
  55. package/lib/Template.js +8 -2
  56. package/lib/TemplatedPathPlugin.js +473 -131
  57. package/lib/WarnCaseSensitiveModulesPlugin.js +1 -0
  58. package/lib/WarnDeprecatedOptionPlugin.js +4 -0
  59. package/lib/WarnNoModeSetPlugin.js +1 -0
  60. package/lib/WatchIgnorePlugin.js +1 -0
  61. package/lib/Watching.js +9 -0
  62. package/lib/WebpackOptionsApply.js +50 -5
  63. package/lib/asset/AssetBytesGenerator.js +11 -3
  64. package/lib/asset/AssetGenerator.js +47 -22
  65. package/lib/asset/AssetModule.js +47 -0
  66. package/lib/asset/AssetModulesPlugin.js +14 -14
  67. package/lib/asset/AssetParser.js +2 -2
  68. package/lib/asset/AssetSourceGenerator.js +8 -0
  69. package/lib/asset/RawDataUrlModule.js +12 -13
  70. package/lib/buildChunkGraph.js +64 -7
  71. package/lib/bun/BunTargetPlugin.js +48 -0
  72. package/lib/cache/AddBuildDependenciesPlugin.js +1 -0
  73. package/lib/cache/AddManagedPathsPlugin.js +3 -0
  74. package/lib/cache/IdleFileCachePlugin.js +4 -0
  75. package/lib/cache/MemoryWithGcCachePlugin.js +1 -0
  76. package/lib/cache/PackFileCacheStrategy.js +1 -0
  77. package/lib/cache/ResolverCachePlugin.js +2 -0
  78. package/lib/cache/mergeEtags.js +2 -0
  79. package/lib/cli.js +109 -19
  80. package/lib/config/browserslistTargetHandler.js +99 -0
  81. package/lib/config/defaults.js +147 -7
  82. package/lib/config/defineConfig.js +31 -0
  83. package/lib/config/normalization.js +5 -0
  84. package/lib/config/target.js +181 -2
  85. package/lib/container/ContainerEntryModule.js +15 -14
  86. package/lib/container/ContainerExposedDependency.js +2 -2
  87. package/lib/container/ContainerReferencePlugin.js +1 -1
  88. package/lib/container/FallbackDependency.js +5 -7
  89. package/lib/container/FallbackModule.js +10 -9
  90. package/lib/container/RemoteModule.js +20 -10
  91. package/lib/container/RemoteRuntimeModule.js +14 -12
  92. package/lib/css/CssGenerator.js +353 -327
  93. package/lib/css/CssInjectStyleRuntimeModule.js +36 -8
  94. package/lib/css/CssLoadingRuntimeModule.js +118 -47
  95. package/lib/css/CssModule.js +52 -23
  96. package/lib/css/CssModulesPlugin.js +107 -124
  97. package/lib/css/CssParser.js +2759 -2205
  98. package/lib/css/syntax.js +2859 -0
  99. package/lib/debug/ProfilingPlugin.js +2 -0
  100. package/lib/deno/DenoTargetPlugin.js +47 -0
  101. package/lib/dependencies/AMDDefineDependency.js +22 -17
  102. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +1 -0
  103. package/lib/dependencies/AMDRequireArrayDependency.js +7 -11
  104. package/lib/dependencies/AMDRequireContextDependency.js +7 -11
  105. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +1 -0
  106. package/lib/dependencies/AMDRequireDependency.js +24 -20
  107. package/lib/dependencies/CachedConstDependency.js +3 -0
  108. package/lib/dependencies/CommonJsDependencyHelpers.js +1 -0
  109. package/lib/dependencies/CommonJsExportRequireDependency.js +79 -31
  110. package/lib/dependencies/CommonJsExportsDependency.js +21 -16
  111. package/lib/dependencies/CommonJsExportsParserPlugin.js +230 -7
  112. package/lib/dependencies/CommonJsFullRequireDependency.js +27 -19
  113. package/lib/dependencies/CommonJsImportsParserPlugin.js +51 -16
  114. package/lib/dependencies/CommonJsPlugin.js +1 -1
  115. package/lib/dependencies/CommonJsRequireContextDependency.js +10 -13
  116. package/lib/dependencies/CommonJsRequireDependency.js +42 -11
  117. package/lib/dependencies/CommonJsSelfReferenceDependency.js +22 -14
  118. package/lib/dependencies/ConstDependency.js +16 -11
  119. package/lib/dependencies/ContextDependency.js +4 -0
  120. package/lib/dependencies/ContextDependencyTemplateAsId.js +1 -1
  121. package/lib/dependencies/ContextElementDependency.js +23 -14
  122. package/lib/dependencies/CreateRequireParserPlugin.js +1 -0
  123. package/lib/dependencies/CreateScriptUrlDependency.js +5 -7
  124. package/lib/dependencies/CriticalDependencyWarning.js +1 -0
  125. package/lib/dependencies/CssIcssExportDependency.js +512 -574
  126. package/lib/dependencies/CssIcssImportDependency.js +9 -9
  127. package/lib/dependencies/CssIcssSymbolDependency.js +22 -15
  128. package/lib/dependencies/CssImportDependency.js +25 -1
  129. package/lib/dependencies/CssUrlDependency.js +23 -14
  130. package/lib/dependencies/DllEntryDependency.js +9 -13
  131. package/lib/dependencies/ExportBindingInitFragment.js +164 -0
  132. package/lib/dependencies/ExportsInfoDependency.js +12 -12
  133. package/lib/dependencies/ExternalModuleDependency.js +8 -5
  134. package/lib/dependencies/ExternalModuleInitFragment.js +7 -5
  135. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +13 -10
  136. package/lib/dependencies/HarmonyAcceptDependency.js +11 -11
  137. package/lib/dependencies/HarmonyAcceptImportDependency.js +1 -0
  138. package/lib/dependencies/HarmonyDetectionParserPlugin.js +47 -22
  139. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +28 -2
  140. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +46 -22
  141. package/lib/dependencies/HarmonyExportExpressionDependency.js +107 -30
  142. package/lib/dependencies/HarmonyExportHeaderDependency.js +7 -9
  143. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +109 -31
  144. package/lib/dependencies/HarmonyExportInitFragment.js +18 -15
  145. package/lib/dependencies/HarmonyExportSpecifierDependency.js +83 -18
  146. package/lib/dependencies/HarmonyExports.js +4 -1
  147. package/lib/dependencies/HarmonyImportDependency.js +24 -0
  148. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +110 -74
  149. package/lib/dependencies/HarmonyImportGuard.js +254 -0
  150. package/lib/dependencies/HarmonyImportSideEffectDependency.js +18 -1
  151. package/lib/dependencies/HarmonyImportSpecifierDependency.js +118 -14
  152. package/lib/dependencies/HarmonyLinkingError.js +1 -0
  153. package/lib/dependencies/HarmonyModulesPlugin.js +1 -0
  154. package/lib/dependencies/{HtmlScriptSrcDependency.js → HtmlEntryDependency.js} +261 -109
  155. package/lib/dependencies/HtmlInlineHtmlDependency.js +107 -0
  156. package/lib/dependencies/HtmlInlineScriptDependency.js +17 -13
  157. package/lib/dependencies/HtmlInlineStyleDependency.js +45 -11
  158. package/lib/dependencies/ImportContextDependency.js +5 -9
  159. package/lib/dependencies/ImportDependency.js +44 -2
  160. package/lib/dependencies/ImportMetaHotAcceptDependency.js +1 -0
  161. package/lib/dependencies/ImportMetaHotDeclineDependency.js +1 -0
  162. package/lib/dependencies/ImportMetaPlugin.js +74 -30
  163. package/lib/dependencies/ImportParserPlugin.js +19 -0
  164. package/lib/dependencies/ImportWeakDependency.js +1 -0
  165. package/lib/dependencies/JsonExportsDependency.js +9 -9
  166. package/lib/dependencies/LoaderImportDependency.js +1 -0
  167. package/lib/dependencies/LocalModule.js +11 -12
  168. package/lib/dependencies/LocalModuleDependency.js +11 -13
  169. package/lib/dependencies/ModuleDecoratorDependency.js +9 -9
  170. package/lib/dependencies/ModuleDependency.js +7 -0
  171. package/lib/dependencies/ModuleHotAcceptDependency.js +1 -0
  172. package/lib/dependencies/ModuleHotDeclineDependency.js +1 -0
  173. package/lib/dependencies/ModuleInitFragmentDependency.js +15 -11
  174. package/lib/dependencies/ProvidedDependency.js +31 -27
  175. package/lib/dependencies/PureExpressionDependency.js +7 -9
  176. package/lib/dependencies/RequireEnsureDependency.js +12 -13
  177. package/lib/dependencies/RequireHeaderDependency.js +3 -4
  178. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +3 -0
  179. package/lib/dependencies/RequireResolveContextDependency.js +7 -11
  180. package/lib/dependencies/RequireResolveDependency.js +1 -0
  181. package/lib/dependencies/RequireResolveHeaderDependency.js +3 -5
  182. package/lib/dependencies/RuntimeRequirementsDependency.js +6 -7
  183. package/lib/dependencies/StaticExportsDependency.js +10 -10
  184. package/lib/dependencies/SystemPlugin.js +2 -0
  185. package/lib/dependencies/URLContextDependency.js +5 -7
  186. package/lib/dependencies/URLDependency.js +14 -16
  187. package/lib/dependencies/UnsupportedDependency.js +8 -13
  188. package/lib/dependencies/WebAssemblyExportImportedDependency.js +11 -16
  189. package/lib/dependencies/WebAssemblyImportDependency.js +14 -16
  190. package/lib/dependencies/WebpackIsIncludedDependency.js +1 -0
  191. package/lib/dependencies/WorkerDependency.js +19 -8
  192. package/lib/dependencies/WorkerPlugin.js +22 -6
  193. package/lib/dependencies/processExportInfo.js +13 -11
  194. package/lib/dll/DelegatedModule.js +29 -15
  195. package/lib/dll/DelegatedModuleFactoryPlugin.js +1 -0
  196. package/lib/dll/DelegatedPlugin.js +1 -0
  197. package/lib/dll/DllEntryPlugin.js +3 -0
  198. package/lib/dll/DllModule.js +3 -2
  199. package/lib/dll/DllPlugin.js +16 -0
  200. package/lib/dll/DllReferencePlugin.js +3 -0
  201. package/lib/dll/LibManifestPlugin.js +1 -0
  202. package/lib/electron/ElectronTargetPlugin.js +22 -4
  203. package/lib/errors/ConcurrentCompilationError.js +1 -0
  204. package/lib/errors/HookWebpackError.js +11 -13
  205. package/lib/errors/IgnoreErrorModuleFactory.js +1 -0
  206. package/lib/errors/InvalidDependenciesModuleWarning.js +2 -0
  207. package/lib/errors/JSONParseError.js +9 -8
  208. package/lib/errors/ModuleBuildError.js +16 -13
  209. package/lib/errors/ModuleDependencyError.js +8 -1
  210. package/lib/errors/ModuleDependencyWarning.js +8 -1
  211. package/lib/errors/ModuleError.js +5 -11
  212. package/lib/errors/ModuleHashingError.js +4 -0
  213. package/lib/errors/ModuleNotFoundError.js +3 -0
  214. package/lib/errors/ModuleParseError.js +5 -11
  215. package/lib/errors/ModuleRestoreError.js +2 -0
  216. package/lib/errors/ModuleStoreError.js +3 -0
  217. package/lib/errors/ModuleWarning.js +7 -11
  218. package/lib/errors/NodeStuffInWebError.js +1 -0
  219. package/lib/errors/NonErrorEmittedError.js +2 -0
  220. package/lib/errors/UnhandledSchemeError.js +1 -0
  221. package/lib/esm/ModuleChunkLoadingPlugin.js +0 -1
  222. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +28 -23
  223. package/lib/hmr/HotModuleReplacement.runtime.js +175 -30
  224. package/lib/hmr/HotModuleReplacementRuntimeModule.js +7 -0
  225. package/lib/hmr/JavascriptHotModuleReplacement.runtime.js +173 -26
  226. package/lib/hmr/LazyCompilationPlugin.js +30 -6
  227. package/lib/html/HtmlGenerator.js +217 -23
  228. package/lib/html/HtmlModule.js +40 -0
  229. package/lib/html/HtmlModulesPlugin.js +219 -55
  230. package/lib/html/HtmlParser.js +1108 -1099
  231. package/lib/html/{walkHtmlTokens.js → syntax.js} +4701 -212
  232. package/lib/ids/IdHelpers.js +4 -2
  233. package/lib/index.js +19 -0
  234. package/lib/javascript/BasicEvaluatedExpression.js +1 -0
  235. package/lib/javascript/EnableChunkLoadingPlugin.js +1 -0
  236. package/lib/javascript/JavascriptGenerator.js +6 -2
  237. package/lib/javascript/JavascriptModule.js +54 -0
  238. package/lib/javascript/JavascriptModulesPlugin.js +257 -102
  239. package/lib/javascript/JavascriptParser.js +285 -158
  240. package/lib/json/JsonModule.js +40 -0
  241. package/lib/json/JsonModulesPlugin.js +7 -0
  242. package/lib/json/JsonParser.js +4 -2
  243. package/lib/library/AssignLibraryPlugin.js +5 -3
  244. package/lib/library/ExportPropertyLibraryPlugin.js +1 -0
  245. package/lib/library/FalseIIFEUmdWarning.js +1 -0
  246. package/lib/library/ModuleLibraryPlugin.js +24 -5
  247. package/lib/library/SystemLibraryPlugin.js +3 -3
  248. package/lib/node/NodeTargetPlugin.js +1 -0
  249. package/lib/node/NodeWatchFileSystem.js +1 -0
  250. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +10 -9
  251. package/lib/node/RequireChunkLoadingRuntimeModule.js +18 -16
  252. package/lib/optimize/ConcatenatedModule.js +395 -77
  253. package/lib/optimize/ConstExportsPlugin.js +211 -0
  254. package/lib/optimize/InlineExports.js +178 -0
  255. package/lib/optimize/InnerGraph.js +372 -275
  256. package/lib/optimize/InnerGraphPlugin.js +196 -99
  257. package/lib/optimize/ModuleConcatenationPlugin.js +56 -25
  258. package/lib/optimize/RealContentHashPlugin.js +14 -1
  259. package/lib/optimize/SideEffectsFlagPlugin.js +234 -64
  260. package/lib/runtime/AsyncModuleRuntimeModule.js +32 -30
  261. package/lib/runtime/AutoPublicPathRuntimeModule.js +11 -5
  262. package/lib/runtime/CompatGetDefaultExportRuntimeModule.js +1 -1
  263. package/lib/runtime/CreateFakeNamespaceObjectRuntimeModule.js +6 -4
  264. package/lib/runtime/DefinePropertyGettersRuntimeModule.js +33 -4
  265. package/lib/runtime/GetChunkFilenameRuntimeModule.js +82 -3
  266. package/lib/runtime/GetTrustedTypesPolicyRuntimeModule.js +1 -1
  267. package/lib/runtime/HasOwnPropertyRuntimeModule.js +1 -1
  268. package/lib/runtime/LoadScriptRuntimeModule.js +18 -13
  269. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +39 -26
  270. package/lib/runtime/MakeNamespaceObjectRuntimeModule.js +1 -1
  271. package/lib/runtime/OnChunksLoadedRuntimeModule.js +4 -4
  272. package/lib/runtime/RelativeUrlRuntimeModule.js +3 -2
  273. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +1 -1
  274. package/lib/runtime/StartupEntrypointRuntimeModule.js +4 -3
  275. package/lib/runtime/WorkerRuntimeModule.js +33 -0
  276. package/lib/schemes/HttpUriPlugin.js +3 -2
  277. package/lib/serialization/AggregateErrorSerializer.js +7 -6
  278. package/lib/serialization/ArraySerializer.js +4 -8
  279. package/lib/serialization/BinaryMiddleware.js +538 -468
  280. package/lib/serialization/DateObjectSerializer.js +2 -2
  281. package/lib/serialization/ErrorObjectSerializer.js +7 -6
  282. package/lib/serialization/MapObjectSerializer.js +5 -9
  283. package/lib/serialization/NullPrototypeObjectSerializer.js +7 -9
  284. package/lib/serialization/ObjectMiddleware.js +50 -13
  285. package/lib/serialization/PlainObjectSerializer.js +9 -8
  286. package/lib/serialization/RegExpObjectSerializer.js +2 -2
  287. package/lib/serialization/Serializer.js +1 -0
  288. package/lib/serialization/SetObjectSerializer.js +4 -8
  289. package/lib/sharing/ConsumeSharedModule.js +6 -7
  290. package/lib/sharing/ConsumeSharedPlugin.js +1 -0
  291. package/lib/sharing/ConsumeSharedRuntimeModule.js +46 -41
  292. package/lib/sharing/ProvideSharedDependency.js +30 -15
  293. package/lib/sharing/ProvideSharedModule.js +30 -11
  294. package/lib/sharing/ProvideSharedPlugin.js +4 -2
  295. package/lib/sharing/SharePlugin.js +3 -0
  296. package/lib/sharing/ShareRuntimeModule.js +18 -16
  297. package/lib/sharing/resolveMatchedConfigs.js +2 -1
  298. package/lib/sharing/utils.js +1 -1
  299. package/lib/stats/DefaultStatsFactoryPlugin.js +4 -6
  300. package/lib/stats/DefaultStatsPrinterPlugin.js +14 -14
  301. package/lib/stats/StatsFactory.js +11 -9
  302. package/lib/stats/StatsPrinter.js +9 -2
  303. package/lib/url/URLParserPlugin.js +5 -2
  304. package/lib/util/AsyncQueue.js +10 -0
  305. package/lib/util/LazyBucketSortedSet.js +5 -0
  306. package/lib/util/LazySet.js +6 -4
  307. package/lib/util/LocConverter.js +11 -1
  308. package/lib/util/Semaphore.js +1 -0
  309. package/lib/util/SourceProcessor.js +106 -0
  310. package/lib/util/TupleSet.js +1 -0
  311. package/lib/util/WeakTupleMap.js +27 -1
  312. package/lib/util/chainedImports.js +3 -3
  313. package/lib/util/comparators.js +2 -2
  314. package/lib/util/concatenate.js +31 -7
  315. package/lib/util/createHash.js +0 -1
  316. package/lib/util/deterministicGrouping.js +19 -12
  317. package/lib/util/extractSourceMap.js +1 -1
  318. package/lib/util/findGraphRoots.js +2 -0
  319. package/lib/util/fs.js +10 -5
  320. package/lib/util/hash/DebugHash.js +1 -0
  321. package/lib/util/hash/hash-digest.js +24 -15
  322. package/lib/util/identifier.js +7 -0
  323. package/lib/util/internalSerializables.js +11 -2
  324. package/lib/util/magicComment.js +42 -0
  325. package/lib/util/makeSerializable.js +1 -0
  326. package/lib/util/nonNumericOnlyHash.js +30 -1
  327. package/lib/util/property.js +7 -1
  328. package/lib/util/publicPathPlaceholder.js +64 -0
  329. package/lib/util/registerExternalSerializer.js +4 -4
  330. package/lib/wasm-async/AsyncWasmModule.js +77 -0
  331. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +1 -1
  332. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -96
  333. package/lib/wasm-async/AsyncWebAssemblyParser.js +1 -1
  334. package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +1 -1
  335. package/lib/wasm-sync/SyncWasmModule.js +39 -0
  336. package/lib/wasm-sync/UnsupportedWebAssemblyFeatureError.js +1 -0
  337. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +6 -3
  338. package/lib/wasm-sync/WasmFinalizeExportsPlugin.js +2 -1
  339. package/lib/wasm-sync/WebAssemblyGenerator.js +1 -0
  340. package/lib/wasm-sync/WebAssemblyInInitialChunkError.js +2 -0
  341. package/lib/wasm-sync/WebAssemblyModulesPlugin.js +10 -0
  342. package/lib/wasm-sync/WebAssemblyParser.js +7 -3
  343. package/lib/web/JsonpChunkLoadingRuntimeModule.js +45 -39
  344. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +11 -10
  345. package/package.json +35 -27
  346. package/schemas/WebpackOptions.check.js +1 -1
  347. package/schemas/WebpackOptions.json +212 -3
  348. package/schemas/plugins/HtmlGeneratorOptions.check.js +1 -1
  349. package/schemas/plugins/HtmlParserOptions.check.d.ts +7 -0
  350. package/schemas/plugins/HtmlParserOptions.check.js +6 -0
  351. package/schemas/plugins/HtmlParserOptions.json +3 -0
  352. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  353. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  354. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  355. package/types.d.ts +1456 -290
  356. package/lib/css/walkCssTokens.js +0 -2020
  357. package/lib/util/AppendOnlyStackedSet.js +0 -93
@@ -0,0 +1,2859 @@
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Tobias Koppers @sokra
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const LocConverter = require("../util/LocConverter");
9
+ const GenericSourceProcessor = require("../util/SourceProcessor");
10
+ const { makeCacheable } = require("../util/identifier");
11
+
12
+ // spec: https://drafts.csswg.org/css-syntax/
13
+
14
+ /**
15
+ * @typedef {object} CssWhitespaceToken
16
+ * @property {number} type
17
+ * @property {number} start byte offset of the first whitespace code point
18
+ * @property {number} end byte offset just past the last whitespace code point
19
+ */
20
+ /**
21
+ * @typedef {object} CssCommentToken
22
+ * @property {number} type
23
+ * @property {number} start byte offset of the opening `/`
24
+ * @property {number} end byte offset just past the closing `/`
25
+ */
26
+ /**
27
+ * @typedef {object} CssStringToken
28
+ * @property {number} type
29
+ * @property {number} start byte offset of the opening quote
30
+ * @property {number} end byte offset just past the closing quote (or EOF for unterminated strings)
31
+ */
32
+ /**
33
+ * @typedef {object} CssBadStringToken
34
+ * @property {number} type
35
+ * @property {number} start byte offset of the opening quote
36
+ * @property {number} end byte offset where parsing gave up (typically the newline that broke the string)
37
+ */
38
+ /**
39
+ * @typedef {object} CssLeftCurlyBracketToken
40
+ * @property {number} type
41
+ * @property {number} start byte offset of `{`
42
+ * @property {number} end `start + 1`
43
+ */
44
+ /**
45
+ * @typedef {object} CssRightCurlyBracketToken
46
+ * @property {number} type
47
+ * @property {number} start byte offset of `}`
48
+ * @property {number} end `start + 1`
49
+ */
50
+ /**
51
+ * @typedef {object} CssLeftSquareBracketToken
52
+ * @property {number} type
53
+ * @property {number} start byte offset of `[`
54
+ * @property {number} end `start + 1`
55
+ */
56
+ /**
57
+ * @typedef {object} CssRightSquareBracketToken
58
+ * @property {number} type
59
+ * @property {number} start byte offset of `]`
60
+ * @property {number} end `start + 1`
61
+ */
62
+ /**
63
+ * @typedef {object} CssLeftParenthesisToken
64
+ * @property {number} type
65
+ * @property {number} start byte offset of `(`
66
+ * @property {number} end `start + 1`
67
+ */
68
+ /**
69
+ * @typedef {object} CssRightParenthesisToken
70
+ * @property {number} type
71
+ * @property {number} start byte offset of `)`
72
+ * @property {number} end `start + 1`
73
+ */
74
+ /**
75
+ * @typedef {object} CssFunctionToken
76
+ * @property {number} type
77
+ * @property {number} start byte offset of the function name's first code point
78
+ * @property {number} end byte offset just past the `(` that closes the function token
79
+ */
80
+ /**
81
+ * @typedef {object} CssUrlToken
82
+ * @property {number} type
83
+ * @property {number} start byte offset of the `url(` keyword (i.e. the `u`)
84
+ * @property {number} end byte offset just past the closing `)` (or EOF)
85
+ * @property {number} contentStart byte offset of the first code point of the unquoted URL content (post leading whitespace)
86
+ * @property {number} contentEnd byte offset just past the last code point of the unquoted URL content (pre trailing whitespace / `)` / EOF)
87
+ */
88
+ /**
89
+ * @typedef {object} CssBadUrlToken
90
+ * @property {number} type
91
+ * @property {number} start byte offset of the `url(` keyword
92
+ * @property {number} end byte offset where parsing gave up (past the recovery `)` or EOF)
93
+ */
94
+ /**
95
+ * @typedef {object} CssColonToken
96
+ * @property {number} type
97
+ * @property {number} start byte offset of `:`
98
+ * @property {number} end `start + 1`
99
+ */
100
+ /**
101
+ * @typedef {object} CssAtKeywordToken
102
+ * @property {number} type
103
+ * @property {number} start byte offset of `@`
104
+ * @property {number} end byte offset just past the last ident-sequence code point
105
+ */
106
+ /**
107
+ * @typedef {object} CssDelimToken
108
+ * @property {number} type
109
+ * @property {number} start byte offset of the delim code point
110
+ * @property {number} end `start + 1`
111
+ */
112
+ /**
113
+ * @typedef {object} CssIdentToken
114
+ * @property {number} type
115
+ * @property {number} start byte offset of the first ident code point
116
+ * @property {number} end byte offset just past the last ident-sequence code point
117
+ */
118
+ /**
119
+ * @typedef {object} CssPercentageToken
120
+ * @property {number} type
121
+ * @property {number} start byte offset of the first numeric code point
122
+ * @property {number} end byte offset just past the `%`
123
+ */
124
+ /**
125
+ * @typedef {object} CssNumberToken
126
+ * @property {number} type
127
+ * @property {number} start byte offset of the first numeric code point
128
+ * @property {number} end byte offset just past the last numeric code point
129
+ */
130
+ /**
131
+ * @typedef {object} CssDimensionToken
132
+ * @property {number} type
133
+ * @property {number} start byte offset of the first numeric code point
134
+ * @property {number} end byte offset just past the last unit ident code point
135
+ * @property {number} unitStart byte offset of the first unit-ident code point (== end of the numeric run)
136
+ */
137
+ /**
138
+ * @typedef {object} CssHashToken
139
+ * @property {number} type
140
+ * @property {number} start byte offset of `#`
141
+ * @property {number} end byte offset just past the last ident-sequence code point
142
+ * @property {boolean} isId true when the hash starts an ident sequence (`#foo`), false for non-ident hashes (`#1abc`)
143
+ */
144
+ /**
145
+ * @typedef {object} CssSemicolonToken
146
+ * @property {number} type
147
+ * @property {number} start byte offset of `;`
148
+ * @property {number} end `start + 1`
149
+ */
150
+ /**
151
+ * @typedef {object} CssCommaToken
152
+ * @property {number} type
153
+ * @property {number} start byte offset of `,`
154
+ * @property {number} end `start + 1`
155
+ */
156
+ /**
157
+ * @typedef {object} CssCdoToken
158
+ * @property {number} type
159
+ * @property {number} start byte offset of `<`
160
+ * @property {number} end byte offset just past `<!--`
161
+ */
162
+ /**
163
+ * @typedef {object} CssCdcToken
164
+ * @property {number} type
165
+ * @property {number} start byte offset of `-`
166
+ * @property {number} end byte offset just past `-->`
167
+ */
168
+ /**
169
+ * @typedef {CssWhitespaceToken | CssCommentToken | CssStringToken | CssBadStringToken | CssLeftCurlyBracketToken | CssRightCurlyBracketToken | CssLeftSquareBracketToken | CssRightSquareBracketToken | CssLeftParenthesisToken | CssRightParenthesisToken | CssFunctionToken | CssUrlToken | CssBadUrlToken | CssColonToken | CssAtKeywordToken | CssDelimToken | CssIdentToken | CssPercentageToken | CssNumberToken | CssDimensionToken | CssHashToken | CssSemicolonToken | CssCommaToken | CssCdoToken | CssCdcToken} CssToken
170
+ */
171
+
172
+ const CC_LINE_FEED = "\n".charCodeAt(0);
173
+ const CC_CARRIAGE_RETURN = "\r".charCodeAt(0);
174
+ const CC_FORM_FEED = "\f".charCodeAt(0);
175
+
176
+ const CC_TAB = "\t".charCodeAt(0);
177
+ const CC_SPACE = " ".charCodeAt(0);
178
+
179
+ const CC_SOLIDUS = "/".charCodeAt(0);
180
+ const CC_REVERSE_SOLIDUS = "\\".charCodeAt(0);
181
+ const CC_ASTERISK = "*".charCodeAt(0);
182
+
183
+ const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
184
+ const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
185
+ const CC_LEFT_CURLY = "{".charCodeAt(0);
186
+ const CC_RIGHT_CURLY = "}".charCodeAt(0);
187
+ const CC_LEFT_SQUARE = "[".charCodeAt(0);
188
+ const CC_RIGHT_SQUARE = "]".charCodeAt(0);
189
+
190
+ const CC_QUOTATION_MARK = '"'.charCodeAt(0);
191
+ const CC_APOSTROPHE = "'".charCodeAt(0);
192
+
193
+ const CC_FULL_STOP = ".".charCodeAt(0);
194
+ const CC_COLON = ":".charCodeAt(0);
195
+ const CC_SEMICOLON = ";".charCodeAt(0);
196
+ const CC_COMMA = ",".charCodeAt(0);
197
+ const CC_PERCENTAGE = "%".charCodeAt(0);
198
+ const CC_AT_SIGN = "@".charCodeAt(0);
199
+
200
+ const CC_LOW_LINE = "_".charCodeAt(0);
201
+ const CC_LOWER_A = "a".charCodeAt(0);
202
+ const CC_LOWER_F = "f".charCodeAt(0);
203
+ const CC_LOWER_E = "e".charCodeAt(0);
204
+ const CC_LOWER_U = "u".charCodeAt(0);
205
+ const CC_LOWER_R = "r".charCodeAt(0);
206
+ const CC_LOWER_L = "l".charCodeAt(0);
207
+ const CC_LOWER_Z = "z".charCodeAt(0);
208
+ const CC_EXCLAMATION = "!".charCodeAt(0);
209
+ const CC_UPPER_A = "A".charCodeAt(0);
210
+ const CC_UPPER_F = "F".charCodeAt(0);
211
+ const CC_UPPER_E = "E".charCodeAt(0);
212
+ const CC_UPPER_U = "U".charCodeAt(0);
213
+ const CC_UPPER_Z = "Z".charCodeAt(0);
214
+ const CC_0 = "0".charCodeAt(0);
215
+ const CC_9 = "9".charCodeAt(0);
216
+
217
+ const CC_NUMBER_SIGN = "#".charCodeAt(0);
218
+ const CC_PLUS_SIGN = "+".charCodeAt(0);
219
+ const CC_HYPHEN_MINUS = "-".charCodeAt(0);
220
+
221
+ const CC_LESS_THAN_SIGN = "<".charCodeAt(0);
222
+ const CC_GREATER_THAN_SIGN = ">".charCodeAt(0);
223
+
224
+ // Lexer token types (CSS Syntax Level 3 §4) plus the `<eof-token>`. Numeric so
225
+ // the per-token `type` slot stays compact and `next` / `consume` / the consume
226
+ // algorithms dispatch on integer `===` instead of string comparison. Exported
227
+ // alongside `readToken` (the per-token lexer primitive) for the unit test.
228
+ const TT_COMMENT = 1;
229
+ const TT_WHITESPACE = 2;
230
+ const TT_STRING = 3;
231
+ const TT_BAD_STRING_TOKEN = 4;
232
+ const TT_HASH = 5;
233
+ const TT_DELIM = 6;
234
+ // The three opening brackets are kept contiguous (7..9) so "is this an opening
235
+ // bracket?" is a single range check (`>= TT_LEFT_PARENTHESIS && <= TT_LEFT_CURLY_BRACKET`).
236
+ const TT_LEFT_PARENTHESIS = 7;
237
+ const TT_LEFT_SQUARE_BRACKET = 8;
238
+ const TT_LEFT_CURLY_BRACKET = 9;
239
+ const TT_RIGHT_PARENTHESIS = 10;
240
+ const TT_RIGHT_SQUARE_BRACKET = 11;
241
+ const TT_RIGHT_CURLY_BRACKET = 12;
242
+ const TT_COMMA = 13;
243
+ const TT_COLON = 14;
244
+ const TT_SEMICOLON = 15;
245
+ const TT_AT_KEYWORD = 16;
246
+ const TT_FUNCTION = 17;
247
+ const TT_URL = 18;
248
+ const TT_BAD_URL_TOKEN = 19;
249
+ const TT_IDENTIFIER = 20;
250
+ const TT_NUMBER = 21;
251
+ const TT_PERCENTAGE = 22;
252
+ const TT_DIMENSION = 23;
253
+ const TT_CDO = 24;
254
+ const TT_CDC = 25;
255
+ const TT_EOF = 26;
256
+
257
+ // The opening bracket types (7..9) and their mirror closers (10..12) are laid
258
+ // out so a closer is always `opener + 3`; `consumeASimpleBlock` uses that
259
+ // directly. The associated block char is a dense array indexed by the opener's
260
+ // offset from `TT_LEFT_PARENTHESIS` — a plain element load instead of a numeric
261
+ // object-key lookup.
262
+ /** @type {SimpleBlockToken[]} */
263
+ const BLOCK_TOKEN_CHAR = ["(", "[", "{"];
264
+
265
+ /**
266
+ * @param {number} cc char code
267
+ * @returns {boolean} true, if cc is a newline (per the spec: LF, CR, or FF)
268
+ */
269
+ const _isNewline = (cc) =>
270
+ cc === CC_LINE_FEED || cc === CC_CARRIAGE_RETURN || cc === CC_FORM_FEED;
271
+
272
+ /**
273
+ * If the source had a CR followed by an LF, advance past the LF —
274
+ * the spec normalises CRLF to LF during preprocessing.
275
+ * @param {number} cc char code already consumed (the CR)
276
+ * @param {string} input input
277
+ * @param {number} pos position just past `cc`
278
+ * @returns {number} position past the CRLF pair (or unchanged for bare CR)
279
+ */
280
+ const consumeExtraNewline = (cc, input, pos) => {
281
+ if (cc === CC_CARRIAGE_RETURN && input.charCodeAt(pos) === CC_LINE_FEED) {
282
+ pos++;
283
+ }
284
+ return pos;
285
+ };
286
+
287
+ /**
288
+ * @param {number} cc char code
289
+ * @returns {boolean} true, if cc is space or tab
290
+ */
291
+ const _isSpace = (cc) => cc === CC_SPACE || cc === CC_TAB;
292
+
293
+ /**
294
+ * @param {number} cc char code
295
+ * @returns {boolean} true, if cc is whitespace (space/tab/newline)
296
+ */
297
+ // Space-first: U+0020 is the common case, so it short-circuits before the
298
+ // rarer tab / newline tests.
299
+ const _isWhiteSpace = (cc) => _isSpace(cc) || _isNewline(cc);
300
+
301
+ /**
302
+ * ident-start code point per the spec: a letter, a non-ASCII code point,
303
+ * or U+005F LOW LINE (`_`).
304
+ * @param {number} cc char code
305
+ * @returns {boolean} true, if cc is an ident-start code point
306
+ */
307
+ const isIdentStartCodePoint = (cc) =>
308
+ (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
309
+ (cc >= CC_UPPER_A && cc <= CC_UPPER_Z) ||
310
+ cc === CC_LOW_LINE ||
311
+ cc >= 0x80;
312
+
313
+ /**
314
+ * @param {number} cc char code
315
+ * @returns {boolean} true, if cc is a digit
316
+ */
317
+ const _isDigit = (cc) => cc >= CC_0 && cc <= CC_9;
318
+
319
+ /**
320
+ * @param {number} cc char code
321
+ * @returns {boolean} true, if cc is a hex digit
322
+ */
323
+ const _isHexDigit = (cc) =>
324
+ _isDigit(cc) ||
325
+ (cc >= CC_UPPER_A && cc <= CC_UPPER_F) ||
326
+ (cc >= CC_LOWER_A && cc <= CC_LOWER_F);
327
+
328
+ /**
329
+ * @param {number} cc char code
330
+ * @returns {boolean} is letter (a-z / A-Z)
331
+ */
332
+ const _isLetter = (cc) =>
333
+ (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
334
+ (cc >= CC_UPPER_A && cc <= CC_UPPER_Z);
335
+
336
+ /**
337
+ * Spec: ident-start = letter / non-ASCII / `_`. Internal helper that
338
+ * accepts an explicit char code (lookahead).
339
+ * @param {number} cc char code
340
+ * @returns {boolean} true, if cc is an ident-start code point
341
+ */
342
+ const _isIdentStartCodePointCC = (cc) =>
343
+ _isLetter(cc) || cc >= 0x80 || cc === CC_LOW_LINE;
344
+
345
+ /**
346
+ * Spec: ident-code = ident-start / digit / hyphen-minus.
347
+ */
348
+ // ASCII lookup built from the predicate so behaviour is identical; non-ASCII
349
+ // (>= 128) keeps the inline check. `_consumeAnIdentSequence` hits this per code
350
+ // point, and class/property names (hyphen + digit heavy) are the longest
351
+ // comparison-chain path — a table load is constant-time instead.
352
+ const _identCharTable = new Uint8Array(128);
353
+ for (let i = 0; i < 128; i++) {
354
+ _identCharTable[i] =
355
+ _isLetter(i) || i === CC_LOW_LINE || _isDigit(i) || i === CC_HYPHEN_MINUS
356
+ ? 1
357
+ : 0;
358
+ }
359
+ /**
360
+ * @param {number} cc char code
361
+ * @returns {boolean} true, if cc is an ident-sequence code point
362
+ */
363
+ const _isIdentCodePoint = (cc) =>
364
+ cc < 128 ? _identCharTable[cc] === 1 : _isIdentStartCodePointCC(cc);
365
+
366
+ /**
367
+ * ASCII case-insensitive equality against a lowercase literal — avoids the
368
+ * `toLowerCase()` allocation and matches CSS's ASCII case-insensitive keyword
369
+ * matching. `lit` must be lowercase ASCII.
370
+ * @param {string} s string to test
371
+ * @param {string} lit lowercase ASCII literal to match
372
+ * @returns {boolean} true, if `s` equals `lit` ignoring ASCII case
373
+ */
374
+ const equalsLowerCase = (s, lit) => {
375
+ if (s.length !== lit.length) return false;
376
+ for (let i = 0; i < lit.length; i++) {
377
+ let c = s.charCodeAt(i);
378
+ if (c >= CC_UPPER_A && c <= CC_UPPER_Z) c |= 0x20;
379
+ if (c !== lit.charCodeAt(i)) return false;
380
+ }
381
+ return true;
382
+ };
383
+
384
+ /**
385
+ * Consume an escaped code point.
386
+ * @param {string} input input
387
+ * @param {number} pos position just past the `\`
388
+ * @returns {number} position past the escape sequence
389
+ */
390
+ const _consumeAnEscapedCodePoint = (input, pos) => {
391
+ // Caller has verified the `\` and the next code point form a valid
392
+ // escape. Hex digits: consume up to 6 hex digits, then one optional
393
+ // whitespace. Non-hex: consume one code point.
394
+ // `\` at EOF: nothing to consume; return pos so callers don't overrun.
395
+ if (pos >= input.length) return pos;
396
+ const cc = input.charCodeAt(pos);
397
+ pos++;
398
+ if (pos === input.length) return pos;
399
+ if (_isHexDigit(cc)) {
400
+ for (let i = 0; i < 5; i++) {
401
+ if (_isHexDigit(input.charCodeAt(pos))) pos++;
402
+ }
403
+ const trail = input.charCodeAt(pos);
404
+ if (_isWhiteSpace(trail)) {
405
+ pos++;
406
+ pos = consumeExtraNewline(trail, input, pos);
407
+ }
408
+ }
409
+ return pos;
410
+ };
411
+
412
+ /**
413
+ * Spec: "two code points are a valid escape" — first is `\`, second is
414
+ * not a newline.
415
+ * @param {string} input input
416
+ * @param {number} pos position of the second code point
417
+ * @param {number=} f first code point (defaults to `input.charCodeAt(pos - 1)`)
418
+ * @param {number=} s second code point (defaults to `input.charCodeAt(pos)`)
419
+ * @returns {boolean} true, if the two code points form a valid escape
420
+ */
421
+ const _ifTwoCodePointsAreValidEscape = (input, pos, f, s) => {
422
+ const first = f || input.charCodeAt(pos - 1);
423
+ const second = s || input.charCodeAt(pos);
424
+ if (first !== CC_REVERSE_SOLIDUS) return false;
425
+ if (_isNewline(second)) return false;
426
+ return true;
427
+ };
428
+
429
+ /**
430
+ * Spec: "three code points would start an ident sequence".
431
+ * @param {string} input input
432
+ * @param {number} pos position
433
+ * @param {number=} f first code point (defaults to `input.charCodeAt(pos - 1)`)
434
+ * @param {number=} s second code point (defaults to `input.charCodeAt(pos)`)
435
+ * @param {number=} t third code point (defaults to `input.charCodeAt(pos + 1)`)
436
+ * @returns {boolean} true, if the three code points start an ident sequence
437
+ */
438
+ const _ifThreeCodePointsWouldStartAnIdentSequence = (input, pos, f, s, t) => {
439
+ const first = f || input.charCodeAt(pos - 1);
440
+ const second = s || input.charCodeAt(pos);
441
+ const third = t || input.charCodeAt(pos + 1);
442
+ if (first === CC_HYPHEN_MINUS) {
443
+ return (
444
+ _isIdentStartCodePointCC(second) ||
445
+ second === CC_HYPHEN_MINUS ||
446
+ _ifTwoCodePointsAreValidEscape(input, pos, second, third)
447
+ );
448
+ }
449
+ if (_isIdentStartCodePointCC(first)) return true;
450
+ if (first === CC_REVERSE_SOLIDUS) {
451
+ return _ifTwoCodePointsAreValidEscape(input, pos, first, second);
452
+ }
453
+ return false;
454
+ };
455
+
456
+ /**
457
+ * Spec: "three code points would start a number".
458
+ * @param {string} input input
459
+ * @param {number} pos position
460
+ * @param {number=} f first code point
461
+ * @param {number=} s second code point
462
+ * @param {number=} t third code point
463
+ * @returns {boolean} true, if the three code points start a number
464
+ */
465
+ const _ifThreeCodePointsWouldStartANumber = (input, pos, f, s, t) => {
466
+ const first = f || input.charCodeAt(pos - 1);
467
+ const second = s || input.charCodeAt(pos);
468
+ const third = t || input.charCodeAt(pos + 1);
469
+ if (first === CC_PLUS_SIGN || first === CC_HYPHEN_MINUS) {
470
+ if (_isDigit(second)) return true;
471
+ return second === CC_FULL_STOP && _isDigit(third);
472
+ }
473
+ if (first === CC_FULL_STOP) return _isDigit(second);
474
+ /* istanbul ignore next -- @preserve: spec-general; every caller passes `pos` just past a +/-/. so `first` is never a bare digit here */
475
+ return _isDigit(first);
476
+ };
477
+
478
+ /**
479
+ * Consume an ident sequence (no validation of the first code points).
480
+ * @param {string} input input
481
+ * @param {number} pos position
482
+ * @returns {number} position just past the last ident-sequence code point
483
+ */
484
+ const _consumeAnIdentSequence = (input, pos) => {
485
+ for (;;) {
486
+ const cc = input.charCodeAt(pos);
487
+ pos++;
488
+ if (_isIdentCodePoint(cc)) continue;
489
+ if (_ifTwoCodePointsAreValidEscape(input, pos, cc)) {
490
+ pos = _consumeAnEscapedCodePoint(input, pos);
491
+ continue;
492
+ }
493
+ return pos - 1;
494
+ }
495
+ };
496
+
497
+ /**
498
+ * @param {number} cc char code
499
+ * @returns {boolean} true, if cc is a non-printable code point
500
+ */
501
+ const _isNonPrintableCodePoint = (cc) =>
502
+ (cc >= 0x00 && cc <= 0x08) ||
503
+ cc === 0x0b ||
504
+ (cc >= 0x0e && cc <= 0x1f) ||
505
+ cc === 0x7f;
506
+
507
+ /**
508
+ * Consume the body of a number per the spec (does not classify integer
509
+ * vs number — caller / token type handles that).
510
+ * @param {string} input input
511
+ * @param {number} pos position at the first numeric / sign code point
512
+ * @returns {number} position just past the number
513
+ */
514
+ const _consumeANumber = (input, pos) => {
515
+ let cc = input.charCodeAt(pos);
516
+ if (cc === CC_HYPHEN_MINUS || cc === CC_PLUS_SIGN) {
517
+ pos++;
518
+ }
519
+ while (_isDigit(input.charCodeAt(pos))) pos++;
520
+ if (
521
+ input.charCodeAt(pos) === CC_FULL_STOP &&
522
+ _isDigit(input.charCodeAt(pos + 1))
523
+ ) {
524
+ pos++;
525
+ while (_isDigit(input.charCodeAt(pos))) pos++;
526
+ }
527
+ cc = input.charCodeAt(pos);
528
+ if (
529
+ (cc === CC_LOWER_E || cc === CC_UPPER_E) &&
530
+ (((input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS ||
531
+ input.charCodeAt(pos + 1) === CC_PLUS_SIGN) &&
532
+ _isDigit(input.charCodeAt(pos + 2))) ||
533
+ _isDigit(input.charCodeAt(pos + 1)))
534
+ ) {
535
+ pos++;
536
+ cc = input.charCodeAt(pos);
537
+ if (cc === CC_PLUS_SIGN || cc === CC_HYPHEN_MINUS) {
538
+ pos++;
539
+ }
540
+ while (_isDigit(input.charCodeAt(pos))) pos++;
541
+ }
542
+ return pos;
543
+ };
544
+
545
+ /**
546
+ * Spec recovery: when the tokenizer realises it's mid-bad-url, consume
547
+ * until `)` or EOF.
548
+ * @param {string} input input
549
+ * @param {number} pos position
550
+ * @returns {number} position past the recovery `)` or EOF
551
+ */
552
+ const _consumeTheRemnantsOfABadUrl = (input, pos) => {
553
+ for (;;) {
554
+ if (pos === input.length) return pos;
555
+ const cc = input.charCodeAt(pos);
556
+ pos++;
557
+ if (cc === CC_RIGHT_PARENTHESIS) return pos;
558
+ if (_ifTwoCodePointsAreValidEscape(input, pos)) {
559
+ pos = _consumeAnEscapedCodePoint(input, pos);
560
+ }
561
+ }
562
+ };
563
+
564
+ /**
565
+ * A mutable lexer token. The `next` / `consume` hot path reuses a single
566
+ * instance per `TokenStream` (the lexer writes into it instead of allocating
567
+ * one object per token), which also keeps the parser's `t.type` reads
568
+ * monomorphic. All fields are present from construction so the shape never
569
+ * transitions; type-specific fields (`isId` / `contentStart` / `contentEnd` /
570
+ * `unitStart`) carry stale values for unrelated token types and are only read
571
+ * by `tokenToNode` for the matching type. Pass a fresh one per `readToken` call
572
+ * to collect the raw token list (e.g. tests).
573
+ * @typedef {object} MutableToken
574
+ * @property {number} type one of the `TT_*` constants
575
+ * @property {number} start byte offset of the token's first code point
576
+ * @property {number} end byte offset just past the token's last code point
577
+ * @property {boolean} isId hash tokens: starts an ident sequence
578
+ * @property {number} contentStart url tokens: first content code point
579
+ * @property {number} contentEnd url tokens: just past the last content code point
580
+ * @property {number} unitStart dimension tokens: first unit-ident code point
581
+ */
582
+
583
+ /**
584
+ * @returns {MutableToken} a fresh lexer token with the canonical shape
585
+ */
586
+ const createToken = () => ({
587
+ type: TT_EOF,
588
+ start: 0,
589
+ end: 0,
590
+ isId: false,
591
+ contentStart: 0,
592
+ contentEnd: 0,
593
+ unitStart: 0
594
+ });
595
+
596
+ /**
597
+ * Populate `out`'s common fields and return it — the lexer functions' return
598
+ * statement (kept tiny so V8 can inline it).
599
+ * @param {MutableToken} out token to populate
600
+ * @param {number} type one of the `TT_*` constants
601
+ * @param {number} start byte offset of the token's first code point
602
+ * @param {number} end byte offset just past the token's last code point
603
+ * @returns {MutableToken} `out`
604
+ */
605
+ const fill = (out, type, start, end) => {
606
+ out.type = type;
607
+ out.start = start;
608
+ out.end = end;
609
+ return out;
610
+ };
611
+
612
+ /**
613
+ * Whitespace token. Caller advances past the leading code point so
614
+ * `start = pos - 1`.
615
+ * @param {string} input input
616
+ * @param {number} pos position just past the first whitespace code point
617
+ * @param {MutableToken} out token to populate
618
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
619
+ */
620
+ function consumeSpace(input, pos, out) {
621
+ const start = pos - 1;
622
+ while (_isWhiteSpace(input.charCodeAt(pos))) pos++;
623
+ return fill(out, TT_WHITESPACE, start, pos);
624
+ }
625
+
626
+ /**
627
+ * Consume a string token. Caller advanced past the opening quote so
628
+ * `pos - 1` holds the ending code point and `pos - 1` is the start.
629
+ * @param {string} input input
630
+ * @param {number} pos position just past the opening quote
631
+ * @param {MutableToken} out token to populate
632
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
633
+ */
634
+ function consumeAStringToken(input, pos, out) {
635
+ const start = pos - 1;
636
+ const endingCodePoint = input.charCodeAt(pos - 1);
637
+ for (;;) {
638
+ if (pos === input.length) {
639
+ return fill(out, TT_STRING, start, pos);
640
+ }
641
+ const cc = input.charCodeAt(pos);
642
+ pos++;
643
+ if (cc === endingCodePoint) {
644
+ return fill(out, TT_STRING, start, pos);
645
+ }
646
+ if (_isNewline(cc)) {
647
+ pos--;
648
+ return fill(out, TT_BAD_STRING_TOKEN, start, pos);
649
+ }
650
+ if (cc === CC_REVERSE_SOLIDUS) {
651
+ // `\` at EOF: string ends here; emit the token so ranges cover all input.
652
+ if (pos === input.length) return fill(out, TT_STRING, start, pos);
653
+ if (_isNewline(input.charCodeAt(pos))) {
654
+ const ccNl = input.charCodeAt(pos);
655
+ pos++;
656
+ pos = consumeExtraNewline(ccNl, input, pos);
657
+ } else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
658
+ pos = _consumeAnEscapedCodePoint(input, pos);
659
+ }
660
+ }
661
+ }
662
+ }
663
+
664
+ /**
665
+ * `#` — hash or delim.
666
+ * @param {string} input input
667
+ * @param {number} pos position just past `#`
668
+ * @param {MutableToken} out token to populate
669
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
670
+ */
671
+ function consumeNumberSign(input, pos, out) {
672
+ const start = pos - 1;
673
+ const first = input.charCodeAt(pos);
674
+ const second = input.charCodeAt(pos + 1);
675
+ if (
676
+ _isIdentCodePoint(first) ||
677
+ _ifTwoCodePointsAreValidEscape(input, pos, first, second)
678
+ ) {
679
+ const third = input.charCodeAt(pos + 2);
680
+ out.isId = _ifThreeCodePointsWouldStartAnIdentSequence(
681
+ input,
682
+ pos,
683
+ first,
684
+ second,
685
+ third
686
+ );
687
+ pos = _consumeAnIdentSequence(input, pos);
688
+ return fill(out, TT_HASH, start, pos);
689
+ }
690
+ return fill(out, TT_DELIM, start, pos);
691
+ }
692
+
693
+ /**
694
+ * `-` — number / cdc / ident / delim.
695
+ * @param {string} input input
696
+ * @param {number} pos position just past `-`
697
+ * @param {MutableToken} out token to populate
698
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
699
+ */
700
+ function consumeHyphenMinus(input, pos, out) {
701
+ if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
702
+ pos--;
703
+ return consumeANumericToken(input, pos, out);
704
+ }
705
+ if (
706
+ input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
707
+ input.charCodeAt(pos + 1) === CC_GREATER_THAN_SIGN
708
+ ) {
709
+ return fill(out, TT_CDC, pos - 1, pos + 2);
710
+ }
711
+ if (_ifThreeCodePointsWouldStartAnIdentSequence(input, pos)) {
712
+ pos--;
713
+ return consumeAnIdentLikeToken(input, pos, out);
714
+ }
715
+ return fill(out, TT_DELIM, pos - 1, pos);
716
+ }
717
+
718
+ /**
719
+ * `.` — number or delim.
720
+ * @param {string} input input
721
+ * @param {number} pos position just past `.`
722
+ * @param {MutableToken} out token to populate
723
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
724
+ */
725
+ function consumeFullStop(input, pos, out) {
726
+ const start = pos - 1;
727
+ if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
728
+ pos--;
729
+ return consumeANumericToken(input, pos, out);
730
+ }
731
+ return fill(out, TT_DELIM, start, pos);
732
+ }
733
+
734
+ /**
735
+ * `+` — number or delim.
736
+ * @param {string} input input
737
+ * @param {number} pos position just past `+`
738
+ * @param {MutableToken} out token to populate
739
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
740
+ */
741
+ function consumePlusSign(input, pos, out) {
742
+ const start = pos - 1;
743
+ if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
744
+ pos--;
745
+ return consumeANumericToken(input, pos, out);
746
+ }
747
+ return fill(out, TT_DELIM, start, pos);
748
+ }
749
+
750
+ /**
751
+ * Numeric token: number / percentage / dimension.
752
+ * @param {string} input input
753
+ * @param {number} pos position at the first numeric/sign code point
754
+ * @param {MutableToken} out token to populate
755
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
756
+ */
757
+ function consumeANumericToken(input, pos, out) {
758
+ const start = pos;
759
+ pos = _consumeANumber(input, pos);
760
+ const first = input.charCodeAt(pos);
761
+ // A unit can only begin with `-`, `\`, or an ident-start code point — exactly
762
+ // the cases where the §4 "would start an ident sequence" check can be true. For
763
+ // a plain number (next char is whitespace / `;` / `,` / `)` / EOF, the common
764
+ // case) skip the two lookahead reads and the call entirely.
765
+ if (
766
+ (first === CC_HYPHEN_MINUS ||
767
+ first === CC_REVERSE_SOLIDUS ||
768
+ _isIdentStartCodePointCC(first)) &&
769
+ _ifThreeCodePointsWouldStartAnIdentSequence(
770
+ input,
771
+ pos,
772
+ first,
773
+ input.charCodeAt(pos + 1),
774
+ input.charCodeAt(pos + 2)
775
+ )
776
+ ) {
777
+ out.unitStart = pos;
778
+ pos = _consumeAnIdentSequence(input, pos);
779
+ return fill(out, TT_DIMENSION, start, pos);
780
+ }
781
+ if (first === CC_PERCENTAGE) {
782
+ return fill(out, TT_PERCENTAGE, start, pos + 1);
783
+ }
784
+ return fill(out, TT_NUMBER, start, pos);
785
+ }
786
+
787
+ /**
788
+ * Consume an unquoted url token. Caller has already eaten `url(` and
789
+ * any leading whitespace.
790
+ * @param {string} input input
791
+ * @param {number} pos position at the first content code point
792
+ * @param {number} fnStart byte offset of the `u` in `url(`
793
+ * @param {MutableToken} out token to populate
794
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
795
+ */
796
+ function consumeAUrlToken(input, pos, fnStart, out) {
797
+ while (_isWhiteSpace(input.charCodeAt(pos))) pos++;
798
+ const contentStart = pos;
799
+ out.contentStart = contentStart;
800
+ for (;;) {
801
+ if (pos === input.length) {
802
+ out.contentEnd = pos - 1;
803
+ return fill(out, TT_URL, fnStart, pos);
804
+ }
805
+ const cc = input.charCodeAt(pos);
806
+ pos++;
807
+ if (cc === CC_RIGHT_PARENTHESIS) {
808
+ out.contentEnd = pos - 1;
809
+ return fill(out, TT_URL, fnStart, pos);
810
+ }
811
+ if (_isWhiteSpace(cc)) {
812
+ const end = pos - 1;
813
+ while (_isWhiteSpace(input.charCodeAt(pos))) pos++;
814
+ if (pos === input.length) {
815
+ out.contentEnd = end;
816
+ return fill(out, TT_URL, fnStart, pos);
817
+ }
818
+ if (input.charCodeAt(pos) === CC_RIGHT_PARENTHESIS) {
819
+ pos++;
820
+ out.contentEnd = end;
821
+ return fill(out, TT_URL, fnStart, pos);
822
+ }
823
+ pos = _consumeTheRemnantsOfABadUrl(input, pos);
824
+ return fill(out, TT_BAD_URL_TOKEN, fnStart, pos);
825
+ }
826
+ if (
827
+ cc === CC_QUOTATION_MARK ||
828
+ cc === CC_APOSTROPHE ||
829
+ cc === CC_LEFT_PARENTHESIS ||
830
+ _isNonPrintableCodePoint(cc)
831
+ ) {
832
+ pos = _consumeTheRemnantsOfABadUrl(input, pos);
833
+ return fill(out, TT_BAD_URL_TOKEN, fnStart, pos);
834
+ }
835
+ if (cc === CC_REVERSE_SOLIDUS) {
836
+ if (_ifTwoCodePointsAreValidEscape(input, pos)) {
837
+ pos = _consumeAnEscapedCodePoint(input, pos);
838
+ } else {
839
+ pos = _consumeTheRemnantsOfABadUrl(input, pos);
840
+ return fill(out, TT_BAD_URL_TOKEN, fnStart, pos);
841
+ }
842
+ }
843
+ }
844
+ }
845
+
846
+ /**
847
+ * Consume an ident-like token: ident / function / url / bad-url.
848
+ * @param {string} input input
849
+ * @param {number} pos position at the first ident-start code point
850
+ * @param {MutableToken} out token to populate
851
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
852
+ */
853
+ function consumeAnIdentLikeToken(input, pos, out) {
854
+ const start = pos;
855
+ pos = _consumeAnIdentSequence(input, pos);
856
+ // `url` case-insensitively (ASCII lower via `| 0x20`) without a
857
+ // `slice().toLowerCase()` allocation per identifier; an escaped ident can't
858
+ // be exactly 3 raw chars, so the length gate keeps this equivalent.
859
+ if (
860
+ pos - start === 3 &&
861
+ (input.charCodeAt(start) | 0x20) === CC_LOWER_U &&
862
+ (input.charCodeAt(start + 1) | 0x20) === CC_LOWER_R &&
863
+ (input.charCodeAt(start + 2) | 0x20) === CC_LOWER_L &&
864
+ input.charCodeAt(pos) === CC_LEFT_PARENTHESIS
865
+ ) {
866
+ pos++;
867
+ const end = pos;
868
+ while (
869
+ _isWhiteSpace(input.charCodeAt(pos)) &&
870
+ _isWhiteSpace(input.charCodeAt(pos + 1))
871
+ ) {
872
+ pos++;
873
+ }
874
+ if (
875
+ input.charCodeAt(pos) === CC_QUOTATION_MARK ||
876
+ input.charCodeAt(pos) === CC_APOSTROPHE ||
877
+ (_isWhiteSpace(input.charCodeAt(pos)) &&
878
+ (input.charCodeAt(pos + 1) === CC_QUOTATION_MARK ||
879
+ input.charCodeAt(pos + 1) === CC_APOSTROPHE))
880
+ ) {
881
+ // End at `end` (the `(`'s closer position), not `pos` — the
882
+ // lookahead-eaten whitespace must be re-tokenized as a whitespace
883
+ // token rather than swallowed silently. The reader resumes at
884
+ // `token.end`, so returning `end` here does that.
885
+ return fill(out, TT_FUNCTION, start, end);
886
+ }
887
+ return consumeAUrlToken(input, pos, start, out);
888
+ }
889
+ if (input.charCodeAt(pos) === CC_LEFT_PARENTHESIS) {
890
+ pos++;
891
+ return fill(out, TT_FUNCTION, start, pos);
892
+ }
893
+ return fill(out, TT_IDENTIFIER, start, pos);
894
+ }
895
+
896
+ /**
897
+ * `<` — CDO or delim.
898
+ * @param {string} input input
899
+ * @param {number} pos position just past `<`
900
+ * @param {MutableToken} out token to populate
901
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
902
+ */
903
+ function consumeLessThan(input, pos, out) {
904
+ if (
905
+ input.charCodeAt(pos) === CC_EXCLAMATION &&
906
+ input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS &&
907
+ input.charCodeAt(pos + 2) === CC_HYPHEN_MINUS
908
+ ) {
909
+ return fill(out, TT_CDO, pos - 1, pos + 3);
910
+ }
911
+ return fill(out, TT_DELIM, pos - 1, pos);
912
+ }
913
+
914
+ /**
915
+ * `@` — at-keyword or delim.
916
+ * @param {string} input input
917
+ * @param {number} pos position just past `@`
918
+ * @param {MutableToken} out token to populate
919
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
920
+ */
921
+ function consumeCommercialAt(input, pos, out) {
922
+ const start = pos - 1;
923
+ if (
924
+ _ifThreeCodePointsWouldStartAnIdentSequence(
925
+ input,
926
+ pos,
927
+ input.charCodeAt(pos),
928
+ input.charCodeAt(pos + 1),
929
+ input.charCodeAt(pos + 2)
930
+ )
931
+ ) {
932
+ pos = _consumeAnIdentSequence(input, pos);
933
+ return fill(out, TT_AT_KEYWORD, start, pos);
934
+ }
935
+ return fill(out, TT_DELIM, start, pos);
936
+ }
937
+
938
+ /**
939
+ * `\` — escape starts an ident-like token, otherwise it's a delim.
940
+ * @param {string} input input
941
+ * @param {number} pos position just past `\`
942
+ * @param {MutableToken} out token to populate
943
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
944
+ */
945
+ function consumeReverseSolidus(input, pos, out) {
946
+ if (_ifTwoCodePointsAreValidEscape(input, pos)) {
947
+ pos--;
948
+ return consumeAnIdentLikeToken(input, pos, out);
949
+ }
950
+ return fill(out, TT_DELIM, pos - 1, pos);
951
+ }
952
+
953
+ /**
954
+ * Per-character dispatcher. The outer loop has already advanced past
955
+ * the lead code point (`pos - 1` is the lead).
956
+ * @param {string} input input
957
+ * @param {number} pos position just past the lead code point
958
+ * @param {number} cc the lead code point (`input.charCodeAt(pos - 1)`, already read by the caller)
959
+ * @param {MutableToken} out token to populate
960
+ * @returns {MutableToken | undefined} the resulting token, or undefined at EOF
961
+ */
962
+ function consumeAToken(input, pos, cc, out) {
963
+ switch (cc) {
964
+ case CC_LINE_FEED:
965
+ case CC_CARRIAGE_RETURN:
966
+ case CC_FORM_FEED:
967
+ case CC_TAB:
968
+ case CC_SPACE:
969
+ return consumeSpace(input, pos, out);
970
+ case CC_QUOTATION_MARK:
971
+ case CC_APOSTROPHE:
972
+ return consumeAStringToken(input, pos, out);
973
+ case CC_NUMBER_SIGN:
974
+ return consumeNumberSign(input, pos, out);
975
+ case CC_LEFT_PARENTHESIS:
976
+ return fill(out, TT_LEFT_PARENTHESIS, pos - 1, pos);
977
+ case CC_RIGHT_PARENTHESIS:
978
+ return fill(out, TT_RIGHT_PARENTHESIS, pos - 1, pos);
979
+ case CC_PLUS_SIGN:
980
+ return consumePlusSign(input, pos, out);
981
+ case CC_COMMA:
982
+ return fill(out, TT_COMMA, pos - 1, pos);
983
+ case CC_HYPHEN_MINUS:
984
+ return consumeHyphenMinus(input, pos, out);
985
+ case CC_FULL_STOP:
986
+ return consumeFullStop(input, pos, out);
987
+ case CC_COLON:
988
+ return fill(out, TT_COLON, pos - 1, pos);
989
+ case CC_SEMICOLON:
990
+ return fill(out, TT_SEMICOLON, pos - 1, pos);
991
+ case CC_LESS_THAN_SIGN:
992
+ return consumeLessThan(input, pos, out);
993
+ case CC_AT_SIGN:
994
+ return consumeCommercialAt(input, pos, out);
995
+ case CC_LEFT_SQUARE:
996
+ return fill(out, TT_LEFT_SQUARE_BRACKET, pos - 1, pos);
997
+ case CC_REVERSE_SOLIDUS:
998
+ return consumeReverseSolidus(input, pos, out);
999
+ case CC_RIGHT_SQUARE:
1000
+ return fill(out, TT_RIGHT_SQUARE_BRACKET, pos - 1, pos);
1001
+ case CC_LEFT_CURLY:
1002
+ return fill(out, TT_LEFT_CURLY_BRACKET, pos - 1, pos);
1003
+ case CC_RIGHT_CURLY:
1004
+ return fill(out, TT_RIGHT_CURLY_BRACKET, pos - 1, pos);
1005
+ default:
1006
+ if (_isDigit(cc)) {
1007
+ pos--;
1008
+ return consumeANumericToken(input, pos, out);
1009
+ }
1010
+ if (cc === CC_LOWER_U || cc === CC_UPPER_U) {
1011
+ // Unicode-range tokens are not produced — fall back to
1012
+ // ident-like to match the existing tokenizer's behaviour.
1013
+ pos--;
1014
+ return consumeAnIdentLikeToken(input, pos, out);
1015
+ }
1016
+ if (isIdentStartCodePoint(cc)) {
1017
+ pos--;
1018
+ return consumeAnIdentLikeToken(input, pos, out);
1019
+ }
1020
+ // EOF is impossible here (caller guarded with the outer
1021
+ // loop's `pos < input.length` check). Anything else: delim.
1022
+ return fill(out, TT_DELIM, pos - 1, pos);
1023
+ }
1024
+ }
1025
+
1026
+ /**
1027
+ * Read one raw token (comment / whitespace / value token) starting at byte
1028
+ * `pos`, writing it into the caller-supplied `out` and returning `out`. The
1029
+ * token's `end` is the next read position. Returns `undefined` at end-of-input —
1030
+ * `pos >= length`, an unterminated comment, or a string ending on a trailing
1031
+ * escape. This is the shared lexer core: `next` reuses one `out` across calls so
1032
+ * the parse hot path allocates no per-token object; loop over it with a fresh
1033
+ * `out` per call to collect the raw token list (e.g. tests). Comment tokens are
1034
+ * returned here; `next` filters them.
1035
+ * @param {string} input input
1036
+ * @param {number} pos byte offset to read from
1037
+ * @param {MutableToken} out token to populate
1038
+ * @returns {MutableToken | undefined} the token, or undefined at EOF
1039
+ */
1040
+ function readToken(input, pos, out) {
1041
+ if (pos >= input.length) return undefined;
1042
+ const cc = input.charCodeAt(pos);
1043
+ // Comment: `/*…*/` is yielded as a token (filtered by `next`).
1044
+ if (cc === CC_SOLIDUS && input.charCodeAt(pos + 1) === CC_ASTERISK) {
1045
+ const start = pos;
1046
+ pos += 2;
1047
+ for (;;) {
1048
+ // EOF in comment: emit the unterminated token so ranges cover all input.
1049
+ if (pos === input.length) return fill(out, TT_COMMENT, start, pos);
1050
+ if (
1051
+ input.charCodeAt(pos) === CC_ASTERISK &&
1052
+ input.charCodeAt(pos + 1) === CC_SOLIDUS
1053
+ ) {
1054
+ return fill(out, TT_COMMENT, start, pos + 2);
1055
+ }
1056
+ pos++;
1057
+ }
1058
+ }
1059
+ // `consumeAToken` dispatches on the lead code point at `pos` (it expects the
1060
+ // position just past the lead and the already-read lead code point).
1061
+ return consumeAToken(input, pos + 1, cc, out);
1062
+ }
1063
+
1064
+ // AST shape mirrors tabatkins/parse-css (the CSS Syntax Level 3 reference), with two deviations: nodes carry a `range` byte offset pair + a lazy `loc` getter, and have no methods beyond it.
1065
+
1066
+ /**
1067
+ * AST node / leaf-token `type` discriminators (spec name where it has one, else
1068
+ * parse-css's PascalCase). Numeric for the same reasons as the `TT_*` token
1069
+ * constants: a compact `Node#type` slot and integer `===` / `Map` keys on the
1070
+ * visitor hot path. Kept as a `NodeType` namespace (not bare constants) because
1071
+ * consumers reference members as `NodeType.AtRule`; exported so visitor maps
1072
+ * (`SourceProcessor#use`) and `CssParser` name nodes instead of a string
1073
+ * literal. A lexer token type never reaches a `Node#type`.
1074
+ * @enum {number}
1075
+ */
1076
+ const NodeType = {
1077
+ Ident: 1,
1078
+ Function: 2,
1079
+ AtKeyword: 3,
1080
+ Hash: 4,
1081
+ String: 5,
1082
+ BadString: 6,
1083
+ Url: 7,
1084
+ BadUrl: 8,
1085
+ Delim: 9,
1086
+ Number: 10,
1087
+ Percentage: 11,
1088
+ Dimension: 12,
1089
+ Whitespace: 13,
1090
+ Colon: 14,
1091
+ Semicolon: 15,
1092
+ Comma: 16,
1093
+ // Preserved tokens for stray closers / CDO / CDC (kept as component values per §5.4.8 "consume a token and return it").
1094
+ RightParenthesis: 17,
1095
+ RightSquareBracket: 18,
1096
+ RightCurlyBracket: 19,
1097
+ CDO: 20,
1098
+ CDC: 21,
1099
+ SimpleBlock: 22,
1100
+ Declaration: 23,
1101
+ AtRule: 24,
1102
+ QualifiedRule: 25,
1103
+ Stylesheet: 26
1104
+ };
1105
+ const {
1106
+ Ident: T_IDENT,
1107
+ Function: T_FUNCTION,
1108
+ AtKeyword: T_AT_KEYWORD,
1109
+ Hash: T_HASH,
1110
+ String: T_STRING,
1111
+ BadString: T_BAD_STRING,
1112
+ Url: T_URL,
1113
+ BadUrl: T_BAD_URL,
1114
+ Delim: T_DELIM,
1115
+ Number: T_NUMBER,
1116
+ Percentage: T_PERCENTAGE,
1117
+ Dimension: T_DIMENSION,
1118
+ Whitespace: T_WHITESPACE,
1119
+ Colon: T_COLON,
1120
+ Semicolon: T_SEMICOLON,
1121
+ Comma: T_COMMA,
1122
+ RightParenthesis: T_RIGHT_PARENTHESIS,
1123
+ RightSquareBracket: T_RIGHT_SQUARE_BRACKET,
1124
+ RightCurlyBracket: T_RIGHT_CURLY_BRACKET,
1125
+ CDO: T_CDO,
1126
+ CDC: T_CDC,
1127
+ SimpleBlock: T_SIMPLE_BLOCK,
1128
+ Declaration: T_DECLARATION,
1129
+ AtRule: T_AT_RULE,
1130
+ QualifiedRule: T_QUALIFIED_RULE,
1131
+ Stylesheet: T_STYLESHEET
1132
+ } = NodeType;
1133
+
1134
+ /**
1135
+ * Base AST node. All concrete nodes (tokens, simple blocks, functions,
1136
+ * declarations) inherit from this and carry the `[start, end)` byte `range`
1137
+ * of the source slice they cover. `loc` is computed on demand from a
1138
+ * shared `LocConverter` so we don't pay for line/column conversion until
1139
+ * a consumer (warning, error, dependency) actually needs it.
1140
+ */
1141
+ class Node {
1142
+ /**
1143
+ * @param {number} type node type discriminator
1144
+ * @param {number} start byte offset of the node's first code point
1145
+ * @param {number} end byte offset just past the node's last code point
1146
+ * @param {LocConverter} locConverter shared loc converter
1147
+ */
1148
+ constructor(type, start, end, locConverter) {
1149
+ /** @type {number} */
1150
+ this.type = type;
1151
+ // Byte range as two inline fields rather than a `[start, end]` array —
1152
+ // one fewer allocation per node and ~56 bytes lighter (×100k+ nodes).
1153
+ /** @type {number} */
1154
+ this.start = start;
1155
+ /** @type {number} */
1156
+ this.end = end;
1157
+ /** @type {LocConverter} */
1158
+ this._locConverter = locConverter;
1159
+ }
1160
+
1161
+ /**
1162
+ * The `[start, end)` byte range as a tuple — compatibility view over
1163
+ * `start` / `end` (builds the array lazily; hot code reads the
1164
+ * fields directly).
1165
+ * @returns {[number, number]} the byte range
1166
+ */
1167
+ get range() {
1168
+ return [this.start, this.end];
1169
+ }
1170
+
1171
+ get loc() {
1172
+ const lc = this._locConverter;
1173
+ // `LocConverter#get` mutates and returns the converter itself, so we
1174
+ // must snapshot `line`/`column` between the two calls.
1175
+ const s = lc.get(this.start);
1176
+ const sl = s.line;
1177
+ const sc = s.column;
1178
+ const e = lc.get(this.end);
1179
+ return {
1180
+ start: { line: sl, column: sc },
1181
+ end: { line: e.line, column: e.column }
1182
+ };
1183
+ }
1184
+
1185
+ /**
1186
+ * Serialize back to source — re-slices the original input (zero-alloc for
1187
+ * untouched nodes).
1188
+ * @returns {string} the source slice for this node
1189
+ */
1190
+ toString() {
1191
+ return this._locConverter._input.slice(this.start, this.end);
1192
+ }
1193
+
1194
+ /**
1195
+ * For name-bearing nodes (function / at-rule): the `name` with CSS escapes
1196
+ * resolved, for case-insensitive keyword matching (`\75 rl` → `url`). Computed
1197
+ * on read via `unescapeIdentifier`'s no-escape fast path. Callers without a
1198
+ * `name` must not read this.
1199
+ * @returns {string} the unescaped name
1200
+ */
1201
+ get unescapedName() {
1202
+ return unescapeIdentifier(
1203
+ /** @type {{ name: string }} */ (/** @type {unknown} */ (this)).name
1204
+ );
1205
+ }
1206
+ }
1207
+
1208
+ /**
1209
+ * @param {string} s numeric text
1210
+ * @returns {"+" | "-" | ""} the spec sign ("" when unsigned)
1211
+ */
1212
+ const _signOf = (s) => {
1213
+ const c = s.charCodeAt(0);
1214
+ return c === CC_PLUS_SIGN ? "+" : c === CC_HYPHEN_MINUS ? "-" : "";
1215
+ };
1216
+
1217
+ /**
1218
+ * @param {string} s numeric text (no unit / `%`)
1219
+ * @returns {"integer" | "number"} the spec type flag
1220
+ */
1221
+ const _typeFlagOf = (s) =>
1222
+ s.includes(".") || s.includes("e") || s.includes("E") ? "number" : "integer";
1223
+
1224
+ /**
1225
+ * Leaf token node — the only `Node` subclass. `value` is the raw source slice
1226
+ * (identifier text, quoted string including quotes, a dimension's full `123px`,
1227
+ * …). Token-specific extras are named by the `HashToken` / `UrlToken` /
1228
+ * `NumberToken` / `DimensionToken` shape typedefs below.
1229
+ *
1230
+ * The numeric accessors (`numericValue` / `typeFlag` / `sign` / `unit`) are
1231
+ * getters, not stored fields: a number / dimension / percentage token costs
1232
+ * nothing beyond the base node unless a consumer reads them, and every leaf
1233
+ * token keeps a single object shape so the walker's `node.type` dispatch stays
1234
+ * monomorphic. They are only meaningful on the matching token type.
1235
+ */
1236
+ class Token extends Node {
1237
+ /**
1238
+ * @param {number} type node type
1239
+ * @param {number} start byte offset of the token's first code point
1240
+ * @param {number} end byte offset just past the token's last code point
1241
+ * @param {LocConverter} locConverter shared loc converter
1242
+ */
1243
+ constructor(type, start, end, locConverter) {
1244
+ super(type, start, end, locConverter);
1245
+ // Lazily sliced from `range` on first `value` read (the common case), or
1246
+ // pre-set when the value isn't the raw range slice (hash / at-keyword /
1247
+ // url strip a prefix or use the content range). Deferring avoids slicing
1248
+ // the many tokens — whitespace especially — whose value is never read.
1249
+ /** @type {string | undefined} */
1250
+ this._value = undefined;
1251
+ }
1252
+
1253
+ /**
1254
+ * @returns {string} the token's value (raw source slice unless overridden)
1255
+ */
1256
+ get value() {
1257
+ const v = this._value;
1258
+ if (v !== undefined) return v;
1259
+ return (this._value = this._locConverter._input.slice(
1260
+ this.start,
1261
+ this.end
1262
+ ));
1263
+ }
1264
+
1265
+ /**
1266
+ * The token's value with CSS escapes resolved (`\2d` → `-`, `\75 rl` → `url`),
1267
+ * per https://www.w3.org/TR/css-syntax-3/#consume-escaped-code-point — the
1268
+ * form to match keywords / export as a CSS-Modules name against. For a string
1269
+ * token it is the content between the quotes (the spec string value). Computed
1270
+ * on read; `unescapeIdentifier` fast-returns the value unchanged when it has no
1271
+ * escapes (the common case), so nothing is stored per token.
1272
+ * @returns {string} the unescaped value
1273
+ */
1274
+ get unescaped() {
1275
+ const v = this.value;
1276
+ // A string token's `value` carries its delimiting quotes; its value is the content between them.
1277
+ return this.type === T_STRING
1278
+ ? unescapeIdentifier(v.slice(1, -1))
1279
+ : unescapeIdentifier(v);
1280
+ }
1281
+
1282
+ /**
1283
+ * Parsed numeric value (number / percentage / dimension tokens). Derived from
1284
+ * `value` on access — the `%` is dropped for percentages and the unit for
1285
+ * dimensions (split with `_consumeANumber`, recomputed here so nothing is
1286
+ * stored per token).
1287
+ * @returns {number} the parsed numeric value
1288
+ */
1289
+ get numericValue() {
1290
+ const v = this.value;
1291
+ if (this.type === T_DIMENSION) {
1292
+ return Number(v.slice(0, _consumeANumber(v, 0)));
1293
+ }
1294
+ if (this.type === T_PERCENTAGE) return Number(v.slice(0, -1));
1295
+ return Number(v);
1296
+ }
1297
+
1298
+ /**
1299
+ * Spec type flag. For number / dimension tokens it's "integer" / "number"
1300
+ * (derived from `value`); for hash tokens it's "id" / "unrestricted" (from the
1301
+ * stored `_isId`). The two senses share the name in the spec; the getter keeps
1302
+ * hash tokens the same object shape as numeric tokens (no own `typeFlag`).
1303
+ * @returns {"integer" | "number" | "id" | "unrestricted"} the spec type flag
1304
+ */
1305
+ get typeFlag() {
1306
+ if (this.type === T_HASH) {
1307
+ // `_isId` is set only on hash tokens (see `tokenToNode`), kept off
1308
+ // `Token`'s declared fields so numeric / plain tokens share one shape.
1309
+ const isId = /** @type {{ _isId: boolean }} */ (
1310
+ /** @type {unknown} */ (this)
1311
+ )._isId;
1312
+ return isId ? "id" : "unrestricted";
1313
+ }
1314
+ const v = this.value;
1315
+ return _typeFlagOf(
1316
+ this.type === T_DIMENSION ? v.slice(0, _consumeANumber(v, 0)) : v
1317
+ );
1318
+ }
1319
+
1320
+ /**
1321
+ * @returns {"+" | "-" | ""} the spec sign (number / percentage / dimension tokens)
1322
+ */
1323
+ get sign() {
1324
+ return _signOf(this.value);
1325
+ }
1326
+
1327
+ /**
1328
+ * @returns {string} the unit, lower-cased per spec (dimension tokens)
1329
+ */
1330
+ get unit() {
1331
+ const v = this.value;
1332
+ return v.slice(_consumeANumber(v, 0)).toLowerCase();
1333
+ }
1334
+ }
1335
+
1336
+ /**
1337
+ * Number token (`123`, `-1.5`, `+2e3`). `value` is the raw source slice (the spec's "value"); `numericValue` / `typeFlag` / `sign` are lazy getters derived from it (see `Token`).
1338
+ * @typedef {Token & { numericValue: number, typeFlag: "integer" | "number", sign: "+" | "-" | "" }} NumberToken
1339
+ */
1340
+
1341
+ /**
1342
+ * Percentage token (`50%`). `value` is the raw slice including `%`; `numericValue` (without `%`) and `sign` are lazy getters.
1343
+ * @typedef {Token & { numericValue: number, sign: "+" | "-" | "" }} PercentageToken
1344
+ */
1345
+
1346
+ /**
1347
+ * Dimension token (`100px`, `1.5em`). `value` is the raw slice (number + unit); `numericValue` / `typeFlag` / `sign` (of the numeric part) and `unit` (lower-cased) are lazy getters.
1348
+ * @typedef {Token & { numericValue: number, typeFlag: "integer" | "number", sign: "+" | "-" | "", unit: string }} DimensionToken
1349
+ */
1350
+
1351
+ // Spec "Assert: …" preconditions are comments only (callers satisfy them); a future `strict` option could reinstate them as throws.
1352
+
1353
+ /**
1354
+ * Hash token (`#foo`). `value` is the name without the leading `#`; `typeFlag` is the spec type flag ("id" when the name forms a valid `<id>` selector, "unrestricted" otherwise).
1355
+ * @typedef {Token & { typeFlag: "id" | "unrestricted" }} HashToken
1356
+ */
1357
+
1358
+ /**
1359
+ * Old-style unquoted URL token (`url(unquoted)`). `value` is the unquoted body;
1360
+ * `contentStart` / `contentEnd` mark the inner content range in the source.
1361
+ * @typedef {Token & { contentStart: number, contentEnd: number }} UrlToken
1362
+ */
1363
+
1364
+ /**
1365
+ * Function node: `name(component-values...)`. `name` is the raw source slice
1366
+ * before the `(` (callers lowercase / unescape as needed); `nameStart` / `nameEnd`
1367
+ * are its `[start, end)` byte offsets; `value` is the component values inside the parentheses.
1368
+ * @typedef {Node & { name: string, nameStart: number, nameEnd: number, value: ComponentValue[] }} FunctionNode
1369
+ */
1370
+
1371
+ /** @typedef {"[" | "(" | "{"} SimpleBlockToken */
1372
+
1373
+ /**
1374
+ * Simple block (`[...]`, `(...)` not preceded by an ident, `{...}`). `token` is
1375
+ * the opening character. `value` is the component values inside. This shape is
1376
+ * produced by `consumeASimpleBlock` (§5.4.9) and appears in preludes.
1377
+ *
1378
+ * Note: `consumeABlock` (§5.4.4) returns the parsed block's separate `decls` /
1379
+ * `rules` lists (per §5.4.5), not a SimpleBlock wrapper — see
1380
+ * `AtRule` / `QualifiedRule`'s `declarations` and `childRules` fields.
1381
+ * @typedef {Node & { token: SimpleBlockToken, value: ComponentValue[] }} SimpleBlock
1382
+ */
1383
+
1384
+ /**
1385
+ * A CSS component value (CSS Syntax §5.4.8): a preserved token, a function, or
1386
+ * a simple block (`Token` also covers `HashToken` / `UrlToken`).
1387
+ * @typedef {Token | FunctionNode | SimpleBlock} ComponentValue
1388
+ */
1389
+
1390
+ /**
1391
+ * A CSS rule — an at-rule or a qualified rule.
1392
+ * @typedef {AtRule | QualifiedRule} Rule
1393
+ */
1394
+
1395
+ /**
1396
+ * Declaration: `name: value [!important][;]`. `name` is the raw property-name
1397
+ * slice; `value` is the trimmed component-value list (whitespace stripped from
1398
+ * both ends); `important` records a stripped `!important`.
1399
+ * @typedef {Node & { name: string, nameStart: number, nameEnd: number, value: ComponentValue[], important: boolean }} Declaration
1400
+ */
1401
+
1402
+ /**
1403
+ * At-rule: `@name <prelude> ;` or `@name <prelude> { ... }`. `name` is the
1404
+ * at-keyword without the leading `@`; `prelude` is the component values up to
1405
+ * the at-rule's `;` / block / enclosing `}`. Per §5.4.2 the block is consumed
1406
+ * into separate `declarations` (a `Declaration[]`) and `childRules` (a `Rule[]`,
1407
+ * each an at-rule or qualified rule); both are `null` for a `;`-terminated
1408
+ * at-rule. `blockStart` / `blockEnd` are the `{` start / `}` end offsets
1409
+ * (webpack extension, not in spec; the spec doesn't track brace positions), or
1410
+ * `-1` / `-1` when there is no block. `range[1]` points past `}` for a block, or
1411
+ * at the `;` / `}` / EOF position otherwise (callers check the byte at `range[1]`
1412
+ * to tell them apart).
1413
+ * @typedef {Node & { name: string, nameStart: number, nameEnd: number, prelude: ComponentValue[], declarations: Declaration[] | null, childRules: Rule[] | null, blockStart: number, blockEnd: number }} AtRule
1414
+ */
1415
+
1416
+ /**
1417
+ * Qualified rule: `<prelude> { <block> }`. `prelude` is the component values
1418
+ * before the `{` (selectors, keyframe parameters, …); `declarations` and
1419
+ * `childRules` are the parsed `{ ... }` body (split per tabatkins/parse-css.js
1420
+ * reference impl), or both `null` when EOF was hit before `{`. `blockStart` /
1421
+ * `blockEnd` are the `{` start / `}` end offsets (webpack extension), or `-1` /
1422
+ * `-1` when there is no block.
1423
+ * @typedef {Node & { prelude: ComponentValue[], declarations: Declaration[] | null, childRules: Rule[] | null, blockStart: number, blockEnd: number }} QualifiedRule
1424
+ */
1425
+
1426
+ /**
1427
+ * Stylesheet (CSS Syntax §5.3.4): the result of `parseAStylesheet`. `rules`
1428
+ * holds the top-level at-rules / qualified rules (top-level declarations are
1429
+ * parse errors and never produced).
1430
+ * @typedef {Node & { rules: Rule[] }} Stylesheet
1431
+ */
1432
+
1433
+ /**
1434
+ * Materialize a single non-block, non-function lexer token as its leaf AST node — the spec's "consume a token" result (§5.4.8 "anything else"), preserving stray closers / CDO / CDC.
1435
+ * @param {MutableToken} t token from the lexer
1436
+ * @param {string} input source
1437
+ * @param {LocConverter} locConverter shared loc converter
1438
+ * @returns {Token} the leaf token node
1439
+ */
1440
+ const tokenToNode = (t, input, locConverter) => {
1441
+ switch (t.type) {
1442
+ case TT_WHITESPACE:
1443
+ return new Token(T_WHITESPACE, t.start, t.end, locConverter);
1444
+ case TT_IDENTIFIER:
1445
+ return new Token(T_IDENT, t.start, t.end, locConverter);
1446
+ case TT_STRING:
1447
+ return new Token(T_STRING, t.start, t.end, locConverter);
1448
+ case TT_DELIM:
1449
+ return new Token(T_DELIM, t.start, t.end, locConverter);
1450
+ // Numeric tokens: construct only — `numericValue` / `typeFlag` / `sign` /
1451
+ // `unit` are lazy `Token` getters, so the `Number()` parse + slices +
1452
+ // `toLowerCase` are skipped for the (common) tokens never read numerically.
1453
+ case TT_NUMBER:
1454
+ return new Token(T_NUMBER, t.start, t.end, locConverter);
1455
+ case TT_PERCENTAGE:
1456
+ return new Token(T_PERCENTAGE, t.start, t.end, locConverter);
1457
+ case TT_DIMENSION:
1458
+ return new Token(T_DIMENSION, t.start, t.end, locConverter);
1459
+ case TT_HASH: {
1460
+ const hash = new Token(T_HASH, t.start, t.end, locConverter);
1461
+ hash._value = input.slice(t.start + 1, t.end);
1462
+ // Store the raw id-ness; the `typeFlag` getter maps it to "id" /
1463
+ // "unrestricted". Kept off `Token`'s declared fields so non-hash
1464
+ // tokens don't carry the slot.
1465
+ /** @type {Token & { _isId: boolean }} */ (hash)._isId = t.isId;
1466
+ return hash;
1467
+ }
1468
+ case TT_AT_KEYWORD: {
1469
+ const at = new Token(T_AT_KEYWORD, t.start, t.end, locConverter);
1470
+ at._value = input.slice(t.start + 1, t.end);
1471
+ return at;
1472
+ }
1473
+ case TT_URL: {
1474
+ const ut = /** @type {CssUrlToken} */ (t);
1475
+ const url = /** @type {UrlToken} */ (
1476
+ new Token(T_URL, t.start, t.end, locConverter)
1477
+ );
1478
+ url._value = input.slice(ut.contentStart, ut.contentEnd);
1479
+ url.contentStart = ut.contentStart;
1480
+ url.contentEnd = ut.contentEnd;
1481
+ return url;
1482
+ }
1483
+ case TT_BAD_STRING_TOKEN:
1484
+ return new Token(T_BAD_STRING, t.start, t.end, locConverter);
1485
+ case TT_BAD_URL_TOKEN:
1486
+ return new Token(T_BAD_URL, t.start, t.end, locConverter);
1487
+ case TT_COLON:
1488
+ return new Token(T_COLON, t.start, t.end, locConverter);
1489
+ case TT_COMMA:
1490
+ return new Token(T_COMMA, t.start, t.end, locConverter);
1491
+ case TT_SEMICOLON:
1492
+ return new Token(T_SEMICOLON, t.start, t.end, locConverter);
1493
+ // Stray closers / CDO / CDC reach here only on malformed input; the spec
1494
+ // preserves them as component values ("consume a token and return it").
1495
+ case TT_RIGHT_PARENTHESIS:
1496
+ return new Token(T_RIGHT_PARENTHESIS, t.start, t.end, locConverter);
1497
+ case TT_RIGHT_SQUARE_BRACKET:
1498
+ return new Token(T_RIGHT_SQUARE_BRACKET, t.start, t.end, locConverter);
1499
+ case TT_RIGHT_CURLY_BRACKET:
1500
+ return new Token(T_RIGHT_CURLY_BRACKET, t.start, t.end, locConverter);
1501
+ case TT_CDO:
1502
+ return new Token(T_CDO, t.start, t.end, locConverter);
1503
+ case TT_CDC:
1504
+ return new Token(T_CDC, t.start, t.end, locConverter);
1505
+ /* istanbul ignore next -- @preserve: unreachable; blocks/functions are routed earlier, comments filtered, EOF guarded by callers */
1506
+ default:
1507
+ throw new Error(`Unexpected token type "${t.type}"`);
1508
+ }
1509
+ };
1510
+
1511
+ /**
1512
+ * Position-based view over the lexer — webpack's stand-in for the spec's
1513
+ * "normalize into a token stream" (CSS Syntax §9). It unifies the lexer and the
1514
+ * stream in one class: the `readToken` primitive lexes one token (the CSS
1515
+ * tokenizer), and the spec token-stream operations `next` / `consume` /
1516
+ * `discard` / `mark` / `restoreMark` / `discardMark` drive it from a byte
1517
+ * cursor. `parse*` entry points wrap a source string in one of these and every
1518
+ * `consume*` algorithm reads tokens from it.
1519
+ *
1520
+ * No token buffer is kept: the cursor is a byte offset and the only state is
1521
+ * the next token (lazily tokenized once and cached until consumed). The
1522
+ * declaration-vs-qualified-rule backtracking in `consumeABlocksContents`
1523
+ * rewinds by `mark`ing / `restoreMark`ing that byte offset, which simply
1524
+ * re-tokenizes the rewound span — comment tokens are filtered here and fire
1525
+ * `onComment` once each, tracked by a monotonic high-water mark so a
1526
+ * re-tokenized span never re-fires them.
1527
+ *
1528
+ * `SourceProcessor` is handed this class (not an instance) and threads it to
1529
+ * the grammar, so a different language can drive the same visitor machinery by
1530
+ * swapping the tokenizer — the per-token `readToken` primitive — for its own.
1531
+ */
1532
+ class TokenStream {
1533
+ /**
1534
+ * @param {string} input source
1535
+ * @param {number=} pos start byte offset (default `0`)
1536
+ * @param {LocConverter=} locConverter shared loc converter (default a fresh one over `input`)
1537
+ * @param {((input: string, start: number, end: number) => number)=} onComment comment-token callback
1538
+ */
1539
+ constructor(
1540
+ input,
1541
+ pos = 0,
1542
+ locConverter = new LocConverter(input),
1543
+ onComment = undefined
1544
+ ) {
1545
+ /** @type {string} */
1546
+ this.input = input;
1547
+ /** @type {LocConverter} */
1548
+ this.locConverter = locConverter;
1549
+ this._onComment = onComment;
1550
+ // Byte offset where the next token is tokenized from.
1551
+ /** @type {number} */
1552
+ this._pos = pos;
1553
+ // Comments before this offset have already fired `onComment`; a
1554
+ // re-tokenized (backtracked) span never re-fires them.
1555
+ /** @type {number} */
1556
+ this._commentHigh = pos;
1557
+ // Single reused token the lexer writes into on the `next` path — see
1558
+ // `MutableToken`. `_next` points at it when a token is cached, else
1559
+ // `undefined` (re-tokenize on next read).
1560
+ /** @type {MutableToken} */
1561
+ this._tok = createToken();
1562
+ /** @type {MutableToken | undefined} the next token, lazily tokenized */
1563
+ this._next = undefined;
1564
+ /** @type {number[]} byte offsets to rewind to */
1565
+ this._marks = [];
1566
+ }
1567
+
1568
+ /**
1569
+ * The next token (CSS Syntax §3 "next token") — the upcoming token without
1570
+ * consuming it; the `<eof-token>` once the source is exhausted. This is the
1571
+ * token the consume algorithms dispatch on (the spec's "process"). Tokenized
1572
+ * from `_pos` on first use and cached until consumed; comment tokens are
1573
+ * skipped here, firing `onComment` once each.
1574
+ * @returns {MutableToken} the next token
1575
+ */
1576
+ next() {
1577
+ if (this._next === undefined) {
1578
+ const input = this.input;
1579
+ const tok = this._tok;
1580
+ let pos = this._pos;
1581
+ for (;;) {
1582
+ const t = readToken(input, pos, tok);
1583
+ if (t === undefined) {
1584
+ this._next = fill(tok, TT_EOF, input.length, input.length);
1585
+ break;
1586
+ }
1587
+ if (t.type === TT_COMMENT) {
1588
+ if (t.start >= this._commentHigh) {
1589
+ if (this._onComment) this._onComment(input, t.start, t.end);
1590
+ this._commentHigh = t.end;
1591
+ }
1592
+ pos = t.end;
1593
+ continue;
1594
+ }
1595
+ this._next = t;
1596
+ break;
1597
+ }
1598
+ }
1599
+ return /** @type {MutableToken} */ (this._next);
1600
+ }
1601
+
1602
+ /**
1603
+ * Consume a token (CSS Syntax §3 "consume a token") — return the next token
1604
+ * and advance the cursor past it. The returned token is valid until the next
1605
+ * `next` re-tokenizes (the reused instance is not cleared by advancing).
1606
+ * @returns {MutableToken} the consumed token
1607
+ */
1608
+ consume() {
1609
+ const t = this.next();
1610
+ if (t.type !== TT_EOF) {
1611
+ this._pos = t.end;
1612
+ this._next = undefined;
1613
+ }
1614
+ return t;
1615
+ }
1616
+
1617
+ /**
1618
+ * Discard a token (CSS Syntax §3 "discard a token") — advance the cursor past
1619
+ * the next token without returning it.
1620
+ * @returns {void}
1621
+ */
1622
+ discard() {
1623
+ const t = this.next();
1624
+ if (t.type !== TT_EOF) {
1625
+ this._pos = t.end;
1626
+ this._next = undefined;
1627
+ }
1628
+ }
1629
+
1630
+ /**
1631
+ * Mark (CSS Syntax §3 "mark") — push the current cursor position.
1632
+ * @returns {void}
1633
+ */
1634
+ mark() {
1635
+ this._marks.push(this._pos);
1636
+ }
1637
+
1638
+ /**
1639
+ * Restore a mark (CSS Syntax §3 "restore a mark") — pop the last mark and
1640
+ * rewind the cursor to it. The rewound span is re-tokenized on the next read;
1641
+ * already-fired comments are not re-fired (`_commentHigh`).
1642
+ * @returns {void}
1643
+ */
1644
+ restoreMark() {
1645
+ this._pos = /** @type {number} */ (this._marks.pop());
1646
+ this._next = undefined;
1647
+ }
1648
+
1649
+ /**
1650
+ * Discard a mark (CSS Syntax §3 "discard a mark") — pop without rewinding.
1651
+ * @returns {void}
1652
+ */
1653
+ discardMark() {
1654
+ this._marks.pop();
1655
+ }
1656
+ }
1657
+
1658
+ /**
1659
+ * Normalize a `parse*` entry point's first argument into a `TokenStream`
1660
+ * (CSS Syntax §9 "normalize into a token stream"). An existing `TokenStream`
1661
+ * is returned as-is (consumed from its current position — it already carries
1662
+ * the shared `LocConverter` and comment hook), so `pos` / `onComment` are
1663
+ * ignored. A raw source string is tokenized from `pos` with a fresh
1664
+ * `LocConverter`; pass a `TokenStream` instead to share one converter across
1665
+ * sub-parses.
1666
+ * @param {string | TokenStream} input source string or an existing stream
1667
+ * @param {number=} pos start byte offset (string input only; default `0`)
1668
+ * @param {((input: string, start: number, end: number) => number)=} onComment comment callback (string input only)
1669
+ * @returns {TokenStream} the stream to consume from
1670
+ */
1671
+ const normalizeIntoTokenStream = (input, pos, onComment) =>
1672
+ input instanceof TokenStream
1673
+ ? input
1674
+ : new TokenStream(input, pos || 0, new LocConverter(input), onComment);
1675
+
1676
+ // === Parser entry points (CSS Syntax Level 3 §5.3) ===
1677
+ // Each `parseA*` is a thin public wrapper over a `consumeA*` algorithm
1678
+ // (§5.4): it takes raw source + a start position (webpack's stand-in for
1679
+ // the spec's "normalize into a token stream") and runs the matching
1680
+ // consume algorithm. The split mirrors tabatkins/parse-css — `parse*`
1681
+ // are the documented entry points, `consume*` are the internal
1682
+ // algorithms that drive the tokenizer.
1683
+
1684
+ /**
1685
+ * @typedef {object} ParseListOptions
1686
+ * @property {((input: string, start: number, end: number) => number)=} comment optional comment-token callback; the public `parse*` entry points use it to build the `TokenStream` so the outer parser's comment tracker still sees magic comments inside the consumed range
1687
+ */
1688
+
1689
+ /**
1690
+ * Parse a stylesheet, CSS Syntax Level 3
1691
+ * [§5.3.4](https://drafts.csswg.org/css-syntax/#parse-stylesheet).
1692
+ * @param {string | TokenStream} input source string or an existing token stream
1693
+ * @param {((input: string, start: number, end: number) => number)=} comment optional comment-token callback (string input only)
1694
+ * @returns {Stylesheet} the parsed stylesheet
1695
+ */
1696
+ const parseAStylesheet = (input, comment) => {
1697
+ // 1. If input is a byte stream for a stylesheet, decode bytes from input, and set input to the result.
1698
+ // 2. Normalize input, and set input to the result.
1699
+ const ts = normalizeIntoTokenStream(input, 0, comment);
1700
+ // 3. Create a new stylesheet, with its location set to location (or null, if location was not passed).
1701
+ const start = ts.next().start;
1702
+ const stylesheet = /** @type {Stylesheet} */ (
1703
+ new Node(T_STYLESHEET, start, start, ts.locConverter)
1704
+ );
1705
+ stylesheet.rules = /** @type {Rule[]} */ ([]);
1706
+ // 4. Consume a stylesheet's contents from input, and set the stylesheet's rules to the result.
1707
+ stylesheet.rules = consumeAStylesheetsContents(ts);
1708
+ stylesheet.end = ts.next().start;
1709
+ // 5. Return the stylesheet.
1710
+ return stylesheet;
1711
+ };
1712
+
1713
+ /**
1714
+ * Parse a stylesheet's contents, CSS Syntax Level 3
1715
+ * [§5.3.5](https://drafts.csswg.org/css-syntax/#parse-stylesheets-contents) —
1716
+ * the top-level rule list via `consumeAStylesheetsContents` (§5.4.1): top-level
1717
+ * declarations are parse errors (never produced) and top-level CDO (`<!--`) /
1718
+ * CDC (`-->`) tokens are discarded.
1719
+ * @param {string | TokenStream} input source string or an existing token stream
1720
+ * @param {((input: string, start: number, end: number) => number)=} comment optional comment-token callback (string input only)
1721
+ * @returns {Rule[]} top-level rules
1722
+ */
1723
+ const parseAStylesheetsContents = (input, comment) => {
1724
+ // 1. Normalize input, and set input to the result.
1725
+ const ts = normalizeIntoTokenStream(input, 0, comment);
1726
+ // 2. Consume a stylesheet’s contents from input, and return the result.
1727
+ return consumeAStylesheetsContents(ts);
1728
+ };
1729
+
1730
+ /**
1731
+ * Parse a block's contents, CSS Syntax Level 3
1732
+ * [§5.3.6](https://drafts.csswg.org/css-syntax/#parse-block-contents).
1733
+ * @param {string | TokenStream} input source string or an existing token stream
1734
+ * @param {number=} pos start position (string input only; just past the opening `{`, or 0)
1735
+ * @param {((input: string, start: number, end: number) => number)=} comment optional comment-token callback (string input only)
1736
+ * @returns {{ decls: Declaration[], rules: Rule[] }} block decls + rules
1737
+ */
1738
+ const parseABlocksContents = (input, pos, comment) => {
1739
+ // 1. Normalize input, and set input to the result.
1740
+ const ts = normalizeIntoTokenStream(input, pos, comment);
1741
+ // 2. Consume a block’s contents from input, and return the result.
1742
+ return consumeABlocksContents(ts);
1743
+ };
1744
+
1745
+ /**
1746
+ * Parse a rule, CSS Syntax Level 3
1747
+ * [§5.3.7](https://drafts.csswg.org/css-syntax/#parse-rule) — discards leading
1748
+ * whitespace, consumes one at-rule / qualified rule, and requires only trailing
1749
+ * whitespace; `undefined` (syntax error) otherwise.
1750
+ * @param {string | TokenStream} input source string or an existing token stream
1751
+ * @param {number=} pos start position (string input only)
1752
+ * @param {((input: string, start: number, end: number) => number)=} comment optional comment-token callback (string input only)
1753
+ * @returns {Rule | undefined} the parsed rule
1754
+ */
1755
+ const parseARule = (input, pos, comment) => {
1756
+ // 1. Normalize input, and set input to the result.
1757
+ const ts = normalizeIntoTokenStream(input, pos, comment);
1758
+ // 2. Discard whitespace from input.
1759
+ while (ts.next().type === TT_WHITESPACE) ts.discard();
1760
+ // 3. If the next token from input is an <EOF-token>, return a syntax error.
1761
+ // Otherwise, if the next token from input is an <at-keyword-token>, consume an at-rule from input, and let rule be the return value.
1762
+ // Otherwise, consume a qualified rule from input and let rule be the return value.
1763
+ // If nothing or an invalid rule error was returned, return a syntax error.
1764
+ const head = ts.next();
1765
+ if (head.type === TT_EOF) return undefined;
1766
+ const rule =
1767
+ head.type === TT_AT_KEYWORD
1768
+ ? consumeAnAtRule(ts)
1769
+ : consumeAQualifiedRule(ts);
1770
+ if (!rule) return undefined;
1771
+ // 4. Discard whitespace from input.
1772
+ while (ts.next().type === TT_WHITESPACE) ts.discard();
1773
+ // 5. If the next token from input is an <EOF-token>, return rule. Otherwise, return a syntax error.
1774
+ return ts.next().type === TT_EOF ? rule : undefined;
1775
+ };
1776
+
1777
+ /**
1778
+ * Parse a declaration, CSS Syntax Level 3
1779
+ * [§5.3.8](https://drafts.csswg.org/css-syntax/#parse-declaration).
1780
+ * @param {string | TokenStream} input source string or an existing token stream
1781
+ * @param {number=} pos start position (string input only)
1782
+ * @param {((input: string, start: number, end: number) => number)=} comment optional comment-token callback (string input only)
1783
+ * @returns {Declaration | undefined} the parsed declaration, or undefined
1784
+ */
1785
+ const parseADeclaration = (input, pos, comment) => {
1786
+ // 1. Normalize input, and set input to the result.
1787
+ const ts = normalizeIntoTokenStream(input, pos, comment);
1788
+ // 2. Discard whitespace from input.
1789
+ while (ts.next().type === TT_WHITESPACE) ts.discard();
1790
+ // 3. Consume a declaration from input. If anything was returned, return it. Otherwise, return a syntax error.
1791
+ return consumeADeclaration(ts);
1792
+ };
1793
+
1794
+ /**
1795
+ * Parse a component value, CSS Syntax Level 3 [§5.3.9](https://drafts.csswg.org/css-syntax/#parse-component-value) — strict entry point that consumes one value and returns `undefined` if non-whitespace input trails (use `consumeAComponentValue` for "one value, ignore the rest").
1796
+ * @param {string | TokenStream} input source string or an existing token stream
1797
+ * @param {number=} pos start position (string input only)
1798
+ * @param {{ comment?: (input: string, start: number, end: number) => number }=} options optional comment-token callback (string input only)
1799
+ * @returns {ComponentValue | undefined} the parsed component value, or `undefined` on empty / trailing-garbage input
1800
+ */
1801
+ const parseAComponentValue = (input, pos, options = {}) => {
1802
+ // 1. Normalize input, and set input to the result.
1803
+ const ts = normalizeIntoTokenStream(input, pos, options.comment);
1804
+ // 2. Discard whitespace from input.
1805
+ while (ts.next().type === TT_WHITESPACE) ts.discard();
1806
+ // 3. If input is empty, return a syntax error.
1807
+ if (ts.next().type === TT_EOF) return undefined;
1808
+ // 4. Consume a component value from input and let value be the return value.
1809
+ const result = consumeAComponentValue(ts);
1810
+ // 5. Discard whitespace from input.
1811
+ while (ts.next().type === TT_WHITESPACE) ts.discard();
1812
+ // 6. If input is empty, return value. Otherwise, return a syntax error.
1813
+ if (ts.next().type === TT_EOF) return result;
1814
+ return undefined;
1815
+ };
1816
+
1817
+ /**
1818
+ * Parse a list of component values, CSS Syntax Level 3
1819
+ * [§5.3.10](https://drafts.csswg.org/css-syntax/#parse-list-of-components).
1820
+ * @param {string | TokenStream} input source string or an existing token stream
1821
+ * @param {number=} pos start position (string input only)
1822
+ * @param {ParseListOptions=} options comment callback
1823
+ * @returns {ComponentValue[]} component values
1824
+ */
1825
+ const parseAListOfComponentValues = (input, pos, options = {}) => {
1826
+ // 1. Normalize input, and set input to the result.
1827
+ const ts = normalizeIntoTokenStream(input, pos, options.comment);
1828
+ // 2. Consume a list of component values from input, and return the result.
1829
+ return consumeAListOfComponentValues(ts);
1830
+ };
1831
+
1832
+ /**
1833
+ * Parse a comma-separated list of component values, CSS Syntax Level 3 [§5.3.11](https://drafts.csswg.org/css-syntax/#parse-comma-list) — consumes one `<comma-token>`-stopped group of component values per iteration until EOF.
1834
+ * @param {string | TokenStream} input source string or an existing token stream
1835
+ * @param {number=} pos start position (string input only)
1836
+ * @param {ParseListOptions=} options comment callback
1837
+ * @returns {ComponentValue[][]} comma-separated groups of component values
1838
+ */
1839
+ const parseACommaSeparatedListOfComponentValues = (
1840
+ input,
1841
+ pos,
1842
+ options = {}
1843
+ ) => {
1844
+ // 1. Normalize input, and set input to the result.
1845
+ const ts = normalizeIntoTokenStream(input, pos, options.comment);
1846
+ // 2. Let groups be an empty list.
1847
+ /** @type {ComponentValue[][]} */
1848
+ const groups = [];
1849
+ // 3. While input is not empty:
1850
+ while (ts.next().type !== TT_EOF) {
1851
+ // 3.1. Consume a list of component values from input, with <comma-token> as the stop token, and append the result to groups.
1852
+ groups.push(consumeAListOfComponentValues(ts, TT_COMMA));
1853
+ // 3.2 Discard a token from input.
1854
+ ts.discard();
1855
+ }
1856
+ // 4. Return groups.
1857
+ return groups;
1858
+ };
1859
+
1860
+ // === Parser algorithms (CSS Syntax Level 3 §5.4) ===
1861
+ // The mutually-recursive consume algorithms the `parse*` entry points drive:
1862
+ // each reads tokens from a `TokenStream` and reuses `consumeAComponentValue`
1863
+ // for nested values, mirroring tabatkins/parse-css.
1864
+
1865
+ /**
1866
+ * Consume a stylesheet's contents, CSS Syntax Level 3 [§5.4.1](https://drafts.csswg.org/css-syntax/#consume-stylesheet-contents) — the top-level rule list: whitespace and CDO (`<!--`) / CDC (`-->`) tokens are discarded, an at-keyword starts an at-rule, and anything else starts a qualified rule (so top-level declarations are parse errors and never produced).
1867
+ *
1868
+ * `onRule` is a webpack extension to the algorithm's output: when given, each
1869
+ * consumed rule is handed to it immediately and not collected, so the walker can
1870
+ * process one top-level rule at a time without materializing the whole
1871
+ * stylesheet (the returned list is then empty). When omitted the rules are
1872
+ * collected and returned as the spec specifies.
1873
+ * @param {TokenStream} ts token stream
1874
+ * @param {((rule: Rule) => void)=} onRule optional per-rule sink (streaming); rules are not collected when given
1875
+ * @returns {Rule[]} top-level rules (empty when `onRule` is given)
1876
+ */
1877
+ const consumeAStylesheetsContents = (ts, onRule) => {
1878
+ // Let rules be an initially empty list of rules.
1879
+ /** @type {Rule[]} */
1880
+ const rules = [];
1881
+
1882
+ // Process input
1883
+ for (;;) {
1884
+ const t = ts.next();
1885
+ // <whitespace-token> / <CDO-token> / <CDC-token>
1886
+ // Discard a token from input.
1887
+ if (t.type === TT_WHITESPACE || t.type === TT_CDO || t.type === TT_CDC) {
1888
+ ts.discard();
1889
+ }
1890
+ // <EOF-token>
1891
+ // Return rules.
1892
+ else if (t.type === TT_EOF) {
1893
+ return rules;
1894
+ }
1895
+ // <at-keyword-token>
1896
+ // Consume an at-rule from input. If anything is returned, append it to rules.
1897
+ else if (t.type === TT_AT_KEYWORD) {
1898
+ const at = consumeAnAtRule(ts);
1899
+ if (at) {
1900
+ if (onRule) onRule(at);
1901
+ else rules.push(at);
1902
+ }
1903
+ }
1904
+ // anything else
1905
+ // Consume a qualified rule from input. If a rule is returned, append it to rules.
1906
+ else {
1907
+ const rule = consumeAQualifiedRule(ts);
1908
+ if (rule) {
1909
+ if (onRule) onRule(rule);
1910
+ else rules.push(rule);
1911
+ }
1912
+ }
1913
+ }
1914
+ };
1915
+
1916
+ /**
1917
+ * Consume an at-rule, CSS Syntax Level 3 [§5.4.2](https://drafts.csswg.org/css-syntax/#consume-at-rule) — the next token must be an <at-keyword-token> (asserted); consumes the prelude up to `;` / `{` / `}` / EOF; `{` consumes the block (§5.4.4) onto `.block`, `;` / EOF is discarded, a top-level `}` (when not `nested`) is appended via `consumeAComponentValue`.
1918
+ * @param {TokenStream} ts token stream
1919
+ * @param {boolean=} nested true inside a `{}` block — a top-level `}` ends the at-rule (left for the caller)
1920
+ * @returns {AtRule | undefined} the parsed at-rule
1921
+ */
1922
+ const consumeAnAtRule = (ts, nested = false) => {
1923
+ // Assert (spec): the next token is an <at-keyword-token>.
1924
+ // Consume a token from input, and let rule be a new at-rule with its name set to the returned token’s value, its prelude initially set to an empty list, and no declarations or child rules.
1925
+ const head = ts.consume();
1926
+ const rule = /** @type {AtRule} */ (
1927
+ new Node(T_AT_RULE, head.start, head.end, ts.locConverter)
1928
+ );
1929
+ rule.name = ts.input.slice(head.start + 1, head.end);
1930
+ rule.nameStart = head.start;
1931
+ rule.nameEnd = head.end;
1932
+ rule.prelude = /** @type {ComponentValue[]} */ ([]);
1933
+ rule.declarations = null;
1934
+ rule.childRules = null;
1935
+ // -1 = no block (the `;` / EOF / nested-`}` at-rule forms).
1936
+ rule.blockStart = -1;
1937
+ rule.blockEnd = -1;
1938
+
1939
+ // Process input
1940
+ for (;;) {
1941
+ const t = ts.next();
1942
+
1943
+ // <semicolon-token>
1944
+ // <EOF-token>
1945
+ // Discard a token from input. If rule is valid in the current context, return it; otherwise return nothing.
1946
+ if (t.type === TT_SEMICOLON || t.type === TT_EOF) {
1947
+ ts.discard();
1948
+ rule.end = t.start;
1949
+ return rule;
1950
+ }
1951
+ // <}-token>
1952
+ // If nested is true: if rule is valid in the current context, return it; otherwise return nothing.
1953
+ // Otherwise, consume a token and append the result to rule’s prelude.
1954
+ else if (t.type === TT_RIGHT_CURLY_BRACKET) {
1955
+ if (nested) {
1956
+ rule.end = t.start;
1957
+ return rule;
1958
+ }
1959
+ rule.prelude.push(consumeATokenAsNode(ts));
1960
+ continue;
1961
+ }
1962
+ // <{-token>
1963
+ // Consume a block from input, and assign the result to rule's declarations and child rules.
1964
+ else if (t.type === TT_LEFT_CURLY_BRACKET) {
1965
+ const block = consumeABlock(ts);
1966
+ rule.declarations = block.decls;
1967
+ rule.childRules = block.rules;
1968
+ rule.blockStart = block.blockStart;
1969
+ rule.blockEnd = block.blockEnd;
1970
+ rule.end = block.blockEnd;
1971
+ return rule;
1972
+ }
1973
+
1974
+ // anything else
1975
+ // Consume a component value from input and append the returned value to rule’s prelude.
1976
+ rule.prelude.push(consumeAComponentValue(ts));
1977
+ }
1978
+ };
1979
+
1980
+ /**
1981
+ * Consume a token (CSS Syntax §3 "consume a token"): advance past the next
1982
+ * token and return it as a leaf AST node. Used directly where the spec says
1983
+ * "consume a token from input" (e.g. the parse-error branches in §5.4.7 /
1984
+ * §5.4.2 / §5.4.3), distinct from `consumeAComponentValue` which would recurse
1985
+ * into a simple block / function.
1986
+ * @param {TokenStream} ts token stream
1987
+ * @returns {Token} the consumed token as a leaf node
1988
+ */
1989
+ const consumeATokenAsNode = (ts) => {
1990
+ const t = ts.consume();
1991
+ return tokenToNode(t, ts.input, ts.locConverter);
1992
+ };
1993
+
1994
+ /**
1995
+ * Consume a qualified rule, CSS Syntax Level 3 [§5.4.3](https://drafts.csswg.org/css-syntax/#consume-qualified-rule) — consumes the prelude (each component value via `consumeAComponentValue`) up to its `{` block; EOF, the optional `stopToken`, or a nested top-level `}` is a parse error returning nothing (the block-less prelude is dropped), while a non-nested top-level `}` is consumed as a parse error and the prelude continues. A returned rule always has a block.
1996
+ * @param {TokenStream} ts token stream
1997
+ * @param {number=} stopToken token type that ends the prelude (parse error → nothing)
1998
+ * @param {boolean=} nested true inside a `{}` block — a top-level `}` ends the rule (left for the caller)
1999
+ * @returns {QualifiedRule | undefined} parsed qualified rule, or `undefined` on a parse error
2000
+ */
2001
+ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
2002
+ const start = ts.next().start;
2003
+ // Let rule be a new qualified rule with its prelude, declarations, and child rules all initially set to empty lists.
2004
+ const rule = /** @type {QualifiedRule} */ (
2005
+ new Node(T_QUALIFIED_RULE, start, start, ts.locConverter)
2006
+ );
2007
+ rule.prelude = /** @type {ComponentValue[]} */ ([]);
2008
+ rule.declarations = null;
2009
+ rule.childRules = null;
2010
+ // -1 = no block (EOF reached before `{`).
2011
+ rule.blockStart = -1;
2012
+ rule.blockEnd = -1;
2013
+
2014
+ // Process input
2015
+ for (;;) {
2016
+ const t = ts.next();
2017
+ // <EOF-token>
2018
+ // stop token (if passed)
2019
+ // This is a parse error. Return nothing.
2020
+ if (t.type === TT_EOF || t.type === stopToken) {
2021
+ return undefined;
2022
+ }
2023
+ // <}-token>
2024
+ // This is a parse error. If nested is true, return nothing. Otherwise, consume a token and append the result to rule’s prelude.
2025
+ else if (t.type === TT_RIGHT_CURLY_BRACKET) {
2026
+ if (nested) return undefined;
2027
+ rule.prelude.push(consumeATokenAsNode(ts));
2028
+ continue;
2029
+ }
2030
+ // <{-token>
2031
+ // If the first two non-<whitespace-token> values of rule's prelude are an <ident-token> whose value starts with "--" followed by a <colon-token>, then:
2032
+ // - If nested is true, consume the remnants of a bad declaration from input, with nested set to true, and return nothing.
2033
+ // - If nested is false, consume a block from input, and return nothing.
2034
+ // (This disambiguates custom-property declarations from nested qualified rules — `--foo: { … }` at top level of a block is a declaration, not a rule.)
2035
+ // Otherwise, consume a block from input, and let child rules be the result.
2036
+ else if (t.type === TT_LEFT_CURLY_BRACKET) {
2037
+ let firstIdx = 0;
2038
+ /* istanbul ignore next -- @preserve: leading whitespace is discarded before the rule, so the prelude never starts with it */
2039
+ while (
2040
+ firstIdx < rule.prelude.length &&
2041
+ rule.prelude[firstIdx].type === T_WHITESPACE
2042
+ ) {
2043
+ firstIdx++;
2044
+ }
2045
+ let secondIdx = firstIdx + 1;
2046
+ while (
2047
+ secondIdx < rule.prelude.length &&
2048
+ rule.prelude[secondIdx].type === T_WHITESPACE
2049
+ ) {
2050
+ secondIdx++;
2051
+ }
2052
+ const first = rule.prelude[firstIdx];
2053
+ const second = rule.prelude[secondIdx];
2054
+ if (
2055
+ first &&
2056
+ first.type === T_IDENT &&
2057
+ // Test the source bytes directly — avoids forcing the lazy `value`
2058
+ // slice just to check the `--` custom-property prefix.
2059
+ ts.input.startsWith("--", first.start) &&
2060
+ second &&
2061
+ second.type === T_COLON
2062
+ ) {
2063
+ /* istanbul ignore if -- @preserve: when nested, `declarationStartLikely` routes every `--x:` to consumeADeclaration (which accepts custom properties), so this fallthrough is unreachable */
2064
+ if (nested) {
2065
+ consumeTheRemnantsOfABadDeclaration(ts, true);
2066
+ } else {
2067
+ consumeABlock(ts);
2068
+ }
2069
+ return undefined;
2070
+ }
2071
+ const block = consumeABlock(ts);
2072
+ rule.declarations = block.decls;
2073
+ rule.childRules = block.rules;
2074
+ rule.blockStart = block.blockStart;
2075
+ rule.blockEnd = block.blockEnd;
2076
+ rule.end = block.blockEnd;
2077
+ return rule;
2078
+ }
2079
+
2080
+ // anything else
2081
+ // Consume a component value from input and append the result to rule’s prelude.
2082
+ rule.prelude.push(consumeAComponentValue(ts));
2083
+ }
2084
+ };
2085
+
2086
+ /**
2087
+ * Consume a block, CSS Syntax Level 3 [§5.4.4](https://drafts.csswg.org/css-syntax/#consume-block) — the next token must be `<{-token>`; discards it, consumes the block's contents (§5.4.5), discards the closing `}`, and returns its `decls` / `rules` pair. We also return the `[start of {, end of }]` offsets so callers can record the block's source position.
2088
+ * @param {TokenStream} ts token stream
2089
+ * @returns {{ decls: Declaration[], rules: Rule[], blockStart: number, blockEnd: number }} block decls + rules and the `{` start / `}` end offsets
2090
+ */
2091
+ const consumeABlock = (ts) => {
2092
+ // Capture the opening `{`'s start before advancing — the stream reuses one
2093
+ // token instance, so `consumeABlocksContents` below would overwrite it.
2094
+ const blockStart = ts.next().start;
2095
+ // Assert (spec): the next token is <{-token>.
2096
+ // Discard a token from input. Consume a block's contents from input and let result be the result. Discard a token from input.
2097
+ ts.discard();
2098
+ const { decls, rules } = consumeABlocksContents(ts);
2099
+ const close = ts.next();
2100
+ const end = close.type === TT_RIGHT_CURLY_BRACKET ? close.end : close.start;
2101
+ ts.discard();
2102
+ return { decls, rules, blockStart, blockEnd: end };
2103
+ };
2104
+
2105
+ /**
2106
+ * 2-token lookahead: is the next non-whitespace pair `<ident> <colon>` (the prerequisite for consume-a-declaration steps 1 + 3)? Used by `consumeABlocksContents` to skip a declaration attempt that would otherwise call consume-the-remnants-of-a-bad-declaration and be undone by `restoreMark`. A webpack fast-path, not a spec algorithm — so the implementation is free to peek cheaply. It scans raw code points after the cached ident for the next significant one (skipping whitespace + comments) rather than tokenizing them, and never advances the stream: the ident stays cached in `_next` for `consumeADeclaration` to reuse instead of re-tokenizing the property name. Comments skipped here fire `onComment` later, when the chosen consume algorithm tokenizes past them (once, in source order, as before).
2107
+ * @param {TokenStream} ts token stream
2108
+ * @returns {boolean} true if consume-a-declaration's step 1 + step 3 would both succeed on the current input
2109
+ */
2110
+ const declarationStartLikely = (ts) => {
2111
+ const t = ts.next();
2112
+ if (t.type !== TT_IDENTIFIER) return false;
2113
+ const input = ts.input;
2114
+ const len = input.length;
2115
+ let pos = t.end;
2116
+ for (;;) {
2117
+ if (pos >= len) return false;
2118
+ const cc = input.charCodeAt(pos);
2119
+ if (_isWhiteSpace(cc)) {
2120
+ pos++;
2121
+ continue;
2122
+ }
2123
+ // Skip a `/* … */` comment (the tokenizer filters comments between tokens).
2124
+ if (cc === CC_SOLIDUS && input.charCodeAt(pos + 1) === CC_ASTERISK) {
2125
+ pos += 2;
2126
+ while (
2127
+ pos < len &&
2128
+ !(
2129
+ input.charCodeAt(pos) === CC_ASTERISK &&
2130
+ input.charCodeAt(pos + 1) === CC_SOLIDUS
2131
+ )
2132
+ ) {
2133
+ pos++;
2134
+ }
2135
+ pos += 2;
2136
+ continue;
2137
+ }
2138
+ // `:` is always a standalone <colon-token>, so the next significant char
2139
+ // being `:` is equivalent to the next token being a <colon-token>.
2140
+ return cc === CC_COLON;
2141
+ }
2142
+ };
2143
+
2144
+ /**
2145
+ * Consume a block's contents, CSS Syntax Level 3 [§5.4.5](https://drafts.csswg.org/css-syntax/#consume-block-contents). Per tabatkins/parse-css.js reference impl: returns separate `decls` and `rules` flat lists, both preserved on EOF / `}` (the spec text's "Return rules" single-list model drops trailing decls because there's no implicit flush before EOF / `}`).
2146
+ *
2147
+ * `onNode` is the same streaming extension `consumeAStylesheetsContents` exposes:
2148
+ * when given, each consumed declaration / rule is handed to it immediately (in
2149
+ * source order) instead of being collected, so the returned lists are empty.
2150
+ * @param {TokenStream} ts token stream
2151
+ * @param {((node: Declaration | Rule) => void)=} onNode optional per-node sink (streaming); nodes are not collected when given
2152
+ * @returns {{ decls: Declaration[], rules: Rule[] }} consumed decls + rules (both empty when `onNode` is given; stops at the enclosing `}` / EOF, left in the stream)
2153
+ */
2154
+ const consumeABlocksContents = (ts, onNode) => {
2155
+ /** @type {Declaration[]} */
2156
+ const decls = [];
2157
+ /** @type {Rule[]} */
2158
+ const rules = [];
2159
+
2160
+ // Process input:
2161
+ for (;;) {
2162
+ const t = ts.next();
2163
+
2164
+ // <whitespace-token> / <semicolon-token>
2165
+ // Discard a token from input.
2166
+ if (t.type === TT_WHITESPACE || t.type === TT_SEMICOLON) {
2167
+ ts.discard();
2168
+ }
2169
+ // <EOF-token> / <}-token>
2170
+ // Return decls and rules.
2171
+ else if (t.type === TT_EOF || t.type === TT_RIGHT_CURLY_BRACKET) {
2172
+ return { decls, rules };
2173
+ }
2174
+ // <at-keyword-token>
2175
+ // Consume an at-rule from input, with nested set to true. If a rule was returned, append it to rules.
2176
+ else if (t.type === TT_AT_KEYWORD) {
2177
+ const atRule = consumeAnAtRule(ts, true);
2178
+ if (atRule) {
2179
+ if (onNode) onNode(atRule);
2180
+ else rules.push(atRule);
2181
+ }
2182
+ }
2183
+ // anything else
2184
+ // Mark input. Consume a declaration from input, with nested set to true.
2185
+ // If a declaration was returned, append it to decls, and discard a mark from input.
2186
+ // Otherwise, restore a mark from input, then consume a qualified rule from input, with nested set to true, and <semicolon-token> as the stop token. If a rule was returned, append it to rules.
2187
+ else {
2188
+ // 2-token peek: consume-a-declaration's steps 1 / 3 require `<ident> <colon>`; if absent it would call consume-the-remnants-of-a-bad-declaration (potentially the rest of the enclosing block) only for the restoreMark to undo it (O(N²) on flat blocks of qualified rules). Skip straight to consume-a-qualified-rule — same observable result.
2189
+ if (declarationStartLikely(ts)) {
2190
+ ts.mark();
2191
+ const decl = consumeADeclaration(ts, true);
2192
+ if (decl) {
2193
+ if (onNode) onNode(decl);
2194
+ else decls.push(decl);
2195
+ ts.discardMark();
2196
+ continue;
2197
+ }
2198
+ ts.restoreMark();
2199
+ }
2200
+ const rule = consumeAQualifiedRule(ts, TT_SEMICOLON, true);
2201
+ if (rule) {
2202
+ if (onNode) onNode(rule);
2203
+ else rules.push(rule);
2204
+ }
2205
+ }
2206
+ }
2207
+ };
2208
+
2209
+ /**
2210
+ * Consume the remnants of a bad declaration, CSS Syntax Level 3 [§5.4.11](https://drafts.csswg.org/css-syntax/#consume-the-remnants-of-a-bad-declaration). Advances the stream past a malformed declaration's tail so the caller (`consumeABlocksContents`) can resume cleanly.
2211
+ * @param {TokenStream} ts token stream
2212
+ * @param {boolean} nested whether the call originates from inside a `{}` block
2213
+ * @returns {void}
2214
+ */
2215
+ const consumeTheRemnantsOfABadDeclaration = (ts, nested) => {
2216
+ // Process input:
2217
+ for (;;) {
2218
+ const t = ts.next();
2219
+ // <eof-token> / <semicolon-token>
2220
+ // Discard a token from input, and return.
2221
+ if (t.type === TT_EOF || t.type === TT_SEMICOLON) {
2222
+ ts.discard();
2223
+ return;
2224
+ }
2225
+ // <}-token>
2226
+ // If nested is true, return. Otherwise, discard a token.
2227
+ if (t.type === TT_RIGHT_CURLY_BRACKET) {
2228
+ if (nested) return;
2229
+ ts.discard();
2230
+ continue;
2231
+ }
2232
+ // anything else
2233
+ // Consume a component value from input, and do nothing.
2234
+ consumeAComponentValue(ts);
2235
+ }
2236
+ };
2237
+
2238
+ /**
2239
+ * Consume a declaration, CSS Syntax Level 3 [§5.4.6](https://drafts.csswg.org/css-syntax/#consume-declaration).
2240
+ * @param {TokenStream} ts token stream
2241
+ * @param {boolean=} nested true inside a `{}` block — a top-level `}` ends the value
2242
+ * @returns {Declaration | undefined} parsed declaration, or `undefined` on the spec's "return nothing" branches (steps 1, 3, 8)
2243
+ */
2244
+ const consumeADeclaration = (ts, nested = false) => {
2245
+ const { input } = ts;
2246
+ // Let decl be a new declaration, with an initially empty name and a value set to an empty list.
2247
+ const start = ts.next().start;
2248
+ const decl = /** @type {Declaration} */ (
2249
+ new Node(T_DECLARATION, start, start, ts.locConverter)
2250
+ );
2251
+ decl.name = "";
2252
+ decl.nameStart = start;
2253
+ decl.nameEnd = start;
2254
+ decl.value = /** @type {ComponentValue[]} */ ([]);
2255
+ decl.important = false;
2256
+
2257
+ // 1. If the next token is an <ident-token>, consume a token from input and set decl's name to the returned token's value.
2258
+ // Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
2259
+ if (ts.next().type === TT_IDENTIFIER) {
2260
+ const head = ts.consume();
2261
+ decl.nameStart = head.start;
2262
+ decl.nameEnd = head.end;
2263
+ decl.name = input.slice(head.start, head.end);
2264
+ } else {
2265
+ consumeTheRemnantsOfABadDeclaration(ts, nested);
2266
+ return undefined;
2267
+ }
2268
+
2269
+ // 2. Discard whitespace from input.
2270
+ while (ts.next().type === TT_WHITESPACE) ts.discard();
2271
+
2272
+ // 3. If the next token is a <colon-token>, discard a token from input.
2273
+ // Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
2274
+ if (ts.next().type === TT_COLON) {
2275
+ ts.discard();
2276
+ } else {
2277
+ consumeTheRemnantsOfABadDeclaration(ts, nested);
2278
+ return undefined;
2279
+ }
2280
+
2281
+ // 4. Discard whitespace from input.
2282
+ while (ts.next().type === TT_WHITESPACE) ts.discard();
2283
+
2284
+ // 5. Consume a list of component values from input, with nested, and with <semicolon-token> as the stop token, and set decl's value to the result.
2285
+ decl.value = consumeAListOfComponentValues(ts, TT_SEMICOLON, nested);
2286
+ decl.end = ts.next().start;
2287
+
2288
+ // 6. If the last two non-<whitespace-token>s in decl's value are a <delim-token> with the value "!" followed by an <ident-token> with a value that is an ASCII case-insensitive match for "important", remove them from decl's value and set decl's important flag.
2289
+ {
2290
+ let last = decl.value.length - 1;
2291
+ while (last >= 0 && decl.value[last].type === T_WHITESPACE) last--;
2292
+ let prev = last - 1;
2293
+ while (prev >= 0 && decl.value[prev].type === T_WHITESPACE) prev--;
2294
+ if (
2295
+ prev >= 0 &&
2296
+ decl.value[last].type === T_IDENT &&
2297
+ equalsLowerCase(
2298
+ /** @type {Token} */ (decl.value[last]).value,
2299
+ "important"
2300
+ ) &&
2301
+ decl.value[prev].type === T_DELIM &&
2302
+ input.charCodeAt(decl.value[prev].start) === CC_EXCLAMATION
2303
+ ) {
2304
+ decl.important = true;
2305
+ decl.value.length = prev;
2306
+ }
2307
+ }
2308
+
2309
+ // 7. While the last item in decl's value is a <whitespace-token>, remove that token.
2310
+ while (
2311
+ decl.value.length > 0 &&
2312
+ decl.value[decl.value.length - 1].type === T_WHITESPACE
2313
+ ) {
2314
+ decl.value.pop();
2315
+ }
2316
+
2317
+ // 8. If decl's name starts with "--" (a custom property), it can contain any value (including a top-level `{}` block) — accept it.
2318
+ // Otherwise, if decl's value contains a top-level simple block with an associated token of <{-token>, return nothing.
2319
+ // (That is, a top-level {}-block is only allowed as the entire value of a non-custom property — for CSS Nesting, `consumeABlocksContents`'s `mark` / `restore a mark` will retry the input as a qualified rule.)
2320
+ // Otherwise, accept the declaration. (The spec also checks "contains any non-whitespace-tokens at the top level" → return nothing; we keep empty-value declarations because callers — e.g. `@value name:;` — rely on them.)
2321
+ const isCustomProperty = input.startsWith("--", decl.nameStart);
2322
+ if (!isCustomProperty) {
2323
+ const value = decl.value;
2324
+ for (let i = 0; i < value.length; i++) {
2325
+ const v = value[i];
2326
+ if (
2327
+ v.type === T_SIMPLE_BLOCK &&
2328
+ /** @type {SimpleBlock} */ (v).token === "{"
2329
+ ) {
2330
+ return undefined;
2331
+ }
2332
+ }
2333
+ }
2334
+
2335
+ // 9. Return decl.
2336
+ return decl;
2337
+ };
2338
+
2339
+ /**
2340
+ * Consume a list of component values, CSS Syntax Level 3 [§5.4.7](https://drafts.csswg.org/css-syntax/#consume-list-of-components) — consumes component values until EOF, the optional `stopToken`, or — when `nested` — a top-level `}` (left in the stream); a non-nested `}` is a parse error appended as a token.
2341
+ * @param {TokenStream} ts token stream
2342
+ * @param {number=} stopToken token type that terminates the list (left unconsumed)
2343
+ * @param {boolean=} nested true inside a `{}` block — a top-level `}` ends the list (left unconsumed)
2344
+ * @returns {ComponentValue[]} consumed component values
2345
+ */
2346
+ const consumeAListOfComponentValues = (ts, stopToken, nested = false) => {
2347
+ /** @type {ComponentValue[]} */
2348
+ const values = [];
2349
+ // Process input
2350
+ for (;;) {
2351
+ const t = ts.next();
2352
+
2353
+ // <eof-token>
2354
+ // stop token (if passed)
2355
+ // Return values.
2356
+ if (t.type === TT_EOF || t.type === stopToken) {
2357
+ return values;
2358
+ }
2359
+ // <}-token>
2360
+ // If nested is true, return values.
2361
+ // Otherwise, this is a parse error. Consume a token from input and append the result to values.
2362
+ if (t.type === TT_RIGHT_CURLY_BRACKET) {
2363
+ if (nested) return values;
2364
+ values.push(consumeATokenAsNode(ts));
2365
+ continue;
2366
+ }
2367
+ // anything else
2368
+ // Consume a component value from input, and append the result to values.
2369
+ values.push(consumeAComponentValue(ts));
2370
+ }
2371
+ };
2372
+
2373
+ /**
2374
+ * Consume a component value, CSS Syntax Level 3 [§5.4.8](https://drafts.csswg.org/css-syntax/#consume-component-value) — consumes the next value (simple block, function, or single token); callers guard against EOF before calling.
2375
+ * @param {TokenStream} ts token stream
2376
+ * @returns {SimpleBlock | FunctionNode | ComponentValue} the consumed component value
2377
+ */
2378
+ const consumeAComponentValue = (ts) => {
2379
+ const t = ts.next();
2380
+ // <{-token> / <[-token> / <(-token> (the three contiguous opening brackets)
2381
+ // Consume a simple block from input and return the result.
2382
+ if (t.type >= TT_LEFT_PARENTHESIS && t.type <= TT_LEFT_CURLY_BRACKET) {
2383
+ return /** @type {SimpleBlock} */ (consumeASimpleBlock(ts));
2384
+ }
2385
+ // <function-token>
2386
+ // Consume a function from input and return the result.
2387
+ if (t.type === TT_FUNCTION) {
2388
+ return /** @type {FunctionNode} */ (consumeAFunction(ts));
2389
+ }
2390
+ // anything else
2391
+ // Consume a token from input and return the result. (Asserted: not EOF.)
2392
+ return consumeATokenAsNode(ts);
2393
+ };
2394
+
2395
+ /**
2396
+ * Consume a simple block, CSS Syntax Level 3 [§5.4.9](https://drafts.csswg.org/css-syntax/#consume-simple-block) — the next token must be `(`, `[`, or `{` (asserted); consumes component values via `consumeAComponentValue` until the mirror closing token (`)`, `]`, `}`) or EOF, returning the partial block on EOF (parse error).
2397
+ * @param {TokenStream} ts token stream
2398
+ * @returns {SimpleBlock | undefined} the parsed simple block
2399
+ */
2400
+ const consumeASimpleBlock = (ts) => {
2401
+ const open = ts.next();
2402
+ // Assert (spec): the next token of input is <{-token>, <[-token>, or <(-token>.
2403
+ // Mirror closing token (`opener + 3`) and the associated block char.
2404
+ const ending = open.type + 3;
2405
+ const token = BLOCK_TOKEN_CHAR[open.type - TT_LEFT_PARENTHESIS];
2406
+
2407
+ // Let block be a new simple block with its associated token set to the next token and with its value initially set to an empty list.
2408
+ const block = /** @type {SimpleBlock} */ (
2409
+ new Node(T_SIMPLE_BLOCK, open.start, open.end, ts.locConverter)
2410
+ );
2411
+ block.token = token;
2412
+ block.value = /** @type {ComponentValue[]} */ ([]);
2413
+
2414
+ // Discard a token from input.
2415
+ ts.discard();
2416
+
2417
+ // Process input
2418
+ for (;;) {
2419
+ const t = ts.next();
2420
+
2421
+ // <eof-token>
2422
+ // ending token
2423
+ // Discard a token from input. Return block.
2424
+ if (t.type === TT_EOF || t.type === ending) {
2425
+ ts.discard();
2426
+ block.end = t.end;
2427
+ return block;
2428
+ }
2429
+
2430
+ // anything else
2431
+ // Consume a component value from input and append the result to block’s value.
2432
+ block.value.push(consumeAComponentValue(ts));
2433
+ }
2434
+ };
2435
+
2436
+ /**
2437
+ * Consume a function, CSS Syntax Level 3 [§5.4.10](https://drafts.csswg.org/css-syntax/#consume-function) — consumes component values up to the matching `)` or EOF (the partial function on EOF is a parse error).
2438
+ * @param {TokenStream} ts token stream
2439
+ * @returns {FunctionNode | undefined} the consumed function node
2440
+ */
2441
+ const consumeAFunction = (ts) => {
2442
+ const { input, locConverter } = ts;
2443
+ // Assert (spec): the next token is a <function-token>.
2444
+ // Consume a token from input, and let function be a new function with its name equal the returned token’s value, and a value set to an empty list.
2445
+ const tFn = ts.consume();
2446
+ const fn = /** @type {FunctionNode} */ (
2447
+ new Node(T_FUNCTION, tFn.start, tFn.end, locConverter)
2448
+ );
2449
+ fn.name = input.slice(tFn.start, tFn.end - 1);
2450
+ fn.nameStart = tFn.start;
2451
+ fn.nameEnd = tFn.end - 1;
2452
+ fn.value = /** @type {ComponentValue[]} */ ([]);
2453
+
2454
+ // Process input
2455
+ for (;;) {
2456
+ const t = ts.next();
2457
+
2458
+ if (t.type === TT_EOF || t.type === TT_RIGHT_PARENTHESIS) {
2459
+ // <eof-token>
2460
+ // <)-token>
2461
+ // Discard a token from input. Return function.
2462
+ ts.discard();
2463
+ fn.end = t.end;
2464
+ return fn;
2465
+ }
2466
+
2467
+ // anything else
2468
+ // Consume a component value from input and append the result to function’s value.
2469
+ fn.value.push(consumeAComponentValue(ts));
2470
+ }
2471
+ };
2472
+
2473
+ // Identifier escape / unescape — operate on the raw text of an
2474
+ // `<ident-token>` (or any source slice that may carry CSS escape sequences).
2475
+ // `escapeIdentifier` produces a CSS-Syntax-3-conformant `<ident-token>` from
2476
+ // an arbitrary string (so the result can be re-tokenized as the same name);
2477
+ // `unescapeIdentifier` reverses tokenizer-time escapes per
2478
+ // https://www.w3.org/TR/css-syntax-3/#consume-escaped-code-point.
2479
+ // Both are pure string functions and have no dependency on the AST; they
2480
+ // live here so the AST module is a one-stop shop for CSS-syntax-level
2481
+ // utilities. `CssParser.js` re-exports them for back-compat with callers
2482
+ // that previously reached them via `getCssParser()`.
2483
+
2484
+ const regexSingleEscape = /[ -,./:-@[\]^`{-~]/;
2485
+ const regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6}) (?![a-fA-F0-9 ])/g;
2486
+ // ASCII escape class per char code: 0 = pass through, 1 = `\<char>` single
2487
+ // escape, 2 = `\HEX ` (control chars). Built from the original predicates so
2488
+ // behaviour is identical; replaces two regex tests per character with one load.
2489
+ const ESCAPE_CLASS_HEX = 2;
2490
+ const ESCAPE_CLASS_SINGLE = 1;
2491
+ const _escapeClassTable = new Uint8Array(128);
2492
+ for (let i = 0; i < 128; i++) {
2493
+ const ch = String.fromCharCode(i);
2494
+ _escapeClassTable[i] = /[\t\n\f\r\v]/.test(ch)
2495
+ ? ESCAPE_CLASS_HEX
2496
+ : ch === "\\" || regexSingleEscape.test(ch)
2497
+ ? ESCAPE_CLASS_SINGLE
2498
+ : 0;
2499
+ }
2500
+
2501
+ /**
2502
+ * Returns escaped identifier.
2503
+ * @param {string} str string
2504
+ * @returns {string} escaped identifier
2505
+ */
2506
+ const _escapeIdentifier = (str) => {
2507
+ let output = "";
2508
+ // Flush safe runs in bulk: only escaped chars break the run, so an
2509
+ // identifier needing no escapes returns `str` unchanged (no allocation).
2510
+ let lastFlush = 0;
2511
+ let needSpaceFix = false;
2512
+ for (let i = 0; i < str.length; i++) {
2513
+ const cc = str.charCodeAt(i);
2514
+ const cls = cc < 128 ? _escapeClassTable[cc] : 0;
2515
+ if (cls === 0) continue;
2516
+ output += str.slice(lastFlush, i);
2517
+ if (cls === ESCAPE_CLASS_SINGLE) {
2518
+ output += `\\${str[i]}`;
2519
+ } else {
2520
+ output += `\\${cc.toString(16).toUpperCase()} `;
2521
+ needSpaceFix = true;
2522
+ }
2523
+ lastFlush = i + 1;
2524
+ }
2525
+ output = lastFlush === 0 ? str : output + str.slice(lastFlush);
2526
+
2527
+ const firstChar = str.charAt(0);
2528
+
2529
+ if (/^-[-\d]/.test(output)) {
2530
+ output = `\\-${output.slice(1)}`;
2531
+ } else if (/\d/.test(firstChar)) {
2532
+ // A leading digit becomes `\3<digit> `, another `\HEX ` run to clean up.
2533
+ output = `\\3${firstChar} ${output.slice(1)}`;
2534
+ needSpaceFix = true;
2535
+ }
2536
+
2537
+ // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
2538
+ // since they’re redundant. Only `\HEX ` runs (above) can produce them; plain
2539
+ // single escapes can't, so skip the scan when none were emitted. Note this is
2540
+ // only possible if the escape isn't preceded by an odd number of backslashes.
2541
+ if (needSpaceFix) {
2542
+ output = output.replace(regexExcessiveSpaces, ($0, $1, $2) => {
2543
+ /* istanbul ignore if -- @preserve: this escaper never emits an odd run of backslashes before a `\HEX` escape (literal `\` is doubled) */
2544
+ if ($1 && $1.length % 2) {
2545
+ // It’s not safe to remove the space, so don’t.
2546
+ return $0;
2547
+ }
2548
+
2549
+ // Strip the space.
2550
+ return ($1 || "") + $2;
2551
+ });
2552
+ }
2553
+
2554
+ return output;
2555
+ };
2556
+
2557
+ /**
2558
+ * Returns hex. Reads up to six hex digits from `str` starting at `start` —
2559
+ * indexed rather than sliced, and case-folded inline, so the common
2560
+ * non-hex escape (e.g. `\:` in `focus\:sr-only`) allocates nothing.
2561
+ * @param {string} str string
2562
+ * @param {number} start index just past the `\`
2563
+ * @returns {[string, number] | undefined} hex
2564
+ */
2565
+ const gobbleHex = (str, start) => {
2566
+ let hex = "";
2567
+ let spaceTerminated = false;
2568
+
2569
+ for (let i = 0; i < 6; i++) {
2570
+ const code = str.charCodeAt(start + i);
2571
+ // valid hex char [0-9 | A-F | a-f]; out-of-range reads NaN -> invalid
2572
+ const valid =
2573
+ (code >= 48 && code <= 57) ||
2574
+ (code >= 65 && code <= 70) ||
2575
+ (code >= 97 && code <= 102);
2576
+ // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
2577
+ spaceTerminated = code === 32;
2578
+ if (!valid) break;
2579
+ // parseInt below is case-insensitive, so keep the original char.
2580
+ hex += str[start + i];
2581
+ }
2582
+
2583
+ if (hex.length === 0) return undefined;
2584
+
2585
+ const codePoint = Number.parseInt(hex, 16);
2586
+ const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff;
2587
+
2588
+ // Add special case for
2589
+ // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
2590
+ // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
2591
+ if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
2592
+ return ["�", hex.length + (spaceTerminated ? 1 : 0)];
2593
+ }
2594
+
2595
+ return [
2596
+ String.fromCodePoint(codePoint),
2597
+ hex.length + (spaceTerminated ? 1 : 0)
2598
+ ];
2599
+ };
2600
+
2601
+ /**
2602
+ * Unescape identifier.
2603
+ * @param {string} str string
2604
+ * @returns {string} unescaped string
2605
+ */
2606
+ const _unescapeIdentifier = (str) => {
2607
+ // `indexOf` is the no-escape fast path and the start offset in one — the
2608
+ // leading safe run is skipped and an unescaped ident returns as-is.
2609
+ const first = str.indexOf("\\");
2610
+ if (first === -1) return str;
2611
+ let ret = "";
2612
+ // Flush safe runs in bulk instead of appending char by char.
2613
+ let lastFlush = 0;
2614
+ for (let i = first; i < str.length; i++) {
2615
+ if (str[i] !== "\\") continue;
2616
+ ret += str.slice(lastFlush, i);
2617
+ const gobbled = gobbleHex(str, i + 1);
2618
+ if (gobbled !== undefined) {
2619
+ ret += gobbled[0];
2620
+ i += gobbled[1];
2621
+ } else if (str[i + 1] === "\\") {
2622
+ // Retain one `\` of an escaped `\\` pair.
2623
+ // https://github.com/postcss/postcss-selector-parser/commit/268c9a7656fb53f543dc620aa5b73a30ec3ff20e
2624
+ ret += "\\";
2625
+ i += 1;
2626
+ } else if (str.length === i + 1) {
2627
+ // A trailing lone `\` is retained.
2628
+ // https://github.com/postcss/postcss-selector-parser/commit/01a6b346e3612ce1ab20219acc26abdc259ccefb
2629
+ ret += "\\";
2630
+ }
2631
+ // Otherwise the lone `\` is dropped; the next char flushes with its run.
2632
+ lastFlush = i + 1;
2633
+ }
2634
+ ret += str.slice(lastFlush);
2635
+
2636
+ return ret;
2637
+ };
2638
+
2639
+ // Cacheable per `compiler.root` — CssParser binds once per parse via
2640
+ // `.bindCache(...)` and reuses for every identifier.
2641
+ const escapeIdentifier = makeCacheable(_escapeIdentifier);
2642
+ const unescapeIdentifier = makeCacheable(_unescapeIdentifier);
2643
+
2644
+ // CSS-typed views over the generic visitor machinery (`util/SourceProcessor`),
2645
+ // re-exported so consumers keep importing them from this module.
2646
+ /**
2647
+ * @typedef {import("../util/SourceProcessor").VisitorContext} VisitorContext
2648
+ * @typedef {import("../util/SourceProcessor").VisitorFn<Node>} VisitorFn
2649
+ * @typedef {import("../util/SourceProcessor").VisitorBucket<Node>} VisitorBucket
2650
+ * @typedef {import("../util/SourceProcessor").VisitorMap<Node>} VisitorMap
2651
+ * @typedef {import("../util/SourceProcessor").CompiledVisitorMap<Node>} CompiledVisitorMap
2652
+ */
2653
+
2654
+ /**
2655
+ * A CSS Syntax §5.4 top-level consumer that streams each top-level node it
2656
+ * produces to `onNode` (in source order) rather than collecting it. Every entry
2657
+ * in `TOP_LEVEL_CONSUMERS` shares this shape, so the walk's `grammar` drives any
2658
+ * `as` mode through one call — a future mode is just another map entry.
2659
+ * @typedef {(ts: TokenStream, onNode: (node: Rule | Declaration) => void) => void} TopLevelConsumer
2660
+ */
2661
+
2662
+ /**
2663
+ * `as` value → the §5.4 consumer that streams its top-level nodes. Keyed by the
2664
+ * public `CssParserOptions.as` enum.
2665
+ * @type {Record<string, TopLevelConsumer>}
2666
+ */
2667
+ const TOP_LEVEL_CONSUMERS = {
2668
+ stylesheet: /** @type {TopLevelConsumer} */ (consumeAStylesheetsContents),
2669
+ "block-contents": consumeABlocksContents
2670
+ };
2671
+
2672
+ /**
2673
+ * @typedef {object} CssProcessOptions
2674
+ * @property {LocConverter=} locConverter shared loc converter (default a fresh one over the input)
2675
+ * @property {((input: string, start: number, end: number) => number)=} comment comment-token callback
2676
+ * @property {boolean=} recurseBlocks walk into block bodies' nested rules (default true)
2677
+ * @property {("stylesheet" | "block-contents")=} as which top-level production to consume the source as (see `TOP_LEVEL_CONSUMERS`): `"stylesheet"` (default) or `"block-contents"` (a block's contents, e.g. an HTML `style` attribute)
2678
+ */
2679
+
2680
+ /**
2681
+ * The CSS `SourceProcessor` grammar: consume top-level rules one at a time
2682
+ * (§5.4.1) and walk each immediately, firing `enter` / `exit` in source order
2683
+ * without building a whole-stylesheet array first. `recurseBlocks: false` skips
2684
+ * walking block bodies' (eagerly parsed) nested rules (caller drives nested
2685
+ * traversal itself).
2686
+ * @param {string} input source text
2687
+ * @param {CompiledVisitorMap} visitors compiled visitor map
2688
+ * @param {CssProcessOptions} options process options
2689
+ */
2690
+ const grammar = (input, visitors, options) => {
2691
+ const { comment } = options;
2692
+ const locConverter = options.locConverter || new LocConverter(input);
2693
+ const recurseBlocks = options.recurseBlocks !== false;
2694
+
2695
+ // Babel's `path.skip()`, children-only. Reset per `enter` dispatch.
2696
+ let skipFlag = false;
2697
+ const visitorCtx = {
2698
+ skipChildren() {
2699
+ skipFlag = true;
2700
+ }
2701
+ };
2702
+
2703
+ /**
2704
+ * Walk a component-value subtree; children are already materialized. Fetches
2705
+ * the node's visitor bucket once (reused for enter + exit) and uses index
2706
+ * loops — `for…of` would allocate an iterator per node on this hot path.
2707
+ * @param {Node} node component-value root
2708
+ * @param {Node | null} parent enclosing node
2709
+ */
2710
+ const walkValue = (node, parent) => {
2711
+ const b = visitors[node.type];
2712
+ let skip = false;
2713
+ if (b !== undefined && b.enter.length !== 0) {
2714
+ skipFlag = false;
2715
+ const e = b.enter;
2716
+ for (let i = 0; i < e.length; i++) e[i](node, parent, visitorCtx);
2717
+ skip = skipFlag;
2718
+ skipFlag = false;
2719
+ }
2720
+ if (!skip) {
2721
+ switch (node.type) {
2722
+ case T_FUNCTION:
2723
+ case T_SIMPLE_BLOCK: {
2724
+ const v = /** @type {FunctionNode | SimpleBlock} */ (node).value;
2725
+ for (let i = 0; i < v.length; i++) walkValue(v[i], node);
2726
+ break;
2727
+ }
2728
+ // All other types are leaf tokens.
2729
+ }
2730
+ }
2731
+ if (b !== undefined) {
2732
+ const x = b.exit;
2733
+ for (let i = 0; i < x.length; i++) x[i](node, parent, visitorCtx);
2734
+ }
2735
+ };
2736
+
2737
+ /**
2738
+ * Walk a structural subtree; an at-rule / qualified-rule's block was parsed
2739
+ * eagerly (§5.4.4), so its `value` holds the nested rules / declarations.
2740
+ * @param {Node} node structural-tree root
2741
+ * @param {Node | null} parent enclosing node
2742
+ */
2743
+ const walkRule = (node, parent) => {
2744
+ const b = visitors[node.type];
2745
+ let skip = false;
2746
+ if (b !== undefined && b.enter.length !== 0) {
2747
+ skipFlag = false;
2748
+ const e = b.enter;
2749
+ for (let i = 0; i < e.length; i++) e[i](node, parent, visitorCtx);
2750
+ skip = skipFlag;
2751
+ skipFlag = false;
2752
+ }
2753
+ if (!skip) {
2754
+ switch (node.type) {
2755
+ case T_AT_RULE:
2756
+ case T_QUALIFIED_RULE: {
2757
+ const r = /** @type {AtRule | QualifiedRule} */ (node);
2758
+ const p = r.prelude;
2759
+ for (let i = 0; i < p.length; i++) walkValue(p[i], node);
2760
+ if (recurseBlocks) {
2761
+ // Declarations then child rules — downstream consumers don't need them strictly interleaved in source order.
2762
+ const decls = r.declarations;
2763
+ if (decls) {
2764
+ for (let i = 0; i < decls.length; i++) walkRule(decls[i], node);
2765
+ }
2766
+ const ch = r.childRules;
2767
+ if (ch) for (let i = 0; i < ch.length; i++) walkRule(ch[i], node);
2768
+ }
2769
+ break;
2770
+ }
2771
+ case T_DECLARATION: {
2772
+ const v = /** @type {Declaration} */ (node).value;
2773
+ for (let i = 0; i < v.length; i++) walkValue(v[i], node);
2774
+ break;
2775
+ }
2776
+ }
2777
+ }
2778
+ if (b !== undefined) {
2779
+ const x = b.exit;
2780
+ for (let i = 0; i < x.length; i++) x[i](node, parent, visitorCtx);
2781
+ }
2782
+ };
2783
+
2784
+ // Stream each top-level node (selected by `as`) to the walker the moment it's
2785
+ // consumed, rather than collecting them first — so the whole AST is never
2786
+ // held at once; peak heap is ~one top-level node's subtree.
2787
+ const ts = new TokenStream(input, 0, locConverter, comment);
2788
+ const consume =
2789
+ TOP_LEVEL_CONSUMERS[options.as || "stylesheet"] ||
2790
+ consumeAStylesheetsContents;
2791
+ consume(ts, (node) => walkRule(node, null));
2792
+ };
2793
+
2794
+ /**
2795
+ * The generic visitor coordinator (`util/SourceProcessor`) bound to the CSS
2796
+ * `grammar`. Babel-style usage:
2797
+ *
2798
+ * ```
2799
+ * processor.use({ [NodeType.AtRule]: (at) => {}, [NodeType.Declaration]: { enter, exit } });
2800
+ * processor.process(source);
2801
+ * ```
2802
+ * @extends {GenericSourceProcessor<Node, CssProcessOptions>}
2803
+ */
2804
+ class SourceProcessor extends GenericSourceProcessor {
2805
+ constructor() {
2806
+ super(grammar);
2807
+ }
2808
+ }
2809
+
2810
+ // The two AST runtime classes — `Node` and its sole subclass `Token` (the
2811
+ // other node shapes are `@typedef`s over `Node`, exported as types only). Plus
2812
+ // the full CSS-Syntax-3 §5.3 `parseA*` entry-point surface, `consumeASimpleBlock`
2813
+ // (the one §5.4 algorithm exposed as a byte entry point for `CssParser`), the
2814
+ // `TokenStream` (so callers can pass a pre-built stream to any `parseA*`), and
2815
+ // the `escape` / `unescapeIdentifier` string utils.
2816
+ module.exports.Node = Node;
2817
+ module.exports.NodeType = NodeType;
2818
+ module.exports.SourceProcessor = SourceProcessor;
2819
+ module.exports.TT_AT_KEYWORD = TT_AT_KEYWORD;
2820
+ module.exports.TT_BAD_STRING_TOKEN = TT_BAD_STRING_TOKEN;
2821
+ module.exports.TT_BAD_URL_TOKEN = TT_BAD_URL_TOKEN;
2822
+ module.exports.TT_CDC = TT_CDC;
2823
+ module.exports.TT_CDO = TT_CDO;
2824
+ module.exports.TT_COLON = TT_COLON;
2825
+ module.exports.TT_COMMA = TT_COMMA;
2826
+ module.exports.TT_COMMENT = TT_COMMENT;
2827
+ module.exports.TT_DELIM = TT_DELIM;
2828
+ module.exports.TT_DIMENSION = TT_DIMENSION;
2829
+ module.exports.TT_EOF = TT_EOF;
2830
+ module.exports.TT_FUNCTION = TT_FUNCTION;
2831
+ module.exports.TT_HASH = TT_HASH;
2832
+ module.exports.TT_IDENTIFIER = TT_IDENTIFIER;
2833
+ module.exports.TT_LEFT_CURLY_BRACKET = TT_LEFT_CURLY_BRACKET;
2834
+ module.exports.TT_LEFT_PARENTHESIS = TT_LEFT_PARENTHESIS;
2835
+ module.exports.TT_LEFT_SQUARE_BRACKET = TT_LEFT_SQUARE_BRACKET;
2836
+ module.exports.TT_NUMBER = TT_NUMBER;
2837
+ module.exports.TT_PERCENTAGE = TT_PERCENTAGE;
2838
+ module.exports.TT_RIGHT_CURLY_BRACKET = TT_RIGHT_CURLY_BRACKET;
2839
+ module.exports.TT_RIGHT_PARENTHESIS = TT_RIGHT_PARENTHESIS;
2840
+ module.exports.TT_RIGHT_SQUARE_BRACKET = TT_RIGHT_SQUARE_BRACKET;
2841
+ module.exports.TT_SEMICOLON = TT_SEMICOLON;
2842
+ module.exports.TT_STRING = TT_STRING;
2843
+ module.exports.TT_URL = TT_URL;
2844
+ module.exports.TT_WHITESPACE = TT_WHITESPACE;
2845
+ module.exports.Token = Token;
2846
+ module.exports.TokenStream = TokenStream;
2847
+ module.exports.equalsLowerCase = equalsLowerCase;
2848
+ module.exports.escapeIdentifier = escapeIdentifier;
2849
+ module.exports.parseABlocksContents = parseABlocksContents;
2850
+ module.exports.parseACommaSeparatedListOfComponentValues =
2851
+ parseACommaSeparatedListOfComponentValues;
2852
+ module.exports.parseAComponentValue = parseAComponentValue;
2853
+ module.exports.parseADeclaration = parseADeclaration;
2854
+ module.exports.parseAListOfComponentValues = parseAListOfComponentValues;
2855
+ module.exports.parseARule = parseARule;
2856
+ module.exports.parseAStylesheet = parseAStylesheet;
2857
+ module.exports.parseAStylesheetsContents = parseAStylesheetsContents;
2858
+ module.exports.readToken = readToken;
2859
+ module.exports.unescapeIdentifier = unescapeIdentifier;