webpack 5.112.1 → 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.
@@ -4893,6 +4893,1071 @@ const _printAttributeSelector = (path, children, writer) => {
4893
4893
  return parts;
4894
4894
  };
4895
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
+
4896
5961
  /**
4897
5962
  * The default CSS node printer — passed to the `SourceProcessor` and fired once a
4898
5963
  * node's visitors and children are done (a developer could supply their own).
@@ -4939,993 +6004,21 @@ const printer = (path, writer) => {
4939
6004
  ? _normalizeNumericToken(raw, _isRgbChannel(path))
4940
6005
  : raw;
4941
6006
  }
4942
- case NodeType.Function: {
4943
- const written = _rawName(path.node);
4944
- // An escape spells the same name (§4.3.7): `c\61 lc(` is `calc()`, so
4945
- // every table below reads what it spells.
4946
- const name = written.includes("\\")
4947
- ? unescapeIdentifier(written)
4948
- : written;
4949
- // A container style query asks whether a custom property's value is the one
4950
- // written here, comparing token streams as written (CSS Conditional 5 §5),
4951
- // so a squeezed whitespace run asks another question. Nothing is rewritten.
4952
- if (
4953
- minify &&
4954
- !path.within(NodeType.Declaration) &&
4955
- _S._inMediaConditionPrelude &&
4956
- asciiCaseInsensitiveMatch(name, "style")
4957
- ) {
4958
- const source = _printedSource(path.node);
4959
- // Unclosed at EOF, the source carries no `)` for the walk to have
4960
- // closed — printed as usual so the parenthesis is written back.
4961
- if (source.endsWith(")")) {
4962
- // Written whole, comments included, so the queued copies are claimed
4963
- // here rather than flushed a second time after the rule.
4964
- writer.takeInserts(path.start(), path.end());
4965
- return source;
4966
- }
4967
- }
4968
- let inner;
4969
- if (path.childCount() === 1) {
4970
- // Half of all functions take one argument, which `_join` would hand
4971
- // straight back — and a trim only ever parts two of them, so no
4972
- // universal a compound implies can stand beside anything here either.
4973
- const only = writer.get(path.child(0));
4974
- inner = only === _SEP ? "" : only;
4975
- } else {
4976
- // A selector function's argument is a selector, so its combinators need
4977
- // no whitespace, as in the enclosing prelude; an `@import`'s
4978
- // `supports()` and a container's `scroll-state()` hold conditions.
4979
- const lower = name.toLowerCase();
4980
- const trim =
4981
- _S._mathFunctionDepth !== 0
4982
- ? _TRIM_MATH
4983
- : path.within(NodeType.Declaration)
4984
- ? _TRIM_NOTHING
4985
- : SELECTOR_FUNCTIONS.has(lower)
4986
- ? _TRIM_COMBINATORS
4987
- : (_S._inSupportsPrelude && lower === "supports") ||
4988
- (_S._inMediaConditionPrelude && lower === "scroll-state")
4989
- ? _TRIM_CONDITIONS
4990
- : _TRIM_NOTHING;
4991
- /** @type {string[]} */
4992
- const parts = [];
4993
- _appendChildTexts(path.node, writer, parts);
4994
- if (minify && trim === _TRIM_CONDITIONS) _keepKeywordComments(parts);
4995
- // A selector function's argument is a selector like the prelude's, so
4996
- // the universal a compound implies says nothing there either — except
4997
- // under test in a `@supports` condition, where only whitespace goes.
4998
- if (minify && trim === _TRIM_COMBINATORS && !_S._inSupportsPrelude) {
4999
- _foldPseudoNames(parts);
5000
- if (_S._transforms.shortenSelectors) _unwrapLoneIs(parts);
5001
- _dropImpliedUniversalSelector(parts);
5002
- }
5003
- inner = _join(parts, !minify, minify ? trim : _TRIM_NOTHING);
5004
- // A leading `+` in An+B is a sign, not a combinator: `+ n` is no
5005
- // argument an engine reads, and `+n` would be one.
5006
- if (minify && NTH_PSEUDO_FUNCTIONS.has(lower)) {
5007
- const at = parts.findIndex(
5008
- (part) => part.length !== 0 && part !== _SEP
5009
- );
5010
- if (parts[at] === "+" && parts[at + 1] === _SEP) {
5011
- inner = `+ ${inner.slice(1)}`;
5012
- }
5013
- }
5014
- // Chrome throws out `attr( name unit )` where `attr( name unit)`
5015
- // parses, so the space before the `)` decides whether the declaration
5016
- // runs — `_join` drops a trailing one like any other.
5017
- if (
5018
- minify &&
5019
- parts[parts.length - 1] === _SEP &&
5020
- asciiCaseInsensitiveMatch(name, "attr")
5021
- ) {
5022
- inner += " ";
5023
- }
5024
- }
5025
- if (!minify) return `${written}(${inner})`;
5026
- // Function names match ASCII case-insensitively, so one lowercase name
5027
- // is what every table below is keyed by; the eleven transforms spelled
5028
- // with a capital are printed the way everything else writes them.
5029
- const fn = asciiLowercaseName(name);
5030
- // As with a unit: a name already lowercase is its own answer, and only a
5031
- // shouted one is looked up. The lookup key still folds inside a
5032
- // substituted value; only the printed spelling stays as written.
5033
- let out =
5034
- _S._inSubstitutedValue ||
5035
- _S._inSupportsSelector ||
5036
- !_S._transforms.foldCase ||
5037
- fn.startsWith("--")
5038
- ? name
5039
- : fn === name
5040
- ? fn
5041
- : CANONICAL_NAMES.get(fn) || fn;
5042
- if (written !== name) {
5043
- out = _S._transforms.rewriteEscapes ? escapeName(out) : written;
5044
- }
5045
- // An+B in a selector: `2n+1` is what `odd` names, and shorter written so.
5046
- if (!path.within(NodeType.Declaration) && NTH_PSEUDO_FUNCTIONS.has(fn)) {
5047
- return _S._inSupportsSelector
5048
- ? `${out}(${inner})`
5049
- : _minifyAnPlusB(out, inner);
5050
- }
5051
- // Every rewrite below is keyed by a name none of these carry.
5052
- if (
5053
- SUBSTITUTION_FUNCTIONS.has(fn) ||
5054
- (!path.within(NodeType.Declaration) && SELECTOR_FUNCTIONS.has(fn))
5055
- ) {
5056
- return `${out}(${inner})`;
5057
- }
5058
- // WHY: an `rgb()` or `rgba()` collapses to the shortest color and anything
5059
- // else keeps its `name(args)` form. A color inside a `@supports` condition
5060
- // is syntax under test — the engines reading `#0000` are not those reading
5061
- // `rgba(0,0,0,0)` — so rewriting it asks another question, and esbuild,
5062
- // clean-css and lightningcss leave the condition alone too. A color read as
5063
- // numbers, in a gradient stop, a `color-mix()` argument or a relative
5064
- // color's origin, stays as written unless converting it is exact, which the
5065
- // legacy sRGB forms are where their channels are bytes.
5066
- let color = _S._inSupportsPrelude
5067
- ? null
5068
- : _minifyColorFunction(
5069
- fn,
5070
- inner,
5071
- _S._hexAlphaAllowed,
5072
- _S._inColorOperand
5073
- ) ||
5074
- (_S._inColorOperand
5075
- ? null
5076
- : _minifyPolarColorFunction(fn, inner, _S._hexAlphaAllowed));
5077
- // A mix no byte holds is written back in the space it was computed in,
5078
- // which can be longer than the call — then the call is the shorter one.
5079
- if (color !== null && color.length > fn.length + inner.length + 2) {
5080
- color = null;
5081
- }
5082
- if (
5083
- color !== null &&
5084
- (color !== "transparent" || !_inTapHighlightColor())
5085
- ) {
5086
- return color === "transparent" && _S._hexAlphaAllowed ? "#0000" : color;
5087
- }
5088
- // `light-dark()` names a color per scheme, which the pair above says with
5089
- // a substitution the scheme itself switches. Two arguments only: the
5090
- // grammar takes exactly that many, and anything else is no such call.
5091
- if (
5092
- _S._loweringLightDark &&
5093
- fn === "light-dark" &&
5094
- path.within(NodeType.Declaration) &&
5095
- !_S._inCustomProperty &&
5096
- !_S._inSupportsPrelude &&
5097
- !_S._inSubstitutedValue
5098
- ) {
5099
- const halves = _splitTopLevelArguments(inner);
5100
- // A half the target cannot read would substitute into a `var()` and make
5101
- // the declaration invalid at computed-value time, where nothing before it
5102
- // is read — so it is left alone rather than lowered.
5103
- if (halves.length === 2 && !_namesUnreadableColor(inner)) {
5104
- _S._lightDarkUsed = true;
5105
- return `var(${LIGHT_PROPERTY},${halves[0].trim()})var(${DARK_PROPERTY},${halves[1].trim()})`;
5106
- }
5107
- }
5108
- // A math function over constants of one unit is that value, written back as
5109
- // a `calc()`: the parentheses stay, since dropping them turns a clamped
5110
- // negative into a parse error, and only the declaration printer does.
5111
- if (!_S._inSupportsPrelude && !_S._inCustomProperty) {
5112
- const term = _foldMathFunction(fn, inner);
5113
- if (term !== null) {
5114
- // A term that means the same bare needs no parentheses anywhere, so
5115
- // it is written as itself; the rest keep a `calc()` for the
5116
- // declaration printer to judge against the property.
5117
- const shape =
5118
- _S._mathFunctionDepth > 1 || _inNumberArgument(path)
5119
- ? _NESTED_TERM_RE
5120
- : _BARE_TERM_RE;
5121
- const folded =
5122
- (shape.test(term) ||
5123
- (_S._mathFunctionDepth === 1 &&
5124
- _NEGATIVE_TERM_RE.test(term) &&
5125
- _inNegativeAcceptingArgument(path))) &&
5126
- !_inCalcRejectingProperty() &&
5127
- !(!/[a-z%]$/i.test(term) && _inNumberOnlyOutsideCalcProperty()) &&
5128
- !_foldLosesClamp(term) &&
5129
- !_foldBelowStepCount(term, path)
5130
- ? term
5131
- : `calc(${term})`;
5132
- // A division can land on a value needing every digit of a double, which
5133
- // is longer than the expression producing it. Only the outermost call
5134
- // is asked, a nested one being a term still to be reduced.
5135
- if (
5136
- _S._mathFunctionDepth > 1 ||
5137
- folded.length < fn.length + inner.length + 2 ||
5138
- _unwrapsShorter(path, folded, fn.length + inner.length + 2)
5139
- ) {
5140
- return folded;
5141
- }
5142
- }
5143
- const reduced = _reduceMathArguments(fn, inner);
5144
- // A `calc()` inside another math function is the parentheses it
5145
- // stands for (CSS Values 4 §10.1), whatever a substitution expands to.
5146
- if (
5147
- fn === "calc" &&
5148
- _S._mathFunctionDepth > 1 &&
5149
- path.within(NodeType.Declaration) &&
5150
- _S._transforms.reduceFunctions
5151
- ) {
5152
- return `(${reduced === null ? inner : reduced})`;
5153
- }
5154
- if (reduced !== null) return `${fn}(${reduced})`;
5155
- }
5156
- // An easing function is only ever a value; `url()` is also an at-rule
5157
- // prelude's (`@import url("a.css")`).
5158
- const easing =
5159
- path.within(NodeType.Declaration) && !_S._inCustomProperty
5160
- ? _minifyEasingFunction(fn, inner)
5161
- : null;
5162
- if (easing !== null) return easing;
5163
- // A translation's percentage is of the element's own box, which always
5164
- // has a size, so a `0%` term beside one length adds nothing to it.
5165
- if (
5166
- _S._transforms.reduceFunctions &&
5167
- path.within(NodeType.Declaration) &&
5168
- !_S._inSubstitutedValue &&
5169
- fn.startsWith("translate") &&
5170
- inner.includes("0%")
5171
- ) {
5172
- const args = _splitTopLevelArguments(inner);
5173
- let changed = false;
5174
- for (let i = 0; i < args.length; i++) {
5175
- // A z slot takes no percentage, so a `0%` there makes the whole
5176
- // declaration invalid, which dropping it would revive.
5177
- if (fn === "translatez" || (fn === "translate3d" && i === 2)) {
5178
- continue;
5179
- }
5180
- const term = _dropZeroPercentTerm(args[i].trim());
5181
- if (term !== null) {
5182
- args[i] = term;
5183
- changed = true;
5184
- }
5185
- }
5186
- // The rewrites below read the arguments as they now stand.
5187
- if (changed) inner = args.join(",");
5188
- }
5189
- // `translateX(v)` is `translate(v)` and `skewX(a)` is `skew(a)`, the second
5190
- // component defaulting to 0. Only for one plain component: a substitution
5191
- // could expand to two, reviving a declaration the browser drops.
5192
- const oneAxis = _S._transforms.reduceFunctions
5193
- ? X_AXIS_TRANSFORMS.get(fn)
5194
- : undefined;
5195
- if (
5196
- oneAxis !== undefined &&
5197
- path.within(NodeType.Declaration) &&
5198
- !_S._inSubstitutedValue &&
5199
- !inner.includes(",") &&
5200
- !containsSubstitutionFunction(inner)
5201
- ) {
5202
- return `${oneAxis}(${inner})`;
5203
- }
5204
- if (path.within(NodeType.Declaration) && !_S._inSubstitutedValue) {
5205
- // An argument that is the amount an omitted one means says nothing,
5206
- // whether or not the zero still carries the unit its grammar drops.
5207
- const omitted = _S._transforms.reduceFunctions
5208
- ? FILTER_FUNCTION_OMITTED.get(fn)
5209
- : undefined;
5210
- if (omitted !== undefined) {
5211
- const bare = _dropCallZeroUnit(fn, inner);
5212
- if (bare === omitted || (omitted === "1" && bare === "100%")) {
5213
- return `${out}()`;
5214
- }
5215
- }
5216
- const reduced = _reduceTransformFunctionDeep(fn, inner);
5217
- if (reduced !== null) return reduced;
5218
- // The stops are folded over what dropping the direction leaves, a
5219
- // gradient carrying both otherwise keeping whichever ran second.
5220
- const shape = _dropDefaultShapeParts(fn, inner);
5221
- if (shape !== null) return `${out}(${shape})`;
5222
- let gradient = _reduceGradientDirection(fn, inner);
5223
- if (gradient === null) {
5224
- gradient = _dropDefaultGradientPosition(fn, inner);
5225
- }
5226
- let stops = _foldGradientStops(
5227
- fn,
5228
- gradient === null ? inner : gradient,
5229
- _S._doublePositionAllowed
5230
- );
5231
- // A double position the target cannot read is written as the two
5232
- // stops it names, over whatever the folds above left.
5233
- if (!_S._doublePositionAllowed) {
5234
- const split = !_S._transforms.lowerUnsupported
5235
- ? null
5236
- : _splitDoublePositionStops(
5237
- fn,
5238
- stops !== null ? stops : gradient !== null ? gradient : inner
5239
- );
5240
- if (split !== null) stops = split;
5241
- }
5242
- if (stops !== null) return `${out}(${stops})`;
5243
- if (gradient !== null) return `${out}(${gradient})`;
5244
- }
5245
- // A zero angle needs no unit where the grammar names `<zero>` beside
5246
- // `<angle>` (CSS Transforms 2, Filter Effects 1).
5247
- if (
5248
- path.within(NodeType.Declaration) &&
5249
- !_S._inSubstitutedValue &&
5250
- _S._transforms.shortenNumbers &&
5251
- ZERO_ANGLE_FUNCTIONS.has(fn)
5252
- ) {
5253
- const bare = inner.replace(_ZERO_ANGLE_ARGUMENT_RE, "$10");
5254
- if (bare !== inner) return `${out}(${bare})`;
5255
- }
5256
- // A name kept escaped keeps its call as written: unquoting it rests on the
5257
- // tokenizer reading `url` through the escape.
5258
- const url =
5259
- _S._inSubstitutedValue || (written !== name && out === written)
5260
- ? null
5261
- : _minifyUrlFunction(fn, inner);
5262
- if (url !== null) return url;
5263
- return `${out}(${inner})`;
5264
- }
5265
- case NodeType.Url: {
5266
- const source = _printedSource(path.node);
5267
- // Closed at EOF: write the `)` back so the url stops where it stopped for
5268
- // the tokenizer rather than swallowing what the printer emits next. Before
5269
- // the rest rather than instead of it, or a second pass is what folds it.
5270
- const unterminated = _isUnterminatedUrl(source);
5271
- const raw = unterminated ? _terminate(source, ")", "\uFFFD") : source;
5272
- // The tokenizer already trimmed the url-token's content, so the padding in
5273
- // `url( a.png )` carries nothing.
5274
- if (!minify) return raw;
5275
- const open = raw.indexOf("(");
5276
- // `url` matches ASCII case-insensitively like any function name. Read in
5277
- // place, since cutting the name out would allocate on every url and all but
5278
- // a shouted one folds to itself. In a substituted value nothing is read.
5279
- let head = null;
5280
- if (!_S._inSubstitutedValue && _S._transforms.foldCase) {
5281
- for (let i = 0; i < open; i++) {
5282
- const c = raw.charCodeAt(i);
5283
- if (c >= CC_UPPER_A && c <= CC_UPPER_Z) {
5284
- head = asciiLowercaseName(raw.slice(0, open + 1));
5285
- break;
5286
- }
5287
- }
5288
- }
5289
- const percent = raw.includes("%");
5290
- /**
5291
- * @returns {string} the url-token as it prints with nothing rendered
5292
- */
5293
- const plainUrl = () => {
5294
- // Padding only exists when the content is whitespace-bounded; checking
5295
- // that costs two char reads, reading the content costs a slice.
5296
- if (
5297
- !percent &&
5298
- !isAsciiWhitespace(raw.charCodeAt(open + 1)) &&
5299
- !isAsciiWhitespace(raw.charCodeAt(raw.length - 2))
5300
- ) {
5301
- return head === null ? raw : head + raw.slice(open + 1);
5302
- }
5303
- const name = head === null ? raw.slice(0, open + 1) : head;
5304
- const value = _urlValue(path, unterminated);
5305
- // A url-token already carrying an escape is one this printer did not
5306
- // write, so its `\%` is not read as the start of a percent-escape.
5307
- return `${name}${
5308
- percent && !value.includes("\\")
5309
- ? _decodePercentEscapes(value, true, "")
5310
- : value
5311
- })`;
5312
- };
5313
- // Ahead of the padding fast path below: a base64 `data:` url is
5314
- // whitespace-free and `%`-free, so it would take it and never be offered.
5315
- if (_S._deferEmbeddedSource !== undefined) {
5316
- const opener = head === null ? raw.slice(0, open + 1) : head;
5317
- const deferred = _deferDataUrl(
5318
- _urlValue(path, unterminated),
5319
- (url) => `${opener}${_serializeUrl(url, '"')})`,
5320
- plainUrl()
5321
- );
5322
- if (deferred !== null) return deferred;
5323
- }
5324
- if (_S._renderEmbeddedSource !== undefined) {
5325
- const renderedUrl = _renderDataUrl(_urlValue(path, unterminated));
5326
- if (renderedUrl !== null) {
5327
- const name = head === null ? raw.slice(0, open + 1) : head;
5328
- return `${name}${_serializeUrl(renderedUrl, '"')})`;
5329
- }
5330
- }
5331
- return plainUrl();
5332
- }
5333
- case NodeType.String: {
5334
- const source = _printedSource(path.node);
5335
- // Closed at EOF: write the quote back, for the same reason as `url()`.
5336
- // Written here rather than after the transforms below, or they read a
5337
- // string with no closing quote and decline it, which a second pass undoes.
5338
- const raw = _isClosedString(source)
5339
- ? source
5340
- : _terminate(source, source[0], "");
5341
- // A custom property hands its string back as written, quotes included.
5342
- if (!minify || _S._inCustomProperty) return raw;
5343
- // `<family-name>` is `<string> | <custom-ident>+`, so a family whose text
5344
- // is a run of identifiers means the same unquoted, two bytes shorter.
5345
- if (
5346
- !_S._inSupportsPrelude &&
5347
- !_S._inSubstitutedValue &&
5348
- ((_inFontFamily() && _isLoneFamilyName(path)) ||
5349
- _isLoneFontFamily(path))
5350
- ) {
5351
- const unquoted = _unquoteFontFamily(raw);
5352
- if (unquoted !== null) return unquoted;
5353
- }
5354
- const written = _minifyString(raw);
5355
- return (_S._charsetPiece === -1 && !_S._assumeUtf8) ||
5356
- !_S._transforms.rewriteEscapes
5357
- ? written
5358
- : _unescapeAstral(written);
5359
- }
5360
- case NodeType.Ident: {
5361
- const raw = _printedSource(path.node);
5362
- if (!minify) return raw;
5363
- // A keyword is matched by what an escape spells, not by how it is written.
5364
- const meaning = raw.includes("\\") ? unescapeIdentifier(raw) : raw;
5365
- // `transparent` is `#0000` where the target reads a hex alpha — the same
5366
- // color, six bytes shorter. The tap-highlight guard still holds: that
5367
- // WebKit bug is about the keyword's *value*, whichever way it is spelled.
5368
- if (
5369
- path.within(NodeType.Declaration) &&
5370
- !_S._inSubstitutedValue &&
5371
- _S._hexAlphaAllowed &&
5372
- _S._transforms.shortenColors &&
5373
- asciiCaseInsensitiveMatch(meaning, "transparent") &&
5374
- !_inTapHighlightColor()
5375
- ) {
5376
- return "#0000";
5377
- }
5378
- // The keyword names each platform's own UI font, which a target that
5379
- // does not read it reaches only through the platform's own name for it.
5380
- if (
5381
- !_S._systemUiAllowed &&
5382
- _S._transforms.lowerUnsupported &&
5383
- path.within(NodeType.Declaration) &&
5384
- !_S._inCustomProperty &&
5385
- !_S._inSubstitutedValue &&
5386
- asciiCaseInsensitiveMatch(meaning, "system-ui") &&
5387
- _inFontFamily()
5388
- ) {
5389
- return SYSTEM_UI_STACK;
5390
- }
5391
- if (
5392
- path.within(NodeType.Declaration) &&
5393
- !_S._inCustomProperty &&
5394
- !_S._inSupportsPrelude &&
5395
- !_S._inSubstitutedValue
5396
- ) {
5397
- // One fold for both questions below, handing back the string where nothing
5398
- // changes. ASCII only: `pin\212A` ends in a Kelvin sign, not a `k`.
5399
- const lowered = asciiLowercaseName(meaning);
5400
- // A named color where the property takes nothing else an identifier could
5401
- // be: `white` is `#fff`, both computing to `rgb(255, 255, 255)`.
5402
- // Elsewhere an identifier may be the author's own name.
5403
- if (_S._transforms.shortenColors) {
5404
- const shorter = getColorNameToShortest().get(lowered);
5405
- if (
5406
- shorter !== undefined &&
5407
- (_inColorOnlyProperty() ||
5408
- _inGradientArgument(path) ||
5409
- _atBackgroundTop(path))
5410
- ) {
5411
- return shorter;
5412
- }
5413
- }
5414
- // A keyword-only or color property names nothing of the author's, so an
5415
- // ident directly in its value (a call's arguments are their own grammar's)
5416
- // is a keyword matched ASCII case-insensitively, but a dashed ident (§4.2).
5417
- if (
5418
- _S._transforms.foldCase &&
5419
- lowered !== raw &&
5420
- path.parent === _S._valueDeclaration &&
5421
- !meaning.startsWith("--") &&
5422
- _inKeywordOnlyValue()
5423
- ) {
5424
- const folded =
5425
- meaning !== raw && _S._transforms.rewriteEscapes
5426
- ? escapeIdentifier(lowered)
5427
- : asciiLowercaseName(raw);
5428
- if (folded !== raw) return folded;
5429
- }
5430
- }
5431
- return _S._transforms.rewriteEscapes ? _minifyIdentEscapes(raw) : raw;
5432
- }
5433
- case NodeType.SimpleBlock: {
5434
- const open = _blockTokenOf(path.node);
5435
- const close = open === "(" ? ")" : open === "[" ? "]" : "}";
5436
- // Outside a declaration value a `[…]` is an attribute selector and a `(…)`
5437
- // a query condition. Inside one they are a grid line-name list and a
5438
- // `calc()` sub-expression, where neither of those rewrites holds.
5439
- const structural = minify && !path.within(NodeType.Declaration);
5440
- /** @type {string[]} */
5441
- let parts;
5442
- if (structural && open === "[") {
5443
- parts = _printAttributeSelector(path, path.children(), writer);
5444
- } else {
5445
- // One array, rather than a child list and a mapped copy of it.
5446
- parts = [];
5447
- _appendChildTexts(path.node, writer, parts);
5448
- }
5449
- if (structural && open === "(" && _S._inMediaConditionPrelude) {
5450
- if (_S._transforms.foldCase) _lowercaseConditionParts(parts);
5451
- if (_S._rangeSpellingAllowed) _useRangeSpelling(parts);
5452
- }
5453
- if (
5454
- minify &&
5455
- structural &&
5456
- open === "(" &&
5457
- (_S._inSupportsPrelude || _S._inMediaConditionPrelude)
5458
- ) {
5459
- _keepKeywordComments(parts);
5460
- }
5461
- let trim =
5462
- minify && _S._mathFunctionDepth !== 0 ? _TRIM_MATH : _TRIM_NOTHING;
5463
- if (structural && open === "(") {
5464
- // `@scope`'s parentheses hold selector lists, so a `:` there starts a
5465
- // pseudo-class and the whitespace before it is a descendant combinator
5466
- // — `@scope (div :hover)` is not `@scope (div:hover)`.
5467
- trim = _inScopePrelude(path) ? _TRIM_COMBINATORS : _TRIM_CONDITIONS;
5468
- if (trim === _TRIM_COMBINATORS) _foldPseudoNames(parts);
5469
- }
5470
- const inner = _join(parts, !minify, trim);
5471
- return `${open}${inner}${close}`;
5472
- }
5473
- case NodeType.Declaration: {
5474
- const name = _rawName(path.node);
5475
- // WHY: a property is matched by what its name means, not by how it was
5476
- // spelled — §4.3.7 lets any of its letters be written as an escape — so
5477
- // every lookup below reads the unescaped name while the printed one
5478
- // keeps the source's. It costs an `indexOf` where nothing is escaped.
5479
- const meaning = path.name();
5480
- // Counted rather than listed: a declaration's value is one component 92
5481
- // times in 100, and the list would be built only to be indexed twice.
5482
- const count = path.childCount();
5483
- // `@property`'s `initial-value` is read against the sibling `syntax`
5484
- // descriptor, so it is as opaque as a custom property's own value.
5485
- const custom =
5486
- meaning.startsWith("--") ||
5487
- (_S._inPropertyRule &&
5488
- asciiCaseInsensitiveMatch(meaning, "initial-value")) ||
5489
- // `@function`'s `result` is the token stream the call substitutes, so
5490
- // it is opaque the same way — and empty when the function returns the
5491
- // guaranteed-invalid value.
5492
- (_S._inFunctionRule && asciiCaseInsensitiveMatch(meaning, "result")) ||
5493
- // A `{}` block standing as the whole value is a token stream the
5494
- // engine holds as written too — no grammar reads it, so a rewrite of
5495
- // it builds a different CSSOM from the same document.
5496
- (count === 1 &&
5497
- path.type(path.child(0)) === NodeType.SimpleBlock &&
5498
- _blockTokenOf(path.child(0)) === "{");
5499
- // A value-less declaration is invalid, so it is already ignored — except
5500
- // on a custom property, where the empty value is the guaranteed-invalid
5501
- // one a `var()` fallback reads.
5502
- if (minify && _S._transforms.removeDeadRules && count === 0 && !custom) {
5503
- return "";
5504
- }
5505
- // The name folded to lowercase, where the value below needed it; `name`
5506
- // itself where it did not, which is what the printed name reads as
5507
- // "nothing to fold".
5508
- let lowered = name;
5509
- let value = "";
5510
- if (count !== 0) {
5511
- // Property names match ASCII case-insensitively and a custom property's
5512
- // does not, so skipping it skips the lowercasing. One fold serves both
5513
- // which value rules apply and how the name prints.
5514
- lowered = custom ? meaning : toLowerCaseIfNeeded(meaning);
5515
- const property = custom ? meaning : _standardSpelling(lowered);
5516
- if (custom) {
5517
- // `getPropertyValue()` hands this text back, so a rewritten token
5518
- // would be a different CSSOM; only the boundaries between them go.
5519
- const from = path.start(path.child(0));
5520
- const to = path.end(path.child(count - 1));
5521
- if (minify) {
5522
- value = _customPropertyValue(path, path.node, writer, from, to);
5523
- } else {
5524
- // Straight from source, so the kept comments in it are already
5525
- // there — claim them, or the writer emits them a second time
5526
- // ahead of the next top-level node.
5527
- writer.takeInserts(from, to);
5528
- value = _S._input.slice(from, to);
5529
- if (to === _S._input.length) value = _closeAtEof(value);
5530
- }
5531
- // Closed as printed, since a `}` may still follow; the grammar leaves it
5532
- // open where nothing does, as the engine keeps it.
5533
- const last = _S._eofPrinted;
5534
- if (
5535
- to === _S._input.length &&
5536
- last !== "" &&
5537
- value.endsWith(last) &&
5538
- last !== _S._eofOpen
5539
- ) {
5540
- _S._openCustomValue = {
5541
- printed: value,
5542
- source: value.slice(0, value.length - last.length) + _S._eofOpen
5543
- };
5544
- }
5545
- } else if (property === "unicode-range") {
5546
- // `U+…` tokenizes as numbers, so the generic numeric normalization
5547
- // would corrupt it; each range is shortened as the urange it is.
5548
- let raw = "";
5549
- for (let at = 0; at < count; at++) {
5550
- raw += _printedSource(path.child(at));
5551
- }
5552
- value = minify
5553
- ? raw
5554
- .split(",")
5555
- .map((one) => _minifyUnicodeRange(one.trim()))
5556
- .join(",")
5557
- : raw;
5558
- } else {
5559
- // Value hashes were already shortened by the hash printer, printing in a
5560
- // value context, so this joins the children's text. A shorthand whose
5561
- // value repeats what the notation implies collapses.
5562
- const shorthand = minify
5563
- ? _collapseShorthand(path, property, path.node, writer)
5564
- : null;
5565
- if (shorthand === null && count === 1) {
5566
- // What a value is 98 times in 100, and `_join` hands a lone fragment
5567
- // back, so it is rewritten without an array. §5.4.6 step 7 pops a
5568
- // trailing whitespace, so that child is never the `""` case.
5569
- value = _valueFragment(writer.get(path.child(0)), property, minify);
5570
- } else {
5571
- /** @type {string[]} */
5572
- let fragments;
5573
- if (shorthand === null) {
5574
- fragments = [];
5575
- _appendChildTexts(path.node, writer, fragments);
5576
- } else {
5577
- fragments = shorthand;
5578
- }
5579
- // In place: a `map` per transform would allocate an array each.
5580
- for (let i = 0; i < fragments.length; i++) {
5581
- fragments[i] = _valueFragment(fragments[i], property, minify);
5582
- }
5583
- // The string transforms below read space-separated components, so
5584
- // only a value none of them covers loses the separators its tokens
5585
- // do not need — a substitution's tokens included.
5586
- const separatorsOnly =
5587
- minify &&
5588
- !AUTO_SECOND_VALUE_PROPERTIES.has(property) &&
5589
- !ALPHA_VALUE_PROPERTIES.has(property) &&
5590
- !RATIO_PROPERTIES.has(property);
5591
- value = _join(
5592
- fragments,
5593
- !minify,
5594
- separatorsOnly ? _TRIM_SEPARATORS : _TRIM_NOTHING
5595
- );
5596
- }
5597
- if (minify) {
5598
- value = _dropDefaultSecondValue(property, value);
5599
- value = _numberAlphaValue(property, value);
5600
- value = _dropRatioDenominator(property, value);
5601
- }
5602
- }
5603
- }
5604
- const important = _isImportant(path.node) ? `${soft}!important` : "";
5605
- // Property names match ASCII case-insensitively; a custom property's
5606
- // does not, which is what `custom` already stands for — nor does an
5607
- // `@font-feature-values` sub-rule's, which names a feature value.
5608
- const folds =
5609
- minify &&
5610
- _S._transforms.foldCase &&
5611
- !custom &&
5612
- !_S._inFeatureValuesRule;
5613
- let printedName =
5614
- folds && lowered !== name ? asciiLowercaseName(name) : name;
5615
- // An escape spells the same name, so the name is written the shortest way.
5616
- if (minify && _S._transforms.rewriteEscapes && meaning !== name) {
5617
- const spelled = escapeName(
5618
- folds ? asciiLowercaseName(meaning) : meaning
5619
- );
5620
- if (spelled.length <= printedName.length) printedName = spelled;
5621
- }
5622
- return `${printedName}:${soft}${value}${important};`;
5623
- }
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);
5624
6019
  case NodeType.AtRule:
5625
- case NodeType.QualifiedRule: {
5626
- // Prefixing reads the prelude's tokens, not its joined text.
5627
- const preludeParts = _S._prefixingOn
5628
- ? /** @type {string[]} */ ([])
5629
- : undefined;
5630
- const prelude = _rulePrelude(path, writer, minify, preludeParts);
5631
- const decls = _declarationsOf(path.node);
5632
- // A qualified rule always has a block; an at-rule has one only when its
5633
- // declaration list is non-null — else it is `@…;`.
5634
- if (decls === null) {
5635
- if (minify) _blockSpans.push(_NO_BLOCK_ENTRY);
5636
- // `@charset` names the encoding the *source* was read in, and what is
5637
- // written out is UTF-8 whatever it said — so carrying it over is at
5638
- // best 22 dead bytes and at worst an encoding the output is not in.
5639
- if (
5640
- minify &&
5641
- _S._transforms.removeDeadRules &&
5642
- asciiCaseInsensitiveMatch(_rawName(path.node), "charset")
5643
- ) {
5644
- return "";
5645
- }
5646
- // The rule only goes where no target reads one: a condition this has
5647
- // not come to yet still needs it, and so does an engine that reads it.
5648
- if (
5649
- minify &&
5650
- _S._transforms.customMedia &&
5651
- _atKeywordOf(path.node) === "custom-media" &&
5652
- _takeCustomMedia(prelude) &&
5653
- !_S._customMediaAllowed
5654
- ) {
5655
- return "";
5656
- }
5657
- // No engine reads `@custom-selector`, so the rule always goes once its
5658
- // name is down — but only where `:is()` can carry what it stands for.
5659
- if (
5660
- minify &&
5661
- _S._transforms.customSelectors &&
5662
- _S._isSelectorAllowed &&
5663
- _atKeywordOf(path.node) === "custom-selector" &&
5664
- _takeCustomSelector(prelude)
5665
- ) {
5666
- return "";
5667
- }
5668
- return `${prelude};`;
5669
- }
5670
- // Read before the body: composing it is what leaves the block empty, and
5671
- // the drop below asks the same question again.
5672
- const unusedRule = _takeUnusedRule(_S._currentNode);
5673
- const rules = _rulesOf(path.node);
5674
- // `nl` prefixes each item: nothing minifying, a line break beautifying
5675
- // (declarations end in `;`, rules don't).
5676
- const nl = minify ? "" : "\n";
5677
- const memoized = _memoizedBlockBody(_S._currentNode);
5678
- const {
5679
- body,
5680
- items,
5681
- texts,
5682
- superseded,
5683
- droppedPrefix,
5684
- addedPrefix,
5685
- deadPrefixed,
5686
- spans
5687
- } =
5688
- memoized === undefined
5689
- ? _composeBlockBody(
5690
- path,
5691
- decls,
5692
- rules,
5693
- writer,
5694
- minify,
5695
- nl,
5696
- _S._currentNode,
5697
- unusedRule
5698
- )
5699
- : // A rule its selector made unused writes nothing, whatever its block.
5700
- _memoizedComposition(unusedRule ? "" : memoized);
5701
- _memoizeBlockBody(
5702
- _S._currentNode,
5703
- memoized === undefined && !unusedRule ? body : null
5704
- );
5705
- if (
5706
- minify &&
5707
- // An empty rule paints nothing, so dropping it leaves the cascade as it
5708
- // was — but only where the block means nothing (`_streamClose` agrees).
5709
- _S._transforms.removeDeadRules &&
5710
- body.length === 0 &&
5711
- // See `_streamClose`: a rule taken out while a `@namespace` after it
5712
- // could still be read would move one up into a live position.
5713
- !(path.parent === null && _S._namespacePrologueOpen) &&
5714
- (path.type() === NodeType.QualifiedRule ||
5715
- DROPPABLE_WHEN_EMPTY_AT_RULES.has(_atKeywordOf(path.node)) ||
5716
- unusedRule)
5717
- ) {
5718
- // The rules written in it are not empty for its being so, and what the
5719
- // hoist writes for them is rules a later copy can take back.
5720
- _blockSpans.push(
5721
- path.type() === NodeType.QualifiedRule
5722
- ? _NO_BLOCK_ENTRY_QUALIFIED
5723
- : _NO_BLOCK_ENTRY
5724
- );
5725
- {
5726
- const written =
5727
- _writeNested(path, _S._currentNode, prelude, null, true) || "";
5728
- if (minify && written.length !== 0) {
5729
- _recordHoistedRule(written, null, 0);
5730
- }
5731
- return written;
5732
- }
5733
- }
5734
- // A rule that says which color scheme it is in says it again as the pair
5735
- // a lowered `light-dark()` reads, and — where it hands the choice to the
5736
- // user's own preference — once more under the query that asks for it.
5737
- let scheme = body;
5738
- let schemeCopy = "";
5739
- if (
5740
- _S._loweringLightDark &&
5741
- _S._sourceNamesLightDark &&
5742
- path.type() === NodeType.QualifiedRule &&
5743
- body.includes("color-scheme:") &&
5744
- // The pair as this writes it, not either name on its own: a
5745
- // block carrying that is one a lowering pass wrote. An authored
5746
- // `--webpack-light` is a declaration of the author's.
5747
- !body.includes(LIGHT_DARK_WRITTEN) &&
5748
- !body.includes(LIGHT_DARK_WRITTEN_DARK)
5749
- ) {
5750
- const written = _COLOR_SCHEME_RE.exec(body);
5751
- const toggle =
5752
- written === null
5753
- ? null
5754
- : _lightDarkToggle(written[1].replace(_IMPORTANT, "").trim());
5755
- if (toggle !== null) {
5756
- // The body's own closing `;` went with the minified print, and the
5757
- // toggle's last is the one this block now ends on.
5758
- scheme = `${body};${toggle.toggle.slice(0, -1)}`;
5759
- if (toggle.flip) {
5760
- schemeCopy = `${_PREFERS_DARK}${soft}{${_OWN_PRELUDE}${soft}{${LIGHT_PROPERTY}:;${DARK_PROPERTY}:initial}}`;
5761
- }
5762
- }
5763
- }
5764
- // The hoist's copy names the prelude as the run settles it, since one
5765
- // taken out is resolved against its parent and may be joined into a list.
5766
- const ownRest = `${soft}{${scheme}${nl}}${schemeCopy}`;
5767
- const rest =
5768
- schemeCopy === ""
5769
- ? ownRest
5770
- : ownRest.replace(_OWN_PRELUDE, () => prelude);
5771
- const text = `${prelude}${rest}`;
5772
- // Prefix copies turn one rule into several, so a rewritten rule is not
5773
- // offered for joining: its text no longer describes a single block. Each
5774
- // block's rules are their own siblings, pairing within their scope.
5775
- let out = text;
5776
- if (_S._prefixingOn) {
5777
- // Read and clear here, so the lookahead is the one rule the writer
5778
- // holds — which only a top-level rule ever is — and never a rule
5779
- // further back.
5780
- const parent = path.parent;
5781
- const top = parent === null;
5782
- const scope = _prefixScope(parent);
5783
- out =
5784
- path.type() === NodeType.AtRule
5785
- ? _prefixAtRule(path, text, prelude, scope, top)
5786
- : _prefixQualifiedRule(
5787
- text,
5788
- /** @type {string[]} */ (preludeParts),
5789
- soft,
5790
- body,
5791
- scope,
5792
- top
5793
- );
5794
- }
5795
- // WHY: pushed whatever it holds, since the parent takes one off per child
5796
- // with a body, so a block carrying no rule still has to be there. A
5797
- // rewritten rule records no nesting — those spans name `text` rather than
5798
- // `out` — but is still the one rule its text says it is. Only a block that
5799
- // recorded something needs an entry of its own, a leaf rule taking the
5800
- // shared one, and nothing is pushed while not minifying.
5801
- if (minify) {
5802
- _blockSpans.push(
5803
- spans === null || out !== text
5804
- ? path.type() === NodeType.QualifiedRule
5805
- ? _NO_BLOCK_ENTRY_QUALIFIED
5806
- : _NO_BLOCK_ENTRY
5807
- : {
5808
- bodyAt: prelude.length + soft.length + 1,
5809
- prelude,
5810
- keyPrelude: _openerKey(`${prelude}{`),
5811
- qualified: path.type() === NodeType.QualifiedRule,
5812
- spans
5813
- }
5814
- );
5815
- }
5816
- // A rewritten rule is several rules with preludes of their own, and each
5817
- // carries this rule's block — so the hoist takes them out. Only where
5818
- // it holds no rule of its own, those naming one prelude of the several.
5819
- const copies =
5820
- out === text ||
5821
- _prefixCopyPreludes.length === 0 ||
5822
- _holdsNested(_S._currentNode)
5823
- ? undefined
5824
- : _prefixCopyPreludes;
5825
- const nested = _writeNested(
5826
- path,
5827
- _S._currentNode,
5828
- prelude,
5829
- ownRest,
5830
- out === text || copies !== undefined,
5831
- copies,
5832
- `${soft}{${body}}`
5833
- );
5834
- if (nested === null) return "";
5835
- if (out !== text) return out + nested;
5836
- // The run wrote this rule's selectors into itself, so what it returns is
5837
- // the whole of both — `text` being this rule's own `prelude` and `rest`.
5838
- if (nested.length !== 0) {
5839
- if (_nestedTookParent) {
5840
- if (minify) _recordHoistedRule(nested, null, 0);
5841
- return nested;
5842
- }
5843
- if (minify) _recordHoistedRule(nested, text, prelude.length);
5844
- return text + nested;
5845
- }
5846
- // Where the block is what a sibling is compared against, remember the rule
5847
- // so the parent parts it without re-scanning for the `{`. An at-rule
5848
- // remembers its block's entries too, so a sibling's rules can join them.
5849
- if (
5850
- minify &&
5851
- path.type() === NodeType.AtRule &&
5852
- MERGEABLE_AT_RULES.has(_atKeywordOf(path.node))
5853
- ) {
5854
- /** @type {RuleEntry[]} */
5855
- const children = [];
5856
- for (let i = 0; i < texts.length; i++) {
5857
- if (texts[i].length === 0) continue;
5858
- if (superseded !== null && superseded.has(i)) continue;
5859
- // The entries have to spell the block this rule printed, prefixes and
5860
- // all: a join rebuilds the body from them, and one built from the
5861
- // unprefixed texts would restore what was dropped.
5862
- if (droppedPrefix !== null && droppedPrefix.has(i)) continue;
5863
- if (deadPrefixed !== null && deadPrefixed.has(items[i])) continue;
5864
- const added = addedPrefix === null ? undefined : addedPrefix.get(i);
5865
- children.push(
5866
- added === undefined
5867
- ? _ruleEntryOf(items[i], texts[i])
5868
- : _opaqueEntry(added + texts[i])
5869
- );
5870
- }
5871
- let head = "";
5872
- for (let i = 0; i < children.length - 1; i++) head += children[i].text;
5873
- _setRuleEntry(_S._currentNode, {
5874
- text,
5875
- prelude: prelude.length,
5876
- atRule: true,
5877
- plain: false,
5878
- listable: LIST_NO,
5879
- listKind: LIST_KIND_SELECTOR,
5880
- children,
5881
- head,
5882
- // The block's own merges have run and settled what they grew, so the
5883
- // text above and the children agree; a list one of them still carries
5884
- // concatenated is one this block says is there to settle.
5885
- unordered: _anyUnordered(children),
5886
- list: null,
5887
- block: null,
5888
- declarations: null
5889
- });
5890
- }
5891
- if (minify && path.type() === NodeType.QualifiedRule) {
5892
- // Only a block of declarations lends its selectors to another's list: a
5893
- // nested rule's `&` stands for the whole list and `:is(…)` takes its most
5894
- // specific selector, so joining preludes moves what the nested beat.
5895
- const plain = rules === null || rules.length === 0;
5896
- const parent = path.parent;
5897
- // Which shape a join would read the prelude with — the parent is gone by
5898
- // then. A keyframe selector is a list of its own shape, which
5899
- // `_rulePrelude` has already rewritten `from` in.
5900
- let listKind = LIST_KIND_SELECTOR;
5901
- if (parent !== null) {
5902
- const parentType = path.type(parent);
5903
- if (parentType === NodeType.QualifiedRule) {
5904
- listKind = LIST_KIND_NESTED;
5905
- } else if (
5906
- parentType === NodeType.AtRule &&
5907
- KEYFRAMES_AT_RULE_RE.test(_atKeywordOf(parent))
5908
- ) {
5909
- listKind = LIST_KIND_KEYFRAME;
5910
- }
5911
- }
5912
- _setRuleEntry(_S._currentNode, {
5913
- text,
5914
- prelude: prelude.length,
5915
- atRule: false,
5916
- plain,
5917
- listable: plain ? LIST_UNKNOWN : LIST_NO,
5918
- listKind,
5919
- children: null,
5920
- head: "",
5921
- unordered: false,
5922
- list: null,
5923
- block: null,
5924
- declarations: null
5925
- });
5926
- }
5927
- return text;
5928
- }
6020
+ case NodeType.QualifiedRule:
6021
+ return _printRuleNode(path, writer, minify, soft);
5929
6022
  case NodeType.Raw:
5930
6023
  // Off-spec passthrough (see `NodeType.Raw`). It sits in a declaration list,
5931
6024
  // so it carries the `;` parting it from the next item, and leaves an entry
@@ -5944,39 +6037,8 @@ const printer = (path, writer) => {
5944
6037
  // string, a bracket — would swallow the `;` and `}` written after it.
5945
6038
  return `${rest === _S._input.length ? _closeAtEof(raw) : raw};`;
5946
6039
  }
5947
- case NodeType.Hash: {
5948
- const raw = _printedSource(path.node);
5949
- if (!minify) return raw;
5950
- // A hash is a hex color in a value and an id in a selector, and ids are
5951
- // case-sensitive, so only the former shortens — at any value depth. An id's
5952
- // escapes still shorten, the `#` held back so its first code point is one.
5953
- if (!path.within(NodeType.Declaration)) {
5954
- return _S._transforms.rewriteEscapes
5955
- ? `#${_minifyIdentEscapes(raw.slice(1))}`
5956
- : raw;
5957
- }
5958
- // Inside a function only the known color functions take color hashes, and a
5959
- // substitution's fallback, being the property's value. `paint()` is the
5960
- // exception: its arguments reach a worklet rather than a declaration.
5961
- const parent = path.parent;
5962
- if (parent !== null && path.type(parent) === NodeType.Function) {
5963
- const fn = unescapeIdentifier(_rawName(parent)).toLowerCase();
5964
- if (
5965
- !COLOR_ARGUMENT_FUNCTIONS.has(fn) &&
5966
- !(SUBSTITUTION_FUNCTIONS.has(fn) && fn !== "paint")
5967
- ) {
5968
- return raw;
5969
- }
5970
- }
5971
- const short = _minifyHash(raw);
5972
- const text = short === null ? raw : short;
5973
- // A hex alpha the target cannot read is written as the `rgba()` it names.
5974
- if (_S._hexAlphaAllowed) return text;
5975
- const lowered = _S._transforms.lowerUnsupported
5976
- ? _hexAlphaFallback(text)
5977
- : null;
5978
- return lowered === null ? text : lowered;
5979
- }
6040
+ case NodeType.Hash:
6041
+ return _printHashNode(path, minify);
5980
6042
  default: {
5981
6043
  // Any remaining leaf token (ident, string, url, delim, …) prints verbatim
5982
6044
  // from its source slice.