@thejaredwilcurt/csslop 0.0.48 → 0.0.49
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.
- package/README.md +3 -3
- package/package.json +3 -3
- package/src/declarations/css-wide-keywords.js +37 -1
- package/src/declarations/fold.js +13 -3
- package/src/declarations/process.js +60 -7
- package/src/index.js +8 -3
- package/src/parser/source-search.js +6 -0
- package/src/rules/optimize.js +422 -139
- package/src/rules/selectors.js +9 -0
- package/src/rules/stringify.js +138 -76
- package/src/utilities.js +0 -13
- package/src/value/minify.js +43 -17
package/src/rules/selectors.js
CHANGED
|
@@ -33,6 +33,11 @@ const COMPOUND_SELECTOR_BOUNDARIES = '>+~ \t\n\r\f';
|
|
|
33
33
|
* @return {number} The index of the next top-level occurrence, or -1 if none remains.
|
|
34
34
|
*/
|
|
35
35
|
function findNextFunctionCallOutsideStrings (text, functionCall, start) {
|
|
36
|
+
// A token that appears nowhere in the selector cannot appear at its top
|
|
37
|
+
// level either, and one search rules that out without walking every character
|
|
38
|
+
if (!text.toLowerCase().includes(functionCall)) {
|
|
39
|
+
return -1;
|
|
40
|
+
}
|
|
36
41
|
let index = start;
|
|
37
42
|
while (index < text.length) {
|
|
38
43
|
const character = text[index];
|
|
@@ -655,6 +660,10 @@ function readWholeSelectorIsParts (selector) {
|
|
|
655
660
|
* @return {Array} An array of one or more processed selector strings.
|
|
656
661
|
*/
|
|
657
662
|
function processIsSelector (selector, hasSiblingSelectors) {
|
|
663
|
+
// Every rewrite below starts from an `:is(`, so a selector without one is left as it is
|
|
664
|
+
if (!selector.toLowerCase().includes(':is(')) {
|
|
665
|
+
return [selector];
|
|
666
|
+
}
|
|
658
667
|
// Replace :is(:link,:visited) and :is(:visited,:link) with :any-link
|
|
659
668
|
selector = selector.replace(/:is\(:link,:visited\)/g, ':any-link');
|
|
660
669
|
selector = selector.replace(/:is\(:visited,:link\)/g, ':any-link');
|
package/src/rules/stringify.js
CHANGED
|
@@ -276,6 +276,143 @@ function stringifyAtRule (rule, context) {
|
|
|
276
276
|
const separator = minifiedPrelude ? ' ' : '';
|
|
277
277
|
return '@' + rule.name + separator + minifiedPrelude + '{' + body + '}';
|
|
278
278
|
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Rewrites `:nth-*()` pseudo-classes into their shortest equivalent forms.
|
|
282
|
+
*
|
|
283
|
+
* @param {string} selector A selector with its whitespace already collapsed.
|
|
284
|
+
* @return {string} The selector with simplified `:nth-*()` pseudo-classes.
|
|
285
|
+
*/
|
|
286
|
+
function simplifyNthPseudoClasses (selector) {
|
|
287
|
+
let minified = selector;
|
|
288
|
+
// Simplify :nth-child(2n+1) to :nth-child(odd)
|
|
289
|
+
minified = minified.replace(/:nth-child\(2n\s*\+\s*1\)/g, ':nth-child(odd)');
|
|
290
|
+
// Simplify :nth-child(2n+0) to :nth-child(2n)
|
|
291
|
+
minified = minified.replace(/:nth-child\(2n\s*\+\s*0\)/g, ':nth-child(2n)');
|
|
292
|
+
// Simplify :nth-child(1n) to :nth-child(n)
|
|
293
|
+
minified = minified.replace(/:nth-child\(1n\)/g, ':nth-child(n)');
|
|
294
|
+
// Remove unnecessary leading + sign from :nth-child()
|
|
295
|
+
minified = minified.replace(/:nth-child\(\+\s*(\d+)\)/g, ':nth-child($1)');
|
|
296
|
+
// Simplify :nth-child(0n+N) to :nth-child(N)
|
|
297
|
+
minified = minified.replace(/:nth-child\(0n\s*\+\s*(\d+)\)/g, ':nth-child($1)');
|
|
298
|
+
// Replace :nth-child(1) with :first-child
|
|
299
|
+
minified = minified.replace(/:nth-child\(1\)/g, ':first-child');
|
|
300
|
+
// Replace :nth-last-child(1) with :last-child
|
|
301
|
+
minified = minified.replace(/:nth-last-child\(1\)/g, ':last-child');
|
|
302
|
+
// Replace :nth-of-type(1) with :first-of-type
|
|
303
|
+
minified = minified.replace(/:nth-of-type\(1\)/g, ':first-of-type');
|
|
304
|
+
// Replace :nth-last-of-type(1) with :last-of-type
|
|
305
|
+
minified = minified.replace(/:nth-last-of-type\(1\)/g, ':last-of-type');
|
|
306
|
+
return minified;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Writes each attribute selector's value quoted or escaped, whichever is
|
|
311
|
+
* shorter, and removes the whitespace inside its brackets.
|
|
312
|
+
*
|
|
313
|
+
* @param {string} selector A selector with its whitespace already collapsed.
|
|
314
|
+
* @return {string} The selector with minified attribute selectors.
|
|
315
|
+
*/
|
|
316
|
+
function minifyAttributeSelectors (selector) {
|
|
317
|
+
let minified = selector;
|
|
318
|
+
// Minify double-quoted attribute selectors: remove inner whitespace and escape when shorter
|
|
319
|
+
minified = minified.replace(/\[\s*([^=]+)\s*=\s*"(.*?)"\s*\]/g, (match, attribute, value) => {
|
|
320
|
+
// Escape special characters that require quoting (spaces, #, ., :, /, ;), and compare lengths
|
|
321
|
+
let escaped = value.replace(/([ #.:/;])/g, '\\$1');
|
|
322
|
+
if (escaped.length < value.length + 2) {
|
|
323
|
+
return '[' + attribute + '=' + escaped + ']';
|
|
324
|
+
}
|
|
325
|
+
return '[' + attribute + '="' + value + '"]';
|
|
326
|
+
});
|
|
327
|
+
// Minify single-quoted attribute selectors: remove inner whitespace and escape when shorter
|
|
328
|
+
minified = minified.replace(/\[\s*([^=]+)\s*=\s*'(.*?)'\s*\]/g, (match, attribute, value) => {
|
|
329
|
+
// Escape special characters that require quoting (spaces, #, ., :, /, ;), and compare lengths
|
|
330
|
+
let escaped = value.replace(/([ #.:/;])/g, '\\$1');
|
|
331
|
+
if (escaped.length < value.length + 2) {
|
|
332
|
+
return '[' + attribute + '=' + escaped + ']';
|
|
333
|
+
}
|
|
334
|
+
return '[' + attribute + '="' + value + '"]';
|
|
335
|
+
});
|
|
336
|
+
// Minify unquoted attribute selectors: quote when unescaping produces a shorter result
|
|
337
|
+
minified = minified.replace(/\[\s*([^=]+)\s*=\s*([^"'].*?)\s*\]/g, (match, attribute, value) => {
|
|
338
|
+
// Unescape special characters (spaces, #, ., :, /, ;) and compare with quoted form
|
|
339
|
+
let unescaped = value.replace(/\\([ #.:/;])/g, '$1');
|
|
340
|
+
if (unescaped.length + 2 < value.length) {
|
|
341
|
+
return '[' + attribute + '="' + unescaped + '"]';
|
|
342
|
+
}
|
|
343
|
+
return '[' + attribute + '=' + value + ']';
|
|
344
|
+
});
|
|
345
|
+
return minified;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Rewrites `:not()` pseudo-classes that negate a pseudo-class with a direct
|
|
350
|
+
* opposite, such as `:not(:enabled)`, into that opposite.
|
|
351
|
+
*
|
|
352
|
+
* @param {string} selector A selector with its whitespace already collapsed.
|
|
353
|
+
* @return {string} The selector with simplified negations.
|
|
354
|
+
*/
|
|
355
|
+
function simplifyNegatedPseudoClasses (selector) {
|
|
356
|
+
let minified = selector;
|
|
357
|
+
// Minify logical combinations
|
|
358
|
+
// Replace :not(:invalid) with :valid, only directly after a form element type selector
|
|
359
|
+
minified = minified.replace(/(?<=\b(?:button|fieldset|form|input|select|textarea)):not\(:invalid\)/g, ':valid');
|
|
360
|
+
// Replace :not(:dir(ltr)) with :dir(rtl)
|
|
361
|
+
minified = minified.replace(/:not\(:dir\(ltr\)\)/g, ':dir(rtl)');
|
|
362
|
+
// Replace a double negation, :not(:not(X)), with the X it wraps
|
|
363
|
+
minified = minified.replace(/:not\(:not\((.*?)\)\)/g, '$1');
|
|
364
|
+
// Replace :not(:enabled) with :disabled
|
|
365
|
+
minified = minified.replace(/:not\(:enabled\)/g, ':disabled');
|
|
366
|
+
// Replace :not(:required) with :optional
|
|
367
|
+
minified = minified.replace(/:not\(:required\)/g, ':optional');
|
|
368
|
+
// Match a link element type selector at the start or after a combinator, with any attribute selectors and an optional :where( wrapper, followed by :not(:link)
|
|
369
|
+
minified = minified.replace(/(^|[\s,>+~])(a|area|link)(?:\[.*?\])*(?::where\()?:not\(:link\)\)?/g, (match) => {
|
|
370
|
+
return match.replace(':not(:link)', ':visited');
|
|
371
|
+
});
|
|
372
|
+
return minified;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Minifies a single selector's spacing, pseudo-classes, pseudo-elements, and
|
|
377
|
+
* attribute selectors. Most selectors hold none of the patterns a group of
|
|
378
|
+
* rewrites looks for, so each group only runs when its marker text is present.
|
|
379
|
+
*
|
|
380
|
+
* @param {string} selector A single selector from a rule's selector list.
|
|
381
|
+
* @return {string} The minified selector.
|
|
382
|
+
*/
|
|
383
|
+
function minifySelector (selector) {
|
|
384
|
+
let minified = unescapeSelector(selector);
|
|
385
|
+
// Collapse whitespace to single space
|
|
386
|
+
minified = minified.replace(/\s+/g, ' ');
|
|
387
|
+
// Strip whitespace around selector combinators and commas
|
|
388
|
+
minified = minified.replace(/\s*([,>+~])\s*/g, '$1');
|
|
389
|
+
// Strip whitespace inside parentheses for pseudo-class arguments
|
|
390
|
+
minified = minified.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')');
|
|
391
|
+
if (minified.includes(':nth-')) {
|
|
392
|
+
minified = simplifyNthPseudoClasses(minified);
|
|
393
|
+
}
|
|
394
|
+
if (minified.includes('::')) {
|
|
395
|
+
// Convert double-colon pseudo-elements to single-colon legacy form
|
|
396
|
+
minified = minified.replace(/::before/g, ':before');
|
|
397
|
+
minified = minified.replace(/::after/g, ':after');
|
|
398
|
+
}
|
|
399
|
+
if (minified.includes('*')) {
|
|
400
|
+
// Strip redundant universal selector `*` when it precedes an ID, class, or attribute selector
|
|
401
|
+
minified = minified.replace(/\*([#.[])/g, '$1');
|
|
402
|
+
}
|
|
403
|
+
if (minified.includes('[') && minified.includes('=')) {
|
|
404
|
+
minified = minifyAttributeSelectors(minified);
|
|
405
|
+
}
|
|
406
|
+
if (minified.includes(':not(')) {
|
|
407
|
+
minified = simplifyNegatedPseudoClasses(minified);
|
|
408
|
+
}
|
|
409
|
+
// Remove redundant leading "& " nesting selector
|
|
410
|
+
minified = minified.replace(/^& /, '');
|
|
411
|
+
minified = mergeAdjacentWherePseudoClasses(minified);
|
|
412
|
+
minified = minifyLanguageSelector(minified);
|
|
413
|
+
return minified;
|
|
414
|
+
}
|
|
415
|
+
|
|
279
416
|
/**
|
|
280
417
|
* Converts a parsed CSS AST rule node into a minified CSS string, dispatching to specialized handlers for each rule type including selectors, `@media`, `@keyframes`, `@layer`, and other at-rules.
|
|
281
418
|
*
|
|
@@ -305,82 +442,7 @@ function stringifyRule (rule, context) {
|
|
|
305
442
|
if (rule.selectors?.length) {
|
|
306
443
|
let uniqueSelectors = [...new Set(rule.selectors)];
|
|
307
444
|
// Minify spacing within selectors (e.g. inside :is(), :where(), etc)
|
|
308
|
-
uniqueSelectors = uniqueSelectors.map(
|
|
309
|
-
let minified = unescapeSelector(selector);
|
|
310
|
-
// Collapse whitespace to single space
|
|
311
|
-
minified = minified.replace(/\s+/g, ' ');
|
|
312
|
-
// Strip whitespace around selector combinators and commas
|
|
313
|
-
minified = minified.replace(/\s*([,>+~])\s*/g, '$1');
|
|
314
|
-
// Strip whitespace inside parentheses for pseudo-class arguments
|
|
315
|
-
minified = minified.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')');
|
|
316
|
-
// Simplify :nth-child(2n+1) to :nth-child(odd)
|
|
317
|
-
minified = minified.replace(/:nth-child\(2n\s*\+\s*1\)/g, ':nth-child(odd)');
|
|
318
|
-
// Simplify :nth-child(2n+0) to :nth-child(2n)
|
|
319
|
-
minified = minified.replace(/:nth-child\(2n\s*\+\s*0\)/g, ':nth-child(2n)');
|
|
320
|
-
// Simplify :nth-child(1n) to :nth-child(n)
|
|
321
|
-
minified = minified.replace(/:nth-child\(1n\)/g, ':nth-child(n)');
|
|
322
|
-
// Remove unnecessary leading + sign from :nth-child()
|
|
323
|
-
minified = minified.replace(/:nth-child\(\+\s*(\d+)\)/g, ':nth-child($1)');
|
|
324
|
-
// Simplify :nth-child(0n+N) to :nth-child(N)
|
|
325
|
-
minified = minified.replace(/:nth-child\(0n\s*\+\s*(\d+)\)/g, ':nth-child($1)');
|
|
326
|
-
// Replace :nth-child(1) with :first-child
|
|
327
|
-
minified = minified.replace(/:nth-child\(1\)/g, ':first-child');
|
|
328
|
-
// Replace :nth-last-child(1) with :last-child
|
|
329
|
-
minified = minified.replace(/:nth-last-child\(1\)/g, ':last-child');
|
|
330
|
-
// Replace :nth-of-type(1) with :first-of-type
|
|
331
|
-
minified = minified.replace(/:nth-of-type\(1\)/g, ':first-of-type');
|
|
332
|
-
// Replace :nth-last-of-type(1) with :last-of-type
|
|
333
|
-
minified = minified.replace(/:nth-last-of-type\(1\)/g, ':last-of-type');
|
|
334
|
-
// Convert double-colon pseudo-elements to single-colon legacy form
|
|
335
|
-
minified = minified.replace(/::before/g, ':before');
|
|
336
|
-
minified = minified.replace(/::after/g, ':after');
|
|
337
|
-
|
|
338
|
-
// Strip redundant universal selector `*` when it precedes an ID, class, or attribute selector
|
|
339
|
-
minified = minified.replace(/\*([#.[])/g, '$1');
|
|
340
|
-
|
|
341
|
-
// Minify double-quoted attribute selectors: remove inner whitespace and escape when shorter
|
|
342
|
-
minified = minified.replace(/\[\s*([^=]+)\s*=\s*"(.*?)"\s*\]/g, (match, attribute, value) => {
|
|
343
|
-
// Escape special characters that require quoting (spaces, #, ., :, /, ;), and compare lengths
|
|
344
|
-
let escaped = value.replace(/([ #.:/;])/g, '\\$1');
|
|
345
|
-
if (escaped.length < value.length + 2) {
|
|
346
|
-
return '[' + attribute + '=' + escaped + ']';
|
|
347
|
-
}
|
|
348
|
-
return '[' + attribute + '="' + value + '"]';
|
|
349
|
-
});
|
|
350
|
-
// Minify single-quoted attribute selectors: remove inner whitespace and escape when shorter
|
|
351
|
-
minified = minified.replace(/\[\s*([^=]+)\s*=\s*'(.*?)'\s*\]/g, (match, attribute, value) => {
|
|
352
|
-
// Escape special characters that require quoting (spaces, #, ., :, /, ;), and compare lengths
|
|
353
|
-
let escaped = value.replace(/([ #.:/;])/g, '\\$1');
|
|
354
|
-
if (escaped.length < value.length + 2) {
|
|
355
|
-
return '[' + attribute + '=' + escaped + ']';
|
|
356
|
-
}
|
|
357
|
-
return '[' + attribute + '="' + value + '"]';
|
|
358
|
-
});
|
|
359
|
-
// Minify unquoted attribute selectors: quote when unescaping produces a shorter result
|
|
360
|
-
minified = minified.replace(/\[\s*([^=]+)\s*=\s*([^"'].*?)\s*\]/g, (match, attribute, value) => {
|
|
361
|
-
// Unescape special characters (spaces, #, ., :, /, ;) and compare with quoted form
|
|
362
|
-
let unescaped = value.replace(/\\([ #.:/;])/g, '$1');
|
|
363
|
-
if (unescaped.length + 2 < value.length) {
|
|
364
|
-
return '[' + attribute + '="' + unescaped + '"]';
|
|
365
|
-
}
|
|
366
|
-
return '[' + attribute + '=' + value + ']';
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
// Minify logical combinations
|
|
370
|
-
minified = minified.replace(/(?<=\b(?:button|fieldset|form|input|select|textarea)):not\(:invalid\)/g, ':valid');
|
|
371
|
-
minified = minified.replace(/:not\(:dir\(ltr\)\)/g, ':dir(rtl)');
|
|
372
|
-
minified = minified.replace(/:not\(:not\((.*?)\)\)/g, '$1');
|
|
373
|
-
minified = minified.replace(/:not\(:enabled\)/g, ':disabled');
|
|
374
|
-
minified = minified.replace(/:not\(:required\)/g, ':optional');
|
|
375
|
-
minified = minified.replace(/(^|[\s,>+~])(a|area|link)(?:\[.*?\])*(?::where\()?:not\(:link\)\)?/g, (match) => {
|
|
376
|
-
return match.replace(':not(:link)', ':visited');
|
|
377
|
-
});
|
|
378
|
-
// Remove redundant leading "& " nesting selector
|
|
379
|
-
minified = minified.replace(/^& /, '');
|
|
380
|
-
minified = mergeAdjacentWherePseudoClasses(minified);
|
|
381
|
-
minified = minifyLanguageSelector(minified);
|
|
382
|
-
return minified;
|
|
383
|
-
});
|
|
445
|
+
uniqueSelectors = uniqueSelectors.map(minifySelector);
|
|
384
446
|
uniqueSelectors = combineLegacyAnySelectors(uniqueSelectors);
|
|
385
447
|
// When this rule is a nesting parent, its whole selector list is treated
|
|
386
448
|
// as :is() for the children's specificity, so a top-level :is() can be
|
package/src/utilities.js
CHANGED
|
@@ -19,19 +19,6 @@ function resolveUnicodeEscape (hex) {
|
|
|
19
19
|
return String.fromCodePoint(codePoint);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Escapes special regex metacharacters in a string so it can be safely used
|
|
24
|
-
* as a literal pattern in a RegExp constructor.
|
|
25
|
-
*
|
|
26
|
-
* @param {string} input The string to escape.
|
|
27
|
-
* @return {string} The escaped string safe for use in a RegExp.
|
|
28
|
-
*/
|
|
29
|
-
function escapeRegexString (input) {
|
|
30
|
-
// Escape all regex metacharacters: . * + ? ^ $ { } ( ) | [ ] backslash
|
|
31
|
-
return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
22
|
export {
|
|
35
|
-
escapeRegexString,
|
|
36
23
|
resolveUnicodeEscape
|
|
37
24
|
};
|
package/src/value/minify.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
findMatchingParenthesis,
|
|
8
8
|
skipParenthesizedGroup,
|
|
9
9
|
skipQuotedString,
|
|
10
|
+
splitTopLevelComponents,
|
|
10
11
|
startsUrlToken
|
|
11
12
|
} from '../parser/source-search.js';
|
|
12
13
|
import { tokenizeCssValue } from '../parser/tokenizer.js';
|
|
@@ -1606,7 +1607,12 @@ function applyPropertyOptimizations (val, property, urlTokens) {
|
|
|
1606
1607
|
// Shorthands: margin, padding, border-width, border-style, border-color, inset
|
|
1607
1608
|
// Check if property supports box-model shorthand collapsing (4 → 3 → 2 → 1 values)
|
|
1608
1609
|
if (/^(margin|padding|inset|border-width|border-style|border-color|gap|overflow)$/.test(property)) {
|
|
1609
|
-
|
|
1610
|
+
// Split components instead of splitting on spaces, so values that legally
|
|
1611
|
+
// touch, like the adjacent hex colors of `#ddd#ddd`, count separately
|
|
1612
|
+
val = collapseShorthandParts(splitTopLevelComponents(val))
|
|
1613
|
+
.join(' ')
|
|
1614
|
+
// A hash token may touch the component before it, so the separator space is not needed
|
|
1615
|
+
.replace(/ #/g, '#');
|
|
1610
1616
|
}
|
|
1611
1617
|
|
|
1612
1618
|
if (property === 'hyphenate-limit-chars') {
|
|
@@ -1760,12 +1766,25 @@ function computeMinifiedValue (declaration) {
|
|
|
1760
1766
|
* Memoizes minified values for the current stylesheet. Minification runs many
|
|
1761
1767
|
* passes over the same declarations (deduplication, shorthand assembly,
|
|
1762
1768
|
* CSS-wide keyword hoisting, stringification), and the result only depends on
|
|
1763
|
-
* the declaration
|
|
1769
|
+
* the declaration's property, its value, and whether it is an assembled
|
|
1770
|
+
* shorthand, so each distinct
|
|
1764
1771
|
* property/value pair is minified once per stylesheet.
|
|
1765
1772
|
*
|
|
1766
|
-
*
|
|
1773
|
+
* Values are filed under their property first, and assembled shorthands are
|
|
1774
|
+
* kept apart from authored declarations, so a lookup reuses the property and
|
|
1775
|
+
* value strings as they are instead of building a combined key on every call.
|
|
1776
|
+
*
|
|
1777
|
+
* @type {Map<string, Map<string, string>>}
|
|
1767
1778
|
*/
|
|
1768
|
-
const
|
|
1779
|
+
const minifiedValuesByProperty = new Map();
|
|
1780
|
+
|
|
1781
|
+
/**
|
|
1782
|
+
* The memoized values of shorthands the minifier assembled itself, filed the
|
|
1783
|
+
* same way as `minifiedValuesByProperty`.
|
|
1784
|
+
*
|
|
1785
|
+
* @type {Map<string, Map<string, string>>}
|
|
1786
|
+
*/
|
|
1787
|
+
const assembledMinifiedValuesByProperty = new Map();
|
|
1769
1788
|
|
|
1770
1789
|
/**
|
|
1771
1790
|
* Discards every memoized value. The minifier calls this at the start of each
|
|
@@ -1773,20 +1792,27 @@ const minifiedValueCache = new Map();
|
|
|
1773
1792
|
* the cache should not outlive the pass that filled it.
|
|
1774
1793
|
*/
|
|
1775
1794
|
function clearMinifiedValueCache () {
|
|
1776
|
-
|
|
1795
|
+
minifiedValuesByProperty.clear();
|
|
1796
|
+
assembledMinifiedValuesByProperty.clear();
|
|
1777
1797
|
}
|
|
1778
1798
|
|
|
1779
1799
|
/**
|
|
1780
|
-
*
|
|
1781
|
-
*
|
|
1782
|
-
* value, so it safely delimits the parts.
|
|
1800
|
+
* Finds the memoized values for a declaration's property, creating the empty
|
|
1801
|
+
* collection the first time the property is seen.
|
|
1783
1802
|
*
|
|
1784
|
-
* @param {object}
|
|
1785
|
-
* @return {string}
|
|
1786
|
-
*/
|
|
1787
|
-
function
|
|
1788
|
-
const
|
|
1789
|
-
|
|
1803
|
+
* @param {object} declaration The CSS declaration object.
|
|
1804
|
+
* @return {Map<string, string>} Map of value to its minified form for that property.
|
|
1805
|
+
*/
|
|
1806
|
+
function getMinifiedValuesForProperty (declaration) {
|
|
1807
|
+
const valuesByProperty = declaration.isAssembledShorthand ?
|
|
1808
|
+
assembledMinifiedValuesByProperty :
|
|
1809
|
+
minifiedValuesByProperty;
|
|
1810
|
+
let minifiedValues = valuesByProperty.get(declaration.property);
|
|
1811
|
+
if (!minifiedValues) {
|
|
1812
|
+
minifiedValues = new Map();
|
|
1813
|
+
valuesByProperty.set(declaration.property, minifiedValues);
|
|
1814
|
+
}
|
|
1815
|
+
return minifiedValues;
|
|
1790
1816
|
}
|
|
1791
1817
|
|
|
1792
1818
|
/**
|
|
@@ -1802,13 +1828,13 @@ function minifyValue (declaration) {
|
|
|
1802
1828
|
if (typeof declaration.value !== 'string') {
|
|
1803
1829
|
return computeMinifiedValue(declaration);
|
|
1804
1830
|
}
|
|
1805
|
-
const
|
|
1806
|
-
const cachedValue =
|
|
1831
|
+
const minifiedValues = getMinifiedValuesForProperty(declaration);
|
|
1832
|
+
const cachedValue = minifiedValues.get(declaration.value);
|
|
1807
1833
|
if (cachedValue !== undefined) {
|
|
1808
1834
|
return cachedValue;
|
|
1809
1835
|
}
|
|
1810
1836
|
const minifiedValue = computeMinifiedValue(declaration);
|
|
1811
|
-
|
|
1837
|
+
minifiedValues.set(declaration.value, minifiedValue);
|
|
1812
1838
|
return minifiedValue;
|
|
1813
1839
|
}
|
|
1814
1840
|
|