webpack 5.112.0 → 5.112.2

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 (263) hide show
  1. package/bin/webpack.js +13 -7
  2. package/lib/Compilation.js +198 -61
  3. package/lib/Compiler.js +12 -25
  4. package/lib/asset/AssetBytesGenerator.js +14 -6
  5. package/lib/asset/AssetBytesParser.js +10 -8
  6. package/lib/asset/AssetGenerator.js +36 -48
  7. package/lib/asset/AssetModule.js +11 -0
  8. package/lib/asset/AssetModulesPlugin.js +5 -4
  9. package/lib/asset/AssetParser.js +8 -8
  10. package/lib/asset/AssetSourceGenerator.js +16 -8
  11. package/lib/asset/AssetSourceParser.js +10 -8
  12. package/lib/asset/WebManifestGenerator.js +11 -6
  13. package/lib/asset/WebManifestParser.js +3 -3
  14. package/lib/async-modules/AsyncModuleHelpers.js +8 -2
  15. package/lib/async-modules/InferAsyncModulesPlugin.js +7 -2
  16. package/lib/cache/CacheFacade.js +1 -1
  17. package/lib/cache/PackFileCacheStrategy.js +6 -10
  18. package/lib/cache/ResolverCachePlugin.js +58 -8
  19. package/lib/config/WebpackOptionsApply.js +8 -28
  20. package/lib/config/cli.js +9 -6
  21. package/lib/config/defaults.js +24 -13
  22. package/lib/config/getClaimedAssetTypes.js +2 -2
  23. package/lib/config/target.js +1 -1
  24. package/lib/context/ContextModule.js +1 -1
  25. package/lib/context/ContextModuleFactory.js +1 -1
  26. package/lib/context/ContextReplacementPlugin.js +3 -9
  27. package/lib/css/CssGenerator.js +40 -47
  28. package/lib/css/CssModule.js +10 -0
  29. package/lib/css/CssModulesPlugin.js +13 -6
  30. package/lib/css/CssParser.js +33 -32
  31. package/lib/css/conventions.js +1 -1
  32. package/lib/css/data.js +224 -219
  33. package/lib/css/syntax-parser.js +1312 -312
  34. package/lib/css/syntax-printer.js +1142 -1025
  35. package/lib/debug/ProfilingPlugin.js +65 -43
  36. package/lib/define/DefinePlugin.js +10 -15
  37. package/lib/define/DotenvPlugin.js +10 -6
  38. package/lib/define/EnvironmentPlugin.js +4 -2
  39. package/lib/dependencies/amd/AMDDefineDependencyParserPlugin.js +1 -2
  40. package/lib/dependencies/amd/AMDRequireDependenciesBlockParserPlugin.js +4 -1
  41. package/lib/dependencies/commonjs/CommonJsExportRequireDependency.js +1 -4
  42. package/lib/dependencies/commonjs/CommonJsExportsParserPlugin.js +18 -33
  43. package/lib/dependencies/commonjs/CommonJsImportsParserPlugin.js +1 -3
  44. package/lib/dependencies/commonjs/CommonJsObjectExportDependency.js +4 -4
  45. package/lib/dependencies/commonjs/CommonJsPlugin.js +2 -5
  46. package/lib/dependencies/commonjs/RequireEnsureDependenciesBlockParserPlugin.js +4 -1
  47. package/lib/dependencies/context/ImportMetaContextDependencyParserPlugin.js +1 -2
  48. package/lib/dependencies/context/ImportMetaGlobDependencyParserPlugin.js +4 -7
  49. package/lib/dependencies/context/ImportMetaGlobHelpers.js +8 -10
  50. package/lib/dependencies/core/CachedConstDependency.js +9 -6
  51. package/lib/dependencies/css/CssIcssExportDependency.js +6 -7
  52. package/lib/dependencies/css/CssIcssImportDependency.js +1 -3
  53. package/lib/dependencies/css/CssUrlDependency.js +2 -6
  54. package/lib/dependencies/esm/ESMDetectionParserPlugin.js +2 -4
  55. package/lib/dependencies/esm/ESMExportExpressionDependency.js +2 -2
  56. package/lib/dependencies/esm/ESMExportImportedSpecifierDependency.js +16 -18
  57. package/lib/dependencies/esm/ESMExportSpecifierDependency.js +20 -3
  58. package/lib/dependencies/esm/ESMExports.js +2 -10
  59. package/lib/dependencies/esm/ESMImportDependency.js +17 -2
  60. package/lib/dependencies/esm/ESMImportDependencyParserPlugin.js +6 -17
  61. package/lib/dependencies/esm/ESMImportGuard.js +11 -23
  62. package/lib/dependencies/esm/ESMImportSideEffectDependency.js +1 -1
  63. package/lib/dependencies/esm/ESMImportSpecifierDependency.js +4 -1
  64. package/lib/dependencies/esm/ESMLiveExportDependency.js +311 -0
  65. package/lib/dependencies/esm/ESMLiveExportParserPlugin.js +862 -0
  66. package/lib/dependencies/esm/ESMModulesPlugin.js +1 -3
  67. package/lib/dependencies/esm/TopLevelAwaitDependency.js +1 -8
  68. package/lib/dependencies/helpers/DynamicExports.js +4 -6
  69. package/lib/dependencies/html/HtmlEntryDependency.js +5 -14
  70. package/lib/dependencies/html/HtmlInlineHtmlDependency.js +1 -4
  71. package/lib/dependencies/html/HtmlInlineScriptDependency.js +2 -6
  72. package/lib/dependencies/html/HtmlInlineStyleDependency.js +1 -4
  73. package/lib/dependencies/import/ImportParserPlugin.js +3 -6
  74. package/lib/dependencies/import-meta/ImportMetaPlugin.js +4 -6
  75. package/lib/dependencies/url/URLParserPlugin.js +1 -1
  76. package/lib/dependencies/worker/WorkerAndWorkletPlugin.js +51 -20
  77. package/lib/devtool/ModuleFilenameHelpers.js +1 -1
  78. package/lib/devtool/SourceMapDevToolPlugin.js +1 -1
  79. package/lib/dll/DelegatedModule.js +3 -3
  80. package/lib/dll/DllModule.js +3 -1
  81. package/lib/dll/DllReferencePlugin.js +1 -4
  82. package/lib/errors/AbstractMethodError.js +1 -1
  83. package/lib/errors/ErrorHelpers.js +2 -1
  84. package/lib/errors/HookWebpackError.js +2 -1
  85. package/lib/errors/JSONParseError.js +4 -4
  86. package/lib/errors/WebpackError.js +3 -2
  87. package/lib/esm/ModuleChunkFormatPlugin.js +36 -1
  88. package/lib/externals/ExternalModule.js +12 -9
  89. package/lib/externals/ExternalModuleFactoryPlugin.js +1 -2
  90. package/lib/externals/ExternalsPlugin.js +1 -1
  91. package/lib/fs/FileSystemInfo.js +4 -4
  92. package/lib/graph/AsyncDependenciesBlock.js +1 -1
  93. package/lib/graph/ChunkGraph.js +5 -0
  94. package/lib/graph/Dependency.js +8 -2
  95. package/lib/graph/ExportsInfo.js +1 -1
  96. package/lib/graph/ModuleGraph.js +14 -3
  97. package/lib/graph/buildChunkGraph.js +17 -19
  98. package/lib/graph/findGraphRoots.js +1 -1
  99. package/lib/hmr/HotModuleReplacement.runtime.js +10 -9
  100. package/lib/hmr/HotModuleReplacementPlugin.js +5 -11
  101. package/lib/hmr/JavascriptHotModuleReplacement.runtime.js +16 -19
  102. package/lib/hmr/LazyCompilationPlugin.js +1 -7
  103. package/lib/html/HtmlGenerator.js +25 -17
  104. package/lib/html/HtmlModule.js +11 -1
  105. package/lib/html/HtmlModulesPlugin.js +12 -11
  106. package/lib/html/HtmlParser.js +10 -9
  107. package/lib/html/builtinEmbeddedRenderer.js +31 -33
  108. package/lib/html/data.js +42 -21
  109. package/lib/html/htmlMinify.js +4 -1
  110. package/lib/html/syntax-parser.js +34 -35
  111. package/lib/html/syntax-printer.js +2 -3
  112. package/lib/html/xmlMinify.js +4 -1
  113. package/lib/ids/HashedModuleIdsPlugin.js +1 -1
  114. package/lib/index.js +13 -12
  115. package/lib/javascript/APIPlugin.js +7 -13
  116. package/lib/javascript/BasicEvaluatedExpression.js +5 -5
  117. package/lib/javascript/CommonJsChunkFormatPlugin.js +2 -7
  118. package/lib/javascript/ConstPlugin.js +1 -1
  119. package/lib/javascript/JavascriptGenerator.js +16 -11
  120. package/lib/javascript/JavascriptMetaInfoPlugin.js +3 -15
  121. package/lib/javascript/JavascriptModule.js +10 -0
  122. package/lib/javascript/JavascriptModulesPlugin.js +2 -4
  123. package/lib/javascript/JavascriptParser.js +99 -11
  124. package/lib/javascript/ScopeAnalyzer.js +183 -107
  125. package/lib/javascript/UseStrictPlugin.js +2 -5
  126. package/lib/javascript/jsMinify.js +1 -1
  127. package/lib/javascript/regexp.js +2 -2
  128. package/lib/javascript/syntax-parser.js +2114 -1506
  129. package/lib/javascript/syntax-printer-data.js +167 -1
  130. package/lib/javascript/syntax-printer.js +9554 -2782
  131. package/lib/json/JsonGenerator.js +15 -7
  132. package/lib/json/JsonModule.js +11 -0
  133. package/lib/json/JsonParser.js +7 -6
  134. package/lib/library/AssignLibraryPlugin.js +1 -1
  135. package/lib/library/JsonpLibraryPlugin.js +1 -1
  136. package/lib/library/ModuleLibraryPlugin.js +399 -23
  137. package/lib/loaders/LoaderRunner.js +76 -38
  138. package/lib/loaders/loadLoader.js +16 -10
  139. package/lib/module/Generator.js +17 -9
  140. package/lib/module/Module.js +2 -2
  141. package/lib/module/NormalModule.js +7 -5
  142. package/lib/module/NormalModuleFactory.js +11 -17
  143. package/lib/module/Parser.js +9 -2
  144. package/lib/module/extractSourceMap.js +69 -18
  145. package/lib/module/findCaseMismatch.js +2 -2
  146. package/lib/node/ExternalModuleInitFragment.js +2 -2
  147. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +4 -12
  148. package/lib/node/ReadFileCompileWasmPlugin.js +2 -6
  149. package/lib/node/nodeConsole.js +4 -3
  150. package/lib/optimize/CircularModulesPlugin.js +1 -1
  151. package/lib/optimize/ConcatenatedModule.js +4 -8
  152. package/lib/optimize/FlagDependencyExportsPlugin.js +6 -11
  153. package/lib/optimize/FlagDependencyUsagePlugin.js +28 -3
  154. package/lib/optimize/InlineExports.js +4 -7
  155. package/lib/optimize/LazyBarrels.js +41 -5
  156. package/lib/optimize/ModuleConcatenationPlugin.js +8 -10
  157. package/lib/optimize/RealContentHashPlugin.js +2 -6
  158. package/lib/optimize/SideEffectsFlagPlugin.js +7 -21
  159. package/lib/optimize/SplitChunksPlugin.js +11 -24
  160. package/lib/optimize/TupleQueue.js +1 -1
  161. package/lib/output/CleanPlugin.js +3 -1
  162. package/lib/output/CopyPlugin.js +3 -4
  163. package/lib/performance/EvalUsagePlugin.js +1 -3
  164. package/lib/performance/HotspotsPlugin.js +33 -9
  165. package/lib/performance/PureAnnotationsPlugin.js +16 -10
  166. package/lib/performance/TopLevelThisPlugin.js +17 -9
  167. package/lib/prefetch/ResourceHintPlugin.js +1 -1
  168. package/lib/provide/ProvidedDependency.js +36 -4
  169. package/lib/rules/BasicEffectRulePlugin.js +1 -1
  170. package/lib/rules/RuleSetCompiler.js +15 -5
  171. package/lib/rules/UseEffectRulePlugin.js +1 -3
  172. package/lib/runtime/AsyncModuleRuntimeModule.js +7 -1
  173. package/lib/runtime/GetChunkFilenameRuntimeModule.js +1 -5
  174. package/lib/runtime/RuntimeGlobals.js +6 -0
  175. package/lib/runtime/RuntimePlugin.js +2 -0
  176. package/lib/schemes/HttpUriPlugin.js +1 -2
  177. package/lib/schemes/VirtualUrlPlugin.js +3 -1
  178. package/lib/serialization/AggregateErrorSerializer.js +3 -3
  179. package/lib/serialization/BinaryMiddleware.js +4 -1
  180. package/lib/serialization/ErrorObjectSerializer.js +2 -5
  181. package/lib/serialization/ObjectMiddleware.js +17 -15
  182. package/lib/serialization/PlainObjectSerializer.js +3 -7
  183. package/lib/serialization/Serializer.js +21 -11
  184. package/lib/serialization/SerializerMiddleware.js +8 -6
  185. package/lib/stats/DefaultStatsFactoryPlugin.js +35 -35
  186. package/lib/stats/DefaultStatsPresetPlugin.js +2 -6
  187. package/lib/stats/DefaultStatsPrinterPlugin.js +12 -11
  188. package/lib/stats/StatsFactory.js +194 -24
  189. package/lib/stats/StatsPrinter.js +126 -13
  190. package/lib/template/DependencyTemplate.js +1 -1
  191. package/lib/template/RuntimeTemplate.js +13 -32
  192. package/lib/template/Template.js +1 -1
  193. package/lib/util/SourceProcessor.js +146 -29
  194. package/lib/util/WeakTupleMap.js +10 -4
  195. package/lib/util/cleverMerge.js +61 -33
  196. package/lib/util/comparators.js +9 -5
  197. package/lib/util/dataURL.js +2 -0
  198. package/lib/util/deprecation.js +4 -3
  199. package/lib/util/globUtils.js +1 -4
  200. package/lib/util/internalSerializables.js +2 -0
  201. package/lib/util/makeSerializable.js +6 -4
  202. package/lib/util/memoize.js +1 -1
  203. package/lib/util/parseJson.js +2 -2
  204. package/lib/util/serialization.js +31 -27
  205. package/lib/wasm/AsyncWasmModule.js +12 -2
  206. package/lib/wasm/AsyncWebAssemblyGenerator.js +12 -4
  207. package/lib/wasm/AsyncWebAssemblyJavascriptGenerator.js +13 -5
  208. package/lib/wasm/AsyncWebAssemblyParser.js +7 -5
  209. package/lib/wasm/UniversalCompileAsyncWasmPlugin.js +2 -6
  210. package/lib/wasm-sync/SyncWasmModule.js +16 -1
  211. package/lib/wasm-sync/WebAssemblyGenerator.js +15 -10
  212. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +13 -5
  213. package/lib/wasm-sync/WebAssemblyParser.js +10 -12
  214. package/lib/web/FetchCompileAsyncWasmPlugin.js +4 -12
  215. package/lib/web/FetchCompileWasmPlugin.js +2 -6
  216. package/package.json +18 -19
  217. package/schemas/WebpackOptions.check.js +3 -3
  218. package/schemas/WebpackOptions.json +2 -2
  219. package/schemas/plugins/BannerPlugin.check.js +3 -3
  220. package/schemas/plugins/HtmlGeneratorOptions.check.js +3 -3
  221. package/schemas/plugins/HtmlParserOptions.check.js +3 -3
  222. package/schemas/plugins/IgnorePlugin.check.js +3 -3
  223. package/schemas/plugins/LoaderOptionsPlugin.check.js +3 -3
  224. package/schemas/plugins/ManifestPlugin.check.js +3 -3
  225. package/schemas/plugins/ProgressPlugin.check.js +3 -3
  226. package/schemas/plugins/SSRManifestPlugin.check.js +3 -3
  227. package/schemas/plugins/SourceMapDevToolPlugin.check.js +3 -3
  228. package/schemas/plugins/WatchIgnorePlugin.check.js +3 -3
  229. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +3 -3
  230. package/schemas/plugins/asset/AssetInlineGeneratorOptions.check.js +3 -3
  231. package/schemas/plugins/asset/AssetParserOptions.check.js +3 -3
  232. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +3 -3
  233. package/schemas/plugins/container/ContainerPlugin.check.js +3 -3
  234. package/schemas/plugins/container/ContainerReferencePlugin.check.js +3 -3
  235. package/schemas/plugins/container/ExternalsType.check.js +3 -3
  236. package/schemas/plugins/container/ModuleFederationPlugin.check.js +3 -3
  237. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +3 -3
  238. package/schemas/plugins/css/CssGeneratorOptions.check.js +3 -3
  239. package/schemas/plugins/css/CssModuleGeneratorOptions.check.js +3 -3
  240. package/schemas/plugins/css/CssModuleParserOptions.check.js +3 -3
  241. package/schemas/plugins/css/CssParserOptions.check.js +3 -3
  242. package/schemas/plugins/debug/ProfilingPlugin.check.js +3 -3
  243. package/schemas/plugins/define/DotenvPluginOptions.check.js +7 -0
  244. package/schemas/plugins/define/DotenvPluginOptions.json +3 -0
  245. package/schemas/plugins/dll/DllPlugin.check.js +3 -3
  246. package/schemas/plugins/dll/DllReferencePlugin.check.js +3 -3
  247. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +3 -3
  248. package/schemas/plugins/ids/OccurrenceChunkIdsPlugin.check.js +3 -3
  249. package/schemas/plugins/ids/OccurrenceModuleIdsPlugin.check.js +3 -3
  250. package/schemas/plugins/json/JsonModulesPluginGenerator.check.js +3 -3
  251. package/schemas/plugins/json/JsonModulesPluginParser.check.js +3 -3
  252. package/schemas/plugins/optimize/AggressiveSplittingPlugin.check.js +3 -3
  253. package/schemas/plugins/optimize/LimitChunkCountPlugin.check.js +3 -3
  254. package/schemas/plugins/optimize/MergeDuplicateChunksPlugin.check.js +3 -3
  255. package/schemas/plugins/optimize/MinChunkSizePlugin.check.js +3 -3
  256. package/schemas/plugins/output/CleanOptions.check.js +7 -0
  257. package/schemas/plugins/output/CleanOptions.json +3 -0
  258. package/schemas/plugins/schemes/HttpUriPlugin.check.js +3 -3
  259. package/schemas/plugins/schemes/VirtualUrlPlugin.check.js +3 -3
  260. package/schemas/plugins/sharing/ConsumeSharedPlugin.check.js +3 -3
  261. package/schemas/plugins/sharing/ProvideSharedPlugin.check.js +3 -3
  262. package/schemas/plugins/sharing/SharePlugin.check.js +3 -3
  263. package/types.d.ts +1482 -358
@@ -179,6 +179,8 @@ const {
179
179
  _joinSlots,
180
180
  _keepKeywordComments,
181
181
  _lowercaseConditionParts,
182
+ _memoizeBlockBody,
183
+ _memoizedBlockBody,
182
184
  _minifyColorFunction,
183
185
  _minifyPolarColorFunction,
184
186
  _nearByteHalf,
@@ -200,6 +202,7 @@ const {
200
202
  _rulesOf,
201
203
  _setHoistedRuns,
202
204
  _setRuleEntry,
205
+ _setVerbatimLeafPrinter,
203
206
  _shortestAuthoredFlex,
204
207
  _shortestColor,
205
208
  _shortestFlexValue,
@@ -1342,11 +1345,7 @@ const _prefixQualifiedRule = (ruleText, parts, soft, body, scope, top) => {
1342
1345
  const removable = bases.every(
1343
1346
  (base, i) =>
1344
1347
  selectors[i].at === -1 ||
1345
- _prefixRemovable(
1346
- PREFIXED_SELECTORS,
1347
- base,
1348
- /** @type {string} */ (listPrefix)
1349
- )
1348
+ _prefixRemovable(PREFIXED_SELECTORS, base, listPrefix)
1350
1349
  );
1351
1350
  // Covered when every selector is: a list of twins says the same as one twin
1352
1351
  // of the list, and the rules writing them need not be one rule.
@@ -2388,12 +2387,17 @@ const _writeNested = (
2388
2387
  const taken = [];
2389
2388
  // A rule written in another names its own parent where its list opens on a
2390
2389
  // combinator, so that parent stays an `&` the next level out writes in.
2391
- const against =
2392
- path.parent !== null && path.type(path.parent) !== NodeType.Stylesheet
2393
- ? _parentNamed(prelude)
2394
- : prelude;
2390
+ // Read only once a rule written in this one asks.
2391
+ /** @type {string | undefined} */
2392
+ let against;
2395
2393
  for (let i = _pendingNested.length - 1; i >= 0; i--) {
2396
2394
  if (_pendingNested[i].owner !== node) continue;
2395
+ if (against === undefined) {
2396
+ against =
2397
+ path.parent !== null && path.type(path.parent) !== NodeType.Stylesheet
2398
+ ? _parentNamed(prelude)
2399
+ : prelude;
2400
+ }
2397
2401
  const one = _pendingNested[i];
2398
2402
  _pendingNested.splice(i, 1);
2399
2403
  one.prelude = _resolveNestedPrelude(one.prelude, against);
@@ -2504,7 +2508,10 @@ const _hoistedEntry = (text, prelude) => ({
2504
2508
  listKind: LIST_KIND_SELECTOR,
2505
2509
  children: null,
2506
2510
  head: "",
2507
- unordered: false
2511
+ unordered: false,
2512
+ list: null,
2513
+ block: null,
2514
+ declarations: null
2508
2515
  });
2509
2516
 
2510
2517
  /**
@@ -2516,7 +2523,7 @@ const _hoistedEntry = (text, prelude) => ({
2516
2523
  * @returns {void}
2517
2524
  */
2518
2525
  const _recordHoistedRule = (text, parent, parentPrelude) => {
2519
- const node = /** @type {Node} */ (_S._currentNode);
2526
+ const node = _S._currentNode;
2520
2527
  if (parent === null && _nestedRuns.length === 1) {
2521
2528
  _setRuleEntry(node, _hoistedEntry(text, _nestedRuns[0].prelude));
2522
2529
  return;
@@ -3190,8 +3197,11 @@ const _numberAlphaValue = (property, value) => {
3190
3197
  const parts = _LONE_PERCENTAGE_RE.exec(value);
3191
3198
  if (parts === null) return value;
3192
3199
  const digits = `00${parts[2]}`;
3193
- const shifted = _normalizeNumber(
3194
- `${parts[1]}${digits.slice(0, -2)}.${digits.slice(-2)}${parts[3] || ""}`
3200
+ // A number rounds to fewer digits than the percentage it was printed as.
3201
+ const shifted = _roundSignificant(
3202
+ _normalizeNumber(
3203
+ `${parts[1]}${digits.slice(0, -2)}.${digits.slice(-2)}${parts[3] || ""}`
3204
+ )
3195
3205
  );
3196
3206
  return shifted.length < value.length ? shifted : value;
3197
3207
  };
@@ -3587,6 +3597,18 @@ const _dropDefaultGradientPosition = (fn, inner) => {
3587
3597
  return match[1] === undefined ? rest : `${match[1]},${rest}`;
3588
3598
  };
3589
3599
 
3600
+ // The basic shapes `_dropDefaultShapeParts` reads, so every other call is
3601
+ // turned away before its arguments are scanned.
3602
+ const _BASIC_SHAPE_FUNCTIONS = new Set([
3603
+ "polygon",
3604
+ "path",
3605
+ "circle",
3606
+ "ellipse",
3607
+ "inset",
3608
+ "rect",
3609
+ "xywh"
3610
+ ]);
3611
+
3590
3612
  /**
3591
3613
  * Drop what a basic shape (CSS Shapes 1 §3) says with its own defaults: a
3592
3614
  * `closest-side` radius, a zero corner `round`, `nonzero`, and the sides an
@@ -3596,7 +3618,11 @@ const _dropDefaultGradientPosition = (fn, inner) => {
3596
3618
  * @returns {string | null} the arguments without them, or null to keep them
3597
3619
  */
3598
3620
  const _dropDefaultShapeParts = (fn, inner) => {
3599
- if (!_S._transforms.reduceFunctions || containsSubstitutionFunction(inner)) {
3621
+ if (
3622
+ !_BASIC_SHAPE_FUNCTIONS.has(fn) ||
3623
+ !_S._transforms.reduceFunctions ||
3624
+ containsSubstitutionFunction(inner)
3625
+ ) {
3600
3626
  return null;
3601
3627
  }
3602
3628
  if (fn === "polygon" || fn === "path") {
@@ -4285,6 +4311,23 @@ const _standardSpelling = (property) => {
4285
4311
  return standard === undefined ? property : standard;
4286
4312
  };
4287
4313
 
4314
+ /**
4315
+ * What `_composeBlockBody` hands back for a block of declarations alone whose
4316
+ * body was composed before.
4317
+ * @param {string} body the body
4318
+ * @returns {ReturnType<typeof _composeBlockBody>} the composition
4319
+ */
4320
+ const _memoizedComposition = (body) => ({
4321
+ body,
4322
+ items: [],
4323
+ texts: [],
4324
+ superseded: null,
4325
+ droppedPrefix: null,
4326
+ addedPrefix: null,
4327
+ deadPrefixed: null,
4328
+ spans: null
4329
+ });
4330
+
4288
4331
  /**
4289
4332
  * Shorten a shorthand declaration's value to an equivalent spelling. A prefixed
4290
4333
  * spelling is a different property, and neither table lists one.
@@ -4465,6 +4508,7 @@ const _dropFontNormals = (components) => {
4465
4508
  * @returns {string[] | null} the fragments to join, or `null` to keep the value
4466
4509
  */
4467
4510
  const _shortenIndividualTransform = (property, components) => {
4511
+ if (property !== "translate" && property !== "scale") return null;
4468
4512
  if (components.length > 3) return null;
4469
4513
  if (components.some((one) => one.includes("(") || one.includes(","))) {
4470
4514
  return null;
@@ -4486,7 +4530,7 @@ const _shortenIndividualTransform = (property, components) => {
4486
4530
  for (let i = 0; i < kept.length; i++) {
4487
4531
  const percentage = _PERCENTAGE_RE.exec(kept[i]);
4488
4532
  if (percentage === null) continue;
4489
- const number = _shiftDecimal(percentage[1], -2);
4533
+ const number = _roundSignificant(_shiftDecimal(percentage[1], -2));
4490
4534
  if (number.length <= kept[i].length) kept[i] = number;
4491
4535
  }
4492
4536
  if (kept.length === 3 && Number(kept[2]) === 1) kept.pop();
@@ -4849,6 +4893,1071 @@ const _printAttributeSelector = (path, children, writer) => {
4849
4893
  return parts;
4850
4894
  };
4851
4895
 
4896
+ /**
4897
+ * The `printer` case for a function.
4898
+ * @param {CssPath} path the accessor positioned on the finished node
4899
+ * @param {PrintContext} writer the print context (children's printed text)
4900
+ * @param {boolean} minify whether minifying
4901
+ * @returns {string} the node's serialized text
4902
+ */
4903
+ const _printFunctionNode = (path, writer, minify) => {
4904
+ const written = _rawName(path.node);
4905
+ // An escape spells the same name (§4.3.7): `c\61 lc(` is `calc()`, so
4906
+ // every table below reads what it spells.
4907
+ const name = written.includes("\\") ? unescapeIdentifier(written) : written;
4908
+ // A container style query asks whether a custom property's value is the one
4909
+ // written here, comparing token streams as written (CSS Conditional 5 §5),
4910
+ // so a squeezed whitespace run asks another question. Nothing is rewritten.
4911
+ if (
4912
+ minify &&
4913
+ !path.within(NodeType.Declaration) &&
4914
+ _S._inMediaConditionPrelude &&
4915
+ asciiCaseInsensitiveMatch(name, "style")
4916
+ ) {
4917
+ const source = _printedSource(path.node);
4918
+ // Unclosed at EOF, the source carries no `)` for the walk to have
4919
+ // closed — printed as usual so the parenthesis is written back.
4920
+ if (source.endsWith(")")) {
4921
+ // Written whole, comments included, so the queued copies are claimed
4922
+ // here rather than flushed a second time after the rule.
4923
+ writer.takeInserts(path.start(), path.end());
4924
+ return source;
4925
+ }
4926
+ }
4927
+ let inner;
4928
+ if (path.childCount() === 1) {
4929
+ // Half of all functions take one argument, which `_join` would hand
4930
+ // straight back — and a trim only ever parts two of them, so no
4931
+ // universal a compound implies can stand beside anything here either.
4932
+ const only = writer.get(path.child(0));
4933
+ inner = only === _SEP ? "" : only;
4934
+ } else {
4935
+ // A selector function's argument is a selector, so its combinators need
4936
+ // no whitespace, as in the enclosing prelude; an `@import`'s
4937
+ // `supports()` and a container's `scroll-state()` hold conditions.
4938
+ const lower = name.toLowerCase();
4939
+ const trim =
4940
+ _S._mathFunctionDepth !== 0
4941
+ ? _TRIM_MATH
4942
+ : path.within(NodeType.Declaration)
4943
+ ? _TRIM_NOTHING
4944
+ : SELECTOR_FUNCTIONS.has(lower)
4945
+ ? _TRIM_COMBINATORS
4946
+ : (_S._inSupportsPrelude && lower === "supports") ||
4947
+ (_S._inMediaConditionPrelude && lower === "scroll-state")
4948
+ ? _TRIM_CONDITIONS
4949
+ : _TRIM_NOTHING;
4950
+ /** @type {string[]} */
4951
+ const parts = [];
4952
+ _appendChildTexts(path.node, writer, parts);
4953
+ if (minify && trim === _TRIM_CONDITIONS) _keepKeywordComments(parts);
4954
+ // A selector function's argument is a selector like the prelude's, so
4955
+ // the universal a compound implies says nothing there either — except
4956
+ // under test in a `@supports` condition, where only whitespace goes.
4957
+ if (minify && trim === _TRIM_COMBINATORS && !_S._inSupportsPrelude) {
4958
+ if (_foldPseudoNames(parts) && _S._transforms.shortenSelectors) {
4959
+ _unwrapLoneIs(parts);
4960
+ }
4961
+ _dropImpliedUniversalSelector(parts);
4962
+ }
4963
+ inner = _join(parts, !minify, minify ? trim : _TRIM_NOTHING);
4964
+ // A leading `+` in An+B is a sign, not a combinator: `+ n` is no
4965
+ // argument an engine reads, and `+n` would be one.
4966
+ if (minify && NTH_PSEUDO_FUNCTIONS.has(lower)) {
4967
+ const at = parts.findIndex((part) => part.length !== 0 && part !== _SEP);
4968
+ if (parts[at] === "+" && parts[at + 1] === _SEP) {
4969
+ inner = `+ ${inner.slice(1)}`;
4970
+ }
4971
+ }
4972
+ // Chrome throws out `attr( name unit )` where `attr( name unit)`
4973
+ // parses, so the space before the `)` decides whether the declaration
4974
+ // runs — `_join` drops a trailing one like any other.
4975
+ if (
4976
+ minify &&
4977
+ parts[parts.length - 1] === _SEP &&
4978
+ asciiCaseInsensitiveMatch(name, "attr")
4979
+ ) {
4980
+ inner += " ";
4981
+ }
4982
+ }
4983
+ if (!minify) return `${written}(${inner})`;
4984
+ // Function names match ASCII case-insensitively, so one lowercase name
4985
+ // is what every table below is keyed by; the eleven transforms spelled
4986
+ // with a capital are printed the way everything else writes them.
4987
+ const fn = asciiLowercaseName(name);
4988
+ // As with a unit: a name already lowercase is its own answer, and only a
4989
+ // shouted one is looked up. The lookup key still folds inside a
4990
+ // substituted value; only the printed spelling stays as written.
4991
+ let out =
4992
+ _S._inSubstitutedValue ||
4993
+ _S._inSupportsSelector ||
4994
+ !_S._transforms.foldCase ||
4995
+ fn.startsWith("--")
4996
+ ? name
4997
+ : fn === name
4998
+ ? fn
4999
+ : CANONICAL_NAMES.get(fn) || fn;
5000
+ if (written !== name) {
5001
+ out = _S._transforms.rewriteEscapes ? escapeName(out) : written;
5002
+ }
5003
+ // An+B in a selector: `2n+1` is what `odd` names, and shorter written so.
5004
+ if (!path.within(NodeType.Declaration) && NTH_PSEUDO_FUNCTIONS.has(fn)) {
5005
+ return _S._inSupportsSelector
5006
+ ? `${out}(${inner})`
5007
+ : _minifyAnPlusB(out, inner);
5008
+ }
5009
+ // Every rewrite below is keyed by a name none of these carry.
5010
+ if (
5011
+ SUBSTITUTION_FUNCTIONS.has(fn) ||
5012
+ (!path.within(NodeType.Declaration) && SELECTOR_FUNCTIONS.has(fn))
5013
+ ) {
5014
+ return `${out}(${inner})`;
5015
+ }
5016
+ // WHY: an `rgb()` or `rgba()` collapses to the shortest color and anything
5017
+ // else keeps its `name(args)` form. A color inside a `@supports` condition
5018
+ // is syntax under test — the engines reading `#0000` are not those reading
5019
+ // `rgba(0,0,0,0)` — so rewriting it asks another question, and esbuild,
5020
+ // clean-css and lightningcss leave the condition alone too. A color read as
5021
+ // numbers, in a gradient stop, a `color-mix()` argument or a relative
5022
+ // color's origin, stays as written unless converting it is exact, which the
5023
+ // legacy sRGB forms are where their channels are bytes.
5024
+ let color = _S._inSupportsPrelude
5025
+ ? null
5026
+ : _minifyColorFunction(
5027
+ fn,
5028
+ inner,
5029
+ _S._hexAlphaAllowed,
5030
+ _S._inColorOperand
5031
+ ) ||
5032
+ (_S._inColorOperand
5033
+ ? null
5034
+ : _minifyPolarColorFunction(fn, inner, _S._hexAlphaAllowed));
5035
+ // A mix no byte holds is written back in the space it was computed in,
5036
+ // which can be longer than the call — then the call is the shorter one.
5037
+ if (color !== null && color.length > fn.length + inner.length + 2) {
5038
+ color = null;
5039
+ }
5040
+ if (color !== null && (color !== "transparent" || !_inTapHighlightColor())) {
5041
+ return color === "transparent" && _S._hexAlphaAllowed ? "#0000" : color;
5042
+ }
5043
+ // `light-dark()` names a color per scheme, which the pair above says with
5044
+ // a substitution the scheme itself switches. Two arguments only: the
5045
+ // grammar takes exactly that many, and anything else is no such call.
5046
+ if (
5047
+ _S._loweringLightDark &&
5048
+ fn === "light-dark" &&
5049
+ path.within(NodeType.Declaration) &&
5050
+ !_S._inCustomProperty &&
5051
+ !_S._inSupportsPrelude &&
5052
+ !_S._inSubstitutedValue
5053
+ ) {
5054
+ const halves = _splitTopLevelArguments(inner);
5055
+ // A half the target cannot read would substitute into a `var()` and make
5056
+ // the declaration invalid at computed-value time, where nothing before it
5057
+ // is read — so it is left alone rather than lowered.
5058
+ if (halves.length === 2 && !_namesUnreadableColor(inner)) {
5059
+ _S._lightDarkUsed = true;
5060
+ return `var(${LIGHT_PROPERTY},${halves[0].trim()})var(${DARK_PROPERTY},${halves[1].trim()})`;
5061
+ }
5062
+ }
5063
+ // A math function over constants of one unit is that value, written back as
5064
+ // a `calc()`: the parentheses stay, since dropping them turns a clamped
5065
+ // negative into a parse error, and only the declaration printer does.
5066
+ if (!_S._inSupportsPrelude && !_S._inCustomProperty) {
5067
+ const term = _foldMathFunction(fn, inner);
5068
+ if (term !== null) {
5069
+ // A term that means the same bare needs no parentheses anywhere, so
5070
+ // it is written as itself; the rest keep a `calc()` for the
5071
+ // declaration printer to judge against the property.
5072
+ const shape =
5073
+ _S._mathFunctionDepth > 1 || _inNumberArgument(path)
5074
+ ? _NESTED_TERM_RE
5075
+ : _BARE_TERM_RE;
5076
+ const folded =
5077
+ (shape.test(term) ||
5078
+ (_S._mathFunctionDepth === 1 &&
5079
+ _NEGATIVE_TERM_RE.test(term) &&
5080
+ _inNegativeAcceptingArgument(path))) &&
5081
+ !_inCalcRejectingProperty() &&
5082
+ !(!/[a-z%]$/i.test(term) && _inNumberOnlyOutsideCalcProperty()) &&
5083
+ !_foldLosesClamp(term) &&
5084
+ !_foldBelowStepCount(term, path)
5085
+ ? term
5086
+ : `calc(${term})`;
5087
+ // A division can land on a value needing every digit of a double, which
5088
+ // is longer than the expression producing it. Only the outermost call
5089
+ // is asked, a nested one being a term still to be reduced.
5090
+ if (
5091
+ _S._mathFunctionDepth > 1 ||
5092
+ folded.length < fn.length + inner.length + 2 ||
5093
+ _unwrapsShorter(path, folded, fn.length + inner.length + 2)
5094
+ ) {
5095
+ return folded;
5096
+ }
5097
+ }
5098
+ const reduced = _reduceMathArguments(fn, inner);
5099
+ // A `calc()` inside another math function is the parentheses it
5100
+ // stands for (CSS Values 4 §10.1), whatever a substitution expands to.
5101
+ if (
5102
+ fn === "calc" &&
5103
+ _S._mathFunctionDepth > 1 &&
5104
+ path.within(NodeType.Declaration) &&
5105
+ _S._transforms.reduceFunctions
5106
+ ) {
5107
+ return `(${reduced === null ? inner : reduced})`;
5108
+ }
5109
+ if (reduced !== null) return `${fn}(${reduced})`;
5110
+ }
5111
+ // An easing function is only ever a value; `url()` is also an at-rule
5112
+ // prelude's (`@import url("a.css")`).
5113
+ const easing =
5114
+ path.within(NodeType.Declaration) && !_S._inCustomProperty
5115
+ ? _minifyEasingFunction(fn, inner)
5116
+ : null;
5117
+ if (easing !== null) return easing;
5118
+ // A translation's percentage is of the element's own box, which always
5119
+ // has a size, so a `0%` term beside one length adds nothing to it.
5120
+ if (
5121
+ _S._transforms.reduceFunctions &&
5122
+ path.within(NodeType.Declaration) &&
5123
+ !_S._inSubstitutedValue &&
5124
+ fn.startsWith("translate") &&
5125
+ inner.includes("0%")
5126
+ ) {
5127
+ const args = _splitTopLevelArguments(inner);
5128
+ let changed = false;
5129
+ for (let i = 0; i < args.length; i++) {
5130
+ // A z slot takes no percentage, so a `0%` there makes the whole
5131
+ // declaration invalid, which dropping it would revive.
5132
+ if (fn === "translatez" || (fn === "translate3d" && i === 2)) {
5133
+ continue;
5134
+ }
5135
+ const term = _dropZeroPercentTerm(args[i].trim());
5136
+ if (term !== null) {
5137
+ args[i] = term;
5138
+ changed = true;
5139
+ }
5140
+ }
5141
+ // The rewrites below read the arguments as they now stand.
5142
+ if (changed) inner = args.join(",");
5143
+ }
5144
+ // `translateX(v)` is `translate(v)` and `skewX(a)` is `skew(a)`, the second
5145
+ // component defaulting to 0. Only for one plain component: a substitution
5146
+ // could expand to two, reviving a declaration the browser drops.
5147
+ const oneAxis = _S._transforms.reduceFunctions
5148
+ ? X_AXIS_TRANSFORMS.get(fn)
5149
+ : undefined;
5150
+ if (
5151
+ oneAxis !== undefined &&
5152
+ path.within(NodeType.Declaration) &&
5153
+ !_S._inSubstitutedValue &&
5154
+ !inner.includes(",") &&
5155
+ !containsSubstitutionFunction(inner)
5156
+ ) {
5157
+ return `${oneAxis}(${inner})`;
5158
+ }
5159
+ if (path.within(NodeType.Declaration) && !_S._inSubstitutedValue) {
5160
+ // An argument that is the amount an omitted one means says nothing,
5161
+ // whether or not the zero still carries the unit its grammar drops.
5162
+ const omitted = _S._transforms.reduceFunctions
5163
+ ? FILTER_FUNCTION_OMITTED.get(fn)
5164
+ : undefined;
5165
+ if (omitted !== undefined) {
5166
+ const bare = _dropCallZeroUnit(fn, inner);
5167
+ if (bare === omitted || (omitted === "1" && bare === "100%")) {
5168
+ return `${out}()`;
5169
+ }
5170
+ }
5171
+ const reduced = _reduceTransformFunctionDeep(fn, inner);
5172
+ if (reduced !== null) return reduced;
5173
+ // The stops are folded over what dropping the direction leaves, a
5174
+ // gradient carrying both otherwise keeping whichever ran second.
5175
+ const shape = _dropDefaultShapeParts(fn, inner);
5176
+ if (shape !== null) return `${out}(${shape})`;
5177
+ let gradient = _reduceGradientDirection(fn, inner);
5178
+ if (gradient === null) {
5179
+ gradient = _dropDefaultGradientPosition(fn, inner);
5180
+ }
5181
+ let stops = _foldGradientStops(
5182
+ fn,
5183
+ gradient === null ? inner : gradient,
5184
+ _S._doublePositionAllowed
5185
+ );
5186
+ // A double position the target cannot read is written as the two
5187
+ // stops it names, over whatever the folds above left.
5188
+ if (!_S._doublePositionAllowed) {
5189
+ const split = !_S._transforms.lowerUnsupported
5190
+ ? null
5191
+ : _splitDoublePositionStops(
5192
+ fn,
5193
+ stops !== null ? stops : gradient !== null ? gradient : inner
5194
+ );
5195
+ if (split !== null) stops = split;
5196
+ }
5197
+ if (stops !== null) return `${out}(${stops})`;
5198
+ if (gradient !== null) return `${out}(${gradient})`;
5199
+ }
5200
+ // A zero angle needs no unit where the grammar names `<zero>` beside
5201
+ // `<angle>` (CSS Transforms 2, Filter Effects 1).
5202
+ if (
5203
+ path.within(NodeType.Declaration) &&
5204
+ !_S._inSubstitutedValue &&
5205
+ _S._transforms.shortenNumbers &&
5206
+ ZERO_ANGLE_FUNCTIONS.has(fn)
5207
+ ) {
5208
+ const bare = inner.replace(_ZERO_ANGLE_ARGUMENT_RE, "$10");
5209
+ if (bare !== inner) return `${out}(${bare})`;
5210
+ }
5211
+ // A name kept escaped keeps its call as written: unquoting it rests on the
5212
+ // tokenizer reading `url` through the escape.
5213
+ const url =
5214
+ _S._inSubstitutedValue || (written !== name && out === written)
5215
+ ? null
5216
+ : _minifyUrlFunction(fn, inner);
5217
+ if (url !== null) return url;
5218
+ return `${out}(${inner})`;
5219
+ };
5220
+
5221
+ /**
5222
+ * The `printer` case for a `url()` token.
5223
+ * @param {CssPath} path the accessor positioned on the finished node
5224
+ * @param {boolean} minify whether minifying
5225
+ * @returns {string} the node's serialized text
5226
+ */
5227
+ const _printUrlNode = (path, minify) => {
5228
+ const source = _printedSource(path.node);
5229
+ // Closed at EOF: write the `)` back so the url stops where it stopped for
5230
+ // the tokenizer rather than swallowing what the printer emits next. Before
5231
+ // the rest rather than instead of it, or a second pass is what folds it.
5232
+ const unterminated = _isUnterminatedUrl(source);
5233
+ const raw = unterminated ? _terminate(source, ")", "\uFFFD") : source;
5234
+ // The tokenizer already trimmed the url-token's content, so the padding in
5235
+ // `url( a.png )` carries nothing.
5236
+ if (!minify) return raw;
5237
+ const open = raw.indexOf("(");
5238
+ // `url` matches ASCII case-insensitively like any function name. Read in
5239
+ // place, since cutting the name out would allocate on every url and all but
5240
+ // a shouted one folds to itself. In a substituted value nothing is read.
5241
+ let head = null;
5242
+ if (!_S._inSubstitutedValue && _S._transforms.foldCase) {
5243
+ for (let i = 0; i < open; i++) {
5244
+ const c = raw.charCodeAt(i);
5245
+ if (c >= CC_UPPER_A && c <= CC_UPPER_Z) {
5246
+ head = asciiLowercaseName(raw.slice(0, open + 1));
5247
+ break;
5248
+ }
5249
+ }
5250
+ }
5251
+ const percent = raw.includes("%");
5252
+ /**
5253
+ * @returns {string} the url-token as it prints with nothing rendered
5254
+ */
5255
+ const plainUrl = () => {
5256
+ // Padding only exists when the content is whitespace-bounded; checking
5257
+ // that costs two char reads, reading the content costs a slice.
5258
+ if (
5259
+ !percent &&
5260
+ !isAsciiWhitespace(raw.charCodeAt(open + 1)) &&
5261
+ !isAsciiWhitespace(raw.charCodeAt(raw.length - 2))
5262
+ ) {
5263
+ return head === null ? raw : head + raw.slice(open + 1);
5264
+ }
5265
+ const name = head === null ? raw.slice(0, open + 1) : head;
5266
+ const value = _urlValue(path, unterminated);
5267
+ // A url-token already carrying an escape is one this printer did not
5268
+ // write, so its `\%` is not read as the start of a percent-escape.
5269
+ return `${name}${
5270
+ percent && !value.includes("\\")
5271
+ ? _decodePercentEscapes(value, true, "")
5272
+ : value
5273
+ })`;
5274
+ };
5275
+ // Ahead of the padding fast path below: a base64 `data:` url is
5276
+ // whitespace-free and `%`-free, so it would take it and never be offered.
5277
+ if (_S._deferEmbeddedSource !== undefined) {
5278
+ const opener = head === null ? raw.slice(0, open + 1) : head;
5279
+ const deferred = _deferDataUrl(
5280
+ _urlValue(path, unterminated),
5281
+ (url) => `${opener}${_serializeUrl(url, '"')})`,
5282
+ plainUrl()
5283
+ );
5284
+ if (deferred !== null) return deferred;
5285
+ }
5286
+ if (_S._renderEmbeddedSource !== undefined) {
5287
+ const renderedUrl = _renderDataUrl(_urlValue(path, unterminated));
5288
+ if (renderedUrl !== null) {
5289
+ const name = head === null ? raw.slice(0, open + 1) : head;
5290
+ return `${name}${_serializeUrl(renderedUrl, '"')})`;
5291
+ }
5292
+ }
5293
+ return plainUrl();
5294
+ };
5295
+
5296
+ /**
5297
+ * The `printer` case for a string.
5298
+ * @param {CssPath} path the accessor positioned on the finished node
5299
+ * @param {boolean} minify whether minifying
5300
+ * @returns {string} the node's serialized text
5301
+ */
5302
+ const _printStringNode = (path, minify) => {
5303
+ const source = _printedSource(path.node);
5304
+ // Closed at EOF: write the quote back, for the same reason as `url()`.
5305
+ // Written here rather than after the transforms below, or they read a
5306
+ // string with no closing quote and decline it, which a second pass undoes.
5307
+ const raw = _isClosedString(source)
5308
+ ? source
5309
+ : _terminate(source, source[0], "");
5310
+ // A custom property hands its string back as written, quotes included.
5311
+ if (!minify || _S._inCustomProperty) return raw;
5312
+ // `<family-name>` is `<string> | <custom-ident>+`, so a family whose text
5313
+ // is a run of identifiers means the same unquoted, two bytes shorter.
5314
+ if (
5315
+ !_S._inSupportsPrelude &&
5316
+ !_S._inSubstitutedValue &&
5317
+ ((_inFontFamily() && _isLoneFamilyName(path)) || _isLoneFontFamily(path))
5318
+ ) {
5319
+ const unquoted = _unquoteFontFamily(raw);
5320
+ if (unquoted !== null) return unquoted;
5321
+ }
5322
+ const written = _minifyString(raw);
5323
+ return (_S._charsetPiece === -1 && !_S._assumeUtf8) ||
5324
+ !_S._transforms.rewriteEscapes
5325
+ ? written
5326
+ : _unescapeAstral(written);
5327
+ };
5328
+
5329
+ /**
5330
+ * The `printer` case for an ident.
5331
+ * @param {CssPath} path the accessor positioned on the finished node
5332
+ * @param {boolean} minify whether minifying
5333
+ * @returns {string} the node's serialized text
5334
+ */
5335
+ const _printIdentNode = (path, minify) => {
5336
+ const raw = _printedSource(path.node);
5337
+ if (!minify) return raw;
5338
+ // A keyword is matched by what an escape spells, not by how it is written.
5339
+ const meaning = raw.includes("\\") ? unescapeIdentifier(raw) : raw;
5340
+ // `transparent` is `#0000` where the target reads a hex alpha — the same
5341
+ // color, six bytes shorter. The tap-highlight guard still holds: that
5342
+ // WebKit bug is about the keyword's *value*, whichever way it is spelled.
5343
+ if (
5344
+ path.within(NodeType.Declaration) &&
5345
+ !_S._inSubstitutedValue &&
5346
+ _S._hexAlphaAllowed &&
5347
+ _S._transforms.shortenColors &&
5348
+ asciiCaseInsensitiveMatch(meaning, "transparent") &&
5349
+ !_inTapHighlightColor()
5350
+ ) {
5351
+ return "#0000";
5352
+ }
5353
+ // The keyword names each platform's own UI font, which a target that
5354
+ // does not read it reaches only through the platform's own name for it.
5355
+ if (
5356
+ !_S._systemUiAllowed &&
5357
+ _S._transforms.lowerUnsupported &&
5358
+ path.within(NodeType.Declaration) &&
5359
+ !_S._inCustomProperty &&
5360
+ !_S._inSubstitutedValue &&
5361
+ asciiCaseInsensitiveMatch(meaning, "system-ui") &&
5362
+ _inFontFamily()
5363
+ ) {
5364
+ return SYSTEM_UI_STACK;
5365
+ }
5366
+ if (
5367
+ path.within(NodeType.Declaration) &&
5368
+ !_S._inCustomProperty &&
5369
+ !_S._inSupportsPrelude &&
5370
+ !_S._inSubstitutedValue
5371
+ ) {
5372
+ // One fold for both questions below, handing back the string where nothing
5373
+ // changes. ASCII only: `pin\212A` ends in a Kelvin sign, not a `k`.
5374
+ const lowered = asciiLowercaseName(meaning);
5375
+ // A named color where the property takes nothing else an identifier could
5376
+ // be: `white` is `#fff`, both computing to `rgb(255, 255, 255)`.
5377
+ // Elsewhere an identifier may be the author's own name.
5378
+ if (_S._transforms.shortenColors) {
5379
+ const shorter = getColorNameToShortest().get(lowered);
5380
+ if (
5381
+ shorter !== undefined &&
5382
+ (_inColorOnlyProperty() ||
5383
+ _inGradientArgument(path) ||
5384
+ _atBackgroundTop(path))
5385
+ ) {
5386
+ return shorter;
5387
+ }
5388
+ }
5389
+ // A keyword-only or color property names nothing of the author's, so an
5390
+ // ident directly in its value (a call's arguments are their own grammar's)
5391
+ // is a keyword matched ASCII case-insensitively, but a dashed ident (§4.2).
5392
+ if (
5393
+ _S._transforms.foldCase &&
5394
+ lowered !== raw &&
5395
+ path.parent === _S._valueDeclaration &&
5396
+ !meaning.startsWith("--") &&
5397
+ _inKeywordOnlyValue()
5398
+ ) {
5399
+ const folded =
5400
+ meaning !== raw && _S._transforms.rewriteEscapes
5401
+ ? escapeIdentifier(lowered)
5402
+ : asciiLowercaseName(raw);
5403
+ if (folded !== raw) return folded;
5404
+ }
5405
+ }
5406
+ return _S._transforms.rewriteEscapes ? _minifyIdentEscapes(raw) : raw;
5407
+ };
5408
+
5409
+ /**
5410
+ * The `printer` case for a simple block.
5411
+ * @param {CssPath} path the accessor positioned on the finished node
5412
+ * @param {PrintContext} writer the print context (children's printed text)
5413
+ * @param {boolean} minify whether minifying
5414
+ * @returns {string} the node's serialized text
5415
+ */
5416
+ const _printSimpleBlockNode = (path, writer, minify) => {
5417
+ const open = _blockTokenOf(path.node);
5418
+ const close = open === "(" ? ")" : open === "[" ? "]" : "}";
5419
+ // Outside a declaration value a `[…]` is an attribute selector and a `(…)`
5420
+ // a query condition. Inside one they are a grid line-name list and a
5421
+ // `calc()` sub-expression, where neither of those rewrites holds.
5422
+ const structural = minify && !path.within(NodeType.Declaration);
5423
+ /** @type {string[]} */
5424
+ let parts;
5425
+ if (structural && open === "[") {
5426
+ parts = _printAttributeSelector(path, path.children(), writer);
5427
+ } else {
5428
+ // One array, rather than a child list and a mapped copy of it.
5429
+ parts = [];
5430
+ _appendChildTexts(path.node, writer, parts);
5431
+ }
5432
+ if (structural && open === "(" && _S._inMediaConditionPrelude) {
5433
+ if (_S._transforms.foldCase) _lowercaseConditionParts(parts);
5434
+ if (_S._rangeSpellingAllowed) _useRangeSpelling(parts);
5435
+ }
5436
+ if (
5437
+ minify &&
5438
+ structural &&
5439
+ open === "(" &&
5440
+ (_S._inSupportsPrelude || _S._inMediaConditionPrelude)
5441
+ ) {
5442
+ _keepKeywordComments(parts);
5443
+ }
5444
+ let trim = minify && _S._mathFunctionDepth !== 0 ? _TRIM_MATH : _TRIM_NOTHING;
5445
+ if (structural && open === "(") {
5446
+ // `@scope`'s parentheses hold selector lists, so a `:` there starts a
5447
+ // pseudo-class and the whitespace before it is a descendant combinator
5448
+ // — `@scope (div :hover)` is not `@scope (div:hover)`.
5449
+ trim = _inScopePrelude(path) ? _TRIM_COMBINATORS : _TRIM_CONDITIONS;
5450
+ if (trim === _TRIM_COMBINATORS) _foldPseudoNames(parts);
5451
+ }
5452
+ const inner = _join(parts, !minify, trim);
5453
+ return `${open}${inner}${close}`;
5454
+ };
5455
+
5456
+ /**
5457
+ * The `printer` case for a declaration.
5458
+ * @param {CssPath} path the accessor positioned on the finished node
5459
+ * @param {PrintContext} writer the print context (children's printed text)
5460
+ * @param {boolean} minify whether minifying
5461
+ * @param {string} soft `""` minifying, `" "` beautifying
5462
+ * @returns {string} the node's serialized text
5463
+ */
5464
+ const _printDeclarationNode = (path, writer, minify, soft) => {
5465
+ const name = _rawName(path.node);
5466
+ // WHY: a property is matched by what its name means, not by how it was
5467
+ // spelled — §4.3.7 lets any of its letters be written as an escape — so
5468
+ // every lookup below reads the unescaped name while the printed one
5469
+ // keeps the source's. It costs an `indexOf` where nothing is escaped.
5470
+ const meaning = path.name();
5471
+ // Counted rather than listed: a declaration's value is one component 92
5472
+ // times in 100, and the list would be built only to be indexed twice.
5473
+ const count = path.childCount();
5474
+ // `@property`'s `initial-value` is read against the sibling `syntax`
5475
+ // descriptor, so it is as opaque as a custom property's own value.
5476
+ const custom =
5477
+ meaning.startsWith("--") ||
5478
+ (_S._inPropertyRule &&
5479
+ asciiCaseInsensitiveMatch(meaning, "initial-value")) ||
5480
+ // `@function`'s `result` is the token stream the call substitutes, so
5481
+ // it is opaque the same way — and empty when the function returns the
5482
+ // guaranteed-invalid value.
5483
+ (_S._inFunctionRule && asciiCaseInsensitiveMatch(meaning, "result")) ||
5484
+ // A `{}` block standing as the whole value is a token stream the
5485
+ // engine holds as written too — no grammar reads it, so a rewrite of
5486
+ // it builds a different CSSOM from the same document.
5487
+ (count === 1 &&
5488
+ path.type(path.child(0)) === NodeType.SimpleBlock &&
5489
+ _blockTokenOf(path.child(0)) === "{");
5490
+ // A value-less declaration is invalid, so it is already ignored — except
5491
+ // on a custom property, where the empty value is the guaranteed-invalid
5492
+ // one a `var()` fallback reads.
5493
+ if (minify && _S._transforms.removeDeadRules && count === 0 && !custom) {
5494
+ return "";
5495
+ }
5496
+ // The name folded to lowercase, where the value below needed it; `name`
5497
+ // itself where it did not, which is what the printed name reads as
5498
+ // "nothing to fold".
5499
+ let lowered = name;
5500
+ let value = "";
5501
+ if (count !== 0) {
5502
+ // Property names match ASCII case-insensitively and a custom property's
5503
+ // does not, so skipping it skips the lowercasing. One fold serves both
5504
+ // which value rules apply and how the name prints.
5505
+ lowered = custom ? meaning : toLowerCaseIfNeeded(meaning);
5506
+ const property = custom ? meaning : _standardSpelling(lowered);
5507
+ if (custom) {
5508
+ // `getPropertyValue()` hands this text back, so a rewritten token
5509
+ // would be a different CSSOM; only the boundaries between them go.
5510
+ const from = path.start(path.child(0));
5511
+ const to = path.end(path.child(count - 1));
5512
+ if (minify) {
5513
+ value = _customPropertyValue(path, path.node, writer, from, to);
5514
+ } else {
5515
+ // Straight from source, so the kept comments in it are already
5516
+ // there — claim them, or the writer emits them a second time
5517
+ // ahead of the next top-level node.
5518
+ writer.takeInserts(from, to);
5519
+ value = _S._input.slice(from, to);
5520
+ if (to === _S._input.length) value = _closeAtEof(value);
5521
+ }
5522
+ // Closed as printed, since a `}` may still follow; the grammar leaves it
5523
+ // open where nothing does, as the engine keeps it.
5524
+ const last = _S._eofPrinted;
5525
+ if (
5526
+ to === _S._input.length &&
5527
+ last !== "" &&
5528
+ value.endsWith(last) &&
5529
+ last !== _S._eofOpen
5530
+ ) {
5531
+ _S._openCustomValue = {
5532
+ printed: value,
5533
+ source: value.slice(0, value.length - last.length) + _S._eofOpen
5534
+ };
5535
+ }
5536
+ } else if (property === "unicode-range") {
5537
+ // `U+…` tokenizes as numbers, so the generic numeric normalization
5538
+ // would corrupt it; each range is shortened as the urange it is.
5539
+ let raw = "";
5540
+ for (let at = 0; at < count; at++) {
5541
+ raw += _printedSource(path.child(at));
5542
+ }
5543
+ value = minify
5544
+ ? raw
5545
+ .split(",")
5546
+ .map((one) => _minifyUnicodeRange(one.trim()))
5547
+ .join(",")
5548
+ : raw;
5549
+ } else {
5550
+ // Value hashes were already shortened by the hash printer, printing in a
5551
+ // value context, so this joins the children's text. A shorthand whose
5552
+ // value repeats what the notation implies collapses.
5553
+ const shorthand = minify
5554
+ ? _collapseShorthand(path, property, path.node, writer)
5555
+ : null;
5556
+ if (shorthand === null && count === 1) {
5557
+ // What a value is 98 times in 100, and `_join` hands a lone fragment
5558
+ // back, so it is rewritten without an array. §5.4.6 step 7 pops a
5559
+ // trailing whitespace, so that child is never the `""` case.
5560
+ value = _valueFragment(writer.get(path.child(0)), property, minify);
5561
+ } else {
5562
+ /** @type {string[]} */
5563
+ let fragments;
5564
+ if (shorthand === null) {
5565
+ fragments = [];
5566
+ _appendChildTexts(path.node, writer, fragments);
5567
+ } else {
5568
+ fragments = shorthand;
5569
+ }
5570
+ // In place: a `map` per transform would allocate an array each.
5571
+ for (let i = 0; i < fragments.length; i++) {
5572
+ fragments[i] = _valueFragment(fragments[i], property, minify);
5573
+ }
5574
+ // The string transforms below read space-separated components, so
5575
+ // only a value none of them covers loses the separators its tokens
5576
+ // do not need — a substitution's tokens included.
5577
+ const separatorsOnly =
5578
+ minify &&
5579
+ !AUTO_SECOND_VALUE_PROPERTIES.has(property) &&
5580
+ !ALPHA_VALUE_PROPERTIES.has(property) &&
5581
+ !RATIO_PROPERTIES.has(property);
5582
+ value = _join(
5583
+ fragments,
5584
+ !minify,
5585
+ separatorsOnly ? _TRIM_SEPARATORS : _TRIM_NOTHING
5586
+ );
5587
+ }
5588
+ if (minify) {
5589
+ value = _dropDefaultSecondValue(property, value);
5590
+ value = _numberAlphaValue(property, value);
5591
+ value = _dropRatioDenominator(property, value);
5592
+ }
5593
+ }
5594
+ }
5595
+ const important = _isImportant(path.node) ? `${soft}!important` : "";
5596
+ // Property names match ASCII case-insensitively; a custom property's
5597
+ // does not, which is what `custom` already stands for — nor does an
5598
+ // `@font-feature-values` sub-rule's, which names a feature value.
5599
+ const folds =
5600
+ minify && _S._transforms.foldCase && !custom && !_S._inFeatureValuesRule;
5601
+ let printedName = folds && lowered !== name ? asciiLowercaseName(name) : name;
5602
+ // An escape spells the same name, so the name is written the shortest way.
5603
+ if (minify && _S._transforms.rewriteEscapes && meaning !== name) {
5604
+ const spelled = escapeName(folds ? asciiLowercaseName(meaning) : meaning);
5605
+ if (spelled.length <= printedName.length) printedName = spelled;
5606
+ }
5607
+ return `${printedName}:${soft}${value}${important};`;
5608
+ };
5609
+
5610
+ /**
5611
+ * The `printer` case for an at-rule or a qualified rule.
5612
+ * @param {CssPath} path the accessor positioned on the finished node
5613
+ * @param {PrintContext} writer the print context (children's printed text)
5614
+ * @param {boolean} minify whether minifying
5615
+ * @param {string} soft `""` minifying, `" "` beautifying
5616
+ * @returns {string} the node's serialized text
5617
+ */
5618
+ const _printRuleNode = (path, writer, minify, soft) => {
5619
+ // Prefixing reads the prelude's tokens, not its joined text.
5620
+ const preludeParts = _S._prefixingOn
5621
+ ? /** @type {string[]} */ ([])
5622
+ : undefined;
5623
+ const prelude = _rulePrelude(path, writer, minify, preludeParts);
5624
+ const decls = _declarationsOf(path.node);
5625
+ // A qualified rule always has a block; an at-rule has one only when its
5626
+ // declaration list is non-null — else it is `@…;`.
5627
+ if (decls === null) {
5628
+ if (minify) _blockSpans.push(_NO_BLOCK_ENTRY);
5629
+ // `@charset` names the encoding the *source* was read in, and what is
5630
+ // written out is UTF-8 whatever it said — so carrying it over is at
5631
+ // best 22 dead bytes and at worst an encoding the output is not in.
5632
+ if (
5633
+ minify &&
5634
+ _S._transforms.removeDeadRules &&
5635
+ asciiCaseInsensitiveMatch(_rawName(path.node), "charset")
5636
+ ) {
5637
+ return "";
5638
+ }
5639
+ // The rule only goes where no target reads one: a condition this has
5640
+ // not come to yet still needs it, and so does an engine that reads it.
5641
+ if (
5642
+ minify &&
5643
+ _S._transforms.customMedia &&
5644
+ _atKeywordOf(path.node) === "custom-media" &&
5645
+ _takeCustomMedia(prelude) &&
5646
+ !_S._customMediaAllowed
5647
+ ) {
5648
+ return "";
5649
+ }
5650
+ // No engine reads `@custom-selector`, so the rule always goes once its
5651
+ // name is down — but only where `:is()` can carry what it stands for.
5652
+ if (
5653
+ minify &&
5654
+ _S._transforms.customSelectors &&
5655
+ _S._isSelectorAllowed &&
5656
+ _atKeywordOf(path.node) === "custom-selector" &&
5657
+ _takeCustomSelector(prelude)
5658
+ ) {
5659
+ return "";
5660
+ }
5661
+ return `${prelude};`;
5662
+ }
5663
+ // Read before the body: composing it is what leaves the block empty, and
5664
+ // the drop below asks the same question again.
5665
+ const unusedRule = _takeUnusedRule(_S._currentNode);
5666
+ const rules = _rulesOf(path.node);
5667
+ // `nl` prefixes each item: nothing minifying, a line break beautifying
5668
+ // (declarations end in `;`, rules don't).
5669
+ const nl = minify ? "" : "\n";
5670
+ const memoized = _memoizedBlockBody(_S._currentNode);
5671
+ const {
5672
+ body,
5673
+ items,
5674
+ texts,
5675
+ superseded,
5676
+ droppedPrefix,
5677
+ addedPrefix,
5678
+ deadPrefixed,
5679
+ spans
5680
+ } =
5681
+ memoized === undefined
5682
+ ? _composeBlockBody(
5683
+ path,
5684
+ decls,
5685
+ rules,
5686
+ writer,
5687
+ minify,
5688
+ nl,
5689
+ _S._currentNode,
5690
+ unusedRule
5691
+ )
5692
+ : // A rule its selector made unused writes nothing, whatever its block.
5693
+ _memoizedComposition(unusedRule ? "" : memoized);
5694
+ _memoizeBlockBody(
5695
+ _S._currentNode,
5696
+ memoized === undefined && !unusedRule ? body : null
5697
+ );
5698
+ if (
5699
+ minify &&
5700
+ // An empty rule paints nothing, so dropping it leaves the cascade as it
5701
+ // was — but only where the block means nothing (`_streamClose` agrees).
5702
+ _S._transforms.removeDeadRules &&
5703
+ body.length === 0 &&
5704
+ // See `_streamClose`: a rule taken out while a `@namespace` after it
5705
+ // could still be read would move one up into a live position.
5706
+ !(path.parent === null && _S._namespacePrologueOpen) &&
5707
+ (path.type() === NodeType.QualifiedRule ||
5708
+ DROPPABLE_WHEN_EMPTY_AT_RULES.has(_atKeywordOf(path.node)) ||
5709
+ unusedRule)
5710
+ ) {
5711
+ // The rules written in it are not empty for its being so, and what the
5712
+ // hoist writes for them is rules a later copy can take back.
5713
+ _blockSpans.push(
5714
+ path.type() === NodeType.QualifiedRule
5715
+ ? _NO_BLOCK_ENTRY_QUALIFIED
5716
+ : _NO_BLOCK_ENTRY
5717
+ );
5718
+ {
5719
+ const written =
5720
+ _writeNested(path, _S._currentNode, prelude, null, true) || "";
5721
+ if (minify && written.length !== 0) {
5722
+ _recordHoistedRule(written, null, 0);
5723
+ }
5724
+ return written;
5725
+ }
5726
+ }
5727
+ // A rule that says which color scheme it is in says it again as the pair
5728
+ // a lowered `light-dark()` reads, and — where it hands the choice to the
5729
+ // user's own preference — once more under the query that asks for it.
5730
+ let scheme = body;
5731
+ let schemeCopy = "";
5732
+ if (
5733
+ _S._loweringLightDark &&
5734
+ _S._sourceNamesLightDark &&
5735
+ path.type() === NodeType.QualifiedRule &&
5736
+ body.includes("color-scheme:") &&
5737
+ // The pair as this writes it, not either name on its own: a
5738
+ // block carrying that is one a lowering pass wrote. An authored
5739
+ // `--webpack-light` is a declaration of the author's.
5740
+ !body.includes(LIGHT_DARK_WRITTEN) &&
5741
+ !body.includes(LIGHT_DARK_WRITTEN_DARK)
5742
+ ) {
5743
+ const written = _COLOR_SCHEME_RE.exec(body);
5744
+ const toggle =
5745
+ written === null
5746
+ ? null
5747
+ : _lightDarkToggle(written[1].replace(_IMPORTANT, "").trim());
5748
+ if (toggle !== null) {
5749
+ // The body's own closing `;` went with the minified print, and the
5750
+ // toggle's last is the one this block now ends on.
5751
+ scheme = `${body};${toggle.toggle.slice(0, -1)}`;
5752
+ if (toggle.flip) {
5753
+ schemeCopy = `${_PREFERS_DARK}${soft}{${_OWN_PRELUDE}${soft}{${LIGHT_PROPERTY}:;${DARK_PROPERTY}:initial}}`;
5754
+ }
5755
+ }
5756
+ }
5757
+ // The hoist's copy names the prelude as the run settles it, since one
5758
+ // taken out is resolved against its parent and may be joined into a list.
5759
+ const ownRest = `${soft}{${scheme}${nl}}${schemeCopy}`;
5760
+ const rest =
5761
+ schemeCopy === "" ? ownRest : ownRest.replace(_OWN_PRELUDE, () => prelude);
5762
+ const text = `${prelude}${rest}`;
5763
+ // Prefix copies turn one rule into several, so a rewritten rule is not
5764
+ // offered for joining: its text no longer describes a single block. Each
5765
+ // block's rules are their own siblings, pairing within their scope.
5766
+ let out = text;
5767
+ if (_S._prefixingOn) {
5768
+ // Read and clear here, so the lookahead is the one rule the writer
5769
+ // holds — which only a top-level rule ever is — and never a rule
5770
+ // further back.
5771
+ const parent = path.parent;
5772
+ const top = parent === null;
5773
+ const scope = _prefixScope(parent);
5774
+ out =
5775
+ path.type() === NodeType.AtRule
5776
+ ? _prefixAtRule(path, text, prelude, scope, top)
5777
+ : _prefixQualifiedRule(
5778
+ text,
5779
+ /** @type {string[]} */ (preludeParts),
5780
+ soft,
5781
+ body,
5782
+ scope,
5783
+ top
5784
+ );
5785
+ }
5786
+ // WHY: pushed whatever it holds, since the parent takes one off per child
5787
+ // with a body, so a block carrying no rule still has to be there. A
5788
+ // rewritten rule records no nesting — those spans name `text` rather than
5789
+ // `out` — but is still the one rule its text says it is. Only a block that
5790
+ // recorded something needs an entry of its own, a leaf rule taking the
5791
+ // shared one, and nothing is pushed while not minifying.
5792
+ if (minify) {
5793
+ _blockSpans.push(
5794
+ spans === null || out !== text
5795
+ ? path.type() === NodeType.QualifiedRule
5796
+ ? _NO_BLOCK_ENTRY_QUALIFIED
5797
+ : _NO_BLOCK_ENTRY
5798
+ : {
5799
+ bodyAt: prelude.length + soft.length + 1,
5800
+ prelude,
5801
+ keyPrelude: _openerKey(`${prelude}{`),
5802
+ qualified: path.type() === NodeType.QualifiedRule,
5803
+ spans
5804
+ }
5805
+ );
5806
+ }
5807
+ // A rewritten rule is several rules with preludes of their own, and each
5808
+ // carries this rule's block — so the hoist takes them out. Only where
5809
+ // it holds no rule of its own, those naming one prelude of the several.
5810
+ const copies =
5811
+ out === text ||
5812
+ _prefixCopyPreludes.length === 0 ||
5813
+ _holdsNested(_S._currentNode)
5814
+ ? undefined
5815
+ : _prefixCopyPreludes;
5816
+ const nested = _writeNested(
5817
+ path,
5818
+ _S._currentNode,
5819
+ prelude,
5820
+ ownRest,
5821
+ out === text || copies !== undefined,
5822
+ copies,
5823
+ `${soft}{${body}}`
5824
+ );
5825
+ if (nested === null) return "";
5826
+ if (out !== text) return out + nested;
5827
+ // The run wrote this rule's selectors into itself, so what it returns is
5828
+ // the whole of both — `text` being this rule's own `prelude` and `rest`.
5829
+ if (nested.length !== 0) {
5830
+ if (_nestedTookParent) {
5831
+ if (minify) _recordHoistedRule(nested, null, 0);
5832
+ return nested;
5833
+ }
5834
+ if (minify) _recordHoistedRule(nested, text, prelude.length);
5835
+ return text + nested;
5836
+ }
5837
+ // Where the block is what a sibling is compared against, remember the rule
5838
+ // so the parent parts it without re-scanning for the `{`. An at-rule
5839
+ // remembers its block's entries too, so a sibling's rules can join them.
5840
+ if (
5841
+ minify &&
5842
+ path.type() === NodeType.AtRule &&
5843
+ MERGEABLE_AT_RULES.has(_atKeywordOf(path.node))
5844
+ ) {
5845
+ /** @type {RuleEntry[]} */
5846
+ const children = [];
5847
+ for (let i = 0; i < texts.length; i++) {
5848
+ if (texts[i].length === 0) continue;
5849
+ if (superseded !== null && superseded.has(i)) continue;
5850
+ // The entries have to spell the block this rule printed, prefixes and
5851
+ // all: a join rebuilds the body from them, and one built from the
5852
+ // unprefixed texts would restore what was dropped.
5853
+ if (droppedPrefix !== null && droppedPrefix.has(i)) continue;
5854
+ if (deadPrefixed !== null && deadPrefixed.has(items[i])) continue;
5855
+ const added = addedPrefix === null ? undefined : addedPrefix.get(i);
5856
+ children.push(
5857
+ added === undefined
5858
+ ? _ruleEntryOf(items[i], texts[i])
5859
+ : _opaqueEntry(added + texts[i])
5860
+ );
5861
+ }
5862
+ let head = "";
5863
+ for (let i = 0; i < children.length - 1; i++) head += children[i].text;
5864
+ _setRuleEntry(_S._currentNode, {
5865
+ text,
5866
+ prelude: prelude.length,
5867
+ atRule: true,
5868
+ plain: false,
5869
+ listable: LIST_NO,
5870
+ listKind: LIST_KIND_SELECTOR,
5871
+ children,
5872
+ head,
5873
+ // The block's own merges have run and settled what they grew, so the
5874
+ // text above and the children agree; a list one of them still carries
5875
+ // concatenated is one this block says is there to settle.
5876
+ unordered: _anyUnordered(children),
5877
+ list: null,
5878
+ block: null,
5879
+ declarations: null
5880
+ });
5881
+ }
5882
+ if (minify && path.type() === NodeType.QualifiedRule) {
5883
+ // Only a block of declarations lends its selectors to another's list: a
5884
+ // nested rule's `&` stands for the whole list and `:is(…)` takes its most
5885
+ // specific selector, so joining preludes moves what the nested beat.
5886
+ const plain = rules === null || rules.length === 0;
5887
+ const parent = path.parent;
5888
+ // Which shape a join would read the prelude with — the parent is gone by
5889
+ // then. A keyframe selector is a list of its own shape, which
5890
+ // `_rulePrelude` has already rewritten `from` in.
5891
+ let listKind = LIST_KIND_SELECTOR;
5892
+ if (parent !== null) {
5893
+ const parentType = path.type(parent);
5894
+ if (parentType === NodeType.QualifiedRule) {
5895
+ listKind = LIST_KIND_NESTED;
5896
+ } else if (
5897
+ parentType === NodeType.AtRule &&
5898
+ KEYFRAMES_AT_RULE_RE.test(_atKeywordOf(parent))
5899
+ ) {
5900
+ listKind = LIST_KIND_KEYFRAME;
5901
+ }
5902
+ }
5903
+ _setRuleEntry(_S._currentNode, {
5904
+ text,
5905
+ prelude: prelude.length,
5906
+ atRule: false,
5907
+ plain,
5908
+ listable: plain ? LIST_UNKNOWN : LIST_NO,
5909
+ listKind,
5910
+ children: null,
5911
+ head: "",
5912
+ unordered: false,
5913
+ list: null,
5914
+ block: null,
5915
+ declarations: null
5916
+ });
5917
+ }
5918
+ return text;
5919
+ };
5920
+
5921
+ /**
5922
+ * The `printer` case for a hash.
5923
+ * @param {CssPath} path the accessor positioned on the finished node
5924
+ * @param {boolean} minify whether minifying
5925
+ * @returns {string} the node's serialized text
5926
+ */
5927
+ const _printHashNode = (path, minify) => {
5928
+ const raw = _printedSource(path.node);
5929
+ if (!minify) return raw;
5930
+ // A hash is a hex color in a value and an id in a selector, and ids are
5931
+ // case-sensitive, so only the former shortens — at any value depth. An id's
5932
+ // escapes still shorten, the `#` held back so its first code point is one.
5933
+ if (!path.within(NodeType.Declaration)) {
5934
+ return _S._transforms.rewriteEscapes
5935
+ ? `#${_minifyIdentEscapes(raw.slice(1))}`
5936
+ : raw;
5937
+ }
5938
+ // Inside a function only the known color functions take color hashes, and a
5939
+ // substitution's fallback, being the property's value. `paint()` is the
5940
+ // exception: its arguments reach a worklet rather than a declaration.
5941
+ const parent = path.parent;
5942
+ if (parent !== null && path.type(parent) === NodeType.Function) {
5943
+ const fn = unescapeIdentifier(_rawName(parent)).toLowerCase();
5944
+ if (
5945
+ !COLOR_ARGUMENT_FUNCTIONS.has(fn) &&
5946
+ !(SUBSTITUTION_FUNCTIONS.has(fn) && fn !== "paint")
5947
+ ) {
5948
+ return raw;
5949
+ }
5950
+ }
5951
+ const short = _minifyHash(raw);
5952
+ const text = short === null ? raw : short;
5953
+ // A hex alpha the target cannot read is written as the `rgba()` it names.
5954
+ if (_S._hexAlphaAllowed) return text;
5955
+ const lowered = _S._transforms.lowerUnsupported
5956
+ ? _hexAlphaFallback(text)
5957
+ : null;
5958
+ return lowered === null ? text : lowered;
5959
+ };
5960
+
4852
5961
  /**
4853
5962
  * The default CSS node printer — passed to the `SourceProcessor` and fired once a
4854
5963
  * node's visitors and children are done (a developer could supply their own).
@@ -4895,984 +6004,21 @@ const printer = (path, writer) => {
4895
6004
  ? _normalizeNumericToken(raw, _isRgbChannel(path))
4896
6005
  : raw;
4897
6006
  }
4898
- case NodeType.Function: {
4899
- const written = _rawName(path.node);
4900
- // An escape spells the same name (§4.3.7): `c\61 lc(` is `calc()`, so
4901
- // every table below reads what it spells.
4902
- const name = written.includes("\\")
4903
- ? unescapeIdentifier(written)
4904
- : written;
4905
- // A container style query asks whether a custom property's value is the one
4906
- // written here, comparing token streams as written (CSS Conditional 5 §5),
4907
- // so a squeezed whitespace run asks another question. Nothing is rewritten.
4908
- if (
4909
- minify &&
4910
- !path.within(NodeType.Declaration) &&
4911
- _S._inMediaConditionPrelude &&
4912
- asciiCaseInsensitiveMatch(name, "style")
4913
- ) {
4914
- const source = _printedSource(path.node);
4915
- // Unclosed at EOF, the source carries no `)` for the walk to have
4916
- // closed — printed as usual so the parenthesis is written back.
4917
- if (source.endsWith(")")) {
4918
- // Written whole, comments included, so the queued copies are claimed
4919
- // here rather than flushed a second time after the rule.
4920
- writer.takeInserts(path.start(), path.end());
4921
- return source;
4922
- }
4923
- }
4924
- let inner;
4925
- if (path.childCount() === 1) {
4926
- // Half of all functions take one argument, which `_join` would hand
4927
- // straight back — and a trim only ever parts two of them, so no
4928
- // universal a compound implies can stand beside anything here either.
4929
- const only = writer.get(path.child(0));
4930
- inner = only === _SEP ? "" : only;
4931
- } else {
4932
- // A selector function's argument is a selector, so its combinators need
4933
- // no whitespace, as in the enclosing prelude; an `@import`'s
4934
- // `supports()` and a container's `scroll-state()` hold conditions.
4935
- const lower = name.toLowerCase();
4936
- const trim =
4937
- _S._mathFunctionDepth !== 0
4938
- ? _TRIM_MATH
4939
- : path.within(NodeType.Declaration)
4940
- ? _TRIM_NOTHING
4941
- : SELECTOR_FUNCTIONS.has(lower)
4942
- ? _TRIM_COMBINATORS
4943
- : (_S._inSupportsPrelude && lower === "supports") ||
4944
- (_S._inMediaConditionPrelude && lower === "scroll-state")
4945
- ? _TRIM_CONDITIONS
4946
- : _TRIM_NOTHING;
4947
- /** @type {string[]} */
4948
- const parts = [];
4949
- _appendChildTexts(path.node, writer, parts);
4950
- if (minify && trim === _TRIM_CONDITIONS) _keepKeywordComments(parts);
4951
- // A selector function's argument is a selector like the prelude's, so
4952
- // the universal a compound implies says nothing there either — except
4953
- // under test in a `@supports` condition, where only whitespace goes.
4954
- if (minify && trim === _TRIM_COMBINATORS && !_S._inSupportsPrelude) {
4955
- _foldPseudoNames(parts);
4956
- if (_S._transforms.shortenSelectors) _unwrapLoneIs(parts);
4957
- _dropImpliedUniversalSelector(parts);
4958
- }
4959
- inner = _join(parts, !minify, minify ? trim : _TRIM_NOTHING);
4960
- // A leading `+` in An+B is a sign, not a combinator: `+ n` is no
4961
- // argument an engine reads, and `+n` would be one.
4962
- if (minify && NTH_PSEUDO_FUNCTIONS.has(lower)) {
4963
- const at = parts.findIndex(
4964
- (part) => part.length !== 0 && part !== _SEP
4965
- );
4966
- if (parts[at] === "+" && parts[at + 1] === _SEP) {
4967
- inner = `+ ${inner.slice(1)}`;
4968
- }
4969
- }
4970
- // Chrome throws out `attr( name unit )` where `attr( name unit)`
4971
- // parses, so the space before the `)` decides whether the declaration
4972
- // runs — `_join` drops a trailing one like any other.
4973
- if (
4974
- minify &&
4975
- parts[parts.length - 1] === _SEP &&
4976
- asciiCaseInsensitiveMatch(name, "attr")
4977
- ) {
4978
- inner += " ";
4979
- }
4980
- }
4981
- if (!minify) return `${written}(${inner})`;
4982
- // Function names match ASCII case-insensitively, so one lowercase name
4983
- // is what every table below is keyed by; the eleven transforms spelled
4984
- // with a capital are printed the way everything else writes them.
4985
- const fn = asciiLowercaseName(name);
4986
- // As with a unit: a name already lowercase is its own answer, and only a
4987
- // shouted one is looked up. The lookup key still folds inside a
4988
- // substituted value; only the printed spelling stays as written.
4989
- let out =
4990
- _S._inSubstitutedValue ||
4991
- _S._inSupportsSelector ||
4992
- !_S._transforms.foldCase ||
4993
- fn.startsWith("--")
4994
- ? name
4995
- : fn === name
4996
- ? fn
4997
- : CANONICAL_NAMES.get(fn) || fn;
4998
- if (written !== name) {
4999
- out = _S._transforms.rewriteEscapes ? escapeName(out) : written;
5000
- }
5001
- // An+B in a selector: `2n+1` is what `odd` names, and shorter written so.
5002
- if (!path.within(NodeType.Declaration) && NTH_PSEUDO_FUNCTIONS.has(fn)) {
5003
- return _S._inSupportsSelector
5004
- ? `${out}(${inner})`
5005
- : _minifyAnPlusB(out, inner);
5006
- }
5007
- // Every rewrite below is keyed by a name none of these carry.
5008
- if (
5009
- SUBSTITUTION_FUNCTIONS.has(fn) ||
5010
- (!path.within(NodeType.Declaration) && SELECTOR_FUNCTIONS.has(fn))
5011
- ) {
5012
- return `${out}(${inner})`;
5013
- }
5014
- // WHY: an `rgb()` or `rgba()` collapses to the shortest color and anything
5015
- // else keeps its `name(args)` form. A color inside a `@supports` condition
5016
- // is syntax under test — the engines reading `#0000` are not those reading
5017
- // `rgba(0,0,0,0)` — so rewriting it asks another question, and esbuild,
5018
- // clean-css and lightningcss leave the condition alone too. A color read as
5019
- // numbers, in a gradient stop, a `color-mix()` argument or a relative
5020
- // color's origin, stays as written unless converting it is exact, which the
5021
- // legacy sRGB forms are where their channels are bytes.
5022
- let color = _S._inSupportsPrelude
5023
- ? null
5024
- : _minifyColorFunction(
5025
- fn,
5026
- inner,
5027
- _S._hexAlphaAllowed,
5028
- _S._inColorOperand
5029
- ) ||
5030
- (_S._inColorOperand
5031
- ? null
5032
- : _minifyPolarColorFunction(fn, inner, _S._hexAlphaAllowed));
5033
- // A mix no byte holds is written back in the space it was computed in,
5034
- // which can be longer than the call — then the call is the shorter one.
5035
- if (color !== null && color.length > fn.length + inner.length + 2) {
5036
- color = null;
5037
- }
5038
- if (
5039
- color !== null &&
5040
- (color !== "transparent" || !_inTapHighlightColor())
5041
- ) {
5042
- return color === "transparent" && _S._hexAlphaAllowed ? "#0000" : color;
5043
- }
5044
- // `light-dark()` names a color per scheme, which the pair above says with
5045
- // a substitution the scheme itself switches. Two arguments only: the
5046
- // grammar takes exactly that many, and anything else is no such call.
5047
- if (
5048
- _S._loweringLightDark &&
5049
- fn === "light-dark" &&
5050
- path.within(NodeType.Declaration) &&
5051
- !_S._inCustomProperty &&
5052
- !_S._inSupportsPrelude &&
5053
- !_S._inSubstitutedValue
5054
- ) {
5055
- const halves = _splitTopLevelArguments(inner);
5056
- // A half the target cannot read would substitute into a `var()` and make
5057
- // the declaration invalid at computed-value time, where nothing before it
5058
- // is read — so it is left alone rather than lowered.
5059
- if (halves.length === 2 && !_namesUnreadableColor(inner)) {
5060
- _S._lightDarkUsed = true;
5061
- return `var(${LIGHT_PROPERTY},${halves[0].trim()})var(${DARK_PROPERTY},${halves[1].trim()})`;
5062
- }
5063
- }
5064
- // A math function over constants of one unit is that value, written back as
5065
- // a `calc()`: the parentheses stay, since dropping them turns a clamped
5066
- // negative into a parse error, and only the declaration printer does.
5067
- if (!_S._inSupportsPrelude && !_S._inCustomProperty) {
5068
- const term = _foldMathFunction(fn, inner);
5069
- if (term !== null) {
5070
- // A term that means the same bare needs no parentheses anywhere, so
5071
- // it is written as itself; the rest keep a `calc()` for the
5072
- // declaration printer to judge against the property.
5073
- const shape =
5074
- _S._mathFunctionDepth > 1 || _inNumberArgument(path)
5075
- ? _NESTED_TERM_RE
5076
- : _BARE_TERM_RE;
5077
- const folded =
5078
- (shape.test(term) ||
5079
- (_S._mathFunctionDepth === 1 &&
5080
- _NEGATIVE_TERM_RE.test(term) &&
5081
- _inNegativeAcceptingArgument(path))) &&
5082
- !_inCalcRejectingProperty() &&
5083
- !(!/[a-z%]$/i.test(term) && _inNumberOnlyOutsideCalcProperty()) &&
5084
- !_foldLosesClamp(term) &&
5085
- !_foldBelowStepCount(term, path)
5086
- ? term
5087
- : `calc(${term})`;
5088
- // A division can land on a value needing every digit of a double, which
5089
- // is longer than the expression producing it. Only the outermost call
5090
- // is asked, a nested one being a term still to be reduced.
5091
- if (
5092
- _S._mathFunctionDepth > 1 ||
5093
- folded.length < fn.length + inner.length + 2 ||
5094
- _unwrapsShorter(path, folded, fn.length + inner.length + 2)
5095
- ) {
5096
- return folded;
5097
- }
5098
- }
5099
- const reduced = _reduceMathArguments(fn, inner);
5100
- // A `calc()` inside another math function is the parentheses it
5101
- // stands for (CSS Values 4 §10.1), whatever a substitution expands to.
5102
- if (
5103
- fn === "calc" &&
5104
- _S._mathFunctionDepth > 1 &&
5105
- path.within(NodeType.Declaration) &&
5106
- _S._transforms.reduceFunctions
5107
- ) {
5108
- return `(${reduced === null ? inner : reduced})`;
5109
- }
5110
- if (reduced !== null) return `${fn}(${reduced})`;
5111
- }
5112
- // An easing function is only ever a value; `url()` is also an at-rule
5113
- // prelude's (`@import url("a.css")`).
5114
- const easing =
5115
- path.within(NodeType.Declaration) && !_S._inCustomProperty
5116
- ? _minifyEasingFunction(fn, inner)
5117
- : null;
5118
- if (easing !== null) return easing;
5119
- // A translation's percentage is of the element's own box, which always
5120
- // has a size, so a `0%` term beside one length adds nothing to it.
5121
- if (
5122
- _S._transforms.reduceFunctions &&
5123
- path.within(NodeType.Declaration) &&
5124
- !_S._inSubstitutedValue &&
5125
- fn.startsWith("translate") &&
5126
- inner.includes("0%")
5127
- ) {
5128
- const args = _splitTopLevelArguments(inner);
5129
- let changed = false;
5130
- for (let i = 0; i < args.length; i++) {
5131
- // A z slot takes no percentage, so a `0%` there makes the whole
5132
- // declaration invalid, which dropping it would revive.
5133
- if (fn === "translatez" || (fn === "translate3d" && i === 2)) {
5134
- continue;
5135
- }
5136
- const term = _dropZeroPercentTerm(args[i].trim());
5137
- if (term !== null) {
5138
- args[i] = term;
5139
- changed = true;
5140
- }
5141
- }
5142
- // The rewrites below read the arguments as they now stand.
5143
- if (changed) inner = args.join(",");
5144
- }
5145
- // `translateX(v)` is `translate(v)` and `skewX(a)` is `skew(a)`, the second
5146
- // component defaulting to 0. Only for one plain component: a substitution
5147
- // could expand to two, reviving a declaration the browser drops.
5148
- const oneAxis = _S._transforms.reduceFunctions
5149
- ? X_AXIS_TRANSFORMS.get(fn)
5150
- : undefined;
5151
- if (
5152
- oneAxis !== undefined &&
5153
- path.within(NodeType.Declaration) &&
5154
- !_S._inSubstitutedValue &&
5155
- !inner.includes(",") &&
5156
- !containsSubstitutionFunction(inner)
5157
- ) {
5158
- return `${oneAxis}(${inner})`;
5159
- }
5160
- if (path.within(NodeType.Declaration) && !_S._inSubstitutedValue) {
5161
- // An argument that is the amount an omitted one means says nothing,
5162
- // whether or not the zero still carries the unit its grammar drops.
5163
- const omitted = _S._transforms.reduceFunctions
5164
- ? FILTER_FUNCTION_OMITTED.get(fn)
5165
- : undefined;
5166
- if (omitted !== undefined) {
5167
- const bare = _dropCallZeroUnit(fn, inner);
5168
- if (bare === omitted || (omitted === "1" && bare === "100%")) {
5169
- return `${out}()`;
5170
- }
5171
- }
5172
- const reduced = _reduceTransformFunctionDeep(fn, inner);
5173
- if (reduced !== null) return reduced;
5174
- // The stops are folded over what dropping the direction leaves, a
5175
- // gradient carrying both otherwise keeping whichever ran second.
5176
- const shape = _dropDefaultShapeParts(fn, inner);
5177
- if (shape !== null) return `${out}(${shape})`;
5178
- let gradient = _reduceGradientDirection(fn, inner);
5179
- if (gradient === null) {
5180
- gradient = _dropDefaultGradientPosition(fn, inner);
5181
- }
5182
- let stops = _foldGradientStops(
5183
- fn,
5184
- gradient === null ? inner : gradient,
5185
- _S._doublePositionAllowed
5186
- );
5187
- // A double position the target cannot read is written as the two
5188
- // stops it names, over whatever the folds above left.
5189
- if (!_S._doublePositionAllowed) {
5190
- const split = !_S._transforms.lowerUnsupported
5191
- ? null
5192
- : _splitDoublePositionStops(
5193
- fn,
5194
- stops !== null ? stops : gradient !== null ? gradient : inner
5195
- );
5196
- if (split !== null) stops = split;
5197
- }
5198
- if (stops !== null) return `${out}(${stops})`;
5199
- if (gradient !== null) return `${out}(${gradient})`;
5200
- }
5201
- // A zero angle needs no unit where the grammar names `<zero>` beside
5202
- // `<angle>` (CSS Transforms 2, Filter Effects 1).
5203
- if (
5204
- path.within(NodeType.Declaration) &&
5205
- !_S._inSubstitutedValue &&
5206
- _S._transforms.shortenNumbers &&
5207
- ZERO_ANGLE_FUNCTIONS.has(fn)
5208
- ) {
5209
- const bare = inner.replace(_ZERO_ANGLE_ARGUMENT_RE, "$10");
5210
- if (bare !== inner) return `${out}(${bare})`;
5211
- }
5212
- // A name kept escaped keeps its call as written: unquoting it rests on the
5213
- // tokenizer reading `url` through the escape.
5214
- const url =
5215
- _S._inSubstitutedValue || (written !== name && out === written)
5216
- ? null
5217
- : _minifyUrlFunction(fn, inner);
5218
- if (url !== null) return url;
5219
- return `${out}(${inner})`;
5220
- }
5221
- case NodeType.Url: {
5222
- const source = _printedSource(path.node);
5223
- // Closed at EOF: write the `)` back so the url stops where it stopped for
5224
- // the tokenizer rather than swallowing what the printer emits next. Before
5225
- // the rest rather than instead of it, or a second pass is what folds it.
5226
- const unterminated = _isUnterminatedUrl(source);
5227
- const raw = unterminated ? _terminate(source, ")", "\uFFFD") : source;
5228
- // The tokenizer already trimmed the url-token's content, so the padding in
5229
- // `url( a.png )` carries nothing.
5230
- if (!minify) return raw;
5231
- const open = raw.indexOf("(");
5232
- // `url` matches ASCII case-insensitively like any function name. Read in
5233
- // place, since cutting the name out would allocate on every url and all but
5234
- // a shouted one folds to itself. In a substituted value nothing is read.
5235
- let head = null;
5236
- if (!_S._inSubstitutedValue && _S._transforms.foldCase) {
5237
- for (let i = 0; i < open; i++) {
5238
- const c = raw.charCodeAt(i);
5239
- if (c >= CC_UPPER_A && c <= CC_UPPER_Z) {
5240
- head = asciiLowercaseName(raw.slice(0, open + 1));
5241
- break;
5242
- }
5243
- }
5244
- }
5245
- const percent = raw.includes("%");
5246
- /**
5247
- * @returns {string} the url-token as it prints with nothing rendered
5248
- */
5249
- const plainUrl = () => {
5250
- // Padding only exists when the content is whitespace-bounded; checking
5251
- // that costs two char reads, reading the content costs a slice.
5252
- if (
5253
- !percent &&
5254
- !isAsciiWhitespace(raw.charCodeAt(open + 1)) &&
5255
- !isAsciiWhitespace(raw.charCodeAt(raw.length - 2))
5256
- ) {
5257
- return head === null ? raw : head + raw.slice(open + 1);
5258
- }
5259
- const name = head === null ? raw.slice(0, open + 1) : head;
5260
- const value = _urlValue(path, unterminated);
5261
- // A url-token already carrying an escape is one this printer did not
5262
- // write, so its `\%` is not read as the start of a percent-escape.
5263
- return `${name}${
5264
- percent && !value.includes("\\")
5265
- ? _decodePercentEscapes(value, true, "")
5266
- : value
5267
- })`;
5268
- };
5269
- // Ahead of the padding fast path below: a base64 `data:` url is
5270
- // whitespace-free and `%`-free, so it would take it and never be offered.
5271
- if (_S._deferEmbeddedSource !== undefined) {
5272
- const opener = head === null ? raw.slice(0, open + 1) : head;
5273
- const deferred = _deferDataUrl(
5274
- _urlValue(path, unterminated),
5275
- (url) => `${opener}${_serializeUrl(url, '"')})`,
5276
- plainUrl()
5277
- );
5278
- if (deferred !== null) return deferred;
5279
- }
5280
- if (_S._renderEmbeddedSource !== undefined) {
5281
- const renderedUrl = _renderDataUrl(_urlValue(path, unterminated));
5282
- if (renderedUrl !== null) {
5283
- const name = head === null ? raw.slice(0, open + 1) : head;
5284
- return `${name}${_serializeUrl(renderedUrl, '"')})`;
5285
- }
5286
- }
5287
- return plainUrl();
5288
- }
5289
- case NodeType.String: {
5290
- const source = _printedSource(path.node);
5291
- // Closed at EOF: write the quote back, for the same reason as `url()`.
5292
- // Written here rather than after the transforms below, or they read a
5293
- // string with no closing quote and decline it, which a second pass undoes.
5294
- const raw = _isClosedString(source)
5295
- ? source
5296
- : _terminate(source, source[0], "");
5297
- // A custom property hands its string back as written, quotes included.
5298
- if (!minify || _S._inCustomProperty) return raw;
5299
- // `<family-name>` is `<string> | <custom-ident>+`, so a family whose text
5300
- // is a run of identifiers means the same unquoted, two bytes shorter.
5301
- if (
5302
- !_S._inSupportsPrelude &&
5303
- !_S._inSubstitutedValue &&
5304
- ((_inFontFamily() && _isLoneFamilyName(path)) ||
5305
- _isLoneFontFamily(path))
5306
- ) {
5307
- const unquoted = _unquoteFontFamily(raw);
5308
- if (unquoted !== null) return unquoted;
5309
- }
5310
- const written = _minifyString(raw);
5311
- return (_S._charsetPiece === -1 && !_S._assumeUtf8) ||
5312
- !_S._transforms.rewriteEscapes
5313
- ? written
5314
- : _unescapeAstral(written);
5315
- }
5316
- case NodeType.Ident: {
5317
- const raw = _printedSource(path.node);
5318
- if (!minify) return raw;
5319
- // A keyword is matched by what an escape spells, not by how it is written.
5320
- const meaning = raw.includes("\\") ? unescapeIdentifier(raw) : raw;
5321
- // `transparent` is `#0000` where the target reads a hex alpha — the same
5322
- // color, six bytes shorter. The tap-highlight guard still holds: that
5323
- // WebKit bug is about the keyword's *value*, whichever way it is spelled.
5324
- if (
5325
- path.within(NodeType.Declaration) &&
5326
- !_S._inSubstitutedValue &&
5327
- _S._hexAlphaAllowed &&
5328
- _S._transforms.shortenColors &&
5329
- asciiCaseInsensitiveMatch(meaning, "transparent") &&
5330
- !_inTapHighlightColor()
5331
- ) {
5332
- return "#0000";
5333
- }
5334
- // The keyword names each platform's own UI font, which a target that
5335
- // does not read it reaches only through the platform's own name for it.
5336
- if (
5337
- !_S._systemUiAllowed &&
5338
- _S._transforms.lowerUnsupported &&
5339
- path.within(NodeType.Declaration) &&
5340
- !_S._inCustomProperty &&
5341
- !_S._inSubstitutedValue &&
5342
- asciiCaseInsensitiveMatch(meaning, "system-ui") &&
5343
- _inFontFamily()
5344
- ) {
5345
- return SYSTEM_UI_STACK;
5346
- }
5347
- if (
5348
- path.within(NodeType.Declaration) &&
5349
- !_S._inCustomProperty &&
5350
- !_S._inSupportsPrelude &&
5351
- !_S._inSubstitutedValue
5352
- ) {
5353
- // One fold for both questions below, handing back the string where nothing
5354
- // changes. ASCII only: `pin\212A` ends in a Kelvin sign, not a `k`.
5355
- const lowered = asciiLowercaseName(meaning);
5356
- // A named color where the property takes nothing else an identifier could
5357
- // be: `white` is `#fff`, both computing to `rgb(255, 255, 255)`.
5358
- // Elsewhere an identifier may be the author's own name.
5359
- if (_S._transforms.shortenColors) {
5360
- const shorter = getColorNameToShortest().get(lowered);
5361
- if (
5362
- shorter !== undefined &&
5363
- (_inColorOnlyProperty() ||
5364
- _inGradientArgument(path) ||
5365
- _atBackgroundTop(path))
5366
- ) {
5367
- return shorter;
5368
- }
5369
- }
5370
- // A keyword-only or color property names nothing of the author's, so an
5371
- // ident directly in its value (a call's arguments are their own grammar's)
5372
- // is a keyword matched ASCII case-insensitively, but a dashed ident (§4.2).
5373
- if (
5374
- _S._transforms.foldCase &&
5375
- lowered !== raw &&
5376
- path.parent === _S._valueDeclaration &&
5377
- !meaning.startsWith("--") &&
5378
- _inKeywordOnlyValue()
5379
- ) {
5380
- const folded =
5381
- meaning !== raw && _S._transforms.rewriteEscapes
5382
- ? escapeIdentifier(lowered)
5383
- : asciiLowercaseName(raw);
5384
- if (folded !== raw) return folded;
5385
- }
5386
- }
5387
- return _S._transforms.rewriteEscapes ? _minifyIdentEscapes(raw) : raw;
5388
- }
5389
- case NodeType.SimpleBlock: {
5390
- const open = _blockTokenOf(path.node);
5391
- const close = open === "(" ? ")" : open === "[" ? "]" : "}";
5392
- // Outside a declaration value a `[…]` is an attribute selector and a `(…)`
5393
- // a query condition. Inside one they are a grid line-name list and a
5394
- // `calc()` sub-expression, where neither of those rewrites holds.
5395
- const structural = minify && !path.within(NodeType.Declaration);
5396
- /** @type {string[]} */
5397
- let parts;
5398
- if (structural && open === "[") {
5399
- parts = _printAttributeSelector(path, path.children(), writer);
5400
- } else {
5401
- // One array, rather than a child list and a mapped copy of it.
5402
- parts = [];
5403
- _appendChildTexts(path.node, writer, parts);
5404
- }
5405
- if (structural && open === "(" && _S._inMediaConditionPrelude) {
5406
- if (_S._transforms.foldCase) _lowercaseConditionParts(parts);
5407
- if (_S._rangeSpellingAllowed) _useRangeSpelling(parts);
5408
- }
5409
- if (
5410
- minify &&
5411
- structural &&
5412
- open === "(" &&
5413
- (_S._inSupportsPrelude || _S._inMediaConditionPrelude)
5414
- ) {
5415
- _keepKeywordComments(parts);
5416
- }
5417
- let trim =
5418
- minify && _S._mathFunctionDepth !== 0 ? _TRIM_MATH : _TRIM_NOTHING;
5419
- if (structural && open === "(") {
5420
- // `@scope`'s parentheses hold selector lists, so a `:` there starts a
5421
- // pseudo-class and the whitespace before it is a descendant combinator
5422
- // — `@scope (div :hover)` is not `@scope (div:hover)`.
5423
- trim = _inScopePrelude(path) ? _TRIM_COMBINATORS : _TRIM_CONDITIONS;
5424
- if (trim === _TRIM_COMBINATORS) _foldPseudoNames(parts);
5425
- }
5426
- const inner = _join(parts, !minify, trim);
5427
- return `${open}${inner}${close}`;
5428
- }
5429
- case NodeType.Declaration: {
5430
- const name = _rawName(path.node);
5431
- // WHY: a property is matched by what its name means, not by how it was
5432
- // spelled — §4.3.7 lets any of its letters be written as an escape — so
5433
- // every lookup below reads the unescaped name while the printed one
5434
- // keeps the source's. It costs an `indexOf` where nothing is escaped.
5435
- const meaning = path.name();
5436
- // Counted rather than listed: a declaration's value is one component 92
5437
- // times in 100, and the list would be built only to be indexed twice.
5438
- const count = path.childCount();
5439
- // `@property`'s `initial-value` is read against the sibling `syntax`
5440
- // descriptor, so it is as opaque as a custom property's own value.
5441
- const custom =
5442
- meaning.startsWith("--") ||
5443
- (_S._inPropertyRule &&
5444
- asciiCaseInsensitiveMatch(meaning, "initial-value")) ||
5445
- // `@function`'s `result` is the token stream the call substitutes, so
5446
- // it is opaque the same way — and empty when the function returns the
5447
- // guaranteed-invalid value.
5448
- (_S._inFunctionRule && asciiCaseInsensitiveMatch(meaning, "result")) ||
5449
- // A `{}` block standing as the whole value is a token stream the
5450
- // engine holds as written too — no grammar reads it, so a rewrite of
5451
- // it builds a different CSSOM from the same document.
5452
- (count === 1 &&
5453
- path.type(path.child(0)) === NodeType.SimpleBlock &&
5454
- _blockTokenOf(path.child(0)) === "{");
5455
- // A value-less declaration is invalid, so it is already ignored — except
5456
- // on a custom property, where the empty value is the guaranteed-invalid
5457
- // one a `var()` fallback reads.
5458
- if (minify && _S._transforms.removeDeadRules && count === 0 && !custom) {
5459
- return "";
5460
- }
5461
- // The name folded to lowercase, where the value below needed it; `name`
5462
- // itself where it did not, which is what the printed name reads as
5463
- // "nothing to fold".
5464
- let lowered = name;
5465
- let value = "";
5466
- if (count !== 0) {
5467
- // Property names match ASCII case-insensitively and a custom property's
5468
- // does not, so skipping it skips the lowercasing. One fold serves both
5469
- // which value rules apply and how the name prints.
5470
- lowered = custom ? meaning : toLowerCaseIfNeeded(meaning);
5471
- const property = custom ? meaning : _standardSpelling(lowered);
5472
- if (custom) {
5473
- // `getPropertyValue()` hands this text back, so a rewritten token
5474
- // would be a different CSSOM; only the boundaries between them go.
5475
- const from = path.start(path.child(0));
5476
- const to = path.end(path.child(count - 1));
5477
- if (minify) {
5478
- value = _customPropertyValue(path, path.node, writer, from, to);
5479
- } else {
5480
- // Straight from source, so the kept comments in it are already
5481
- // there — claim them, or the writer emits them a second time
5482
- // ahead of the next top-level node.
5483
- writer.takeInserts(from, to);
5484
- value = _S._input.slice(from, to);
5485
- if (to === _S._input.length) value = _closeAtEof(value);
5486
- }
5487
- // Closed as printed, since a `}` may still follow; the grammar leaves it
5488
- // open where nothing does, as the engine keeps it.
5489
- const last = _S._eofPrinted;
5490
- if (
5491
- to === _S._input.length &&
5492
- last !== "" &&
5493
- value.endsWith(last) &&
5494
- last !== _S._eofOpen
5495
- ) {
5496
- _S._openCustomValue = {
5497
- printed: value,
5498
- source: value.slice(0, value.length - last.length) + _S._eofOpen
5499
- };
5500
- }
5501
- } else if (property === "unicode-range") {
5502
- // `U+…` tokenizes as numbers, so the generic numeric normalization
5503
- // would corrupt it; each range is shortened as the urange it is.
5504
- let raw = "";
5505
- for (let at = 0; at < count; at++) {
5506
- raw += _printedSource(path.child(at));
5507
- }
5508
- value = minify
5509
- ? raw
5510
- .split(",")
5511
- .map((one) => _minifyUnicodeRange(one.trim()))
5512
- .join(",")
5513
- : raw;
5514
- } else {
5515
- // Value hashes were already shortened by the hash printer, printing in a
5516
- // value context, so this joins the children's text. A shorthand whose
5517
- // value repeats what the notation implies collapses.
5518
- const shorthand = minify
5519
- ? _collapseShorthand(path, property, path.node, writer)
5520
- : null;
5521
- if (shorthand === null && count === 1) {
5522
- // What a value is 98 times in 100, and `_join` hands a lone fragment
5523
- // back, so it is rewritten without an array. §5.4.6 step 7 pops a
5524
- // trailing whitespace, so that child is never the `""` case.
5525
- value = _valueFragment(writer.get(path.child(0)), property, minify);
5526
- } else {
5527
- /** @type {string[]} */
5528
- let fragments;
5529
- if (shorthand === null) {
5530
- fragments = [];
5531
- _appendChildTexts(path.node, writer, fragments);
5532
- } else {
5533
- fragments = shorthand;
5534
- }
5535
- // In place: a `map` per transform would allocate an array each.
5536
- for (let i = 0; i < fragments.length; i++) {
5537
- fragments[i] = _valueFragment(fragments[i], property, minify);
5538
- }
5539
- // The string transforms below read space-separated components, so
5540
- // only a value none of them covers loses the separators its tokens
5541
- // do not need — a substitution's tokens included.
5542
- const separatorsOnly =
5543
- minify &&
5544
- !AUTO_SECOND_VALUE_PROPERTIES.has(property) &&
5545
- !ALPHA_VALUE_PROPERTIES.has(property) &&
5546
- !RATIO_PROPERTIES.has(property);
5547
- value = _join(
5548
- fragments,
5549
- !minify,
5550
- separatorsOnly ? _TRIM_SEPARATORS : _TRIM_NOTHING
5551
- );
5552
- }
5553
- if (minify) {
5554
- value = _dropDefaultSecondValue(property, value);
5555
- value = _numberAlphaValue(property, value);
5556
- value = _dropRatioDenominator(property, value);
5557
- }
5558
- }
5559
- }
5560
- const important = _isImportant(path.node) ? `${soft}!important` : "";
5561
- // Property names match ASCII case-insensitively; a custom property's
5562
- // does not, which is what `custom` already stands for — nor does an
5563
- // `@font-feature-values` sub-rule's, which names a feature value.
5564
- const folds =
5565
- minify &&
5566
- _S._transforms.foldCase &&
5567
- !custom &&
5568
- !_S._inFeatureValuesRule;
5569
- let printedName =
5570
- folds && lowered !== name ? asciiLowercaseName(name) : name;
5571
- // An escape spells the same name, so the name is written the shortest way.
5572
- if (minify && _S._transforms.rewriteEscapes && meaning !== name) {
5573
- const spelled = escapeName(
5574
- folds ? asciiLowercaseName(meaning) : meaning
5575
- );
5576
- if (spelled.length <= printedName.length) printedName = spelled;
5577
- }
5578
- return `${printedName}:${soft}${value}${important};`;
5579
- }
6007
+ case NodeType.Function:
6008
+ return _printFunctionNode(path, writer, minify);
6009
+ case NodeType.Url:
6010
+ return _printUrlNode(path, minify);
6011
+ case NodeType.String:
6012
+ return _printStringNode(path, minify);
6013
+ case NodeType.Ident:
6014
+ return _printIdentNode(path, minify);
6015
+ case NodeType.SimpleBlock:
6016
+ return _printSimpleBlockNode(path, writer, minify);
6017
+ case NodeType.Declaration:
6018
+ return _printDeclarationNode(path, writer, minify, soft);
5580
6019
  case NodeType.AtRule:
5581
- case NodeType.QualifiedRule: {
5582
- // Prefixing reads the prelude's tokens, not its joined text.
5583
- const preludeParts = _S._prefixingOn
5584
- ? /** @type {string[]} */ ([])
5585
- : undefined;
5586
- const prelude = _rulePrelude(path, writer, minify, preludeParts);
5587
- const decls = _declarationsOf(path.node);
5588
- // A qualified rule always has a block; an at-rule has one only when its
5589
- // declaration list is non-null — else it is `@…;`.
5590
- if (decls === null) {
5591
- if (minify) _blockSpans.push(_NO_BLOCK_ENTRY);
5592
- // `@charset` names the encoding the *source* was read in, and what is
5593
- // written out is UTF-8 whatever it said — so carrying it over is at
5594
- // best 22 dead bytes and at worst an encoding the output is not in.
5595
- if (
5596
- minify &&
5597
- _S._transforms.removeDeadRules &&
5598
- asciiCaseInsensitiveMatch(_rawName(path.node), "charset")
5599
- ) {
5600
- return "";
5601
- }
5602
- // The rule only goes where no target reads one: a condition this has
5603
- // not come to yet still needs it, and so does an engine that reads it.
5604
- if (
5605
- minify &&
5606
- _S._transforms.customMedia &&
5607
- _atKeywordOf(path.node) === "custom-media" &&
5608
- _takeCustomMedia(prelude) &&
5609
- !_S._customMediaAllowed
5610
- ) {
5611
- return "";
5612
- }
5613
- // No engine reads `@custom-selector`, so the rule always goes once its
5614
- // name is down — but only where `:is()` can carry what it stands for.
5615
- if (
5616
- minify &&
5617
- _S._transforms.customSelectors &&
5618
- _S._isSelectorAllowed &&
5619
- _atKeywordOf(path.node) === "custom-selector" &&
5620
- _takeCustomSelector(prelude)
5621
- ) {
5622
- return "";
5623
- }
5624
- return `${prelude};`;
5625
- }
5626
- // Read before the body: composing it is what leaves the block empty, and
5627
- // the drop below asks the same question again.
5628
- const unusedRule = _takeUnusedRule(/** @type {Node} */ (_S._currentNode));
5629
- const rules = _rulesOf(path.node);
5630
- // `nl` prefixes each item: nothing minifying, a line break beautifying
5631
- // (declarations end in `;`, rules don't).
5632
- const nl = minify ? "" : "\n";
5633
- const {
5634
- body,
5635
- items,
5636
- texts,
5637
- superseded,
5638
- droppedPrefix,
5639
- addedPrefix,
5640
- deadPrefixed,
5641
- spans
5642
- } = _composeBlockBody(
5643
- path,
5644
- decls,
5645
- rules,
5646
- writer,
5647
- minify,
5648
- nl,
5649
- _S._currentNode,
5650
- unusedRule
5651
- );
5652
- if (
5653
- minify &&
5654
- // An empty rule paints nothing, so dropping it leaves the cascade as it
5655
- // was — but only where the block means nothing (`_streamClose` agrees).
5656
- _S._transforms.removeDeadRules &&
5657
- body.length === 0 &&
5658
- // See `_streamClose`: a rule taken out while a `@namespace` after it
5659
- // could still be read would move one up into a live position.
5660
- !(path.parent === null && _S._namespacePrologueOpen) &&
5661
- (path.type() === NodeType.QualifiedRule ||
5662
- DROPPABLE_WHEN_EMPTY_AT_RULES.has(_atKeywordOf(path.node)) ||
5663
- unusedRule)
5664
- ) {
5665
- // The rules written in it are not empty for its being so, and what the
5666
- // hoist writes for them is rules a later copy can take back.
5667
- _blockSpans.push(
5668
- path.type() === NodeType.QualifiedRule
5669
- ? _NO_BLOCK_ENTRY_QUALIFIED
5670
- : _NO_BLOCK_ENTRY
5671
- );
5672
- {
5673
- const written =
5674
- _writeNested(
5675
- path,
5676
- /** @type {Node} */ (_S._currentNode),
5677
- prelude,
5678
- null,
5679
- true
5680
- ) || "";
5681
- if (minify && written.length !== 0) {
5682
- _recordHoistedRule(written, null, 0);
5683
- }
5684
- return written;
5685
- }
5686
- }
5687
- // A rule that says which color scheme it is in says it again as the pair
5688
- // a lowered `light-dark()` reads, and — where it hands the choice to the
5689
- // user's own preference — once more under the query that asks for it.
5690
- let scheme = body;
5691
- let schemeCopy = "";
5692
- if (
5693
- _S._loweringLightDark &&
5694
- _S._sourceNamesLightDark &&
5695
- path.type() === NodeType.QualifiedRule &&
5696
- body.includes("color-scheme:") &&
5697
- // The pair as this writes it, not either name on its own: a
5698
- // block carrying that is one a lowering pass wrote. An authored
5699
- // `--webpack-light` is a declaration of the author's.
5700
- !body.includes(LIGHT_DARK_WRITTEN) &&
5701
- !body.includes(LIGHT_DARK_WRITTEN_DARK)
5702
- ) {
5703
- const written = _COLOR_SCHEME_RE.exec(body);
5704
- const toggle =
5705
- written === null
5706
- ? null
5707
- : _lightDarkToggle(written[1].replace(_IMPORTANT, "").trim());
5708
- if (toggle !== null) {
5709
- // The body's own closing `;` went with the minified print, and the
5710
- // toggle's last is the one this block now ends on.
5711
- scheme = `${body};${toggle.toggle.slice(0, -1)}`;
5712
- if (toggle.flip) {
5713
- schemeCopy = `${_PREFERS_DARK}${soft}{${_OWN_PRELUDE}${soft}{${LIGHT_PROPERTY}:;${DARK_PROPERTY}:initial}}`;
5714
- }
5715
- }
5716
- }
5717
- // The hoist's copy names the prelude as the run settles it, since one
5718
- // taken out is resolved against its parent and may be joined into a list.
5719
- const ownRest = `${soft}{${scheme}${nl}}${schemeCopy}`;
5720
- const rest =
5721
- schemeCopy === ""
5722
- ? ownRest
5723
- : ownRest.replace(_OWN_PRELUDE, () => prelude);
5724
- const text = `${prelude}${rest}`;
5725
- // Prefix copies turn one rule into several, so a rewritten rule is not
5726
- // offered for joining: its text no longer describes a single block. Each
5727
- // block's rules are their own siblings, pairing within their scope.
5728
- let out = text;
5729
- if (_S._prefixingOn) {
5730
- // Read and clear here, so the lookahead is the one rule the writer
5731
- // holds — which only a top-level rule ever is — and never a rule
5732
- // further back.
5733
- const parent = path.parent;
5734
- const top = parent === null;
5735
- const scope = _prefixScope(parent);
5736
- out =
5737
- path.type() === NodeType.AtRule
5738
- ? _prefixAtRule(path, text, prelude, scope, top)
5739
- : _prefixQualifiedRule(
5740
- text,
5741
- /** @type {string[]} */ (preludeParts),
5742
- soft,
5743
- body,
5744
- scope,
5745
- top
5746
- );
5747
- }
5748
- // WHY: pushed whatever it holds, since the parent takes one off per child
5749
- // with a body, so a block carrying no rule still has to be there. A
5750
- // rewritten rule records no nesting — those spans name `text` rather than
5751
- // `out` — but is still the one rule its text says it is. Only a block that
5752
- // recorded something needs an entry of its own, a leaf rule taking the
5753
- // shared one, and nothing is pushed while not minifying.
5754
- if (minify) {
5755
- _blockSpans.push(
5756
- spans === null || out !== text
5757
- ? path.type() === NodeType.QualifiedRule
5758
- ? _NO_BLOCK_ENTRY_QUALIFIED
5759
- : _NO_BLOCK_ENTRY
5760
- : {
5761
- bodyAt: prelude.length + soft.length + 1,
5762
- prelude,
5763
- keyPrelude: _openerKey(`${prelude}{`),
5764
- qualified: path.type() === NodeType.QualifiedRule,
5765
- spans
5766
- }
5767
- );
5768
- }
5769
- // A rewritten rule is several rules with preludes of their own, and each
5770
- // carries this rule's block — so the hoist takes them out. Only where
5771
- // it holds no rule of its own, those naming one prelude of the several.
5772
- const copies =
5773
- out === text ||
5774
- _prefixCopyPreludes.length === 0 ||
5775
- _holdsNested(/** @type {Node} */ (_S._currentNode))
5776
- ? undefined
5777
- : _prefixCopyPreludes;
5778
- const nested = _writeNested(
5779
- path,
5780
- /** @type {Node} */ (_S._currentNode),
5781
- prelude,
5782
- ownRest,
5783
- out === text || copies !== undefined,
5784
- copies,
5785
- `${soft}{${body}}`
5786
- );
5787
- if (nested === null) return "";
5788
- if (out !== text) return out + nested;
5789
- // The run wrote this rule's selectors into itself, so what it returns is
5790
- // the whole of both — `text` being this rule's own `prelude` and `rest`.
5791
- if (nested.length !== 0) {
5792
- if (_nestedTookParent) {
5793
- if (minify) _recordHoistedRule(nested, null, 0);
5794
- return nested;
5795
- }
5796
- if (minify) _recordHoistedRule(nested, text, prelude.length);
5797
- return text + nested;
5798
- }
5799
- // Where the block is what a sibling is compared against, remember the rule
5800
- // so the parent parts it without re-scanning for the `{`. An at-rule
5801
- // remembers its block's entries too, so a sibling's rules can join them.
5802
- if (
5803
- minify &&
5804
- path.type() === NodeType.AtRule &&
5805
- MERGEABLE_AT_RULES.has(_atKeywordOf(path.node))
5806
- ) {
5807
- /** @type {RuleEntry[]} */
5808
- const children = [];
5809
- for (let i = 0; i < texts.length; i++) {
5810
- if (texts[i].length === 0) continue;
5811
- if (superseded !== null && superseded.has(i)) continue;
5812
- // The entries have to spell the block this rule printed, prefixes and
5813
- // all: a join rebuilds the body from them, and one built from the
5814
- // unprefixed texts would restore what was dropped.
5815
- if (droppedPrefix !== null && droppedPrefix.has(i)) continue;
5816
- if (deadPrefixed !== null && deadPrefixed.has(items[i])) continue;
5817
- const added = addedPrefix === null ? undefined : addedPrefix.get(i);
5818
- children.push(
5819
- added === undefined
5820
- ? _ruleEntryOf(items[i], texts[i])
5821
- : _opaqueEntry(added + texts[i])
5822
- );
5823
- }
5824
- let head = "";
5825
- for (let i = 0; i < children.length - 1; i++) head += children[i].text;
5826
- _setRuleEntry(_S._currentNode, {
5827
- text,
5828
- prelude: prelude.length,
5829
- atRule: true,
5830
- plain: false,
5831
- listable: LIST_NO,
5832
- listKind: LIST_KIND_SELECTOR,
5833
- children,
5834
- head,
5835
- // The block's own merges have run and settled what they grew, so the
5836
- // text above and the children agree; a list one of them still carries
5837
- // concatenated is one this block says is there to settle.
5838
- unordered: _anyUnordered(children)
5839
- });
5840
- }
5841
- if (minify && path.type() === NodeType.QualifiedRule) {
5842
- // Only a block of declarations lends its selectors to another's list: a
5843
- // nested rule's `&` stands for the whole list and `:is(…)` takes its most
5844
- // specific selector, so joining preludes moves what the nested beat.
5845
- const plain = rules === null || rules.length === 0;
5846
- const parent = path.parent;
5847
- // Which shape a join would read the prelude with — the parent is gone by
5848
- // then. A keyframe selector is a list of its own shape, which
5849
- // `_rulePrelude` has already rewritten `from` in.
5850
- let listKind = LIST_KIND_SELECTOR;
5851
- if (parent !== null) {
5852
- const parentType = path.type(parent);
5853
- if (parentType === NodeType.QualifiedRule) {
5854
- listKind = LIST_KIND_NESTED;
5855
- } else if (
5856
- parentType === NodeType.AtRule &&
5857
- KEYFRAMES_AT_RULE_RE.test(_atKeywordOf(parent))
5858
- ) {
5859
- listKind = LIST_KIND_KEYFRAME;
5860
- }
5861
- }
5862
- _setRuleEntry(_S._currentNode, {
5863
- text,
5864
- prelude: prelude.length,
5865
- atRule: false,
5866
- plain,
5867
- listable: plain ? LIST_UNKNOWN : LIST_NO,
5868
- listKind,
5869
- children: null,
5870
- head: "",
5871
- unordered: false
5872
- });
5873
- }
5874
- return text;
5875
- }
6020
+ case NodeType.QualifiedRule:
6021
+ return _printRuleNode(path, writer, minify, soft);
5876
6022
  case NodeType.Raw:
5877
6023
  // Off-spec passthrough (see `NodeType.Raw`). It sits in a declaration list,
5878
6024
  // so it carries the `;` parting it from the next item, and leaves an entry
@@ -5891,39 +6037,8 @@ const printer = (path, writer) => {
5891
6037
  // string, a bracket — would swallow the `;` and `}` written after it.
5892
6038
  return `${rest === _S._input.length ? _closeAtEof(raw) : raw};`;
5893
6039
  }
5894
- case NodeType.Hash: {
5895
- const raw = _printedSource(path.node);
5896
- if (!minify) return raw;
5897
- // A hash is a hex color in a value and an id in a selector, and ids are
5898
- // case-sensitive, so only the former shortens — at any value depth. An id's
5899
- // escapes still shorten, the `#` held back so its first code point is one.
5900
- if (!path.within(NodeType.Declaration)) {
5901
- return _S._transforms.rewriteEscapes
5902
- ? `#${_minifyIdentEscapes(raw.slice(1))}`
5903
- : raw;
5904
- }
5905
- // Inside a function only the known color functions take color hashes, and a
5906
- // substitution's fallback, being the property's value. `paint()` is the
5907
- // exception: its arguments reach a worklet rather than a declaration.
5908
- const parent = path.parent;
5909
- if (parent !== null && path.type(parent) === NodeType.Function) {
5910
- const fn = unescapeIdentifier(_rawName(parent)).toLowerCase();
5911
- if (
5912
- !COLOR_ARGUMENT_FUNCTIONS.has(fn) &&
5913
- !(SUBSTITUTION_FUNCTIONS.has(fn) && fn !== "paint")
5914
- ) {
5915
- return raw;
5916
- }
5917
- }
5918
- const short = _minifyHash(raw);
5919
- const text = short === null ? raw : short;
5920
- // A hex alpha the target cannot read is written as the `rgba()` it names.
5921
- if (_S._hexAlphaAllowed) return text;
5922
- const lowered = _S._transforms.lowerUnsupported
5923
- ? _hexAlphaFallback(text)
5924
- : null;
5925
- return lowered === null ? text : lowered;
5926
- }
6040
+ case NodeType.Hash:
6041
+ return _printHashNode(path, minify);
5927
6042
  default: {
5928
6043
  // Any remaining leaf token (ident, string, url, delim, …) prints verbatim
5929
6044
  // from its source slice.
@@ -6034,4 +6149,6 @@ const _renderDataUrl = (url) => {
6034
6149
  return buildDataURI(read.parsed, rendered);
6035
6150
  };
6036
6151
 
6152
+ _setVerbatimLeafPrinter(printer);
6153
+
6037
6154
  module.exports.printer = printer;