webpack 5.112.0 → 5.112.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/webpack.js +13 -7
- package/lib/Compilation.js +198 -61
- package/lib/Compiler.js +12 -25
- package/lib/asset/AssetBytesGenerator.js +14 -6
- package/lib/asset/AssetBytesParser.js +10 -8
- package/lib/asset/AssetGenerator.js +36 -48
- package/lib/asset/AssetModule.js +11 -0
- package/lib/asset/AssetModulesPlugin.js +5 -4
- package/lib/asset/AssetParser.js +8 -8
- package/lib/asset/AssetSourceGenerator.js +16 -8
- package/lib/asset/AssetSourceParser.js +10 -8
- package/lib/asset/WebManifestGenerator.js +11 -6
- package/lib/asset/WebManifestParser.js +3 -3
- package/lib/async-modules/AsyncModuleHelpers.js +8 -2
- package/lib/async-modules/InferAsyncModulesPlugin.js +7 -2
- package/lib/cache/CacheFacade.js +1 -1
- package/lib/cache/PackFileCacheStrategy.js +6 -10
- package/lib/cache/ResolverCachePlugin.js +58 -8
- package/lib/config/WebpackOptionsApply.js +8 -28
- package/lib/config/cli.js +9 -6
- package/lib/config/defaults.js +24 -13
- package/lib/config/getClaimedAssetTypes.js +2 -2
- package/lib/config/target.js +1 -1
- package/lib/context/ContextModule.js +1 -1
- package/lib/context/ContextModuleFactory.js +1 -1
- package/lib/context/ContextReplacementPlugin.js +3 -9
- package/lib/css/CssGenerator.js +40 -47
- package/lib/css/CssModule.js +10 -0
- package/lib/css/CssModulesPlugin.js +13 -6
- package/lib/css/CssParser.js +33 -32
- package/lib/css/conventions.js +1 -1
- package/lib/css/data.js +224 -219
- package/lib/css/syntax-parser.js +1312 -312
- package/lib/css/syntax-printer.js +1142 -1025
- package/lib/debug/ProfilingPlugin.js +65 -43
- package/lib/define/DefinePlugin.js +10 -15
- package/lib/define/DotenvPlugin.js +10 -6
- package/lib/define/EnvironmentPlugin.js +4 -2
- package/lib/dependencies/amd/AMDDefineDependencyParserPlugin.js +1 -2
- package/lib/dependencies/amd/AMDRequireDependenciesBlockParserPlugin.js +4 -1
- package/lib/dependencies/commonjs/CommonJsExportRequireDependency.js +1 -4
- package/lib/dependencies/commonjs/CommonJsExportsParserPlugin.js +18 -33
- package/lib/dependencies/commonjs/CommonJsImportsParserPlugin.js +1 -3
- package/lib/dependencies/commonjs/CommonJsObjectExportDependency.js +4 -4
- package/lib/dependencies/commonjs/CommonJsPlugin.js +2 -5
- package/lib/dependencies/commonjs/RequireEnsureDependenciesBlockParserPlugin.js +4 -1
- package/lib/dependencies/context/ImportMetaContextDependencyParserPlugin.js +1 -2
- package/lib/dependencies/context/ImportMetaGlobDependencyParserPlugin.js +4 -7
- package/lib/dependencies/context/ImportMetaGlobHelpers.js +8 -10
- package/lib/dependencies/core/CachedConstDependency.js +9 -6
- package/lib/dependencies/css/CssIcssExportDependency.js +6 -7
- package/lib/dependencies/css/CssIcssImportDependency.js +1 -3
- package/lib/dependencies/css/CssUrlDependency.js +2 -6
- package/lib/dependencies/esm/ESMDetectionParserPlugin.js +2 -4
- package/lib/dependencies/esm/ESMExportExpressionDependency.js +2 -2
- package/lib/dependencies/esm/ESMExportImportedSpecifierDependency.js +16 -18
- package/lib/dependencies/esm/ESMExportSpecifierDependency.js +20 -3
- package/lib/dependencies/esm/ESMExports.js +2 -10
- package/lib/dependencies/esm/ESMImportDependency.js +17 -2
- package/lib/dependencies/esm/ESMImportDependencyParserPlugin.js +6 -17
- package/lib/dependencies/esm/ESMImportGuard.js +11 -23
- package/lib/dependencies/esm/ESMImportSideEffectDependency.js +1 -1
- package/lib/dependencies/esm/ESMImportSpecifierDependency.js +4 -1
- package/lib/dependencies/esm/ESMLiveExportDependency.js +311 -0
- package/lib/dependencies/esm/ESMLiveExportParserPlugin.js +862 -0
- package/lib/dependencies/esm/ESMModulesPlugin.js +1 -3
- package/lib/dependencies/esm/TopLevelAwaitDependency.js +1 -8
- package/lib/dependencies/helpers/DynamicExports.js +4 -6
- package/lib/dependencies/html/HtmlEntryDependency.js +5 -14
- package/lib/dependencies/html/HtmlInlineHtmlDependency.js +1 -4
- package/lib/dependencies/html/HtmlInlineScriptDependency.js +2 -6
- package/lib/dependencies/html/HtmlInlineStyleDependency.js +1 -4
- package/lib/dependencies/import/ImportParserPlugin.js +3 -6
- package/lib/dependencies/import-meta/ImportMetaPlugin.js +4 -6
- package/lib/dependencies/url/URLParserPlugin.js +1 -1
- package/lib/dependencies/worker/WorkerAndWorkletPlugin.js +51 -20
- package/lib/devtool/ModuleFilenameHelpers.js +1 -1
- package/lib/devtool/SourceMapDevToolPlugin.js +1 -1
- package/lib/dll/DelegatedModule.js +3 -3
- package/lib/dll/DllModule.js +3 -1
- package/lib/dll/DllReferencePlugin.js +1 -4
- package/lib/errors/AbstractMethodError.js +1 -1
- package/lib/errors/ErrorHelpers.js +2 -1
- package/lib/errors/HookWebpackError.js +2 -1
- package/lib/errors/JSONParseError.js +4 -4
- package/lib/errors/WebpackError.js +3 -2
- package/lib/esm/ModuleChunkFormatPlugin.js +36 -1
- package/lib/externals/ExternalModule.js +12 -9
- package/lib/externals/ExternalModuleFactoryPlugin.js +1 -2
- package/lib/externals/ExternalsPlugin.js +1 -1
- package/lib/fs/FileSystemInfo.js +4 -4
- package/lib/graph/AsyncDependenciesBlock.js +1 -1
- package/lib/graph/ChunkGraph.js +5 -0
- package/lib/graph/Dependency.js +8 -2
- package/lib/graph/ExportsInfo.js +1 -1
- package/lib/graph/ModuleGraph.js +14 -3
- package/lib/graph/buildChunkGraph.js +17 -19
- package/lib/graph/findGraphRoots.js +1 -1
- package/lib/hmr/HotModuleReplacement.runtime.js +10 -9
- package/lib/hmr/HotModuleReplacementPlugin.js +5 -11
- package/lib/hmr/JavascriptHotModuleReplacement.runtime.js +16 -19
- package/lib/hmr/LazyCompilationPlugin.js +1 -7
- package/lib/html/HtmlGenerator.js +25 -17
- package/lib/html/HtmlModule.js +11 -1
- package/lib/html/HtmlModulesPlugin.js +12 -11
- package/lib/html/HtmlParser.js +10 -9
- package/lib/html/builtinEmbeddedRenderer.js +31 -33
- package/lib/html/data.js +42 -21
- package/lib/html/htmlMinify.js +4 -1
- package/lib/html/syntax-parser.js +34 -35
- package/lib/html/syntax-printer.js +2 -3
- package/lib/html/xmlMinify.js +4 -1
- package/lib/ids/HashedModuleIdsPlugin.js +1 -1
- package/lib/index.js +13 -12
- package/lib/javascript/APIPlugin.js +7 -13
- package/lib/javascript/BasicEvaluatedExpression.js +5 -5
- package/lib/javascript/CommonJsChunkFormatPlugin.js +2 -7
- package/lib/javascript/ConstPlugin.js +1 -1
- package/lib/javascript/JavascriptGenerator.js +16 -11
- package/lib/javascript/JavascriptMetaInfoPlugin.js +3 -15
- package/lib/javascript/JavascriptModule.js +10 -0
- package/lib/javascript/JavascriptModulesPlugin.js +2 -4
- package/lib/javascript/JavascriptParser.js +99 -11
- package/lib/javascript/ScopeAnalyzer.js +183 -107
- package/lib/javascript/UseStrictPlugin.js +2 -5
- package/lib/javascript/jsMinify.js +1 -1
- package/lib/javascript/regexp.js +2 -2
- package/lib/javascript/syntax-parser.js +2114 -1506
- package/lib/javascript/syntax-printer-data.js +167 -1
- package/lib/javascript/syntax-printer.js +9554 -2782
- package/lib/json/JsonGenerator.js +15 -7
- package/lib/json/JsonModule.js +11 -0
- package/lib/json/JsonParser.js +7 -6
- package/lib/library/AssignLibraryPlugin.js +1 -1
- package/lib/library/JsonpLibraryPlugin.js +1 -1
- package/lib/library/ModuleLibraryPlugin.js +399 -23
- package/lib/loaders/LoaderRunner.js +76 -38
- package/lib/loaders/loadLoader.js +16 -10
- package/lib/module/Generator.js +17 -9
- package/lib/module/Module.js +2 -2
- package/lib/module/NormalModule.js +7 -5
- package/lib/module/NormalModuleFactory.js +11 -17
- package/lib/module/Parser.js +9 -2
- package/lib/module/extractSourceMap.js +69 -18
- package/lib/module/findCaseMismatch.js +2 -2
- package/lib/node/ExternalModuleInitFragment.js +2 -2
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +4 -12
- package/lib/node/ReadFileCompileWasmPlugin.js +2 -6
- package/lib/node/nodeConsole.js +4 -3
- package/lib/optimize/CircularModulesPlugin.js +1 -1
- package/lib/optimize/ConcatenatedModule.js +4 -8
- package/lib/optimize/FlagDependencyExportsPlugin.js +6 -11
- package/lib/optimize/FlagDependencyUsagePlugin.js +28 -3
- package/lib/optimize/InlineExports.js +4 -7
- package/lib/optimize/LazyBarrels.js +41 -5
- package/lib/optimize/ModuleConcatenationPlugin.js +8 -10
- package/lib/optimize/RealContentHashPlugin.js +2 -6
- package/lib/optimize/SideEffectsFlagPlugin.js +7 -21
- package/lib/optimize/SplitChunksPlugin.js +11 -24
- package/lib/optimize/TupleQueue.js +1 -1
- package/lib/output/CleanPlugin.js +3 -1
- package/lib/output/CopyPlugin.js +3 -4
- package/lib/performance/EvalUsagePlugin.js +1 -3
- package/lib/performance/HotspotsPlugin.js +33 -9
- package/lib/performance/PureAnnotationsPlugin.js +16 -10
- package/lib/performance/TopLevelThisPlugin.js +17 -9
- package/lib/prefetch/ResourceHintPlugin.js +1 -1
- package/lib/provide/ProvidedDependency.js +36 -4
- package/lib/rules/BasicEffectRulePlugin.js +1 -1
- package/lib/rules/RuleSetCompiler.js +15 -5
- package/lib/rules/UseEffectRulePlugin.js +1 -3
- package/lib/runtime/AsyncModuleRuntimeModule.js +7 -1
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +1 -5
- package/lib/runtime/RuntimeGlobals.js +6 -0
- package/lib/runtime/RuntimePlugin.js +2 -0
- package/lib/schemes/HttpUriPlugin.js +1 -2
- package/lib/schemes/VirtualUrlPlugin.js +3 -1
- package/lib/serialization/AggregateErrorSerializer.js +3 -3
- package/lib/serialization/BinaryMiddleware.js +4 -1
- package/lib/serialization/ErrorObjectSerializer.js +2 -5
- package/lib/serialization/ObjectMiddleware.js +17 -15
- package/lib/serialization/PlainObjectSerializer.js +3 -7
- package/lib/serialization/Serializer.js +21 -11
- package/lib/serialization/SerializerMiddleware.js +8 -6
- package/lib/stats/DefaultStatsFactoryPlugin.js +35 -35
- package/lib/stats/DefaultStatsPresetPlugin.js +2 -6
- package/lib/stats/DefaultStatsPrinterPlugin.js +12 -11
- package/lib/stats/StatsFactory.js +194 -24
- package/lib/stats/StatsPrinter.js +126 -13
- package/lib/template/DependencyTemplate.js +1 -1
- package/lib/template/RuntimeTemplate.js +13 -32
- package/lib/template/Template.js +1 -1
- package/lib/util/SourceProcessor.js +146 -29
- package/lib/util/WeakTupleMap.js +10 -4
- package/lib/util/cleverMerge.js +61 -33
- package/lib/util/comparators.js +9 -5
- package/lib/util/dataURL.js +2 -0
- package/lib/util/deprecation.js +4 -3
- package/lib/util/globUtils.js +1 -4
- package/lib/util/internalSerializables.js +2 -0
- package/lib/util/makeSerializable.js +6 -4
- package/lib/util/memoize.js +1 -1
- package/lib/util/parseJson.js +2 -2
- package/lib/util/serialization.js +31 -27
- package/lib/wasm/AsyncWasmModule.js +12 -2
- package/lib/wasm/AsyncWebAssemblyGenerator.js +12 -4
- package/lib/wasm/AsyncWebAssemblyJavascriptGenerator.js +13 -5
- package/lib/wasm/AsyncWebAssemblyParser.js +7 -5
- package/lib/wasm/UniversalCompileAsyncWasmPlugin.js +2 -6
- package/lib/wasm-sync/SyncWasmModule.js +16 -1
- package/lib/wasm-sync/WebAssemblyGenerator.js +15 -10
- package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +13 -5
- package/lib/wasm-sync/WebAssemblyParser.js +10 -12
- package/lib/web/FetchCompileAsyncWasmPlugin.js +4 -12
- package/lib/web/FetchCompileWasmPlugin.js +2 -6
- package/package.json +18 -19
- package/schemas/WebpackOptions.check.js +3 -3
- package/schemas/WebpackOptions.json +2 -2
- package/schemas/plugins/BannerPlugin.check.js +3 -3
- package/schemas/plugins/HtmlGeneratorOptions.check.js +3 -3
- package/schemas/plugins/HtmlParserOptions.check.js +3 -3
- package/schemas/plugins/IgnorePlugin.check.js +3 -3
- package/schemas/plugins/LoaderOptionsPlugin.check.js +3 -3
- package/schemas/plugins/ManifestPlugin.check.js +3 -3
- package/schemas/plugins/ProgressPlugin.check.js +3 -3
- package/schemas/plugins/SSRManifestPlugin.check.js +3 -3
- package/schemas/plugins/SourceMapDevToolPlugin.check.js +3 -3
- package/schemas/plugins/WatchIgnorePlugin.check.js +3 -3
- package/schemas/plugins/asset/AssetGeneratorOptions.check.js +3 -3
- package/schemas/plugins/asset/AssetInlineGeneratorOptions.check.js +3 -3
- package/schemas/plugins/asset/AssetParserOptions.check.js +3 -3
- package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +3 -3
- package/schemas/plugins/container/ContainerPlugin.check.js +3 -3
- package/schemas/plugins/container/ContainerReferencePlugin.check.js +3 -3
- package/schemas/plugins/container/ExternalsType.check.js +3 -3
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +3 -3
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +3 -3
- package/schemas/plugins/css/CssGeneratorOptions.check.js +3 -3
- package/schemas/plugins/css/CssModuleGeneratorOptions.check.js +3 -3
- package/schemas/plugins/css/CssModuleParserOptions.check.js +3 -3
- package/schemas/plugins/css/CssParserOptions.check.js +3 -3
- package/schemas/plugins/debug/ProfilingPlugin.check.js +3 -3
- package/schemas/plugins/define/DotenvPluginOptions.check.js +7 -0
- package/schemas/plugins/define/DotenvPluginOptions.json +3 -0
- package/schemas/plugins/dll/DllPlugin.check.js +3 -3
- package/schemas/plugins/dll/DllReferencePlugin.check.js +3 -3
- package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +3 -3
- package/schemas/plugins/ids/OccurrenceChunkIdsPlugin.check.js +3 -3
- package/schemas/plugins/ids/OccurrenceModuleIdsPlugin.check.js +3 -3
- package/schemas/plugins/json/JsonModulesPluginGenerator.check.js +3 -3
- package/schemas/plugins/json/JsonModulesPluginParser.check.js +3 -3
- package/schemas/plugins/optimize/AggressiveSplittingPlugin.check.js +3 -3
- package/schemas/plugins/optimize/LimitChunkCountPlugin.check.js +3 -3
- package/schemas/plugins/optimize/MergeDuplicateChunksPlugin.check.js +3 -3
- package/schemas/plugins/optimize/MinChunkSizePlugin.check.js +3 -3
- package/schemas/plugins/output/CleanOptions.check.js +7 -0
- package/schemas/plugins/output/CleanOptions.json +3 -0
- package/schemas/plugins/schemes/HttpUriPlugin.check.js +3 -3
- package/schemas/plugins/schemes/VirtualUrlPlugin.check.js +3 -3
- package/schemas/plugins/sharing/ConsumeSharedPlugin.check.js +3 -3
- package/schemas/plugins/sharing/ProvideSharedPlugin.check.js +3 -3
- package/schemas/plugins/sharing/SharePlugin.check.js +3 -3
- package/types.d.ts +1482 -358
package/lib/css/syntax-parser.js
CHANGED
|
@@ -285,6 +285,8 @@ const CC_LOW_LINE = "_".charCodeAt(0);
|
|
|
285
285
|
const CC_LOWER_A = "a".charCodeAt(0);
|
|
286
286
|
const CC_LOWER_D = "d".charCodeAt(0);
|
|
287
287
|
const CC_LOWER_F = "f".charCodeAt(0);
|
|
288
|
+
const CC_LOWER_I = "i".charCodeAt(0);
|
|
289
|
+
const CC_LOWER_S = "s".charCodeAt(0);
|
|
288
290
|
const CC_LOWER_E = "e".charCodeAt(0);
|
|
289
291
|
const CC_LOWER_U = "u".charCodeAt(0);
|
|
290
292
|
const CC_LOWER_R = "r".charCodeAt(0);
|
|
@@ -1790,13 +1792,13 @@ const _NO_SKIP_TYPES = new Uint8Array(32);
|
|
|
1790
1792
|
// rules, the common case. Every consumer reads these lists read-only, so one
|
|
1791
1793
|
// instance replaces an empty array per rule, and freezing makes a push fail.
|
|
1792
1794
|
const _EMPTY_LIST = /** @type {Rule[]} */ (
|
|
1793
|
-
/** @type {
|
|
1795
|
+
/** @type {Rule[] | ReadonlyArray<never>} */ (Object.freeze([]))
|
|
1794
1796
|
);
|
|
1795
1797
|
// `consumeABlock`'s and `consumeABlocksContentsInto`'s results, written instead
|
|
1796
1798
|
// of returned — see there. Read them immediately; the next block overwrites them.
|
|
1797
1799
|
/** @type {Declaration[]} */
|
|
1798
1800
|
let _blockDecls = /** @type {Declaration[]} */ (
|
|
1799
|
-
/** @type {
|
|
1801
|
+
/** @type {Declaration[] | Rule[]} */ (_EMPTY_LIST)
|
|
1800
1802
|
);
|
|
1801
1803
|
/** @type {Rule[]} */
|
|
1802
1804
|
let _blockRules = _EMPTY_LIST;
|
|
@@ -1804,7 +1806,7 @@ let _blockStart = 0;
|
|
|
1804
1806
|
let _blockEnd = 0;
|
|
1805
1807
|
/** @type {Declaration[]} */
|
|
1806
1808
|
let _bcDecls = /** @type {Declaration[]} */ (
|
|
1807
|
-
/** @type {
|
|
1809
|
+
/** @type {Declaration[] | Rule[]} */ (_EMPTY_LIST)
|
|
1808
1810
|
);
|
|
1809
1811
|
/** @type {Rule[]} */
|
|
1810
1812
|
let _bcRules = _EMPTY_LIST;
|
|
@@ -1888,7 +1890,9 @@ let _declBodies = [];
|
|
|
1888
1890
|
/** @type {Node[][]} */
|
|
1889
1891
|
let _ruleBodies = [];
|
|
1890
1892
|
|
|
1891
|
-
let _locConverter = /** @type {LocConverter} */ (
|
|
1893
|
+
let _locConverter = /** @type {LocConverter} */ (
|
|
1894
|
+
/** @type {LocConverter | null} */ (null)
|
|
1895
|
+
);
|
|
1892
1896
|
// The caller's input before §3.3 replaced its NULs, the same length, which
|
|
1893
1897
|
// `A.source` slices so a node reads exactly as written.
|
|
1894
1898
|
let _originalInput = "";
|
|
@@ -1896,9 +1900,10 @@ let _originalInput = "";
|
|
|
1896
1900
|
// Node refs are integers here but typed `Node` across the parser; these are
|
|
1897
1901
|
// identity casts that just satisfy the type system at the boundary.
|
|
1898
1902
|
/** @type {(n: Node) => number} */
|
|
1899
|
-
const _nodeIndex = (n) =>
|
|
1903
|
+
const _nodeIndex = (n) =>
|
|
1904
|
+
/** @type {number} */ (/** @type {number | Node} */ (n));
|
|
1900
1905
|
/** @type {(i: number) => Node} */
|
|
1901
|
-
const _nodeRef = (i) => /** @type {Node} */ (/** @type {
|
|
1906
|
+
const _nodeRef = (i) => /** @type {Node} */ (/** @type {Node | number} */ (i));
|
|
1902
1907
|
|
|
1903
1908
|
/** @param {number} need minimum capacity */
|
|
1904
1909
|
const _grow = (need) => {
|
|
@@ -2105,6 +2110,16 @@ const _setValue = (r, list) => {
|
|
|
2105
2110
|
}
|
|
2106
2111
|
_listPool.push(list);
|
|
2107
2112
|
};
|
|
2113
|
+
/**
|
|
2114
|
+
* Empties the body arrays between top-level nodes by popping: `length = 0`
|
|
2115
|
+
* drops the backing store, so the next rule's first push reallocates it.
|
|
2116
|
+
*/
|
|
2117
|
+
const _dropBodies = () => {
|
|
2118
|
+
for (let k = _declBodies.length; k > 0; k--) {
|
|
2119
|
+
_declBodies.pop();
|
|
2120
|
+
_ruleBodies.pop();
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2108
2123
|
/** @type {(r: Node, decls: Node[], childRules: Node[]) => void} */
|
|
2109
2124
|
const _setBody = (r, decls, childRules) => {
|
|
2110
2125
|
_bodyIdx[_nodeIndex(r)] = _declBodies.length + 1;
|
|
@@ -2583,7 +2598,7 @@ const normalizeIntoTokenStream = (input, pos, onComment) => {
|
|
|
2583
2598
|
// Shared frozen empty list for a block body with no declarations / child rules,
|
|
2584
2599
|
// so equal-empty reads return one reference (mirrors `_EMPTY_LIST`).
|
|
2585
2600
|
const _READER_EMPTY = /** @type {Rule[]} */ (
|
|
2586
|
-
/** @type {
|
|
2601
|
+
/** @type {Rule[] | ReadonlyArray<never>} */ (Object.freeze([]))
|
|
2587
2602
|
);
|
|
2588
2603
|
|
|
2589
2604
|
/**
|
|
@@ -2651,7 +2666,7 @@ const _readRefList = (store, list) => {
|
|
|
2651
2666
|
// that reader's `_store` snapshot by `_i`. Created once rather than per store,
|
|
2652
2667
|
// so all readers share one hidden map and stay monomorphic across parses.
|
|
2653
2668
|
const NODE_READER_PROTO = /** @type {NodeReader} */ ({
|
|
2654
|
-
_store: /** @type {NodeStore} */ (/** @type {
|
|
2669
|
+
_store: /** @type {NodeStore} */ (/** @type {NodeStore | null} */ (null)),
|
|
2655
2670
|
_i: 0,
|
|
2656
2671
|
get type() {
|
|
2657
2672
|
return this._store.types[this._i];
|
|
@@ -2880,7 +2895,9 @@ const _finishStore = () => {
|
|
|
2880
2895
|
_ruleBodies = [];
|
|
2881
2896
|
_listPool.length = 0;
|
|
2882
2897
|
_S._input = "";
|
|
2883
|
-
_locConverter = /** @type {LocConverter} */ (
|
|
2898
|
+
_locConverter = /** @type {LocConverter} */ (
|
|
2899
|
+
/** @type {LocConverter | null} */ (null)
|
|
2900
|
+
);
|
|
2884
2901
|
return store;
|
|
2885
2902
|
};
|
|
2886
2903
|
|
|
@@ -3271,8 +3288,8 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
3271
3288
|
// two non-whitespace tokens the `--foo: {` disambiguation below needs.
|
|
3272
3289
|
const skip = _skipSelectorPrelude;
|
|
3273
3290
|
// Non-skip: first two non-whitespace prelude nodes (computed at the `{`).
|
|
3274
|
-
let first = /** @type {Node} */ (/** @type {
|
|
3275
|
-
let second = /** @type {Node} */ (/** @type {
|
|
3291
|
+
let first = /** @type {Node} */ (/** @type {Node | number} */ (0));
|
|
3292
|
+
let second = /** @type {Node} */ (/** @type {Node | number} */ (0));
|
|
3276
3293
|
// Skip mode: the same two tokens tracked by token type + start, so a dropped
|
|
3277
3294
|
// leaf selector token needs no materialized node just for the disambiguation.
|
|
3278
3295
|
// `0` (no token type) = unset.
|
|
@@ -4445,7 +4462,7 @@ const TOP_LEVEL_CONSUMERS = {
|
|
|
4445
4462
|
* @property {boolean=} convertLengthUnits rewrite a length into a shorter unit it is exactly equal in (`16px` -> `1pc`); off by default because it earns nothing once the asset is compressed, and only read while printing. A time is always rewritten
|
|
4446
4463
|
* @property {boolean=} convertApproximateColors write a polar or Lab color as the nearest hex even where that hex only approximates it: a channel too near a `.5` boundary for two engines to round it alike, and a color outside the sRGB gamut, which hex can only clip. Off by default, and only read while printing; a space engines read through transfers of their own (`a98-rgb`, `prophoto-rgb`) is left alone either way
|
|
4447
4464
|
* @property {boolean=} mergeDistantRules give a rule the selectors of a later one printing the same block, and an at-rule the block of a later one stating the same condition, past the rules between them; off by default, since it reorders the cascade and is sound only where nothing between declares what the block being moved does for an element it can match — a condition between counting for what its own rules declare
|
|
4448
|
-
* @property {boolean=} dropOverriddenDeclarations drop a declaration a later one in the same block overrides even where
|
|
4465
|
+
* @property {boolean=} dropOverriddenDeclarations drop a declaration a later one in the same block overrides even where no target is selected, wherever Chromium, Gecko and WebKit each read the later value; off by default, and only read while printing. With a `browserslist` selection the drop is already made wherever every browser it names is known to read the later value, so this widens the case where no target is selected — a selection naming a browser the tables do not cover still answers for the whole of it
|
|
4449
4466
|
* @property {boolean=} rewriteCustomProperties shorten a custom property's value the way any other value is shortened (`--x:#ffffff` -> `#fff`); off by default because `getPropertyValue()` hands that text back, and only read while printing. What it may rewrite is what any other value's tokens may be, a color in a substitution's fallback included — that being the property's value rather than the function's own argument
|
|
4450
4467
|
* @property {EmbeddedSourceRenderer=} renderEmbeddedSource renders source this stylesheet embeds: the payload of a `url()` `data:` URL whose media type names a language webpack knows (SVG, CSS, HTML, JSON, JavaScript). Absent, a data URL is emitted exactly as written
|
|
4451
4468
|
* @property {{ [name: string]: string }=} pseudoClasses each pseudo-class to write as a class instead (`{ "focus-visible": "focus-visible" }`), so a script can apply it where the engine does not. Only read while printing
|
|
@@ -4475,8 +4492,38 @@ const TOP_LEVEL_CONSUMERS = {
|
|
|
4475
4492
|
// recursive call sites monomorphic and allocates no closure per parse.
|
|
4476
4493
|
/** @typedef {import("../util/SourceProcessor").CompiledVisitorBucket<CssPath>} CompiledVisitorBucket */
|
|
4477
4494
|
/** @type {CompiledVisitorMap} */
|
|
4478
|
-
let _visitors = /** @type {CompiledVisitorMap} */ (
|
|
4495
|
+
let _visitors = /** @type {CompiledVisitorMap} */ (
|
|
4496
|
+
/** @type {CompiledVisitorMap | never[]} */ ([])
|
|
4497
|
+
);
|
|
4479
4498
|
let _recurseBlocks = true;
|
|
4499
|
+
// WHY: a block of declarations alone prints the same wherever its source text
|
|
4500
|
+
// and the few rules read around it are the same, and half the blocks a sheet
|
|
4501
|
+
// holds are written again; each body is composed once and handed back after.
|
|
4502
|
+
/** @type {Map<string, string> | null} */
|
|
4503
|
+
let _blockMemo = null;
|
|
4504
|
+
/** @type {Node | null} the rule `_blockMemoBody` was found for */
|
|
4505
|
+
let _blockMemoNode = null;
|
|
4506
|
+
/** @type {string | undefined} the body found for it, undefined to compose one */
|
|
4507
|
+
let _blockMemoBody;
|
|
4508
|
+
/** @type {string} the key a miss stores its body under, "" for none */
|
|
4509
|
+
let _blockMemoKey = "";
|
|
4510
|
+
// The tables a block is looked up in, one per what encloses it: the top level's
|
|
4511
|
+
// is `_blockMemo`. `_blockMemoTable` is the last one keyed, and
|
|
4512
|
+
// `_blockMemoKeyTable` the one `_blockMemoKey` belongs to.
|
|
4513
|
+
/** @type {Map<string, Map<string, string>>} */
|
|
4514
|
+
const _blockMemoScopes = new Map();
|
|
4515
|
+
/** @type {Map<string, string>} */
|
|
4516
|
+
let _blockMemoTable = new Map();
|
|
4517
|
+
/** @type {Map<string, string>} */
|
|
4518
|
+
let _blockMemoKeyTable = _blockMemoTable;
|
|
4519
|
+
// The same for one declaration, where its block is not one written before.
|
|
4520
|
+
/** @type {Map<string, string> | null} */
|
|
4521
|
+
let _declarationMemo = null;
|
|
4522
|
+
/** @type {string} the key the declaration being walked stores its text under */
|
|
4523
|
+
let _declarationMemoKey = "";
|
|
4524
|
+
// The declaration memo's keys whose value is one component.
|
|
4525
|
+
/** @type {Set<string> | null} */
|
|
4526
|
+
let _singleValueKeys = null;
|
|
4480
4527
|
// Printing needs a faithful serialization, so it keeps dropped input as `NodeType.Raw`
|
|
4481
4528
|
// nodes; a walk-only parse leaves this false and allocates none.
|
|
4482
4529
|
let _printing = false;
|
|
@@ -4518,7 +4565,7 @@ const _grammarOnComment = (_src, start, end) => {
|
|
|
4518
4565
|
_S._currentNode = node;
|
|
4519
4566
|
_currentParent = null;
|
|
4520
4567
|
_currentIndex = 0;
|
|
4521
|
-
const bucket =
|
|
4568
|
+
const bucket = _commentBucket;
|
|
4522
4569
|
const e = bucket.enter;
|
|
4523
4570
|
for (let i = 0; i < e.length; i++) e[i](A);
|
|
4524
4571
|
const x = bucket.exit;
|
|
@@ -4572,6 +4619,76 @@ const _mayBeMathFunction = (start) => {
|
|
|
4572
4619
|
return (_MATH_NAME_FIRST_LETTERS & (1 << (first - 0x61))) !== 0;
|
|
4573
4620
|
};
|
|
4574
4621
|
|
|
4622
|
+
// The leaves the CSS printer writes as their source text, whitespace as `_SEP`
|
|
4623
|
+
// and an identifier or hash outside a value (`_printVerbatimLeaf`).
|
|
4624
|
+
const _VERBATIM_LEAVES = new Uint8Array(32);
|
|
4625
|
+
_VERBATIM_LEAVES[NodeType.Whitespace] = 1;
|
|
4626
|
+
_VERBATIM_LEAVES[NodeType.Delim] = 1;
|
|
4627
|
+
_VERBATIM_LEAVES[NodeType.Comma] = 1;
|
|
4628
|
+
_VERBATIM_LEAVES[NodeType.Colon] = 1;
|
|
4629
|
+
_VERBATIM_LEAVES[NodeType.Ident] = 1;
|
|
4630
|
+
_VERBATIM_LEAVES[NodeType.Hash] = 1;
|
|
4631
|
+
/** @type {EXPECTED_FUNCTION | null} */
|
|
4632
|
+
let _verbatimLeafPrinter = null;
|
|
4633
|
+
|
|
4634
|
+
/**
|
|
4635
|
+
* Name the printer that writes `_VERBATIM_LEAVES` as their source text, so a
|
|
4636
|
+
* walk printing through it stores their text without calling it.
|
|
4637
|
+
* @param {EXPECTED_FUNCTION} printer the CSS printer
|
|
4638
|
+
* @returns {void}
|
|
4639
|
+
*/
|
|
4640
|
+
const setVerbatimLeafPrinter = (printer) => {
|
|
4641
|
+
_verbatimLeafPrinter = printer;
|
|
4642
|
+
};
|
|
4643
|
+
|
|
4644
|
+
/**
|
|
4645
|
+
* Store what the CSS printer writes for a `_VERBATIM_LEAVES` node, without a
|
|
4646
|
+
* call through it: they are half the nodes a stylesheet holds. Most print as
|
|
4647
|
+
* their source, which the writer reads only once something asks for it.
|
|
4648
|
+
* @param {number} ty the node's type
|
|
4649
|
+
* @param {Node} node the node
|
|
4650
|
+
* @param {Node | null} parent its parent
|
|
4651
|
+
* @param {PrintContext} writer the print context
|
|
4652
|
+
* @returns {boolean} false to call the printer instead
|
|
4653
|
+
*/
|
|
4654
|
+
const _printVerbatimLeaf = (ty, node, parent, writer) => {
|
|
4655
|
+
if (ty === NodeType.Whitespace) {
|
|
4656
|
+
writer.printedAs(node, _SEP);
|
|
4657
|
+
return true;
|
|
4658
|
+
}
|
|
4659
|
+
// An identifier is a keyword and a hash a color the printer may rewrite only
|
|
4660
|
+
// in a value, and their escapes are respelled anywhere.
|
|
4661
|
+
const ident = ty === NodeType.Ident || ty === NodeType.Hash;
|
|
4662
|
+
if (ident && parent !== null && _within[NodeType.Declaration] !== 0) {
|
|
4663
|
+
return false;
|
|
4664
|
+
}
|
|
4665
|
+
const i = _nodeIndex(node);
|
|
4666
|
+
const start = _starts[i];
|
|
4667
|
+
const end = _ends[i];
|
|
4668
|
+
// The end of the input is where `_printedSource` resolves a trailing escape.
|
|
4669
|
+
if (end === _S._input.length) {
|
|
4670
|
+
const text = _printedSource(node);
|
|
4671
|
+
if (ident) {
|
|
4672
|
+
if (text.includes("\\")) return false;
|
|
4673
|
+
writer.printedAs(node, text);
|
|
4674
|
+
} else {
|
|
4675
|
+
writer.printedAs(node, text === "\\" ? "\\\n" : text);
|
|
4676
|
+
}
|
|
4677
|
+
return true;
|
|
4678
|
+
}
|
|
4679
|
+
if (ident) {
|
|
4680
|
+
if (_rangeHasEscape(start, end)) return false;
|
|
4681
|
+
} else if (
|
|
4682
|
+
end - start === 1 &&
|
|
4683
|
+
_S._input.charCodeAt(start) === CC_REVERSE_SOLIDUS
|
|
4684
|
+
) {
|
|
4685
|
+
writer.printedAs(node, "\\\n");
|
|
4686
|
+
return true;
|
|
4687
|
+
}
|
|
4688
|
+
writer.printedSource(node, _printedSource);
|
|
4689
|
+
return true;
|
|
4690
|
+
};
|
|
4691
|
+
|
|
4575
4692
|
/**
|
|
4576
4693
|
* Walk a component-value subtree; children are already materialized. Fetches
|
|
4577
4694
|
* the node's visitor bucket once (reused for enter + exit) and uses index
|
|
@@ -4665,7 +4782,15 @@ const _walkValue = (node, parent, index, writer) => {
|
|
|
4665
4782
|
_within[ty]--;
|
|
4666
4783
|
_S._inColorOperand = previousColorOperand;
|
|
4667
4784
|
}
|
|
4668
|
-
|
|
4785
|
+
if (
|
|
4786
|
+
b !== undefined ||
|
|
4787
|
+
writer === undefined ||
|
|
4788
|
+
_VERBATIM_LEAVES[ty] === 0 ||
|
|
4789
|
+
writer._printer !== _verbatimLeafPrinter ||
|
|
4790
|
+
!_printVerbatimLeaf(ty, node, parent, writer)
|
|
4791
|
+
) {
|
|
4792
|
+
_exitNode(node, parent, index, b, writer);
|
|
4793
|
+
}
|
|
4669
4794
|
if (enteredMath) _S._mathFunctionDepth--;
|
|
4670
4795
|
if (enteredStepped) _S._steppedFunctionDepth--;
|
|
4671
4796
|
if (enteredSupports) {
|
|
@@ -4871,6 +4996,74 @@ const _enterAtRulePrelude = (i0) => {
|
|
|
4871
4996
|
}
|
|
4872
4997
|
};
|
|
4873
4998
|
|
|
4999
|
+
/**
|
|
5000
|
+
* The key a declaration prints under: its source, with the at-rules that read
|
|
5001
|
+
* one as a descriptor.
|
|
5002
|
+
* @param {Node} node the declaration
|
|
5003
|
+
* @param {number} i0 its node index
|
|
5004
|
+
* @returns {string} the key, "" where it is not one to reuse
|
|
5005
|
+
*/
|
|
5006
|
+
const _declarationMemoKeyOf = (node, i0) => {
|
|
5007
|
+
const start = _starts[i0];
|
|
5008
|
+
const end = _ends[i0];
|
|
5009
|
+
const input = _S._input;
|
|
5010
|
+
if (end === input.length) return "";
|
|
5011
|
+
const text = input.slice(start, end);
|
|
5012
|
+
// A comment kept in the value is written where it stands, once.
|
|
5013
|
+
if (text.includes("/*")) return "";
|
|
5014
|
+
// Bare where no descriptor at-rule is around, which no other key can equal:
|
|
5015
|
+
// those start with a letter naming one, and no source holds a NUL.
|
|
5016
|
+
return !_S._inPropertyRule && !_S._inFunctionRule && !_S._inFeatureValuesRule
|
|
5017
|
+
? text
|
|
5018
|
+
: `${_S._inPropertyRule ? "p" : ""}${_S._inFunctionRule ? "f" : ""}${
|
|
5019
|
+
_S._inFeatureValuesRule ? "v" : ""
|
|
5020
|
+
}\0${text}`;
|
|
5021
|
+
};
|
|
5022
|
+
|
|
5023
|
+
/**
|
|
5024
|
+
* The key a qualified rule's block of declarations is composed under: its
|
|
5025
|
+
* source, with what the walk around it decides of how it prints.
|
|
5026
|
+
* @param {number} i0 the rule's node index
|
|
5027
|
+
* @param {Node | null} parent the node it sits in
|
|
5028
|
+
* @returns {string} the key, "" where the block is not one to reuse
|
|
5029
|
+
*/
|
|
5030
|
+
const _blockMemoKeyOf = (i0, parent) => {
|
|
5031
|
+
const start = _aux1[i0];
|
|
5032
|
+
const end = _ends[i0];
|
|
5033
|
+
const input = _S._input;
|
|
5034
|
+
if (start === -1 || end === input.length) return "";
|
|
5035
|
+
// A comment kept in the block is written where it stands, once.
|
|
5036
|
+
const block = input.slice(start, end);
|
|
5037
|
+
if (block.includes("/*")) return "";
|
|
5038
|
+
let around = "";
|
|
5039
|
+
if (parent !== null) {
|
|
5040
|
+
const p = _nodeIndex(parent);
|
|
5041
|
+
if (_types[p] !== NodeType.AtRule) return "";
|
|
5042
|
+
around = input.slice(_starts[p], _aux0[p]);
|
|
5043
|
+
}
|
|
5044
|
+
// What is around it picks the table, so the key is the block alone and no
|
|
5045
|
+
// copy of it is built per lookup.
|
|
5046
|
+
if (
|
|
5047
|
+
around.length === 0 &&
|
|
5048
|
+
!_S._inPropertyRule &&
|
|
5049
|
+
!_S._inFunctionRule &&
|
|
5050
|
+
!_S._inFeatureValuesRule
|
|
5051
|
+
) {
|
|
5052
|
+
_blockMemoTable = /** @type {Map<string, string>} */ (_blockMemo);
|
|
5053
|
+
return block;
|
|
5054
|
+
}
|
|
5055
|
+
const scope = `${_S._inPropertyRule ? "p" : ""}${_S._inFunctionRule ? "f" : ""}${
|
|
5056
|
+
_S._inFeatureValuesRule ? "v" : ""
|
|
5057
|
+
}${around}`;
|
|
5058
|
+
let table = _blockMemoScopes.get(scope);
|
|
5059
|
+
if (table === undefined) {
|
|
5060
|
+
table = new Map();
|
|
5061
|
+
_blockMemoScopes.set(scope, table);
|
|
5062
|
+
}
|
|
5063
|
+
_blockMemoTable = table;
|
|
5064
|
+
return block;
|
|
5065
|
+
};
|
|
5066
|
+
|
|
4874
5067
|
/**
|
|
4875
5068
|
* Walk a structural subtree; an at-rule / qualified-rule's block was parsed
|
|
4876
5069
|
* eagerly (§5.4.4), so its `value` holds the nested rules / declarations. When
|
|
@@ -4920,9 +5113,24 @@ const _walkRule = (node, parent, index, writer) => {
|
|
|
4920
5113
|
const bi = _bodyIdx[i0];
|
|
4921
5114
|
if (bi !== 0) {
|
|
4922
5115
|
const decls = _declBodies[bi - 1];
|
|
4923
|
-
|
|
4924
|
-
|
|
5116
|
+
const key =
|
|
5117
|
+
_blockMemo !== null &&
|
|
5118
|
+
ty === NodeType.QualifiedRule &&
|
|
5119
|
+
decls.length !== 0 &&
|
|
5120
|
+
_ruleBodies[bi - 1].length === 0
|
|
5121
|
+
? _blockMemoKeyOf(i0, parent)
|
|
5122
|
+
: "";
|
|
5123
|
+
const table = _blockMemoTable;
|
|
5124
|
+
const memo = key === "" ? undefined : table.get(key);
|
|
5125
|
+
if (memo === undefined) {
|
|
5126
|
+
for (let i = 0; i < decls.length; i++) {
|
|
5127
|
+
_walkRule(decls[i], node, i, writer);
|
|
5128
|
+
}
|
|
4925
5129
|
}
|
|
5130
|
+
_blockMemoNode = node;
|
|
5131
|
+
_blockMemoBody = memo;
|
|
5132
|
+
_blockMemoKey = memo === undefined ? key : "";
|
|
5133
|
+
_blockMemoKeyTable = table;
|
|
4926
5134
|
const ch = _ruleBodies[bi - 1];
|
|
4927
5135
|
for (let i = 0; i < ch.length; i++) _walkRule(ch[i], node, i, writer);
|
|
4928
5136
|
}
|
|
@@ -4931,6 +5139,28 @@ const _walkRule = (node, parent, index, writer) => {
|
|
|
4931
5139
|
_S._inFunctionRule = prevFunction;
|
|
4932
5140
|
_S._inFeatureValuesRule = prevFeatureValues;
|
|
4933
5141
|
} else if (ty === NodeType.Declaration) {
|
|
5142
|
+
const key =
|
|
5143
|
+
_declarationMemo !== null && b === undefined
|
|
5144
|
+
? _declarationMemoKeyOf(node, i0)
|
|
5145
|
+
: "";
|
|
5146
|
+
if (key !== "") {
|
|
5147
|
+
const memo = /** @type {Map<string, string>} */ (_declarationMemo).get(
|
|
5148
|
+
key
|
|
5149
|
+
);
|
|
5150
|
+
if (memo !== undefined) {
|
|
5151
|
+
_within[ty]--;
|
|
5152
|
+
// Its value's tokens print no text, so a box merge reads what it
|
|
5153
|
+
// needs of them off the flags.
|
|
5154
|
+
if (_mayBeMergeableName(node)) {
|
|
5155
|
+
_flags[i0] |= /** @type {Set<string>} */ (_singleValueKeys).has(key)
|
|
5156
|
+
? _FLAG_MEMO_PRINTED | _FLAG_SINGLE_VALUE
|
|
5157
|
+
: _FLAG_MEMO_PRINTED;
|
|
5158
|
+
}
|
|
5159
|
+
/** @type {PrintContext} */ (writer).printedAs(node, memo);
|
|
5160
|
+
return;
|
|
5161
|
+
}
|
|
5162
|
+
_declarationMemoKey = key;
|
|
5163
|
+
}
|
|
4934
5164
|
const vs = _listStarts[i0];
|
|
4935
5165
|
const ve = vs + _listLens[i0];
|
|
4936
5166
|
// A declaration's value is a value context (its hashes are colors, not
|
|
@@ -4978,6 +5208,47 @@ const _walkRule = (node, parent, index, writer) => {
|
|
|
4978
5208
|
_within[ty]--;
|
|
4979
5209
|
}
|
|
4980
5210
|
_exitNode(node, parent, index, b, writer);
|
|
5211
|
+
if (_declarationMemoKey !== "") {
|
|
5212
|
+
const context = /** @type {PrintContext} */ (writer);
|
|
5213
|
+
const printed = context.get(node);
|
|
5214
|
+
if (!_mayBeMergeableName(node)) {
|
|
5215
|
+
/** @type {Map<string, string>} */ (_declarationMemo).set(
|
|
5216
|
+
_declarationMemoKey,
|
|
5217
|
+
printed
|
|
5218
|
+
);
|
|
5219
|
+
} else if (printed.includes(":")) {
|
|
5220
|
+
/** @type {Map<string, string>} */ (_declarationMemo).set(
|
|
5221
|
+
_declarationMemoKey,
|
|
5222
|
+
printed
|
|
5223
|
+
);
|
|
5224
|
+
if (_valueComponents(A, node, context).length === 1) {
|
|
5225
|
+
/** @type {Set<string>} */ (_singleValueKeys).add(_declarationMemoKey);
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
_declarationMemoKey = "";
|
|
5229
|
+
}
|
|
5230
|
+
};
|
|
5231
|
+
|
|
5232
|
+
// What a declaration printed from the memo carries in its flags, past the
|
|
5233
|
+
// public `Flag` bits: that its value's tokens printed nothing, and whether that
|
|
5234
|
+
// value is one component, which is all a box merge reads of them.
|
|
5235
|
+
const _FLAG_MEMO_PRINTED = 8;
|
|
5236
|
+
const _FLAG_SINGLE_VALUE = 16;
|
|
5237
|
+
|
|
5238
|
+
/**
|
|
5239
|
+
* Whether a declaration's value is one component, as `_valueComponents` splits
|
|
5240
|
+
* it, answered off its flags where it printed from the memo.
|
|
5241
|
+
* @param {CssPath} path the accessor
|
|
5242
|
+
* @param {Node} node the declaration
|
|
5243
|
+
* @param {PrintContext} writer the print context (children's printed text)
|
|
5244
|
+
* @returns {boolean} true when it is one component
|
|
5245
|
+
*/
|
|
5246
|
+
const _isSingleValue = (path, node, writer) => {
|
|
5247
|
+
const flags = _flags[_nodeIndex(node)];
|
|
5248
|
+
if ((flags & _FLAG_MEMO_PRINTED) !== 0) {
|
|
5249
|
+
return (flags & _FLAG_SINGLE_VALUE) !== 0;
|
|
5250
|
+
}
|
|
5251
|
+
return _valueComponents(path, node, writer).length === 1;
|
|
4981
5252
|
};
|
|
4982
5253
|
|
|
4983
5254
|
// WHY: nested-block streaming.
|
|
@@ -5078,8 +5349,8 @@ const _frameChainKey = Array.from({ length: _STREAM_MAX_DEPTH }, () => null);
|
|
|
5078
5349
|
* travels outward — only `scope` is replaced, by one standing for more — so the
|
|
5079
5350
|
* collected and the streamed path split a rule the same way, which is what makes
|
|
5080
5351
|
* the two agree on what a duplicate is.
|
|
5081
|
-
* @typedef {{ scope: RuleScope, key: string, at: number, len: number }} RuleSpan
|
|
5082
|
-
* @typedef {{ prefix: string, rules: Map<string,
|
|
5352
|
+
* @typedef {{ scope: RuleScope, key: string, at: number, len: number, taken: TakenPiece | null }} RuleSpan
|
|
5353
|
+
* @typedef {{ prefix: string, rules: Map<string, RuleSpan>, selectors: Map<string, RuleSpan | RuleSpan[]>, inner: Map<string, RuleScope>, noShadow: boolean }} RuleScope
|
|
5083
5354
|
* @typedef {{ bodyAt: number, prelude: string, keyPrelude: string, qualified: boolean, spans: RuleSpan[] }} BlockSpans
|
|
5084
5355
|
*/
|
|
5085
5356
|
// A span no later copy is read against: it is there to be cut, not to key a
|
|
@@ -5090,14 +5361,108 @@ const UNKEYED = "";
|
|
|
5090
5361
|
/** @type {BlockSpans[]} */
|
|
5091
5362
|
const _blockSpans = [];
|
|
5092
5363
|
const _NO_RULE_SPANS = /** @type {RuleSpan[]} */ (
|
|
5093
|
-
/** @type {
|
|
5364
|
+
/** @type {RuleSpan[] | ReadonlyArray<never>} */ (Object.freeze([]))
|
|
5094
5365
|
);
|
|
5095
5366
|
const _NO_BLOCK_SPANS = /** @type {BlockSpans[]} */ (
|
|
5096
|
-
/** @type {
|
|
5367
|
+
/** @type {BlockSpans[] | ReadonlyArray<never>} */ (Object.freeze([]))
|
|
5097
5368
|
);
|
|
5098
5369
|
/**
|
|
5099
|
-
*
|
|
5370
|
+
* A top-level node's text as written out, which later rules cut dead rules from.
|
|
5371
|
+
* A cut is recorded against the text the piece last held, so a run of cuts builds
|
|
5372
|
+
* the text once, when something reads it, rather than once per cut.
|
|
5100
5373
|
*/
|
|
5374
|
+
class TakenPiece {
|
|
5375
|
+
/**
|
|
5376
|
+
* @param {number} piece the writer's index for it
|
|
5377
|
+
* @param {string} text the text it went out as
|
|
5378
|
+
* @param {number} empties its length with every rule it holds cut, less one, or -1
|
|
5379
|
+
*/
|
|
5380
|
+
constructor(piece, text, empties) {
|
|
5381
|
+
/** @type {number} */
|
|
5382
|
+
this.piece = piece;
|
|
5383
|
+
/** @type {RuleSpan[]} shared and empty until `_noteSpans` gives it its own */
|
|
5384
|
+
this.spans = _NO_RULE_SPANS;
|
|
5385
|
+
/** @type {number} */
|
|
5386
|
+
this.empties = empties;
|
|
5387
|
+
/** @type {string} */
|
|
5388
|
+
this._text = text;
|
|
5389
|
+
/** @type {number[] | null} the ranges of `_text` cut since, as sorted start, end pairs */
|
|
5390
|
+
this._cuts = null;
|
|
5391
|
+
/** @type {number} */
|
|
5392
|
+
this.length = text.length;
|
|
5393
|
+
}
|
|
5394
|
+
|
|
5395
|
+
/**
|
|
5396
|
+
* @returns {string} what the piece reads now
|
|
5397
|
+
*/
|
|
5398
|
+
get text() {
|
|
5399
|
+
const cuts = this._cuts;
|
|
5400
|
+
if (cuts !== null) {
|
|
5401
|
+
/** @type {string[]} */
|
|
5402
|
+
const parts = [];
|
|
5403
|
+
let from = 0;
|
|
5404
|
+
for (let i = 0; i < cuts.length; i += 2) {
|
|
5405
|
+
parts.push(this._text.slice(from, cuts[i]));
|
|
5406
|
+
from = cuts[i + 1];
|
|
5407
|
+
}
|
|
5408
|
+
parts.push(this._text.slice(from));
|
|
5409
|
+
this._text = parts.join("");
|
|
5410
|
+
this._cuts = null;
|
|
5411
|
+
}
|
|
5412
|
+
return this._text;
|
|
5413
|
+
}
|
|
5414
|
+
|
|
5415
|
+
/**
|
|
5416
|
+
* @param {string} value what the piece reads from now on
|
|
5417
|
+
*/
|
|
5418
|
+
set text(value) {
|
|
5419
|
+
this._text = value;
|
|
5420
|
+
this._cuts = null;
|
|
5421
|
+
this.length = value.length;
|
|
5422
|
+
}
|
|
5423
|
+
|
|
5424
|
+
/**
|
|
5425
|
+
* @param {number} at an offset in what the piece reads now
|
|
5426
|
+
* @returns {number} the code unit there, read without building the text
|
|
5427
|
+
*/
|
|
5428
|
+
charCodeAt(at) {
|
|
5429
|
+
return this._text.charCodeAt(this._uncut(at));
|
|
5430
|
+
}
|
|
5431
|
+
|
|
5432
|
+
/**
|
|
5433
|
+
* Take `len` code units out at `from`, offsets in what the piece reads now.
|
|
5434
|
+
* @param {number} from where the cut starts
|
|
5435
|
+
* @param {number} len how much it takes
|
|
5436
|
+
* @returns {void}
|
|
5437
|
+
*/
|
|
5438
|
+
cut(from, len) {
|
|
5439
|
+
const start = this._uncut(from);
|
|
5440
|
+
const end = this._uncut(from + len - 1) + 1;
|
|
5441
|
+
const cuts = this._cuts === null ? [] : this._cuts;
|
|
5442
|
+
// The ranges cut before inside this one are taken with it.
|
|
5443
|
+
let at = 0;
|
|
5444
|
+
while (at < cuts.length && cuts[at] < start) at += 2;
|
|
5445
|
+
let past = at;
|
|
5446
|
+
while (past < cuts.length && cuts[past + 1] <= end) past += 2;
|
|
5447
|
+
cuts.splice(at, past - at, start, end);
|
|
5448
|
+
this._cuts = cuts;
|
|
5449
|
+
this.length -= len;
|
|
5450
|
+
}
|
|
5451
|
+
|
|
5452
|
+
/**
|
|
5453
|
+
* @param {number} at an offset in what the piece reads now
|
|
5454
|
+
* @returns {number} the same offset in `_text`
|
|
5455
|
+
*/
|
|
5456
|
+
_uncut(at) {
|
|
5457
|
+
const cuts = this._cuts;
|
|
5458
|
+
if (cuts === null) return at;
|
|
5459
|
+
let offset = at;
|
|
5460
|
+
for (let i = 0; i < cuts.length && cuts[i] <= offset; i += 2) {
|
|
5461
|
+
offset += cuts[i + 1] - cuts[i];
|
|
5462
|
+
}
|
|
5463
|
+
return offset;
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5101
5466
|
// Every rule taken so far, sheet-wide, by what encloses it and then by the text
|
|
5102
5467
|
// it printed to — neither of which changes once written (see `RuleSpan`).
|
|
5103
5468
|
/** @type {Map<string, RuleScope> | null} */
|
|
@@ -5123,7 +5488,8 @@ const _ruleScopeFor = (prefix) => {
|
|
|
5123
5488
|
prefix,
|
|
5124
5489
|
rules: new Map(),
|
|
5125
5490
|
selectors: new Map(),
|
|
5126
|
-
inner: new Map()
|
|
5491
|
+
inner: new Map(),
|
|
5492
|
+
noShadow: _NO_SHADOW_SCOPE_RE.test(prefix)
|
|
5127
5493
|
};
|
|
5128
5494
|
scopes.set(prefix, scope);
|
|
5129
5495
|
}
|
|
@@ -5406,10 +5772,7 @@ const _streamEmitChild = (d, child) => {
|
|
|
5406
5772
|
const opener = _namedLayerOpener(text);
|
|
5407
5773
|
if (opener === null) {
|
|
5408
5774
|
if (_frameSeenLayers[d] !== null) {
|
|
5409
|
-
_forgetLayersWritten(
|
|
5410
|
-
/** @type {Map<string, SeenLayer>} */ (_frameSeenLayers[d]),
|
|
5411
|
-
text
|
|
5412
|
-
);
|
|
5775
|
+
_forgetLayersWritten(_frameSeenLayers[d], text);
|
|
5413
5776
|
}
|
|
5414
5777
|
} else {
|
|
5415
5778
|
let layers = _frameSeenLayers[d];
|
|
@@ -5855,7 +6218,7 @@ const _streamConsumeBlock = (ts, rule) => {
|
|
|
5855
6218
|
/** @type {HeldTopLevel | null} */
|
|
5856
6219
|
let _heldTopLevel = null;
|
|
5857
6220
|
|
|
5858
|
-
/** @typedef {{ taken: TakenPiece, span: RuleSpan, entry: RuleEntry, prelude: number,
|
|
6221
|
+
/** @typedef {{ taken: TakenPiece, span: RuleSpan, entry: RuleEntry, prelude: number, grown: boolean }} PriorTopLevel the top-level rule last written out, the piece it stands in, how far into it its block starts, and whether a later rule's selectors joined its list */
|
|
5859
6222
|
|
|
5860
6223
|
// The rule written out before the held one, kept so a join only its settled
|
|
5861
6224
|
// block qualifies for can still be made. Null when that was not one whole rule.
|
|
@@ -5943,18 +6306,23 @@ const _drainTopLevelSpans = (node) => {
|
|
|
5943
6306
|
const _topLevelSpans = (own, text) => {
|
|
5944
6307
|
if (own === undefined) return null;
|
|
5945
6308
|
if (own === _WHOLE_TEXT_SPANS) {
|
|
5946
|
-
return [
|
|
6309
|
+
return [
|
|
6310
|
+
{ scope: _rootScope(), key: text, at: 0, len: text.length, taken: null }
|
|
6311
|
+
];
|
|
5947
6312
|
}
|
|
5948
6313
|
/** @type {RuleSpan[]} */
|
|
5949
6314
|
const out = own.qualified
|
|
5950
|
-
? [{ scope: _rootScope(), key: text, at: 0, len: text.length }]
|
|
6315
|
+
? [{ scope: _rootScope(), key: text, at: 0, len: text.length, taken: null }]
|
|
5951
6316
|
: [];
|
|
5952
|
-
|
|
6317
|
+
const spans = own.spans;
|
|
6318
|
+
for (let i = 0; i < spans.length; i++) {
|
|
6319
|
+
const span = spans[i];
|
|
5953
6320
|
out.push({
|
|
5954
6321
|
scope: _enclosingRuleScope(span.scope, own.keyPrelude),
|
|
5955
6322
|
key: span.key,
|
|
5956
6323
|
at: own.bodyAt + span.at,
|
|
5957
|
-
len: span.len
|
|
6324
|
+
len: span.len,
|
|
6325
|
+
taken: null
|
|
5958
6326
|
});
|
|
5959
6327
|
}
|
|
5960
6328
|
return out;
|
|
@@ -5985,8 +6353,7 @@ const _takeDeduped = (writer, piece, text, spans, own, chain) => {
|
|
|
5985
6353
|
DROPPABLE_WHEN_EMPTY_AT_RULES.has(_atRuleName(own.prelude))))
|
|
5986
6354
|
? own.bodyAt
|
|
5987
6355
|
: -1;
|
|
5988
|
-
|
|
5989
|
-
const taken = { piece, text, spans: [], empties };
|
|
6356
|
+
const taken = new TakenPiece(piece, text, empties);
|
|
5990
6357
|
_noteSpans(writer, taken, spans, chain);
|
|
5991
6358
|
return taken;
|
|
5992
6359
|
};
|
|
@@ -6005,18 +6372,29 @@ const _noteSpans = (writer, taken, spans, chain) => {
|
|
|
6005
6372
|
// `taken.spans` holds, and one not yet pushed would keep its old offset.
|
|
6006
6373
|
for (const span of spans) {
|
|
6007
6374
|
if (chain.length !== 0) span.scope = _enclosingRuleScope(span.scope, chain);
|
|
6008
|
-
taken
|
|
6375
|
+
span.taken = taken;
|
|
6376
|
+
}
|
|
6377
|
+
// A copy its own size: a first push would give the list room for 17, and
|
|
6378
|
+
// the piece holds it until the sheet ends.
|
|
6379
|
+
if (taken.spans.length === 0) {
|
|
6380
|
+
taken.spans = [...spans];
|
|
6381
|
+
} else {
|
|
6382
|
+
for (const span of spans) taken.spans.push(span);
|
|
6009
6383
|
}
|
|
6010
6384
|
for (const span of spans) {
|
|
6011
6385
|
if (span.key === UNKEYED) continue;
|
|
6012
6386
|
const rules = span.scope.rules;
|
|
6013
6387
|
const before = rules.get(span.key);
|
|
6014
|
-
if (before !== undefined)
|
|
6015
|
-
|
|
6388
|
+
if (before !== undefined) {
|
|
6389
|
+
_cutSpan(writer, /** @type {TakenPiece} */ (before.taken), before);
|
|
6390
|
+
}
|
|
6391
|
+
rules.set(span.key, span);
|
|
6016
6392
|
}
|
|
6017
6393
|
// After every copy is taken back, so an earlier rule a cut here left flat is
|
|
6018
6394
|
// read as flat. A distant merge moves a block up past others, out of this order.
|
|
6019
6395
|
if (!_S._transforms.removeDeadRules || _mergingDistantRules) return;
|
|
6396
|
+
// TODO add an option to make this opt-in: over the CSS corpus it costs ~10% of
|
|
6397
|
+
// minify CPU for 248 of 2,456,527 gzip bytes.
|
|
6020
6398
|
for (const span of spans) {
|
|
6021
6399
|
if (span.key !== UNKEYED && span.len !== 0) {
|
|
6022
6400
|
_shadowEarlierRule(writer, taken, span);
|
|
@@ -6032,6 +6410,29 @@ const _ROLLBACK_RE = /^revert(?:-[a-z]+)?$/i;
|
|
|
6032
6410
|
// blocks are gathered into one without their rules noted, so none is there.
|
|
6033
6411
|
const _NO_SHADOW_SCOPE_RE = /@(?:-[a-z]+-)?keyframes|@layer/i;
|
|
6034
6412
|
|
|
6413
|
+
// What each prior `_shadowEarlierRule` compared against declares, with the text
|
|
6414
|
+
// that was read from, so a prior a later rule writes nothing of is passed over.
|
|
6415
|
+
/** @type {Map<RuleSpan, { text: string, names: Set<string> }>} */
|
|
6416
|
+
const _shadowDeclared = new Map();
|
|
6417
|
+
|
|
6418
|
+
/**
|
|
6419
|
+
* The names a printed flat rule's declarations write, as `_cascadeName` reads
|
|
6420
|
+
* them.
|
|
6421
|
+
* @param {string} rule the rule's printed text
|
|
6422
|
+
* @param {number} open where its `{` sits
|
|
6423
|
+
* @returns {Set<string>} the names
|
|
6424
|
+
*/
|
|
6425
|
+
const _declaredNames = (rule, open) => {
|
|
6426
|
+
/** @type {Set<string>} */
|
|
6427
|
+
const names = new Set();
|
|
6428
|
+
for (const declaration of _splitPrintedDeclarations(
|
|
6429
|
+
rule.slice(open + 1, -1)
|
|
6430
|
+
)) {
|
|
6431
|
+
if (declaration.indexOf(":") > 0) names.add(_cascadeName(declaration));
|
|
6432
|
+
}
|
|
6433
|
+
return names;
|
|
6434
|
+
};
|
|
6435
|
+
|
|
6035
6436
|
/**
|
|
6036
6437
|
* The declarations of a printed block body, split on the `;` between them; a
|
|
6037
6438
|
* nested block is a piece of its own.
|
|
@@ -6114,24 +6515,30 @@ const _readWherever = (values, earlier) => {
|
|
|
6114
6515
|
* @returns {void}
|
|
6115
6516
|
*/
|
|
6116
6517
|
const _shadowEarlierRule = (writer, taken, span) => {
|
|
6117
|
-
const
|
|
6118
|
-
const
|
|
6518
|
+
const whole = taken.text;
|
|
6519
|
+
const at = span.at;
|
|
6520
|
+
const brace = whole.indexOf("{", at);
|
|
6119
6521
|
// Only a qualified rule: an at-rule is no one cascade. One a later cut
|
|
6120
6522
|
// leaves flat is read as one, so every rule is noted, flat or not.
|
|
6121
6523
|
if (
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6524
|
+
brace <= at ||
|
|
6525
|
+
brace >= at + span.len ||
|
|
6526
|
+
whole.charCodeAt(at) === CC_AT_SIGN ||
|
|
6527
|
+
span.scope.noShadow
|
|
6125
6528
|
) {
|
|
6126
6529
|
return;
|
|
6127
6530
|
}
|
|
6128
|
-
const
|
|
6531
|
+
const open = brace - at;
|
|
6532
|
+
const selector = whole.slice(at, brace);
|
|
6129
6533
|
const selectors = span.scope.selectors;
|
|
6130
|
-
const
|
|
6131
|
-
|
|
6132
|
-
|
|
6534
|
+
const noted = selectors.get(selector);
|
|
6535
|
+
// A lone prior is held bare: almost every selector is written once.
|
|
6536
|
+
if (noted === undefined) {
|
|
6537
|
+
selectors.set(selector, span);
|
|
6133
6538
|
return;
|
|
6134
6539
|
}
|
|
6540
|
+
const priors = Array.isArray(noted) ? noted : [noted];
|
|
6541
|
+
const text = whole.slice(at, at + span.len);
|
|
6135
6542
|
// Per name, the later values and whether one every target reads is among
|
|
6136
6543
|
// them, and whether one of those is `!important`.
|
|
6137
6544
|
/** @type {Map<string, { values: Set<string>, read: boolean, important: boolean }>} */
|
|
@@ -6161,11 +6568,24 @@ const _shadowEarlierRule = (writer, taken, span) => {
|
|
|
6161
6568
|
}
|
|
6162
6569
|
}
|
|
6163
6570
|
for (const prior of priors) {
|
|
6164
|
-
if (prior.
|
|
6165
|
-
const
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6571
|
+
if (prior.len === 0) continue;
|
|
6572
|
+
const priorTaken = /** @type {TakenPiece} */ (prior.taken);
|
|
6573
|
+
const before = priorTaken.text.slice(prior.at, prior.at + prior.len);
|
|
6574
|
+
// A prior writing none of the names this rule writes keeps all it has, so
|
|
6575
|
+
// it is not split again for every later rule under its selector.
|
|
6576
|
+
let declared = _shadowDeclared.get(prior);
|
|
6577
|
+
if (declared === undefined || declared.text !== before) {
|
|
6578
|
+
declared = { text: before, names: _declaredNames(before, open) };
|
|
6579
|
+
_shadowDeclared.set(prior, declared);
|
|
6580
|
+
}
|
|
6581
|
+
let shared = false;
|
|
6582
|
+
for (const name of later.keys()) {
|
|
6583
|
+
if (declared.names.has(name)) {
|
|
6584
|
+
shared = true;
|
|
6585
|
+
break;
|
|
6586
|
+
}
|
|
6587
|
+
}
|
|
6588
|
+
if (!shared) continue;
|
|
6169
6589
|
/* istanbul ignore if -- @preserve: a join grows a list only before its rule
|
|
6170
6590
|
is noted unless distant merges are on, which skip this; kept as the guard */
|
|
6171
6591
|
if (
|
|
@@ -6207,18 +6627,17 @@ const _shadowEarlierRule = (writer, taken, span) => {
|
|
|
6207
6627
|
}
|
|
6208
6628
|
if (!dropped) continue;
|
|
6209
6629
|
if (kept.length === 0) {
|
|
6210
|
-
_cutSpan(writer,
|
|
6630
|
+
_cutSpan(writer, priorTaken, prior);
|
|
6211
6631
|
continue;
|
|
6212
6632
|
}
|
|
6213
|
-
_rewriteSpan(
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6633
|
+
_rewriteSpan(writer, priorTaken, prior, `${selector}{${kept.join(";")}}`);
|
|
6634
|
+
_rekeyShrunkSpan(writer, priorTaken, prior);
|
|
6635
|
+
}
|
|
6636
|
+
if (priors === noted) {
|
|
6637
|
+
priors.push(span);
|
|
6638
|
+
} else {
|
|
6639
|
+
selectors.set(selector, [/** @type {RuleSpan} */ (noted), span]);
|
|
6220
6640
|
}
|
|
6221
|
-
priors.push({ taken, span });
|
|
6222
6641
|
};
|
|
6223
6642
|
|
|
6224
6643
|
/**
|
|
@@ -6315,15 +6734,7 @@ const _joinGatheredSeam = (writer, taken, at, entry) => {
|
|
|
6315
6734
|
) {
|
|
6316
6735
|
return;
|
|
6317
6736
|
}
|
|
6318
|
-
if (
|
|
6319
|
-
!_lendSelectorsTo(
|
|
6320
|
-
writer,
|
|
6321
|
-
taken,
|
|
6322
|
-
before,
|
|
6323
|
-
prelude,
|
|
6324
|
-
first.text.slice(0, first.prelude)
|
|
6325
|
-
)
|
|
6326
|
-
) {
|
|
6737
|
+
if (!_lendSelectorsTo(writer, taken, before, prelude, _entryPrelude(first))) {
|
|
6327
6738
|
return;
|
|
6328
6739
|
}
|
|
6329
6740
|
_cutSpan(writer, taken, after);
|
|
@@ -6335,18 +6746,19 @@ const _joinGatheredSeam = (writer, taken, at, entry) => {
|
|
|
6335
6746
|
* it, so this runs until none is left.
|
|
6336
6747
|
* @param {PrintContext} writer the print context
|
|
6337
6748
|
* @param {TakenPiece} taken the piece the cut was made in
|
|
6749
|
+
* @param {RuleSpan[]} held the rules that held what was cut, the only ones it can have emptied
|
|
6338
6750
|
* @returns {void}
|
|
6339
6751
|
*/
|
|
6340
|
-
const _cutEmptiedSpans = (writer, taken) => {
|
|
6752
|
+
const _cutEmptiedSpans = (writer, taken, held) => {
|
|
6341
6753
|
if (!_S._transforms.removeDeadRules) return;
|
|
6342
6754
|
// Every span is a rule dropped empty wherever it stands: a qualified rule, or
|
|
6343
6755
|
// one of the at-rules `DROPPABLE_WHEN_EMPTY_AT_RULES` names (`_collectRuleSpans`).
|
|
6344
|
-
for (const other of
|
|
6756
|
+
for (const other of held) {
|
|
6345
6757
|
const end = other.at + other.len;
|
|
6346
6758
|
if (
|
|
6347
6759
|
other.len > 2 &&
|
|
6348
|
-
taken.
|
|
6349
|
-
taken.
|
|
6760
|
+
taken.charCodeAt(end - 1) === CC_RIGHT_CURLY &&
|
|
6761
|
+
taken.charCodeAt(end - 2) === CC_LEFT_CURLY
|
|
6350
6762
|
) {
|
|
6351
6763
|
_cutSpan(writer, taken, other);
|
|
6352
6764
|
return;
|
|
@@ -6369,17 +6781,17 @@ const _cutSpan = (writer, taken, span) => {
|
|
|
6369
6781
|
// The rule stood last in its block, so the `;` in front of it is one the
|
|
6370
6782
|
// printer drops itself — it goes with the rule rather than being left behind.
|
|
6371
6783
|
if (
|
|
6372
|
-
taken.
|
|
6373
|
-
taken.
|
|
6784
|
+
taken.charCodeAt(from + len) === CC_RIGHT_CURLY &&
|
|
6785
|
+
taken.charCodeAt(from - 1) === CC_SEMICOLON
|
|
6374
6786
|
) {
|
|
6375
6787
|
from -= 1;
|
|
6376
6788
|
len += 1;
|
|
6377
6789
|
}
|
|
6378
|
-
taken.
|
|
6379
|
-
if (taken.empties !== -1 && taken.
|
|
6790
|
+
taken.cut(from, len);
|
|
6791
|
+
if (taken.empties !== -1 && taken.length === taken.empties + 1) {
|
|
6380
6792
|
taken.text = "";
|
|
6381
6793
|
}
|
|
6382
|
-
writer.
|
|
6794
|
+
writer.deferRetractable(taken.piece, taken);
|
|
6383
6795
|
const end = from + len;
|
|
6384
6796
|
/** @type {RuleSpan[] | null} */
|
|
6385
6797
|
let shrunk = null;
|
|
@@ -6401,8 +6813,8 @@ const _cutSpan = (writer, taken, span) => {
|
|
|
6401
6813
|
span.len = 0;
|
|
6402
6814
|
if (shrunk !== null) {
|
|
6403
6815
|
for (const other of shrunk) _rekeyShrunkSpan(writer, taken, other);
|
|
6816
|
+
_cutEmptiedSpans(writer, taken, shrunk);
|
|
6404
6817
|
}
|
|
6405
|
-
_cutEmptiedSpans(writer, taken);
|
|
6406
6818
|
};
|
|
6407
6819
|
|
|
6408
6820
|
/**
|
|
@@ -6419,15 +6831,14 @@ const _rekeyShrunkSpan = (writer, taken, span) => {
|
|
|
6419
6831
|
const rules = span.scope.rules;
|
|
6420
6832
|
const other = rules.get(key);
|
|
6421
6833
|
_rekeySpan(taken, span);
|
|
6422
|
-
if (other === undefined || other
|
|
6423
|
-
|
|
6424
|
-
}
|
|
6834
|
+
if (other === undefined || other === span || other.len === 0) return;
|
|
6835
|
+
const otherTaken = /** @type {TakenPiece} */ (other.taken);
|
|
6425
6836
|
const earlier =
|
|
6426
|
-
|
|
6427
|
-
(
|
|
6837
|
+
otherTaken.piece < taken.piece ||
|
|
6838
|
+
(otherTaken.piece === taken.piece && other.at < span.at);
|
|
6428
6839
|
if (earlier) {
|
|
6429
|
-
rules.set(key,
|
|
6430
|
-
_cutSpan(writer,
|
|
6840
|
+
rules.set(key, span);
|
|
6841
|
+
_cutSpan(writer, otherTaken, other);
|
|
6431
6842
|
} else {
|
|
6432
6843
|
_cutSpan(writer, taken, span);
|
|
6433
6844
|
}
|
|
@@ -6647,13 +7058,13 @@ const _priorOf = (taken, entry) => {
|
|
|
6647
7058
|
return null;
|
|
6648
7059
|
}
|
|
6649
7060
|
// A rule whose selectors may not join a list is still a neighbor: the copy
|
|
6650
|
-
// that only restates it goes whatever its block holds.
|
|
7061
|
+
// that only restates it goes whatever its block holds. Whether they may is
|
|
7062
|
+
// asked of `entry` once a later rule prints the same block.
|
|
6651
7063
|
return {
|
|
6652
7064
|
taken,
|
|
6653
7065
|
span,
|
|
6654
7066
|
entry,
|
|
6655
7067
|
prelude: entry.prelude,
|
|
6656
|
-
listable: _entryListable(entry),
|
|
6657
7068
|
grown: false
|
|
6658
7069
|
};
|
|
6659
7070
|
};
|
|
@@ -6682,12 +7093,9 @@ const _settlePrior = (writer) => {
|
|
|
6682
7093
|
const _rekeySpan = (taken, span) => {
|
|
6683
7094
|
if (span.key === UNKEYED) return;
|
|
6684
7095
|
const rules = span.scope.rules;
|
|
6685
|
-
|
|
6686
|
-
if (registered !== undefined && registered.span === span) {
|
|
6687
|
-
rules.delete(span.key);
|
|
6688
|
-
}
|
|
7096
|
+
if (rules.get(span.key) === span) rules.delete(span.key);
|
|
6689
7097
|
span.key = taken.text.slice(span.at, span.at + span.len);
|
|
6690
|
-
if (!rules.has(span.key)) rules.set(span.key,
|
|
7098
|
+
if (!rules.has(span.key)) rules.set(span.key, span);
|
|
6691
7099
|
};
|
|
6692
7100
|
|
|
6693
7101
|
/**
|
|
@@ -6727,7 +7135,10 @@ const _spanEntry = (text, prelude) => ({
|
|
|
6727
7135
|
listKind: LIST_KIND_SELECTOR,
|
|
6728
7136
|
children: null,
|
|
6729
7137
|
head: "",
|
|
6730
|
-
unordered: false
|
|
7138
|
+
unordered: false,
|
|
7139
|
+
list: null,
|
|
7140
|
+
block: null,
|
|
7141
|
+
declarations: null
|
|
6731
7142
|
});
|
|
6732
7143
|
|
|
6733
7144
|
/**
|
|
@@ -6832,7 +7243,9 @@ const _joinsNeighbor = (writer, before, rule) => {
|
|
|
6832
7243
|
return same && _foldsNeighborBody(writer, before, rule);
|
|
6833
7244
|
}
|
|
6834
7245
|
if (!same) {
|
|
6835
|
-
if (!before.
|
|
7246
|
+
if (!_entryListable(before.entry) || !_entryListable(rule.entry)) {
|
|
7247
|
+
return false;
|
|
7248
|
+
}
|
|
6836
7249
|
const selector = tail.slice(second.at, second.at + rule.prelude);
|
|
6837
7250
|
if (
|
|
6838
7251
|
!_lendSelectorsTo(writer, before.taken, first, before.prelude, selector)
|
|
@@ -6918,14 +7331,11 @@ const _joinsPrior = (writer, prior, held) =>
|
|
|
6918
7331
|
*/
|
|
6919
7332
|
const _joinsPriorEntry = (writer, prior, entry, parted) => {
|
|
6920
7333
|
if (!_S._transforms.mergeRules) return false;
|
|
6921
|
-
if (entry.prelude === -1 || entry.atRule
|
|
6922
|
-
return false;
|
|
6923
|
-
}
|
|
7334
|
+
if (entry.prelude === -1 || entry.atRule) return false;
|
|
6924
7335
|
// A kept comment between the two was written above the later rule, which is
|
|
6925
7336
|
// read where it still stands rather than here, the flush having cleared it.
|
|
6926
7337
|
if (parted) return false;
|
|
6927
7338
|
const span = prior.span;
|
|
6928
|
-
if (!prior.listable) return false;
|
|
6929
7339
|
// Read where the rule now stands, never where it was taken: a cut inside its
|
|
6930
7340
|
// block, or a list this already grew, leaves the entry it went out as stale.
|
|
6931
7341
|
if (span.len <= prior.prelude) return false;
|
|
@@ -6942,6 +7352,8 @@ const _joinsPriorEntry = (writer, prior, entry, parted) => {
|
|
|
6942
7352
|
) {
|
|
6943
7353
|
return false;
|
|
6944
7354
|
}
|
|
7355
|
+
// After the blocks compare, which almost none do: the shape test is a regex.
|
|
7356
|
+
if (!_entryListable(entry) || !_entryListable(prior.entry)) return false;
|
|
6945
7357
|
// The same rule twice is one rule: nothing stands between them, so the later
|
|
6946
7358
|
// copy only restates what the earlier already says.
|
|
6947
7359
|
if (
|
|
@@ -6963,7 +7375,7 @@ const _joinsPriorEntry = (writer, prior, entry, parted) => {
|
|
|
6963
7375
|
prior.taken,
|
|
6964
7376
|
span,
|
|
6965
7377
|
prior.prelude,
|
|
6966
|
-
|
|
7378
|
+
_entryPrelude(entry)
|
|
6967
7379
|
)
|
|
6968
7380
|
) {
|
|
6969
7381
|
return false;
|
|
@@ -7161,13 +7573,7 @@ const _mergeIntoDistantRule = (writer, entry) => {
|
|
|
7161
7573
|
// grew is longer than the one that was taken.
|
|
7162
7574
|
const preludeNow = span.len - block.length;
|
|
7163
7575
|
if (
|
|
7164
|
-
!_lendSelectorsTo(
|
|
7165
|
-
writer,
|
|
7166
|
-
taken,
|
|
7167
|
-
span,
|
|
7168
|
-
preludeNow,
|
|
7169
|
-
entry.text.slice(0, entry.prelude)
|
|
7170
|
-
)
|
|
7576
|
+
!_lendSelectorsTo(writer, taken, span, preludeNow, _entryPrelude(entry))
|
|
7171
7577
|
) {
|
|
7172
7578
|
return false;
|
|
7173
7579
|
}
|
|
@@ -7227,13 +7633,11 @@ const _orderDistantLists = (writer) => {
|
|
|
7227
7633
|
* Merge the box families whose four longhands two joined blocks wrote between
|
|
7228
7634
|
* them, as `_mergeBoxLonghands` does for one block, so the join prints what the
|
|
7229
7635
|
* one rule written whole would.
|
|
7230
|
-
* @param {
|
|
7231
|
-
* @returns {
|
|
7636
|
+
* @param {PrintedDeclaration[]} declarations the joined block's declarations
|
|
7637
|
+
* @returns {PrintedDeclaration[]} them, each family it could merge merged
|
|
7232
7638
|
*/
|
|
7233
|
-
const _mergeJoinedBoxLonghands = (
|
|
7234
|
-
if (!_S._transforms.mergeLonghands) return
|
|
7235
|
-
const declarations = _blockDeclarations(body);
|
|
7236
|
-
if (declarations === null) return body;
|
|
7639
|
+
const _mergeJoinedBoxLonghands = (declarations) => {
|
|
7640
|
+
if (!_S._transforms.mergeLonghands) return declarations;
|
|
7237
7641
|
/** @type {Map<string, number>} */
|
|
7238
7642
|
const at = new Map();
|
|
7239
7643
|
/** @type {Set<string>} */
|
|
@@ -7285,13 +7689,29 @@ const _mergeJoinedBoxLonghands = (body) => {
|
|
|
7285
7689
|
const box = _collapseBox(values);
|
|
7286
7690
|
if (box === null) continue;
|
|
7287
7691
|
if (texts === null) texts = declarations.map((one) => one.text);
|
|
7288
|
-
texts[first] =
|
|
7289
|
-
|
|
7692
|
+
texts[first] = `${shorthand}:${_joinSlots(box)}${
|
|
7693
|
+
important ? "!important" : ""
|
|
7694
|
+
}`;
|
|
7290
7695
|
for (const index of indexes) if (index !== first) texts[index] = "";
|
|
7291
7696
|
}
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7697
|
+
if (texts === null) return declarations;
|
|
7698
|
+
/** @type {PrintedDeclaration[]} */
|
|
7699
|
+
const out = [];
|
|
7700
|
+
for (let i = 0; i < texts.length; i++) {
|
|
7701
|
+
const text = texts[i];
|
|
7702
|
+
if (text.length === 0) continue;
|
|
7703
|
+
if (text === declarations[i].text) {
|
|
7704
|
+
out.push(declarations[i]);
|
|
7705
|
+
continue;
|
|
7706
|
+
}
|
|
7707
|
+
const colon = text.indexOf(":");
|
|
7708
|
+
out.push({
|
|
7709
|
+
property: text.slice(0, colon),
|
|
7710
|
+
value: text.slice(colon + 1),
|
|
7711
|
+
text
|
|
7712
|
+
});
|
|
7713
|
+
}
|
|
7714
|
+
return out;
|
|
7295
7715
|
};
|
|
7296
7716
|
|
|
7297
7717
|
/**
|
|
@@ -7304,7 +7724,7 @@ const _mergeJoinedBoxLonghands = (body) => {
|
|
|
7304
7724
|
const _joinedAtRuleSpans = (merged) => {
|
|
7305
7725
|
// An at-rule's entry is always built with its children, never `null`.
|
|
7306
7726
|
const children = /** @type {RuleEntry[]} */ (merged.children);
|
|
7307
|
-
const prelude =
|
|
7727
|
+
const prelude = _entryPrelude(merged);
|
|
7308
7728
|
/** @type {RuleSpan[]} */
|
|
7309
7729
|
const spans = [];
|
|
7310
7730
|
let at = 0;
|
|
@@ -7322,7 +7742,8 @@ const _joinedAtRuleSpans = (merged) => {
|
|
|
7322
7742
|
scope: _rootScope(),
|
|
7323
7743
|
key: child.text,
|
|
7324
7744
|
at,
|
|
7325
|
-
len: child.text.length
|
|
7745
|
+
len: child.text.length,
|
|
7746
|
+
taken: null
|
|
7326
7747
|
});
|
|
7327
7748
|
} else {
|
|
7328
7749
|
const inner = _joinedAtRuleSpans(child);
|
|
@@ -7332,7 +7753,8 @@ const _joinedAtRuleSpans = (merged) => {
|
|
|
7332
7753
|
scope: _rootScope(),
|
|
7333
7754
|
key: UNKEYED,
|
|
7334
7755
|
at,
|
|
7335
|
-
len: child.text.length
|
|
7756
|
+
len: child.text.length,
|
|
7757
|
+
taken: null
|
|
7336
7758
|
});
|
|
7337
7759
|
}
|
|
7338
7760
|
for (const span of inner.spans) {
|
|
@@ -7340,7 +7762,8 @@ const _joinedAtRuleSpans = (merged) => {
|
|
|
7340
7762
|
scope: _enclosingRuleScope(span.scope, inner.keyPrelude),
|
|
7341
7763
|
key: span.key,
|
|
7342
7764
|
at: at + inner.bodyAt + span.at,
|
|
7343
|
-
len: span.len
|
|
7765
|
+
len: span.len,
|
|
7766
|
+
taken: null
|
|
7344
7767
|
});
|
|
7345
7768
|
}
|
|
7346
7769
|
}
|
|
@@ -7388,7 +7811,7 @@ const _joinedSpans = (before, beforeOwn, entry, own, merged) => {
|
|
|
7388
7811
|
) {
|
|
7389
7812
|
return _WHOLE_TEXT_SPANS;
|
|
7390
7813
|
}
|
|
7391
|
-
const prelude =
|
|
7814
|
+
const prelude = _entryPrelude(merged);
|
|
7392
7815
|
const length = entry.text.length - entry.prelude - 2;
|
|
7393
7816
|
const at = merged.text.length - 1 - length - (merged.prelude + 1);
|
|
7394
7817
|
const shift = at + own.bodyAt - (entry.prelude + 1);
|
|
@@ -7399,7 +7822,8 @@ const _joinedSpans = (before, beforeOwn, entry, own, merged) => {
|
|
|
7399
7822
|
scope: span.scope,
|
|
7400
7823
|
key: span.key,
|
|
7401
7824
|
at: span.at + shift,
|
|
7402
|
-
len: span.len
|
|
7825
|
+
len: span.len,
|
|
7826
|
+
taken: null
|
|
7403
7827
|
});
|
|
7404
7828
|
}
|
|
7405
7829
|
return {
|
|
@@ -7444,8 +7868,7 @@ const _takeTopLevel = (node, writer, offset, line, column) => {
|
|
|
7444
7868
|
const at = writer.takeRetractable(node, offset, line, column, own);
|
|
7445
7869
|
_settlePrior(writer);
|
|
7446
7870
|
if (_topLevelRules !== null) {
|
|
7447
|
-
|
|
7448
|
-
const taken = { piece: at, text: own, spans: [], empties: -1 };
|
|
7871
|
+
const taken = new TakenPiece(at, own, -1);
|
|
7449
7872
|
if (_prefixPieces === null) _prefixPieces = new Map();
|
|
7450
7873
|
_prefixPieces.set(at, taken);
|
|
7451
7874
|
_topLevelRules.push(taken);
|
|
@@ -7697,8 +8120,7 @@ const _walkTopLevel = (node, writer) => {
|
|
|
7697
8120
|
_nodeCount = 0;
|
|
7698
8121
|
_flatTop = 0;
|
|
7699
8122
|
// Body indices recycle with node ids.
|
|
7700
|
-
|
|
7701
|
-
_ruleBodies.length = 0;
|
|
8123
|
+
_dropBodies();
|
|
7702
8124
|
if (_walkStopped && writer === undefined) throw WALK_STOPPED;
|
|
7703
8125
|
};
|
|
7704
8126
|
|
|
@@ -7712,8 +8134,7 @@ const _recycleTopLevel = (node) => {
|
|
|
7712
8134
|
if (_flatTop > _flatPeak) _flatPeak = _flatTop;
|
|
7713
8135
|
_nodeCount = 0;
|
|
7714
8136
|
_flatTop = 0;
|
|
7715
|
-
|
|
7716
|
-
_ruleBodies.length = 0;
|
|
8137
|
+
_dropBodies();
|
|
7717
8138
|
};
|
|
7718
8139
|
|
|
7719
8140
|
// The store buffers grow to the largest single top-level rule ever parsed and
|
|
@@ -8048,6 +8469,21 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
8048
8469
|
}
|
|
8049
8470
|
_visitors = visitors;
|
|
8050
8471
|
_commentBucket = visitors[NodeType.Comment];
|
|
8472
|
+
_blockMemo =
|
|
8473
|
+
writer !== undefined &&
|
|
8474
|
+
writer.options.mode === "minify" &&
|
|
8475
|
+
visitors.length === 0 &&
|
|
8476
|
+
_unusedSymbols === null &&
|
|
8477
|
+
!_S._prefixingOn &&
|
|
8478
|
+
!_colorFallbacksNeeded &&
|
|
8479
|
+
_S._renderEmbeddedSource === undefined &&
|
|
8480
|
+
_S._deferEmbeddedSource === undefined
|
|
8481
|
+
? new Map()
|
|
8482
|
+
: null;
|
|
8483
|
+
_declarationMemo = _blockMemo === null ? null : new Map();
|
|
8484
|
+
_blockMemoScopes.clear();
|
|
8485
|
+
_singleValueKeys = _blockMemo === null ? null : new Set();
|
|
8486
|
+
_declarationMemoKey = "";
|
|
8051
8487
|
_walkStopped = false;
|
|
8052
8488
|
|
|
8053
8489
|
// WHY: the comment sink fires the `Comment` visitor bucket and, while
|
|
@@ -8210,6 +8646,15 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
8210
8646
|
_blockSpans.length = 0;
|
|
8211
8647
|
_ruleScopes = null;
|
|
8212
8648
|
_rootRuleScope = null;
|
|
8649
|
+
_shadowDeclared.clear();
|
|
8650
|
+
_blockMemo = null;
|
|
8651
|
+
_blockMemoScopes.clear();
|
|
8652
|
+
_blockMemoTable = _blockMemoKeyTable = new Map();
|
|
8653
|
+
_declarationMemo = null;
|
|
8654
|
+
_singleValueKeys = null;
|
|
8655
|
+
_blockMemoNode = null;
|
|
8656
|
+
_blockMemoBody = undefined;
|
|
8657
|
+
_blockMemoKey = "";
|
|
8213
8658
|
// Each holds a piece of the sheet just printed, so the last one printed
|
|
8214
8659
|
// would be held until the next parse replaces them.
|
|
8215
8660
|
_distantRules.clear();
|
|
@@ -8221,7 +8666,9 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
8221
8666
|
_anonymousLayers = 0;
|
|
8222
8667
|
_S._input = "";
|
|
8223
8668
|
_originalInput = "";
|
|
8224
|
-
_locConverter = /** @type {LocConverter} */ (
|
|
8669
|
+
_locConverter = /** @type {LocConverter} */ (
|
|
8670
|
+
/** @type {LocConverter | null} */ (null)
|
|
8671
|
+
);
|
|
8225
8672
|
_declBodies.length = 0;
|
|
8226
8673
|
_ruleBodies.length = 0;
|
|
8227
8674
|
_flatTop = 0;
|
|
@@ -8230,7 +8677,9 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
8230
8677
|
_flatGrowHint = _flatPeak;
|
|
8231
8678
|
_flat = EMPTY_I32;
|
|
8232
8679
|
}
|
|
8233
|
-
_visitors = /** @type {CompiledVisitorMap} */ (
|
|
8680
|
+
_visitors = /** @type {CompiledVisitorMap} */ (
|
|
8681
|
+
/** @type {CompiledVisitorMap | never[]} */ ([])
|
|
8682
|
+
);
|
|
8234
8683
|
_commentBucket = undefined;
|
|
8235
8684
|
if (_capacity > _SHRINK_CAPACITY) {
|
|
8236
8685
|
// +1: node ids are 1-based and grow fires at `id >= capacity`.
|
|
@@ -8891,6 +9340,10 @@ const _landsAlike = (value, shorter) => {
|
|
|
8891
9340
|
* @returns {string} the rounded number, or `num`
|
|
8892
9341
|
*/
|
|
8893
9342
|
const _roundSignificant = (num, unit = "") => {
|
|
9343
|
+
// A number written in at most six characters carries at most six significant
|
|
9344
|
+
// digits, a sign and dot only taking away, so rounding hands back what it was
|
|
9345
|
+
// given. Almost every number is this short.
|
|
9346
|
+
if (num.length <= _SIGNIFICANT_DIGITS) return num;
|
|
8894
9347
|
const digits =
|
|
8895
9348
|
unit === "" ||
|
|
8896
9349
|
unit === "%" ||
|
|
@@ -8898,15 +9351,24 @@ const _roundSignificant = (num, unit = "") => {
|
|
|
8898
9351
|
ANGLE_UNITS.has(unit)
|
|
8899
9352
|
? _SIGNIFICANT_DIGITS
|
|
8900
9353
|
: _SCALED_LENGTH_DIGITS;
|
|
8901
|
-
// A number written in at most six characters carries at most six significant
|
|
8902
|
-
// digits, a sign and dot only taking away, so rounding hands back what it was
|
|
8903
|
-
// given. Almost every number is this short.
|
|
8904
|
-
if (num.length <= _SIGNIFICANT_DIGITS) return num;
|
|
8905
9354
|
if (num.includes("e") || num.includes("E")) return num;
|
|
8906
9355
|
const value = Number(num);
|
|
8907
9356
|
if (!Number.isFinite(value) || Math.abs(value) >= _ROUNDING_LIMIT) return num;
|
|
8908
9357
|
let rounded = value.toPrecision(digits);
|
|
8909
|
-
if (
|
|
9358
|
+
if (unit === "%") {
|
|
9359
|
+
// Gecko parses a percentage as a single-precision fraction laid out in 1/60px,
|
|
9360
|
+
// so the digits must reach that float: `14.285714%` drifts, `14.2857143%` not.
|
|
9361
|
+
const parsed = Math.fround(value / 100);
|
|
9362
|
+
for (
|
|
9363
|
+
let more = digits;
|
|
9364
|
+
more < _SCALED_LENGTH_DIGITS &&
|
|
9365
|
+
Math.fround(Number(rounded) / 100) !== parsed;
|
|
9366
|
+
more++
|
|
9367
|
+
) {
|
|
9368
|
+
rounded = value.toPrecision(more + 1);
|
|
9369
|
+
}
|
|
9370
|
+
if (Math.fround(Number(rounded) / 100) !== parsed) return num;
|
|
9371
|
+
} else if (digits === _SCALED_LENGTH_DIGITS) {
|
|
8910
9372
|
// The fewest digits landing where the written length does, asked again of
|
|
8911
9373
|
// each answer until it holds, which is what a second pass would ask.
|
|
8912
9374
|
let from = value;
|
|
@@ -9651,6 +10113,17 @@ const _readByEveryEngine = (profile) => {
|
|
|
9651
10113
|
return true;
|
|
9652
10114
|
};
|
|
9653
10115
|
|
|
10116
|
+
/**
|
|
10117
|
+
* Whether the targets read an ability: the selection's browsers, or with none
|
|
10118
|
+
* each engine shipping today, which is what `dropOverriddenDeclarations` assumes.
|
|
10119
|
+
* @param {number | undefined} profile its `getSupportProfiles()` row
|
|
10120
|
+
* @returns {boolean} true when the later value it names is read
|
|
10121
|
+
*/
|
|
10122
|
+
const _readsOverridingValue = (profile) =>
|
|
10123
|
+
_prefixBrowsers === null
|
|
10124
|
+
? profile !== undefined && _readByEveryEngine(profile)
|
|
10125
|
+
: _readsAll(profile);
|
|
10126
|
+
|
|
9654
10127
|
/**
|
|
9655
10128
|
* The vendor spellings at least one target browser still needs for a construct —
|
|
9656
10129
|
* a target at version V needs spelling S when some `[browser, from, to]` of S has
|
|
@@ -10776,6 +11249,20 @@ const _minifyPolarColorFunction = (
|
|
|
10776
11249
|
fn === "lab" || fn === "lch"
|
|
10777
11250
|
? _labToLinearSrgb(l, a, b)
|
|
10778
11251
|
: _oklabToLinearSrgb(l, a, b);
|
|
11252
|
+
// WHY: a hex mixed in `oklch` reaches it through a conversion, and Chromium
|
|
11253
|
+
// reads a hue there as missing below a chroma of 0.02 — so `color-mix(in
|
|
11254
|
+
// oklch, currentColor 35%, #0000)` paints hue 0 where `oklch()` named one.
|
|
11255
|
+
if (fn === "oklch" && (a !== 0 || b !== 0)) {
|
|
11256
|
+
const space = getColorSpaceModel().get(fn);
|
|
11257
|
+
const clamped = linear.map((c) => Math.min(1, Math.max(0, c)));
|
|
11258
|
+
if (
|
|
11259
|
+
space !== undefined &&
|
|
11260
|
+
space.uncertainHue !== null &&
|
|
11261
|
+
space.uncertainHue(space.to(clamped))
|
|
11262
|
+
) {
|
|
11263
|
+
return null;
|
|
11264
|
+
}
|
|
11265
|
+
}
|
|
10779
11266
|
// `_channelByte` decides the gamut from the encoded value, where the
|
|
10780
11267
|
// question is whether clamping would move the byte.
|
|
10781
11268
|
raw = linear.map((c) => _gammaEncode(c) * 255);
|
|
@@ -10999,7 +11486,7 @@ const _resolveColor = (text) => {
|
|
|
10999
11486
|
}
|
|
11000
11487
|
// Every call that answers records them, so a non-null answer is one.
|
|
11001
11488
|
const raw = /** @type {number[]} */ (
|
|
11002
|
-
/** @type {
|
|
11489
|
+
/** @type {number[] | null} */ (_fallbackChannels)
|
|
11003
11490
|
);
|
|
11004
11491
|
return {
|
|
11005
11492
|
linear: raw.map((channel) => _gammaDecode(channel / 255)),
|
|
@@ -11832,7 +12319,7 @@ const _CUSTOM_MEDIA_REFERENCE_RE = /^\(\s*(--[^\s()]+)\s*\)$/;
|
|
|
11832
12319
|
// The same, anywhere in a printed `(…)` group.
|
|
11833
12320
|
const _NESTED_CUSTOM_MEDIA_REFERENCE_RE = /\(\s*(--[^\s()]+)\s*\)/g;
|
|
11834
12321
|
// A media type: an identifier, which no keyword between conditions is.
|
|
11835
|
-
const _MEDIA_TYPE_RE = /^-?[a-
|
|
12322
|
+
const _MEDIA_TYPE_RE = /^-?[a-zA-Z_\u0080-][\w\u0080--]*$/;
|
|
11836
12323
|
|
|
11837
12324
|
/**
|
|
11838
12325
|
* @param {string} token a printed query token
|
|
@@ -12374,10 +12861,11 @@ const _lowerDirSelector = (parts) => {
|
|
|
12374
12861
|
* Empty parts are stepped over — a comment between the colons printed away, and
|
|
12375
12862
|
* the tokenizer had already dropped it anyway.
|
|
12376
12863
|
* @param {string[]} parts the prelude's printed parts
|
|
12377
|
-
* @returns {
|
|
12864
|
+
* @returns {boolean} whether a pseudo it read may be an `:is()` for `_unwrapLoneIs`
|
|
12378
12865
|
*/
|
|
12379
12866
|
const _foldPseudoNames = (parts) => {
|
|
12380
12867
|
const dropping = _S._transforms.shortenSelectors;
|
|
12868
|
+
let mayUnwrap = false;
|
|
12381
12869
|
// The two non-empty parts before the current one.
|
|
12382
12870
|
let first = -1;
|
|
12383
12871
|
let second = -1;
|
|
@@ -12388,10 +12876,19 @@ const _foldPseudoNames = (parts) => {
|
|
|
12388
12876
|
// not, `linearGradient` being an SVG element, and nor does an id, class or
|
|
12389
12877
|
// attribute value. A functional pseudo folds its own name alone.
|
|
12390
12878
|
if (second !== -1 && parts[second] === ":") {
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
part
|
|
12394
|
-
|
|
12879
|
+
if (
|
|
12880
|
+
part.length > 4 &&
|
|
12881
|
+
part.charCodeAt(2) === CC_LEFT_PARENTHESIS &&
|
|
12882
|
+
(part.charCodeAt(0) | 0x20) === CC_LOWER_I
|
|
12883
|
+
) {
|
|
12884
|
+
mayUnwrap = true;
|
|
12885
|
+
}
|
|
12886
|
+
if (_S._transforms.foldCase && !part.includes("(")) {
|
|
12887
|
+
const lowered = asciiLowercaseName(part);
|
|
12888
|
+
if (lowered !== part) {
|
|
12889
|
+
part = lowered;
|
|
12890
|
+
parts[i] = lowered;
|
|
12891
|
+
}
|
|
12395
12892
|
}
|
|
12396
12893
|
// Folded first, so the legacy name below is matched against the folded
|
|
12397
12894
|
// text rather than folded a second time.
|
|
@@ -12407,6 +12904,7 @@ const _foldPseudoNames = (parts) => {
|
|
|
12407
12904
|
first = second;
|
|
12408
12905
|
second = i;
|
|
12409
12906
|
}
|
|
12907
|
+
return mayUnwrap;
|
|
12410
12908
|
};
|
|
12411
12909
|
|
|
12412
12910
|
// A legacy pseudo-element written with one colon, which `:is()` cannot hold.
|
|
@@ -12450,7 +12948,7 @@ const _isCompoundText = (selector) => {
|
|
|
12450
12948
|
* Unwrap each `:is()` holding one compound, which matches the same elements with
|
|
12451
12949
|
* the same specificity. Only where the targets read every pseudo it names, since
|
|
12452
12950
|
* `:is()` forgives an argument it cannot parse and a bare compound does not.
|
|
12453
|
-
* @param {string[]} parts a selector's printed parts, rewritten in place
|
|
12951
|
+
* @param {string[]} parts a selector's printed parts, rewritten in place, called only where `_foldPseudoNames` read a pseudo that may be an `:is()`
|
|
12454
12952
|
*/
|
|
12455
12953
|
const _unwrapLoneIs = (parts) => {
|
|
12456
12954
|
let before = -1;
|
|
@@ -12473,12 +12971,16 @@ const _unwrapLoneIs = (parts) => {
|
|
|
12473
12971
|
) {
|
|
12474
12972
|
pastElement = true;
|
|
12475
12973
|
}
|
|
12476
|
-
const
|
|
12477
|
-
if (
|
|
12974
|
+
const isCall =
|
|
12478
12975
|
!pastElement &&
|
|
12479
12976
|
call.length > 4 &&
|
|
12480
|
-
|
|
12481
|
-
call.charCodeAt(
|
|
12977
|
+
(call.charCodeAt(0) | 0x20) === CC_LOWER_I &&
|
|
12978
|
+
(call.charCodeAt(1) | 0x20) === CC_LOWER_S &&
|
|
12979
|
+
call.charCodeAt(2) === CC_LEFT_PARENTHESIS &&
|
|
12980
|
+
call.charCodeAt(call.length - 1) === CC_RIGHT_PARENTHESIS;
|
|
12981
|
+
const inner = isCall ? call.slice(3, -1) : "";
|
|
12982
|
+
if (
|
|
12983
|
+
isCall &&
|
|
12482
12984
|
_isCompoundText(inner) &&
|
|
12483
12985
|
!inner.includes("::") &&
|
|
12484
12986
|
!_LEGACY_PSEUDO_ELEMENT_RE.test(inner) &&
|
|
@@ -12697,7 +13199,8 @@ const _dropRedundantSupportsParens = (parts) => {
|
|
|
12697
13199
|
continue;
|
|
12698
13200
|
}
|
|
12699
13201
|
let text = part;
|
|
12700
|
-
|
|
13202
|
+
// Only an opening pair is one to drop, which almost no condition holds.
|
|
13203
|
+
while (text.includes("((")) {
|
|
12701
13204
|
const close = _matchingParens(text);
|
|
12702
13205
|
if (close === null) break;
|
|
12703
13206
|
let at = -1;
|
|
@@ -12725,6 +13228,7 @@ const _dropRedundantSupportsParens = (parts) => {
|
|
|
12725
13228
|
}
|
|
12726
13229
|
if (groups !== 1) return;
|
|
12727
13230
|
const text = parts[only];
|
|
13231
|
+
if (text.charCodeAt(1) !== CC_LEFT_PARENTHESIS) return;
|
|
12728
13232
|
const close = _matchingParens(text);
|
|
12729
13233
|
if (
|
|
12730
13234
|
close !== null &&
|
|
@@ -12891,7 +13395,8 @@ const _selectsFeatureless = (parts, at) => {
|
|
|
12891
13395
|
* @returns {void}
|
|
12892
13396
|
*/
|
|
12893
13397
|
const _dropImpliedUniversalSelector = (parts) => {
|
|
12894
|
-
|
|
13398
|
+
// A native search first: almost no selector holds a `*` at all.
|
|
13399
|
+
if (!_S._transforms.shortenSelectors || !parts.includes("*")) return;
|
|
12895
13400
|
let previous = -1;
|
|
12896
13401
|
for (let i = 0; i < parts.length; i++) {
|
|
12897
13402
|
const part = parts[i];
|
|
@@ -13581,6 +14086,47 @@ const _foldFollowingLonghands = (path, items, texts, superseded) => {
|
|
|
13581
14086
|
}
|
|
13582
14087
|
return written.has(base) && _prefixedPropertyRemovable(base, property);
|
|
13583
14088
|
};
|
|
14089
|
+
// Each declaration's property, read once: the walk back from every longhand
|
|
14090
|
+
// asks again of the ones before it, and a fold keeps the property it rewrites.
|
|
14091
|
+
/** @type {(string | undefined)[]} */
|
|
14092
|
+
const properties = [];
|
|
14093
|
+
// The number `_declarationTable` gives each, or -1 for one it does not name.
|
|
14094
|
+
/** @type {number[]} */
|
|
14095
|
+
const ids = [];
|
|
14096
|
+
const table = _declarationTable();
|
|
14097
|
+
/**
|
|
14098
|
+
* The property a declaration sets, read through `_canonicalProperty`.
|
|
14099
|
+
* @param {number} at the declaration's index
|
|
14100
|
+
* @returns {string} its property
|
|
14101
|
+
*/
|
|
14102
|
+
const propertyAt = (at) => {
|
|
14103
|
+
let property = properties[at];
|
|
14104
|
+
if (property === undefined) {
|
|
14105
|
+
const printed = _printedProperty(texts[at], texts[at].indexOf(":"));
|
|
14106
|
+
const id = table.ids.get(printed);
|
|
14107
|
+
if (id === undefined) {
|
|
14108
|
+
property = printed;
|
|
14109
|
+
ids[at] = -1;
|
|
14110
|
+
} else {
|
|
14111
|
+
ids[at] = table.canonical[id];
|
|
14112
|
+
property = table.names[ids[at]];
|
|
14113
|
+
}
|
|
14114
|
+
properties[at] = property;
|
|
14115
|
+
}
|
|
14116
|
+
return property;
|
|
14117
|
+
};
|
|
14118
|
+
/**
|
|
14119
|
+
* Whether two declarations must keep their order, as `_orderMatters` reads it.
|
|
14120
|
+
* @param {number} a a declaration's index, its property already read
|
|
14121
|
+
* @param {number} b another's
|
|
14122
|
+
* @returns {boolean} true when the two must keep their order
|
|
14123
|
+
*/
|
|
14124
|
+
const orderMattersAt = (a, b) => {
|
|
14125
|
+
const property = propertyAt(b);
|
|
14126
|
+
return ids[a] !== -1 && ids[b] !== -1
|
|
14127
|
+
? _orderMattersById(table, ids[a], ids[b])
|
|
14128
|
+
: _orderMatters(/** @type {string} */ (properties[a]), property);
|
|
14129
|
+
};
|
|
13584
14130
|
for (let i = 0; i < items.length; i++) {
|
|
13585
14131
|
// A dropped declaration prints nothing, so nothing stands where it stood.
|
|
13586
14132
|
if (texts[i].length === 0 || superseded.has(i)) continue;
|
|
@@ -13591,7 +14137,16 @@ const _foldFollowingLonghands = (path, items, texts, superseded) => {
|
|
|
13591
14137
|
if (dropsWithPrefix(i)) continue;
|
|
13592
14138
|
if (
|
|
13593
14139
|
shorthand !== -1 &&
|
|
13594
|
-
_foldIntoEarlier(
|
|
14140
|
+
_foldIntoEarlier(
|
|
14141
|
+
path,
|
|
14142
|
+
items,
|
|
14143
|
+
texts,
|
|
14144
|
+
superseded,
|
|
14145
|
+
i,
|
|
14146
|
+
dropsWithPrefix,
|
|
14147
|
+
propertyAt,
|
|
14148
|
+
orderMattersAt
|
|
14149
|
+
)
|
|
13595
14150
|
) {
|
|
13596
14151
|
superseded.add(i);
|
|
13597
14152
|
continue;
|
|
@@ -13615,29 +14170,39 @@ let _anyLonghand = null;
|
|
|
13615
14170
|
* @param {Set<number>} superseded the indices already dropped
|
|
13616
14171
|
* @param {number} from the longhand's index
|
|
13617
14172
|
* @param {(at: number) => boolean} drops whether a declaration is dropped later
|
|
14173
|
+
* @param {(at: number) => string} propertyAt the property a declaration sets, read through `_canonicalProperty`
|
|
14174
|
+
* @param {(a: number, b: number) => boolean} orderMattersAt `_orderMatters` of two declarations' properties, the first already read
|
|
13618
14175
|
* @returns {boolean} true when it was folded into one before it
|
|
13619
14176
|
*/
|
|
13620
|
-
const _foldIntoEarlier = (
|
|
14177
|
+
const _foldIntoEarlier = (
|
|
14178
|
+
path,
|
|
14179
|
+
items,
|
|
14180
|
+
texts,
|
|
14181
|
+
superseded,
|
|
14182
|
+
from,
|
|
14183
|
+
drops,
|
|
14184
|
+
propertyAt,
|
|
14185
|
+
orderMattersAt
|
|
14186
|
+
) => {
|
|
13621
14187
|
if (_anyLonghand === null) {
|
|
13622
14188
|
_anyLonghand = new Set();
|
|
13623
14189
|
for (const longhands of getShorthandLonghands().values()) {
|
|
13624
14190
|
for (const longhand of longhands) _anyLonghand.add(longhand);
|
|
13625
14191
|
}
|
|
13626
14192
|
}
|
|
13627
|
-
const property =
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
|
|
14193
|
+
const property = propertyAt(from);
|
|
14194
|
+
const bare =
|
|
14195
|
+
property.charCodeAt(0) === CC_HYPHEN_MINUS
|
|
14196
|
+
? property.replace(_VENDOR_ITEM_PREFIX_RE, "")
|
|
14197
|
+
: property;
|
|
14198
|
+
if (!_anyLonghand.has(bare)) {
|
|
13631
14199
|
return false;
|
|
13632
14200
|
}
|
|
13633
14201
|
for (let at = from - 1; at >= 0; at--) {
|
|
13634
14202
|
if (texts[at].length === 0 || superseded.has(at)) continue;
|
|
13635
14203
|
if (path.type(items[at]) !== NodeType.Declaration) return false;
|
|
13636
14204
|
if (drops(at)) continue;
|
|
13637
|
-
|
|
13638
|
-
_printedProperty(texts[at], texts[at].indexOf(":"))
|
|
13639
|
-
);
|
|
13640
|
-
if (_orderMatters(property, other)) {
|
|
14205
|
+
if (orderMattersAt(from, at)) {
|
|
13641
14206
|
return _foldInto(path, items, texts, at, from);
|
|
13642
14207
|
}
|
|
13643
14208
|
}
|
|
@@ -14570,9 +15135,9 @@ const _familyValue = (property, longhands, values) => {
|
|
|
14570
15135
|
* @returns {boolean} true when no target is left reading the earlier one
|
|
14571
15136
|
*/
|
|
14572
15137
|
const _valueReadByTarget = (property, value) => {
|
|
14573
|
-
// No selection names no browser to answer for, so
|
|
14574
|
-
//
|
|
14575
|
-
if (_prefixBrowsers === null) return
|
|
15138
|
+
// No selection names no browser to answer for, so the earlier one stands
|
|
15139
|
+
// unless the option assumes each engine shipping today, read against below.
|
|
15140
|
+
if (_prefixBrowsers === null && !_dropOverriddenDeclarations) return false;
|
|
14576
15141
|
// A selection naming a browser the tables do not cover answers for less than
|
|
14577
15142
|
// the whole of it. Naming a browser states a target, which the option that
|
|
14578
15143
|
// assumes one does not override, so this stands whatever it says.
|
|
@@ -14582,7 +15147,7 @@ const _valueReadByTarget = (property, value) => {
|
|
|
14582
15147
|
const substitutions = _heldSubstitutions(value);
|
|
14583
15148
|
if (substitutions !== null) {
|
|
14584
15149
|
for (const name of substitutions) {
|
|
14585
|
-
if (!
|
|
15150
|
+
if (!_readsOverridingValue(_valueSupportedFrom(name))) return false;
|
|
14586
15151
|
}
|
|
14587
15152
|
return true;
|
|
14588
15153
|
}
|
|
@@ -14592,7 +15157,7 @@ const _valueReadByTarget = (property, value) => {
|
|
|
14592
15157
|
const components = _splitPrintedComponents(value);
|
|
14593
15158
|
if (components.length > 1) return _textDecorationRead(components);
|
|
14594
15159
|
}
|
|
14595
|
-
return
|
|
15160
|
+
return _readsOverridingValue(
|
|
14596
15161
|
_valueSupportedFrom(`${property} ${toLowerCaseIfNeeded(value)}`)
|
|
14597
15162
|
);
|
|
14598
15163
|
}
|
|
@@ -14604,9 +15169,10 @@ const _valueReadByTarget = (property, value) => {
|
|
|
14604
15169
|
return false;
|
|
14605
15170
|
}
|
|
14606
15171
|
const fn = toLowerCaseIfNeeded(value.slice(0, open));
|
|
14607
|
-
if (!
|
|
15172
|
+
if (!_readsOverridingValue(_valueSupportedFrom(fn))) return false;
|
|
14608
15173
|
if (fn.endsWith("gradient")) {
|
|
14609
|
-
|
|
15174
|
+
// A one-stop gradient is CSS Images 4's, which not every engine reads yet.
|
|
15175
|
+
return _gradientArgumentsRead(value.slice(open + 1, -1), 2);
|
|
14610
15176
|
}
|
|
14611
15177
|
const args = value.slice(open + 1, -1).match(_COLOR_ARGUMENT_RE);
|
|
14612
15178
|
if (args === null) return false;
|
|
@@ -14660,33 +15226,61 @@ const _GRADIENT_AMOUNT_RE = /^[+-]?(?:\d+\.?\d*|\.\d+)([a-z]+|%)?$/i;
|
|
|
14660
15226
|
* line, shape or position of keywords and amounts, and stops of a color every
|
|
14661
15227
|
* engine names. A call inside, or an interpolation space, is not asked here.
|
|
14662
15228
|
* @param {string} inner the printed arguments
|
|
15229
|
+
* @param {number} stopsNeeded how many color stops the arguments must hold
|
|
14663
15230
|
* @returns {boolean} true when the target reads every one
|
|
14664
15231
|
*/
|
|
14665
|
-
const _gradientArgumentsRead = (inner) => {
|
|
14666
|
-
|
|
14667
|
-
|
|
15232
|
+
const _gradientArgumentsRead = (inner, stopsNeeded) => {
|
|
15233
|
+
const args = _splitTopLevelArguments(inner);
|
|
15234
|
+
let stops = 0;
|
|
15235
|
+
for (let index = 0; index < args.length; index++) {
|
|
15236
|
+
// A line, shape or position opens the list alone; a stop is one color
|
|
15237
|
+
// and its amounts. Anything else, an empty argument too, is invalid.
|
|
15238
|
+
if (args[index].trim() === "") return false;
|
|
15239
|
+
let words = 0;
|
|
15240
|
+
let colors = 0;
|
|
15241
|
+
for (const component of _splitPrintedComponents(args[index].trim())) {
|
|
14668
15242
|
const lowered = toLowerCaseIfNeeded(component);
|
|
14669
|
-
if (_GRADIENT_KEYWORDS.has(lowered))
|
|
15243
|
+
if (_GRADIENT_KEYWORDS.has(lowered)) {
|
|
15244
|
+
if (lowered === "transparent" || lowered === "currentcolor") {
|
|
15245
|
+
colors++;
|
|
15246
|
+
} else {
|
|
15247
|
+
words++;
|
|
15248
|
+
}
|
|
15249
|
+
if (words !== 0 && (index !== 0 || colors !== 0)) return false;
|
|
15250
|
+
if (colors > 1) return false;
|
|
15251
|
+
continue;
|
|
15252
|
+
}
|
|
14670
15253
|
const amount = _GRADIENT_AMOUNT_RE.exec(lowered);
|
|
14671
15254
|
if (amount !== null) {
|
|
14672
15255
|
// A unit some target does not read drops the gradient there.
|
|
14673
15256
|
const unit = amount[1];
|
|
14674
15257
|
if (unit === undefined || unit === "%") continue;
|
|
14675
|
-
if (
|
|
14676
|
-
|
|
15258
|
+
if (!_readsOverridingValue(_valueSupportedFrom(`unit ${unit}`))) {
|
|
15259
|
+
return false;
|
|
15260
|
+
}
|
|
15261
|
+
continue;
|
|
14677
15262
|
}
|
|
15263
|
+
let color = false;
|
|
14678
15264
|
if (_LONE_HEX_RE.test(component)) {
|
|
14679
|
-
if (
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
15265
|
+
if (
|
|
15266
|
+
component.length !== 4 &&
|
|
15267
|
+
component.length !== 7 &&
|
|
15268
|
+
!_S._hexAlphaAllowed
|
|
15269
|
+
) {
|
|
15270
|
+
return false;
|
|
15271
|
+
}
|
|
15272
|
+
color = true;
|
|
15273
|
+
} else if (
|
|
15274
|
+
getColorNameToRgb().has(lowered) &&
|
|
15275
|
+
!LATER_COLOR_NAMES.has(lowered)
|
|
15276
|
+
) {
|
|
15277
|
+
color = true;
|
|
14685
15278
|
}
|
|
14686
|
-
return false;
|
|
15279
|
+
if (!color || ++colors > 1 || words !== 0) return false;
|
|
14687
15280
|
}
|
|
15281
|
+
if (colors !== 0) stops++;
|
|
14688
15282
|
}
|
|
14689
|
-
return
|
|
15283
|
+
return stops >= stopsNeeded;
|
|
14690
15284
|
};
|
|
14691
15285
|
|
|
14692
15286
|
/**
|
|
@@ -14714,7 +15308,7 @@ const _textDecorationRead = (components) => {
|
|
|
14714
15308
|
);
|
|
14715
15309
|
if (keywords.includes(toLowerCaseIfNeeded(written))) continue;
|
|
14716
15310
|
// A color or an amount, read as a gradient's stop or length is.
|
|
14717
|
-
if (!_gradientArgumentsRead(written)) return false;
|
|
15311
|
+
if (!_gradientArgumentsRead(written, 0)) return false;
|
|
14718
15312
|
}
|
|
14719
15313
|
return true;
|
|
14720
15314
|
};
|
|
@@ -14794,6 +15388,17 @@ const _writeIsDead = (path, writer, earlier, later, keyframe) => {
|
|
|
14794
15388
|
);
|
|
14795
15389
|
};
|
|
14796
15390
|
|
|
15391
|
+
/**
|
|
15392
|
+
* A property's first word, what an unnamed property of its family starts with:
|
|
15393
|
+
* `margin` for `margin-top`.
|
|
15394
|
+
* @param {string} name a property
|
|
15395
|
+
* @returns {string} the word before its first `-`
|
|
15396
|
+
*/
|
|
15397
|
+
const _firstPropertyWord = (name) => {
|
|
15398
|
+
const dash = name.indexOf("-");
|
|
15399
|
+
return dash === -1 ? name : name.slice(0, dash);
|
|
15400
|
+
};
|
|
15401
|
+
|
|
14797
15402
|
/**
|
|
14798
15403
|
* Merge a box family's four longhands into their shorthand, which sets exactly
|
|
14799
15404
|
* those four (see `BOX_LONGHANDS`) — so the rule computes the same either way.
|
|
@@ -14834,7 +15439,22 @@ const _mergeBoxLonghands = (
|
|
|
14834
15439
|
// tail uncovered, dropping its declaration.
|
|
14835
15440
|
/** @type {Set<Node> | null} */
|
|
14836
15441
|
let claimed = null;
|
|
14837
|
-
|
|
15442
|
+
// Only an entry whose first longhand the block writes can merge, and blocks
|
|
15443
|
+
// write few properties against a table of dozens of entries.
|
|
15444
|
+
const firsts = _mergeTableFirsts(table);
|
|
15445
|
+
/** @type {number[]} */
|
|
15446
|
+
const candidates = [];
|
|
15447
|
+
for (const property of at.keys()) {
|
|
15448
|
+
const entries = firsts.get(property);
|
|
15449
|
+
if (entries !== undefined) {
|
|
15450
|
+
for (const entry of entries) candidates.push(entry);
|
|
15451
|
+
}
|
|
15452
|
+
}
|
|
15453
|
+
if (candidates.length === 0) return null;
|
|
15454
|
+
// In table order, as the claims between entries are made.
|
|
15455
|
+
if (candidates.length > 1) candidates.sort((a, b) => a - b);
|
|
15456
|
+
for (let c = 0; c < candidates.length; c++) {
|
|
15457
|
+
const e = candidates[c];
|
|
14838
15458
|
const shorthand = table[e][0];
|
|
14839
15459
|
const longhands = table[e][1];
|
|
14840
15460
|
// A plain loop: this runs for every table entry of every rule, where one
|
|
@@ -14869,10 +15489,9 @@ const _mergeBoxLonghands = (
|
|
|
14869
15489
|
// merges the same before and after it is reordered. A pair family states no
|
|
14870
15490
|
// prefix at all — its longhands are the two names — so a property no dataset
|
|
14871
15491
|
// names is read by either one's first segment.
|
|
14872
|
-
const
|
|
14873
|
-
|
|
14874
|
-
const
|
|
14875
|
-
const families = [prefix, ...longhands.map(head)];
|
|
15492
|
+
const prefix =
|
|
15493
|
+
BOX_FAMILY_PREFIX.get(shorthand) || _firstPropertyWord(shorthand);
|
|
15494
|
+
const families = [prefix, ...longhands.map(_firstPropertyWord)];
|
|
14876
15495
|
let blocked = false;
|
|
14877
15496
|
for (
|
|
14878
15497
|
let i = ordered[0] + 1;
|
|
@@ -14904,15 +15523,14 @@ const _mergeBoxLonghands = (
|
|
|
14904
15523
|
if (indexes.some((i) => _isImportant(decls[i]) !== important)) continue;
|
|
14905
15524
|
const values = [];
|
|
14906
15525
|
for (const i of indexes) {
|
|
14907
|
-
|
|
14908
|
-
if (components.length !== 1) break;
|
|
15526
|
+
if (!_isSingleValue(path, decls[i], writer)) break;
|
|
14909
15527
|
// What the declaration prints, not what its tokens spell: a zero length
|
|
14910
15528
|
// loses its unit as a declaration is written, and a token knows no property.
|
|
14911
15529
|
const text = writer.get(decls[i]);
|
|
14912
15530
|
const colon = text.indexOf(":");
|
|
14913
15531
|
values.push(
|
|
14914
15532
|
colon === -1
|
|
14915
|
-
?
|
|
15533
|
+
? _valueComponents(path, decls[i], writer)[0]
|
|
14916
15534
|
: text.slice(colon + 1, _printedValueEnd(text, colon))
|
|
14917
15535
|
);
|
|
14918
15536
|
}
|
|
@@ -15027,7 +15645,7 @@ const _mergeBoxLonghands = (
|
|
|
15027
15645
|
};
|
|
15028
15646
|
|
|
15029
15647
|
// A `<custom-ident>` as printed; one the printer keeps escaped fails it.
|
|
15030
|
-
const _GRID_IDENT_RE = /^(?:--|-?[a-
|
|
15648
|
+
const _GRID_IDENT_RE = /^(?:--|-?[a-zA-Z_\u0080-\uFFFF])[-\w\u0080-\uFFFF]*$/;
|
|
15031
15649
|
const _GRID_INTEGER_RE = /^[-+]?\d+$/;
|
|
15032
15650
|
|
|
15033
15651
|
/**
|
|
@@ -15324,6 +15942,30 @@ const MERGE_FAMILY = 1;
|
|
|
15324
15942
|
const MERGE_SLASH = 2;
|
|
15325
15943
|
const MERGE_ORDERED = 3;
|
|
15326
15944
|
|
|
15945
|
+
/** @type {WeakMap<[string, string[]][], Map<string, number[]>>} */
|
|
15946
|
+
const _mergeTableFirstsCache = new WeakMap();
|
|
15947
|
+
|
|
15948
|
+
/**
|
|
15949
|
+
* A merge table's entries by their first longhand, which a block has to write
|
|
15950
|
+
* for the entry to merge.
|
|
15951
|
+
* @param {[string, string[]][]} table a merge table
|
|
15952
|
+
* @returns {Map<string, number[]>} the indexes of the entries each first longhand leads
|
|
15953
|
+
*/
|
|
15954
|
+
const _mergeTableFirsts = (table) => {
|
|
15955
|
+
let firsts = _mergeTableFirstsCache.get(table);
|
|
15956
|
+
if (firsts === undefined) {
|
|
15957
|
+
firsts = new Map();
|
|
15958
|
+
for (let e = 0; e < table.length; e++) {
|
|
15959
|
+
const first = table[e][1][0];
|
|
15960
|
+
const entries = firsts.get(first);
|
|
15961
|
+
if (entries === undefined) firsts.set(first, [e]);
|
|
15962
|
+
else entries.push(e);
|
|
15963
|
+
}
|
|
15964
|
+
_mergeTableFirstsCache.set(table, firsts);
|
|
15965
|
+
}
|
|
15966
|
+
return firsts;
|
|
15967
|
+
};
|
|
15968
|
+
|
|
15327
15969
|
// Each table as its entries, read by index: a `for…of` over the map would
|
|
15328
15970
|
// allocate an entry pair per row of every table for every block merged.
|
|
15329
15971
|
/** @type {[[string, string[]][], number][]} */
|
|
@@ -15358,6 +16000,14 @@ const MERGE_TABLES = [
|
|
|
15358
16000
|
* concatenated: its own, or one a child carries. The run that grew it settles
|
|
15359
16001
|
* it where the run ends (`_settleJoinedSeams`), so a join stays what it costs
|
|
15360
16002
|
* to append rather than re-reading the list it has
|
|
16003
|
+
* @property {string | null} list its prelude where a join wrote it, so the next
|
|
16004
|
+
* join reads or appends to it rather than cutting it back out of `text`, which
|
|
16005
|
+
* would flatten the whole run so far; null to read it off `text`
|
|
16006
|
+
* @property {string | null} block the text from its `{` on, held beside `list`
|
|
16007
|
+
* for the same reason
|
|
16008
|
+
* @property {PrintedDeclaration[] | null} declarations its block's declarations
|
|
16009
|
+
* where a join of two blocks under one prelude wrote them, so the next join
|
|
16010
|
+
* reads them rather than parsing the whole block again; null to parse `text`
|
|
15361
16011
|
*/
|
|
15362
16012
|
|
|
15363
16013
|
// Each printed rule the merge can join -> its entry, by node id. Emptied per
|
|
@@ -15450,7 +16100,10 @@ const _opaqueEntry = (text) => ({
|
|
|
15450
16100
|
listKind: LIST_KIND_SELECTOR,
|
|
15451
16101
|
children: null,
|
|
15452
16102
|
head: "",
|
|
15453
|
-
unordered: false
|
|
16103
|
+
unordered: false,
|
|
16104
|
+
list: null,
|
|
16105
|
+
block: null,
|
|
16106
|
+
declarations: null
|
|
15454
16107
|
});
|
|
15455
16108
|
|
|
15456
16109
|
/**
|
|
@@ -15462,7 +16115,7 @@ const _opaqueEntry = (text) => ({
|
|
|
15462
16115
|
const _entryListable = (entry) => {
|
|
15463
16116
|
if (entry.listable === LIST_UNKNOWN) {
|
|
15464
16117
|
entry.listable = _isJoinablePrelude(
|
|
15465
|
-
|
|
16118
|
+
_entryPrelude(entry),
|
|
15466
16119
|
entry.listKind === LIST_KIND_KEYFRAME,
|
|
15467
16120
|
entry.listKind === LIST_KIND_NESTED
|
|
15468
16121
|
)
|
|
@@ -15501,22 +16154,33 @@ const _STRING = String.raw`"(?:[^"\\]|\\[\s\S])*"|'(?:[^'\\]|\\[\s\S])*'`;
|
|
|
15501
16154
|
const _ATTRIBUTE = String.raw`\[(?:${_IDENT}\|)?${_IDENT}(?:[~|^$*]?=(?:${_IDENT}|${_STRING})(?: [iI])?)?\]`;
|
|
15502
16155
|
// CSS whitespace: U+00A0 is an identifier's character, not a separator.
|
|
15503
16156
|
const _SPACE = "[ \\t\\n\\f\\r]";
|
|
15504
|
-
|
|
16157
|
+
/**
|
|
16158
|
+
* A pattern matching the word in any ASCII case, where an `i` flag would make V8
|
|
16159
|
+
* case-fold the whole `\u00A0-\uFFFF` class too, costing milliseconds to compile.
|
|
16160
|
+
* @param {string} word a lowercase word
|
|
16161
|
+
* @returns {string} the pattern
|
|
16162
|
+
*/
|
|
16163
|
+
const _anyCase = (word) =>
|
|
16164
|
+
word.replace(/[a-z]/g, (letter) => `[${letter}${letter.toUpperCase()}]`);
|
|
16165
|
+
const _LEGACY_ELEMENT_NAMES = [...LEGACY_PSEUDO_ELEMENTS]
|
|
16166
|
+
.map(_anyCase)
|
|
16167
|
+
.join("|");
|
|
15505
16168
|
// A pseudo-class with no selector inside it: argument-less, or the `An+B` of an
|
|
15506
16169
|
// `:nth-*()`, whose grammar is closed — the one argument a shape may carry.
|
|
15507
16170
|
const _PSEUDO_CLASS = String.raw`:(?!(?:${_LEGACY_ELEMENT_NAMES})(?![-\w]))(?:(?:${[
|
|
15508
16171
|
...NTH_PSEUDO_FUNCTIONS
|
|
15509
|
-
]
|
|
15510
|
-
|
|
15511
|
-
)})\(${_SPACE}*(
|
|
16172
|
+
]
|
|
16173
|
+
.map(_anyCase)
|
|
16174
|
+
.join("|")})\(${_SPACE}*(?:${_anyCase("odd")}|${_anyCase(
|
|
16175
|
+
"even"
|
|
16176
|
+
)}|[-+]?(?:\d*[nN](?:${_SPACE}*[-+]${_SPACE}*\d+)?|\d+))${_SPACE}*\)|[-\w]+(?![-\w(]))`;
|
|
15512
16177
|
// A pseudo-element ends the selector it is in: anything after it is no shape
|
|
15513
16178
|
// an engine parses.
|
|
15514
16179
|
const _PSEUDO_ELEMENT = String.raw`(?:::[-\w]+|:(?:${_LEGACY_ELEMENT_NAMES}))(?![-\w(])`;
|
|
15515
16180
|
const _COMPOUND = `(?:\\*|${_IDENT}|[.#]${_IDENT}|${_ATTRIBUTE})(?:[.#]${_IDENT}|${_ATTRIBUTE}|${_PSEUDO_CLASS})*`;
|
|
15516
16181
|
const _COMBINATOR = `(?:${_SPACE}*[>+~]${_SPACE}*|${_SPACE}+)`;
|
|
15517
16182
|
const _JOINABLE_SELECTOR_RE = new RegExp(
|
|
15518
|
-
`^(?:${_COMPOUND}${_COMBINATOR})*${_COMPOUND}(?:${_PSEUDO_ELEMENT})
|
|
15519
|
-
"i"
|
|
16183
|
+
`^(?:${_COMPOUND}${_COMBINATOR})*${_COMPOUND}(?:${_PSEUDO_ELEMENT})?$`
|
|
15520
16184
|
);
|
|
15521
16185
|
|
|
15522
16186
|
// The same, for a rule nested in another: `&` joins there because an engine that
|
|
@@ -15524,8 +16188,7 @@ const _JOINABLE_SELECTOR_RE = new RegExp(
|
|
|
15524
16188
|
// dropped together rather than one taking the other down.
|
|
15525
16189
|
const _NESTED_COMPOUND = `(?:&(?:${_COMPOUND})?|${_COMPOUND})`;
|
|
15526
16190
|
const _JOINABLE_NESTED_RE = new RegExp(
|
|
15527
|
-
`^(?:${_NESTED_COMPOUND}${_COMBINATOR})*${_NESTED_COMPOUND}(?:${_PSEUDO_ELEMENT})
|
|
15528
|
-
"i"
|
|
16191
|
+
`^(?:${_NESTED_COMPOUND}${_COMBINATOR})*${_NESTED_COMPOUND}(?:${_PSEUDO_ELEMENT})?$`
|
|
15529
16192
|
);
|
|
15530
16193
|
|
|
15531
16194
|
// A keyframe selector is a percentage or `to` — `from` is already printed `0%`.
|
|
@@ -15717,6 +16380,8 @@ const _blockProperties = (body) => {
|
|
|
15717
16380
|
// The `!important` a printed declaration ends in, whatever it was written as.
|
|
15718
16381
|
const _PRINTED_IMPORTANT_RE = /!\s*important$/i;
|
|
15719
16382
|
|
|
16383
|
+
/** @typedef {{ property: string, value: string, text: string }} PrintedDeclaration one declaration of a printed block: its lowercased property, its value and its whole text */
|
|
16384
|
+
|
|
15720
16385
|
/**
|
|
15721
16386
|
* The declarations one printed block holds, each as the whole of what it is
|
|
15722
16387
|
* written as. Built on the same walk `_blockProperties` makes, since what a
|
|
@@ -15724,10 +16389,10 @@ const _PRINTED_IMPORTANT_RE = /!\s*important$/i;
|
|
|
15724
16389
|
* else — a nested rule of its own, whose declarations are read at its place
|
|
15725
16390
|
* rather than at the block's.
|
|
15726
16391
|
* @param {string} body the block's printed body, between its braces
|
|
15727
|
-
* @returns {
|
|
16392
|
+
* @returns {PrintedDeclaration[] | null} the declarations, in order, or null
|
|
15728
16393
|
*/
|
|
15729
16394
|
const _blockDeclarations = (body) => {
|
|
15730
|
-
/** @type {
|
|
16395
|
+
/** @type {PrintedDeclaration[]} */
|
|
15731
16396
|
const out = [];
|
|
15732
16397
|
let depth = 0;
|
|
15733
16398
|
let quote = 0;
|
|
@@ -15807,6 +16472,28 @@ const _rangeEquals = (a, aStart, aEnd, b, bStart, bEnd) => {
|
|
|
15807
16472
|
return true;
|
|
15808
16473
|
};
|
|
15809
16474
|
|
|
16475
|
+
/**
|
|
16476
|
+
* A rule's prelude, read from where a join holds it.
|
|
16477
|
+
* @param {RuleEntry} entry the rule
|
|
16478
|
+
* @returns {string} its prelude, as printed
|
|
16479
|
+
*/
|
|
16480
|
+
const _entryPrelude = (entry) =>
|
|
16481
|
+
entry.list === null ? entry.text.slice(0, entry.prelude) : entry.list;
|
|
16482
|
+
|
|
16483
|
+
/**
|
|
16484
|
+
* Whether two rules' blocks say the same, each read from where it is held.
|
|
16485
|
+
* @param {RuleEntry} a one rule
|
|
16486
|
+
* @param {RuleEntry} b another
|
|
16487
|
+
* @returns {boolean} true when the blocks are equal
|
|
16488
|
+
*/
|
|
16489
|
+
const _blocksEqual = (a, b) => {
|
|
16490
|
+
const aText = a.block === null ? a.text : a.block;
|
|
16491
|
+
const aStart = a.block === null ? a.prelude : 0;
|
|
16492
|
+
const bText = b.block === null ? b.text : b.block;
|
|
16493
|
+
const bStart = b.block === null ? b.prelude : 0;
|
|
16494
|
+
return _rangeEquals(aText, aStart, aText.length, bText, bStart, bText.length);
|
|
16495
|
+
};
|
|
16496
|
+
|
|
15810
16497
|
// A layer with no name: the one at-rule prelude that is not the same rule twice.
|
|
15811
16498
|
const ANONYMOUS_LAYER_RE = /^@layer[ \t\n\r\f]*$/i;
|
|
15812
16499
|
|
|
@@ -15902,7 +16589,11 @@ const _joinedHead = (joined) => {
|
|
|
15902
16589
|
* @returns {string} the rule the block prints as
|
|
15903
16590
|
*/
|
|
15904
16591
|
const _joinedBlockText = (prelude, head, joined) => {
|
|
15905
|
-
|
|
16592
|
+
const last = joined[joined.length - 1];
|
|
16593
|
+
let lastText = last.text;
|
|
16594
|
+
// Read off the block where a join holds one: the text before it is a list a
|
|
16595
|
+
// run of joins is still growing, which reading any of the text flattens.
|
|
16596
|
+
if (last.block !== null) return `${prelude}{${head}${lastText}}`;
|
|
15906
16597
|
while (
|
|
15907
16598
|
lastText.length !== 0 &&
|
|
15908
16599
|
lastText.charCodeAt(lastText.length - 1) === CC_SEMICOLON
|
|
@@ -15943,16 +16634,16 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
15943
16634
|
// cutting each one's prelude and block out to answer that is two pieces of
|
|
15944
16635
|
// string per meeting for an answer that is usually no.
|
|
15945
16636
|
const preludesEqual = _rangeEquals(
|
|
15946
|
-
before.text,
|
|
16637
|
+
before.list === null ? before.text : before.list,
|
|
15947
16638
|
0,
|
|
15948
16639
|
before.prelude,
|
|
15949
|
-
entry.text,
|
|
16640
|
+
entry.list === null ? entry.text : entry.list,
|
|
15950
16641
|
0,
|
|
15951
16642
|
entry.prelude
|
|
15952
16643
|
);
|
|
15953
16644
|
if (before.atRule) {
|
|
15954
16645
|
if (!preludesEqual) return null;
|
|
15955
|
-
const beforePrelude =
|
|
16646
|
+
const beforePrelude = _entryPrelude(before);
|
|
15956
16647
|
// CSS Cascade 5 §6.4.1: every `@layer {` opens a layer of its own, and a
|
|
15957
16648
|
// later layer beats an earlier one whatever the selectors say — so joining
|
|
15958
16649
|
// the two hands the block back to specificity.
|
|
@@ -16036,17 +16727,13 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
16036
16727
|
listKind: LIST_KIND_SELECTOR,
|
|
16037
16728
|
children: joined,
|
|
16038
16729
|
head,
|
|
16039
|
-
unordered
|
|
16730
|
+
unordered,
|
|
16731
|
+
list: beforePrelude,
|
|
16732
|
+
block: null,
|
|
16733
|
+
declarations: null
|
|
16040
16734
|
};
|
|
16041
16735
|
}
|
|
16042
|
-
const blocksEqual =
|
|
16043
|
-
before.text,
|
|
16044
|
-
before.prelude,
|
|
16045
|
-
before.text.length,
|
|
16046
|
-
entry.text,
|
|
16047
|
-
entry.prelude,
|
|
16048
|
-
entry.text.length
|
|
16049
|
-
);
|
|
16736
|
+
const blocksEqual = _blocksEqual(before, entry);
|
|
16050
16737
|
// The same selector twice is one rule: nothing stands between them, so its
|
|
16051
16738
|
// declarations are read in the order they were written either way.
|
|
16052
16739
|
if (preludesEqual) {
|
|
@@ -16056,12 +16743,14 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
16056
16743
|
// Declarations after a nested rule are the implicit `& {…}` the engine
|
|
16057
16744
|
// builds for them, which is a rule the sheet did not have.
|
|
16058
16745
|
if (!before.plain) return null;
|
|
16059
|
-
const beforePrelude =
|
|
16060
|
-
const body = before.text.slice(before.prelude + 1, -1);
|
|
16746
|
+
const beforePrelude = _entryPrelude(before);
|
|
16061
16747
|
const rest = entry.text.slice(entry.prelude + 1, -1);
|
|
16062
16748
|
// One block holds a property once, so two blocks join only where the later
|
|
16063
16749
|
// declaration is one the earlier could not have been read behind.
|
|
16064
|
-
const earlier =
|
|
16750
|
+
const earlier =
|
|
16751
|
+
before.declarations === null
|
|
16752
|
+
? _blockDeclarations(before.text.slice(before.prelude + 1, -1))
|
|
16753
|
+
: before.declarations;
|
|
16065
16754
|
if (earlier === null) return null;
|
|
16066
16755
|
// The later block's declarations, and only where it holds nothing else: a
|
|
16067
16756
|
// nested rule leaves the properties readable but not their values.
|
|
@@ -16109,19 +16798,42 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
16109
16798
|
}
|
|
16110
16799
|
const kept =
|
|
16111
16800
|
dropped.size === 0
|
|
16112
|
-
?
|
|
16113
|
-
: earlier
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
|
|
16801
|
+
? earlier
|
|
16802
|
+
: earlier.filter((_, at) => !dropped.has(at));
|
|
16803
|
+
const added =
|
|
16804
|
+
later !== null
|
|
16805
|
+
? later
|
|
16806
|
+
: rest.length === 0
|
|
16807
|
+
? []
|
|
16808
|
+
: _blockDeclarations(rest);
|
|
16809
|
+
/** @type {PrintedDeclaration[] | null} */
|
|
16810
|
+
let declarations = null;
|
|
16811
|
+
let joined;
|
|
16812
|
+
// Carried as a list only where reading the joined text back would split it
|
|
16813
|
+
// the same way, which a quote the split reads as left open would not.
|
|
16814
|
+
if (
|
|
16815
|
+
added === null ||
|
|
16816
|
+
!_lastDeclarationCloses(added) ||
|
|
16817
|
+
(before.declarations === null && !_lastDeclarationCloses(earlier))
|
|
16818
|
+
) {
|
|
16819
|
+
const keptText = kept.map((one) => one.text).join(";");
|
|
16820
|
+
joined = _orderJoinedBody(
|
|
16821
|
+
keptText.length === 0 || rest.length === 0
|
|
16822
|
+
? keptText + rest
|
|
16823
|
+
: _mergeJoinedBody(`${keptText};${rest}`),
|
|
16824
|
+
before.plain && entry.plain
|
|
16825
|
+
);
|
|
16826
|
+
} else {
|
|
16827
|
+
declarations = _orderJoinedDeclarations(
|
|
16828
|
+
kept.length === 0 || added.length === 0
|
|
16829
|
+
? [...kept, ...added]
|
|
16830
|
+
: _mergeJoinedBoxLonghands([...kept, ...added]),
|
|
16831
|
+
before.plain && entry.plain
|
|
16832
|
+
);
|
|
16833
|
+
// Each block already dropped the `;` its own `}` made redundant, so the
|
|
16834
|
+
// one that is no longer last needs it back.
|
|
16835
|
+
joined = declarations.map((one) => one.text).join(";");
|
|
16836
|
+
}
|
|
16125
16837
|
return {
|
|
16126
16838
|
text: `${beforePrelude}{${joined}}`,
|
|
16127
16839
|
prelude: before.prelude,
|
|
@@ -16136,7 +16848,10 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
16136
16848
|
head: "",
|
|
16137
16849
|
// The two preludes are equal, so the list this keeps is the one `before`
|
|
16138
16850
|
// already carried — settled or not.
|
|
16139
|
-
unordered: before.unordered
|
|
16851
|
+
unordered: before.unordered,
|
|
16852
|
+
list: beforePrelude,
|
|
16853
|
+
block: null,
|
|
16854
|
+
declarations
|
|
16140
16855
|
};
|
|
16141
16856
|
}
|
|
16142
16857
|
if (!blocksEqual || !_entryListable(before) || !_entryListable(entry)) {
|
|
@@ -16145,12 +16860,11 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
16145
16860
|
// Concatenated, never canonicalized here: a run of joins would then re-read
|
|
16146
16861
|
// the whole list once per rule. It is noted as a list still to settle, and
|
|
16147
16862
|
// the run that grew it orders it once where it ends.
|
|
16148
|
-
const list = `${
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
)}`;
|
|
16863
|
+
const list = `${_entryPrelude(before)},${_entryPrelude(entry)}`;
|
|
16864
|
+
const block =
|
|
16865
|
+
before.block === null ? before.text.slice(before.prelude) : before.block;
|
|
16152
16866
|
return {
|
|
16153
|
-
text: list +
|
|
16867
|
+
text: list + block,
|
|
16154
16868
|
prelude: list.length,
|
|
16155
16869
|
atRule: false,
|
|
16156
16870
|
plain: true,
|
|
@@ -16158,13 +16872,30 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
16158
16872
|
listKind: before.listKind,
|
|
16159
16873
|
children: null,
|
|
16160
16874
|
head: "",
|
|
16161
|
-
unordered: true
|
|
16875
|
+
unordered: true,
|
|
16876
|
+
list,
|
|
16877
|
+
block,
|
|
16878
|
+
declarations: null
|
|
16162
16879
|
};
|
|
16163
16880
|
};
|
|
16164
16881
|
|
|
16165
16882
|
/**
|
|
16166
|
-
*
|
|
16167
|
-
*
|
|
16883
|
+
* `_mergeJoinedBoxLonghands` over a joined block's text.
|
|
16884
|
+
* @param {string} body the joined block's declarations
|
|
16885
|
+
* @returns {string} the body, each family it could merge merged
|
|
16886
|
+
*/
|
|
16887
|
+
const _mergeJoinedBody = (body) => {
|
|
16888
|
+
if (!_S._transforms.mergeLonghands) return body;
|
|
16889
|
+
const declarations = _blockDeclarations(body);
|
|
16890
|
+
if (declarations === null) return body;
|
|
16891
|
+
const merged = _mergeJoinedBoxLonghands(declarations);
|
|
16892
|
+
return merged === declarations
|
|
16893
|
+
? body
|
|
16894
|
+
: merged.map((one) => one.text).join(";");
|
|
16895
|
+
};
|
|
16896
|
+
|
|
16897
|
+
/**
|
|
16898
|
+
* `_orderJoinedDeclarations` over a joined block's text.
|
|
16168
16899
|
* @param {string} body the joined body, its braces excluded
|
|
16169
16900
|
* @param {boolean} plain whether it holds declarations and nothing else
|
|
16170
16901
|
* @returns {string} the body, reordered where that is allowed
|
|
@@ -16172,10 +16903,64 @@ const _joinRuleEntries = (before, entry, owned) => {
|
|
|
16172
16903
|
const _orderJoinedBody = (body, plain) => {
|
|
16173
16904
|
if (!plain || !_S._transforms.reorderDeclarations) return body;
|
|
16174
16905
|
const declarations = _blockDeclarations(body);
|
|
16175
|
-
if (declarations === null
|
|
16176
|
-
const
|
|
16177
|
-
|
|
16178
|
-
|
|
16906
|
+
if (declarations === null) return body;
|
|
16907
|
+
const ordered = _orderJoinedDeclarations(declarations, plain);
|
|
16908
|
+
return ordered === declarations
|
|
16909
|
+
? body
|
|
16910
|
+
: ordered.map((one) => one.text).join(";");
|
|
16911
|
+
};
|
|
16912
|
+
|
|
16913
|
+
/**
|
|
16914
|
+
* Whether the last of a printed block's declarations leaves the split that read
|
|
16915
|
+
* them where it started: outside a string and every bracket. Only the last can
|
|
16916
|
+
* end elsewhere, since the split parts the others only there.
|
|
16917
|
+
* @param {PrintedDeclaration[]} declarations the declarations, in order
|
|
16918
|
+
* @returns {boolean} true when a text joined after them is read on its own
|
|
16919
|
+
*/
|
|
16920
|
+
const _lastDeclarationCloses = (declarations) => {
|
|
16921
|
+
if (declarations.length === 0) return true;
|
|
16922
|
+
const text = declarations[declarations.length - 1].text;
|
|
16923
|
+
let depth = 0;
|
|
16924
|
+
let quote = 0;
|
|
16925
|
+
for (let i = 0; i < text.length; i++) {
|
|
16926
|
+
const cc = text.charCodeAt(i);
|
|
16927
|
+
if (quote !== 0) {
|
|
16928
|
+
if (cc === CC_REVERSE_SOLIDUS) {
|
|
16929
|
+
i++;
|
|
16930
|
+
} else if (cc === quote) {
|
|
16931
|
+
quote = 0;
|
|
16932
|
+
}
|
|
16933
|
+
} else if (cc === CC_QUOTATION_MARK || cc === CC_APOSTROPHE) {
|
|
16934
|
+
quote = cc;
|
|
16935
|
+
} else if (
|
|
16936
|
+
cc === CC_LEFT_PARENTHESIS ||
|
|
16937
|
+
cc === CC_LEFT_SQUARE ||
|
|
16938
|
+
cc === CC_LEFT_CURLY
|
|
16939
|
+
) {
|
|
16940
|
+
depth++;
|
|
16941
|
+
} else if (
|
|
16942
|
+
cc === CC_RIGHT_PARENTHESIS ||
|
|
16943
|
+
cc === CC_RIGHT_SQUARE ||
|
|
16944
|
+
cc === CC_RIGHT_CURLY
|
|
16945
|
+
) {
|
|
16946
|
+
depth--;
|
|
16947
|
+
}
|
|
16948
|
+
}
|
|
16949
|
+
return quote === 0 && depth === 0;
|
|
16950
|
+
};
|
|
16951
|
+
|
|
16952
|
+
/**
|
|
16953
|
+
* A joined style rule's declarations in the order `_declarationOrder` prints
|
|
16954
|
+
* one in, which each half was already in but the two concatenated need not be.
|
|
16955
|
+
* @param {PrintedDeclaration[]} declarations the joined block's declarations
|
|
16956
|
+
* @param {boolean} plain whether it holds declarations and nothing else
|
|
16957
|
+
* @returns {PrintedDeclaration[]} them, reordered where that is allowed
|
|
16958
|
+
*/
|
|
16959
|
+
const _orderJoinedDeclarations = (declarations, plain) => {
|
|
16960
|
+
if (!plain || !_S._transforms.reorderDeclarations) return declarations;
|
|
16961
|
+
if (declarations.length < 2) return declarations;
|
|
16962
|
+
const order = _declarationOrder(declarations.map((one) => one.text));
|
|
16963
|
+
return order === null ? declarations : order.map((at) => declarations[at]);
|
|
16179
16964
|
};
|
|
16180
16965
|
|
|
16181
16966
|
// The group rules whose declarations, nested in a style rule, are the implicit
|
|
@@ -16237,7 +17022,7 @@ const _noteStreamedLayerClose = (d, opener, at) => {
|
|
|
16237
17022
|
layers.set(opener, {
|
|
16238
17023
|
at,
|
|
16239
17024
|
subtree: false,
|
|
16240
|
-
taken:
|
|
17025
|
+
taken: new TakenPiece(at, "}", -1)
|
|
16241
17026
|
});
|
|
16242
17027
|
};
|
|
16243
17028
|
|
|
@@ -16500,7 +17285,7 @@ const _nothingBetweenShadows = (items, texts, from, to, declared, subjects) => {
|
|
|
16500
17285
|
// selectors and pseudos are gone: what either holds may look like a combinator.
|
|
16501
17286
|
const _SUBJECT_STRIP_RE = new RegExp(
|
|
16502
17287
|
`${_ATTRIBUTE}|${_PSEUDO_CLASS}|${_PSEUDO_ELEMENT}`,
|
|
16503
|
-
"
|
|
17288
|
+
"g"
|
|
16504
17289
|
);
|
|
16505
17290
|
const _SUBJECT_TYPE_RE = /(?:^|[\s>+~])([-\w\u00A0-\uFFFF]+)[^\s>+~]*$/;
|
|
16506
17291
|
|
|
@@ -16532,11 +17317,11 @@ const _subjectTypes = (prelude) => {
|
|
|
16532
17317
|
* @returns {Set<string> | null} the type names
|
|
16533
17318
|
*/
|
|
16534
17319
|
const _entrySubjectTypes = (entry) => {
|
|
16535
|
-
if (!entry.atRule) return _subjectTypes(
|
|
17320
|
+
if (!entry.atRule) return _subjectTypes(_entryPrelude(entry));
|
|
16536
17321
|
/** @type {Set<string>} */
|
|
16537
17322
|
const out = new Set();
|
|
16538
17323
|
for (const child of /** @type {RuleEntry[]} */ (entry.children)) {
|
|
16539
|
-
const types = _subjectTypes(
|
|
17324
|
+
const types = _subjectTypes(_entryPrelude(child));
|
|
16540
17325
|
if (types === null) return null;
|
|
16541
17326
|
for (const type of types) out.add(type);
|
|
16542
17327
|
}
|
|
@@ -16565,7 +17350,7 @@ const _subjectsDisjoint = (a, b) => {
|
|
|
16565
17350
|
* @returns {boolean} true where its block may be read for what it declares
|
|
16566
17351
|
*/
|
|
16567
17352
|
const _readableCondition = (entry) => {
|
|
16568
|
-
const name = _atRuleName(
|
|
17353
|
+
const name = _atRuleName(_entryPrelude(entry));
|
|
16569
17354
|
return name !== "layer" && MERGEABLE_AT_RULES.has(name);
|
|
16570
17355
|
};
|
|
16571
17356
|
|
|
@@ -16604,7 +17389,7 @@ const _atRuleDeclaredProperties = (entry) => {
|
|
|
16604
17389
|
* @returns {void}
|
|
16605
17390
|
*/
|
|
16606
17391
|
const _mergeDistantAtRule = (items, texts, held, entry, at) => {
|
|
16607
|
-
const prelude =
|
|
17392
|
+
const prelude = _entryPrelude(entry);
|
|
16608
17393
|
// WHY: a layer's place in the cascade is where its name is first written, not
|
|
16609
17394
|
// what its prelude states, so two blocks naming one are not the pair of equal
|
|
16610
17395
|
// conditions this joins. `_mergeNamedLayerBlocks` gathers those under the rule
|
|
@@ -16712,7 +17497,12 @@ const _dropRulesWrittenAgain = (items, texts) => {
|
|
|
16712
17497
|
/** @type {Map<string, number[]> | null} */
|
|
16713
17498
|
let held = null;
|
|
16714
17499
|
for (let i = 0; i < items.length; i++) {
|
|
16715
|
-
if (
|
|
17500
|
+
if (
|
|
17501
|
+
texts[i].length === 0 ||
|
|
17502
|
+
_types[_nodeIndex(items[i])] === NodeType.Declaration
|
|
17503
|
+
) {
|
|
17504
|
+
continue;
|
|
17505
|
+
}
|
|
16716
17506
|
const entry = _ruleEntryOf(items[i], texts[i]);
|
|
16717
17507
|
// Keyframes of one selector are not one cascade in every engine: an older
|
|
16718
17508
|
// one reads only the last block written for it.
|
|
@@ -16724,7 +17514,7 @@ const _dropRulesWrittenAgain = (items, texts) => {
|
|
|
16724
17514
|
) {
|
|
16725
17515
|
continue;
|
|
16726
17516
|
}
|
|
16727
|
-
const prelude =
|
|
17517
|
+
const prelude = _entryPrelude(entry);
|
|
16728
17518
|
if (held === null) held = new Map();
|
|
16729
17519
|
const earlier = held.get(prelude);
|
|
16730
17520
|
if (earlier === undefined) {
|
|
@@ -17106,17 +17896,19 @@ const _canonicalSelectorList = (prelude) => {
|
|
|
17106
17896
|
if (!prelude.includes(",")) return prelude;
|
|
17107
17897
|
const list = _splitSelectorList(prelude);
|
|
17108
17898
|
if (list.length < 2) return prelude;
|
|
17109
|
-
//
|
|
17110
|
-
// distinct, and already in order —
|
|
17111
|
-
//
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17899
|
+
// Strictly increasing answers both questions the common case turns on —
|
|
17900
|
+
// every selector distinct, and already in order — without a set: only two
|
|
17901
|
+
// equal selectors compare as zero.
|
|
17902
|
+
let i = 1;
|
|
17903
|
+
while (i < list.length && _compareSelectors(list[i - 1], list[i]) < 0) i++;
|
|
17904
|
+
if (i === list.length) return prelude;
|
|
17905
|
+
list.sort(_compareSelectors);
|
|
17906
|
+
let kept = 1;
|
|
17907
|
+
for (let j = 1; j < list.length; j++) {
|
|
17908
|
+
if (list[j] !== list[kept - 1]) list[kept++] = list[j];
|
|
17117
17909
|
}
|
|
17118
|
-
|
|
17119
|
-
return
|
|
17910
|
+
list.length = kept;
|
|
17911
|
+
return list.join(",");
|
|
17120
17912
|
};
|
|
17121
17913
|
|
|
17122
17914
|
/**
|
|
@@ -17155,10 +17947,13 @@ const _settleJoinedSeams = (entry) => {
|
|
|
17155
17947
|
const head = _joinedHead(settled);
|
|
17156
17948
|
return {
|
|
17157
17949
|
...entry,
|
|
17158
|
-
text: _joinedBlockText(
|
|
17950
|
+
text: _joinedBlockText(_entryPrelude(entry), head, settled),
|
|
17159
17951
|
children: settled,
|
|
17160
17952
|
head,
|
|
17161
|
-
unordered: false
|
|
17953
|
+
unordered: false,
|
|
17954
|
+
list: null,
|
|
17955
|
+
block: null,
|
|
17956
|
+
declarations: null
|
|
17162
17957
|
};
|
|
17163
17958
|
};
|
|
17164
17959
|
|
|
@@ -17173,7 +17968,7 @@ const _settleJoinedSeams = (entry) => {
|
|
|
17173
17968
|
const _canonicalizeJoined = (entry) => {
|
|
17174
17969
|
if (entry.atRule) return _settleJoinedSeams(entry);
|
|
17175
17970
|
if (entry.prelude === -1) return entry;
|
|
17176
|
-
const prelude =
|
|
17971
|
+
const prelude = _entryPrelude(entry);
|
|
17177
17972
|
const canonical = _canonicalSelectorList(prelude);
|
|
17178
17973
|
if (canonical === prelude) {
|
|
17179
17974
|
entry.unordered = false;
|
|
@@ -17183,7 +17978,10 @@ const _canonicalizeJoined = (entry) => {
|
|
|
17183
17978
|
...entry,
|
|
17184
17979
|
text: canonical + entry.text.slice(entry.prelude),
|
|
17185
17980
|
prelude: canonical.length,
|
|
17186
|
-
unordered: false
|
|
17981
|
+
unordered: false,
|
|
17982
|
+
list: null,
|
|
17983
|
+
block: null,
|
|
17984
|
+
declarations: null
|
|
17187
17985
|
};
|
|
17188
17986
|
};
|
|
17189
17987
|
|
|
@@ -17358,8 +18156,9 @@ const _rulePrelude = (path, writer, minify, outParts) => {
|
|
|
17358
18156
|
if (_S._transforms.customSelectors && _customSelectors.size !== 0) {
|
|
17359
18157
|
_writeCustomSelector(parts);
|
|
17360
18158
|
}
|
|
17361
|
-
_foldPseudoNames(parts)
|
|
17362
|
-
|
|
18159
|
+
if (_foldPseudoNames(parts) && _S._transforms.shortenSelectors) {
|
|
18160
|
+
_unwrapLoneIs(parts);
|
|
18161
|
+
}
|
|
17363
18162
|
// After the fold, which is what makes the names above match, and before the
|
|
17364
18163
|
// class replacement, so a pseudo written as a class is one either way.
|
|
17365
18164
|
if (!_S._isSelectorAllowed) {
|
|
@@ -17410,9 +18209,7 @@ const _rulePrelude = (path, writer, minify, outParts) => {
|
|
|
17410
18209
|
}
|
|
17411
18210
|
const list = _canonicalSelectorList(prelude);
|
|
17412
18211
|
if (_readsThroughGoneEngine(list)) {
|
|
17413
|
-
/** @type {Set<Node>} */ (_unusedRules).add(
|
|
17414
|
-
/** @type {Node} */ (_S._currentNode)
|
|
17415
|
-
);
|
|
18212
|
+
/** @type {Set<Node>} */ (_unusedRules).add(_S._currentNode);
|
|
17416
18213
|
return list;
|
|
17417
18214
|
}
|
|
17418
18215
|
if (_unusedSymbols === null) return list;
|
|
@@ -17420,9 +18217,7 @@ const _rulePrelude = (path, writer, minify, outParts) => {
|
|
|
17420
18217
|
// Marked rather than dropped here: the rule's own printer takes it out, the
|
|
17421
18218
|
// same way it takes out one whose block ends up empty.
|
|
17422
18219
|
if (kept !== null) return kept;
|
|
17423
|
-
/** @type {Set<Node>} */ (_unusedRules).add(
|
|
17424
|
-
/** @type {Node} */ (_S._currentNode)
|
|
17425
|
-
);
|
|
18220
|
+
/** @type {Set<Node>} */ (_unusedRules).add(_S._currentNode);
|
|
17426
18221
|
return list;
|
|
17427
18222
|
}
|
|
17428
18223
|
if (
|
|
@@ -17435,9 +18230,7 @@ const _rulePrelude = (path, writer, minify, outParts) => {
|
|
|
17435
18230
|
unescapeIdentifier(prelude.slice(_rawName(path.node).length + 1).trim())
|
|
17436
18231
|
)
|
|
17437
18232
|
) {
|
|
17438
|
-
/** @type {Set<Node>} */ (_unusedRules).add(
|
|
17439
|
-
/** @type {Node} */ (_S._currentNode)
|
|
17440
|
-
);
|
|
18233
|
+
/** @type {Set<Node>} */ (_unusedRules).add(_S._currentNode);
|
|
17441
18234
|
}
|
|
17442
18235
|
return prelude;
|
|
17443
18236
|
};
|
|
@@ -17904,6 +18697,39 @@ const _canonicalProperty = (property) => {
|
|
|
17904
18697
|
const _orderMatters = (a, b) => {
|
|
17905
18698
|
if (a === b || a === "all" || b === "all") return true;
|
|
17906
18699
|
if (a.startsWith("--") || b.startsWith("--")) return false;
|
|
18700
|
+
let row = _orderMattersMemo.get(a);
|
|
18701
|
+
let matters = row === undefined ? undefined : row.get(b);
|
|
18702
|
+
if (matters !== undefined) return matters;
|
|
18703
|
+
matters = _computeOrderMatters(a, b);
|
|
18704
|
+
// The names are the author's, so a sheet of made-up ones starts again.
|
|
18705
|
+
if (_orderMattersEntries === _ORDER_MATTERS_ENTRIES) {
|
|
18706
|
+
_orderMattersMemo.clear();
|
|
18707
|
+
_orderMattersEntries = 0;
|
|
18708
|
+
row = undefined;
|
|
18709
|
+
}
|
|
18710
|
+
if (row === undefined) {
|
|
18711
|
+
row = new Map();
|
|
18712
|
+
_orderMattersMemo.set(a, row);
|
|
18713
|
+
}
|
|
18714
|
+
row.set(b, matters);
|
|
18715
|
+
_orderMattersEntries++;
|
|
18716
|
+
return matters;
|
|
18717
|
+
};
|
|
18718
|
+
|
|
18719
|
+
// `_orderMatters` by its first property, then its second: a sheet names few
|
|
18720
|
+
// properties, and the walks back over a block ask of the same pairs again.
|
|
18721
|
+
/** @type {Map<string, Map<string, boolean>>} */
|
|
18722
|
+
const _orderMattersMemo = new Map();
|
|
18723
|
+
let _orderMattersEntries = 0;
|
|
18724
|
+
const _ORDER_MATTERS_ENTRIES = 1 << 15;
|
|
18725
|
+
|
|
18726
|
+
/**
|
|
18727
|
+
* `_orderMatters` without the memo.
|
|
18728
|
+
* @param {string} a a property, read through `_canonicalProperty`
|
|
18729
|
+
* @param {string} b another
|
|
18730
|
+
* @returns {boolean} true when the two must keep their order
|
|
18731
|
+
*/
|
|
18732
|
+
const _computeOrderMatters = (a, b) => {
|
|
17907
18733
|
// A corner's radius is no side's: `border-bottom` sets no `-radius` it starts.
|
|
17908
18734
|
if (
|
|
17909
18735
|
a.charCodeAt(0) !== CC_HYPHEN_MINUS &&
|
|
@@ -17912,7 +18738,7 @@ const _orderMatters = (a, b) => {
|
|
|
17912
18738
|
) {
|
|
17913
18739
|
return false;
|
|
17914
18740
|
}
|
|
17915
|
-
if (a
|
|
18741
|
+
if (_extendsProperty(a, b) || _extendsProperty(b, a)) return true;
|
|
17916
18742
|
// A vendor property no standard one aliases says nothing of what it sets
|
|
17917
18743
|
// beyond its words: `-webkit-border-before` is a border side.
|
|
17918
18744
|
if (
|
|
@@ -17938,6 +18764,18 @@ const _orderMatters = (a, b) => {
|
|
|
17938
18764
|
);
|
|
17939
18765
|
};
|
|
17940
18766
|
|
|
18767
|
+
/**
|
|
18768
|
+
* Whether a property name is another's followed by more words: `margin-top` of
|
|
18769
|
+
* `margin`. Read in place, as a `${base}-` prefix would be built on every call.
|
|
18770
|
+
* @param {string} name a property
|
|
18771
|
+
* @param {string} base another
|
|
18772
|
+
* @returns {boolean} true when `name` starts with `base` and a `-`
|
|
18773
|
+
*/
|
|
18774
|
+
const _extendsProperty = (name, base) =>
|
|
18775
|
+
name.length > base.length &&
|
|
18776
|
+
name.charCodeAt(base.length) === CC_HYPHEN_MINUS &&
|
|
18777
|
+
name.startsWith(base);
|
|
18778
|
+
|
|
17941
18779
|
// A box side, corner or axis a property names, physical or flow-relative.
|
|
17942
18780
|
const _PHYSICAL_SIDE_RE =
|
|
17943
18781
|
/-(?:top|right|bottom|left|x|y)(?=-|$)|^(?:(?:min|max)-)?(?:top|right|bottom|left|width|height)$|-(?:width|height)$/;
|
|
@@ -17967,6 +18805,11 @@ const _blocksMerge = (between, later, families) => {
|
|
|
17967
18805
|
);
|
|
17968
18806
|
};
|
|
17969
18807
|
|
|
18808
|
+
// The properties `_sideRoot` reads as `inset`'s and as `size`'s sides.
|
|
18809
|
+
const _INSET_SIDE_RE = /^(?:top|right|bottom|left)$/;
|
|
18810
|
+
const _SIZE_PROPERTY_RE =
|
|
18811
|
+
/^(?:(?:min|max)-)?(?:width|height|inline-size|block-size)$/;
|
|
18812
|
+
|
|
17970
18813
|
/**
|
|
17971
18814
|
* The word a property's sides share: `margin` for `margin-left` and
|
|
17972
18815
|
* `margin-inline-start`, `inset` for `top`, `size` for `width` and `inline-size`.
|
|
@@ -17974,11 +18817,12 @@ const _blocksMerge = (between, later, families) => {
|
|
|
17974
18817
|
* @returns {string} that word
|
|
17975
18818
|
*/
|
|
17976
18819
|
const _sideRoot = (property) => {
|
|
17977
|
-
const bare =
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
|
|
18820
|
+
const bare =
|
|
18821
|
+
property.charCodeAt(0) === CC_HYPHEN_MINUS
|
|
18822
|
+
? property.replace(_VENDOR_ITEM_PREFIX_RE, "")
|
|
18823
|
+
: property;
|
|
18824
|
+
if (_INSET_SIDE_RE.test(bare)) return "inset";
|
|
18825
|
+
if (_SIZE_PROPERTY_RE.test(bare)) return "size";
|
|
17982
18826
|
const dash = bare.indexOf("-");
|
|
17983
18827
|
return dash === -1 ? bare : bare.slice(0, dash);
|
|
17984
18828
|
};
|
|
@@ -18009,8 +18853,8 @@ const _writesOnlyDeclarations = (path, items, texts) => {
|
|
|
18009
18853
|
* @returns {number[] | null} where each comes from, or null to keep them as written
|
|
18010
18854
|
*/
|
|
18011
18855
|
const _declarationOrder = (texts) => {
|
|
18012
|
-
const
|
|
18013
|
-
/** @type {
|
|
18856
|
+
const table = _declarationTable();
|
|
18857
|
+
/** @type {number[]} */
|
|
18014
18858
|
const properties = [];
|
|
18015
18859
|
/** @type {number[]} */
|
|
18016
18860
|
const rankOf = [];
|
|
@@ -18020,20 +18864,38 @@ const _declarationOrder = (texts) => {
|
|
|
18020
18864
|
if (colon <= 0) return null;
|
|
18021
18865
|
const property = toLowerCaseIfNeeded(text.slice(0, colon));
|
|
18022
18866
|
if (property.startsWith("--")) {
|
|
18023
|
-
properties.push(
|
|
18867
|
+
properties.push(-1);
|
|
18024
18868
|
rankOf.push(-1);
|
|
18025
18869
|
continue;
|
|
18026
18870
|
}
|
|
18027
|
-
const
|
|
18028
|
-
if (
|
|
18871
|
+
const id = table.ids.get(property);
|
|
18872
|
+
if (id === undefined) return null;
|
|
18873
|
+
const rank = table.ranks[id];
|
|
18029
18874
|
if (rank !== -1) ranked = true;
|
|
18030
|
-
properties.push(
|
|
18875
|
+
properties.push(table.canonical[id]);
|
|
18031
18876
|
rankOf.push(rank);
|
|
18032
18877
|
}
|
|
18033
18878
|
if (!ranked) return null;
|
|
18034
|
-
|
|
18035
|
-
|
|
18036
|
-
|
|
18879
|
+
// Already in rank order, which half of all blocks are: the stable sort would
|
|
18880
|
+
// hand every index back where it stands.
|
|
18881
|
+
let inOrder = true;
|
|
18882
|
+
for (let i = 1; i < rankOf.length && inOrder; i++) {
|
|
18883
|
+
if (rankOf[i] < rankOf[i - 1]) inOrder = false;
|
|
18884
|
+
}
|
|
18885
|
+
if (inOrder) return null;
|
|
18886
|
+
// A stable insertion sort by rank: blocks are short, and this allocates no
|
|
18887
|
+
// comparator and no copy as `Array#sort` does.
|
|
18888
|
+
/** @type {number[]} */
|
|
18889
|
+
const order = [];
|
|
18890
|
+
for (let i = 0; i < rankOf.length; i++) {
|
|
18891
|
+
let at = order.length;
|
|
18892
|
+
order.push(i);
|
|
18893
|
+
while (at > 0 && rankOf[order[at - 1]] > rankOf[i]) {
|
|
18894
|
+
order[at] = order[at - 1];
|
|
18895
|
+
at--;
|
|
18896
|
+
}
|
|
18897
|
+
order[at] = i;
|
|
18898
|
+
}
|
|
18037
18899
|
let moved = false;
|
|
18038
18900
|
for (let i = 0; i < order.length; i++) {
|
|
18039
18901
|
if (order[i] !== i) moved = true;
|
|
@@ -18042,7 +18904,7 @@ const _declarationOrder = (texts) => {
|
|
|
18042
18904
|
for (let j = i + 1; j < order.length; j++) {
|
|
18043
18905
|
if (
|
|
18044
18906
|
order[j] < order[i] &&
|
|
18045
|
-
|
|
18907
|
+
_orderMattersById(table, properties[order[i]], properties[order[j]])
|
|
18046
18908
|
) {
|
|
18047
18909
|
return null;
|
|
18048
18910
|
}
|
|
@@ -18051,6 +18913,73 @@ const _declarationOrder = (texts) => {
|
|
|
18051
18913
|
return moved ? order : null;
|
|
18052
18914
|
};
|
|
18053
18915
|
|
|
18916
|
+
/**
|
|
18917
|
+
* Every property `getDeclarationRanks` names by a small number: its rank, the
|
|
18918
|
+
* number of the property `_canonicalProperty` reads it as, and `_orderMatters`
|
|
18919
|
+
* of two such numbers, filled in as pairs are asked of.
|
|
18920
|
+
* @typedef {object} DeclarationTable
|
|
18921
|
+
* @property {Map<string, number>} ids each property's number
|
|
18922
|
+
* @property {Int32Array} ranks the rank of each number
|
|
18923
|
+
* @property {Int32Array} canonical the number of the property each is read as
|
|
18924
|
+
* @property {string[]} names the property each number stands for
|
|
18925
|
+
* @property {number} all the number of `all`
|
|
18926
|
+
* @property {Uint8Array} matters `_orderMatters` by `a * names.length + b`: 0 unknown, 1 no, 2 yes
|
|
18927
|
+
*/
|
|
18928
|
+
|
|
18929
|
+
/** @type {DeclarationTable | undefined} */
|
|
18930
|
+
let _declarationTableValue;
|
|
18931
|
+
|
|
18932
|
+
/**
|
|
18933
|
+
* @returns {DeclarationTable} the table, built on first use
|
|
18934
|
+
*/
|
|
18935
|
+
const _declarationTable = () => {
|
|
18936
|
+
if (_declarationTableValue !== undefined) return _declarationTableValue;
|
|
18937
|
+
const rankMap = getDeclarationRanks();
|
|
18938
|
+
/** @type {Map<string, number>} */
|
|
18939
|
+
const ids = new Map();
|
|
18940
|
+
/** @type {string[]} */
|
|
18941
|
+
const names = [];
|
|
18942
|
+
for (const name of rankMap.keys()) {
|
|
18943
|
+
ids.set(name, names.length);
|
|
18944
|
+
names.push(name);
|
|
18945
|
+
}
|
|
18946
|
+
const ranks = new Int32Array(names.length);
|
|
18947
|
+
const canonical = new Int32Array(names.length);
|
|
18948
|
+
for (let i = 0; i < names.length; i++) {
|
|
18949
|
+
ranks[i] = /** @type {number} */ (rankMap.get(names[i]));
|
|
18950
|
+
const id = ids.get(_canonicalProperty(names[i]));
|
|
18951
|
+
canonical[i] = id === undefined ? i : id;
|
|
18952
|
+
}
|
|
18953
|
+
_declarationTableValue = {
|
|
18954
|
+
ids,
|
|
18955
|
+
ranks,
|
|
18956
|
+
canonical,
|
|
18957
|
+
names,
|
|
18958
|
+
all: /** @type {number} */ (ids.get("all")),
|
|
18959
|
+
matters: new Uint8Array(names.length * names.length)
|
|
18960
|
+
};
|
|
18961
|
+
return _declarationTableValue;
|
|
18962
|
+
};
|
|
18963
|
+
|
|
18964
|
+
/**
|
|
18965
|
+
* `_orderMatters` of two numbered properties, -1 for a custom one.
|
|
18966
|
+
* @param {DeclarationTable} table the numbering
|
|
18967
|
+
* @param {number} a a property's canonical number
|
|
18968
|
+
* @param {number} b another
|
|
18969
|
+
* @returns {boolean} true when the two must keep their order
|
|
18970
|
+
*/
|
|
18971
|
+
const _orderMattersById = (table, a, b) => {
|
|
18972
|
+
if (a === b || a === table.all || b === table.all) return true;
|
|
18973
|
+
// Two custom properties are both -1 yet never trade places: they share a rank.
|
|
18974
|
+
if (a === -1 || b === -1) return false;
|
|
18975
|
+
const at = a * table.names.length + b;
|
|
18976
|
+
const known = table.matters[at];
|
|
18977
|
+
if (known !== 0) return known === 2;
|
|
18978
|
+
const matters = _computeOrderMatters(table.names[a], table.names[b]);
|
|
18979
|
+
table.matters[at] = matters ? 2 : 1;
|
|
18980
|
+
return matters;
|
|
18981
|
+
};
|
|
18982
|
+
|
|
18054
18983
|
/**
|
|
18055
18984
|
* Put one declaration list together: the list-wide transforms (merge a family's
|
|
18056
18985
|
* longhands into their shorthand, drop what a later declaration supersedes, add
|
|
@@ -18227,7 +19156,8 @@ const _composeBlockBody = (
|
|
|
18227
19156
|
for (const node of held) pendingRules.add(node);
|
|
18228
19157
|
}
|
|
18229
19158
|
}
|
|
18230
|
-
|
|
19159
|
+
// A block of declarations alone, most of them, holds no rule to join or drop.
|
|
19160
|
+
if (minify && items !== decls) {
|
|
18231
19161
|
// Before the joins, so a rule it takes out stands in none of their way;
|
|
18232
19162
|
// and again after any join, which can write a rule again in its turn.
|
|
18233
19163
|
if (_S._transforms.removeDeadRules) {
|
|
@@ -18430,6 +19360,7 @@ const _composeBlockBody = (
|
|
|
18430
19360
|
for (let i = 0; i < properties.length; i++) {
|
|
18431
19361
|
if (properties[i].length === 0) continue;
|
|
18432
19362
|
present.add(properties[i]);
|
|
19363
|
+
(_isImportant(items[i]) ? importantAt : plainAt).set(properties[i], i);
|
|
18433
19364
|
// A value is only ever asked for against its own property, so the
|
|
18434
19365
|
// two share one set.
|
|
18435
19366
|
if (values[i].length !== 0) {
|
|
@@ -18488,7 +19419,32 @@ const _composeBlockBody = (
|
|
|
18488
19419
|
? _prefixedPropertyName(property)
|
|
18489
19420
|
: undefined;
|
|
18490
19421
|
if (base !== undefined) {
|
|
18491
|
-
|
|
19422
|
+
// An engine still reading the vendor name as an alias cascades the
|
|
19423
|
+
// two as one property, so it is dead only behind a twin that wins.
|
|
19424
|
+
const importantBase = importantAt.get(base);
|
|
19425
|
+
const plainBase = plainAt.get(base);
|
|
19426
|
+
const important = _isImportant(items[i]);
|
|
19427
|
+
const baseWins = important
|
|
19428
|
+
? importantBase !== undefined && importantBase > i
|
|
19429
|
+
: importantBase !== undefined ||
|
|
19430
|
+
(plainBase !== undefined && plainBase > i);
|
|
19431
|
+
// ...or behind one of the same importance saying the same thing.
|
|
19432
|
+
const twinAt = important ? importantBase : plainBase;
|
|
19433
|
+
const sameTwin =
|
|
19434
|
+
twinAt !== undefined &&
|
|
19435
|
+
(important || importantBase === undefined) &&
|
|
19436
|
+
texts[twinAt].slice(
|
|
19437
|
+
base.length + 1,
|
|
19438
|
+
_printedValueEnd(texts[twinAt], base.length)
|
|
19439
|
+
) ===
|
|
19440
|
+
texts[i].slice(
|
|
19441
|
+
property.length + 1,
|
|
19442
|
+
_printedValueEnd(texts[i], property.length)
|
|
19443
|
+
);
|
|
19444
|
+
if (
|
|
19445
|
+
(baseWins || sameTwin) &&
|
|
19446
|
+
_prefixedPropertyRemovable(base, property)
|
|
19447
|
+
) {
|
|
18492
19448
|
if (droppedPrefix === null) droppedPrefix = new Set();
|
|
18493
19449
|
droppedPrefix.add(i);
|
|
18494
19450
|
continue;
|
|
@@ -18588,6 +19544,10 @@ const _composeBlockBody = (
|
|
|
18588
19544
|
: null;
|
|
18589
19545
|
/** @type {string[]} */
|
|
18590
19546
|
const pieceTexts = [];
|
|
19547
|
+
// The body before its last piece, and that piece: the `;` a `}` makes
|
|
19548
|
+
// redundant is cut off the piece, as reading the body's end would flatten it.
|
|
19549
|
+
let prior = "";
|
|
19550
|
+
let last = "";
|
|
18591
19551
|
for (let i = 0; i < texts.length; i++) {
|
|
18592
19552
|
const isBlock =
|
|
18593
19553
|
bodies !== 0 && path.type(items[i]) !== NodeType.Declaration;
|
|
@@ -18630,6 +19590,8 @@ const _composeBlockBody = (
|
|
|
18630
19590
|
}
|
|
18631
19591
|
body += nl;
|
|
18632
19592
|
const at = body.length;
|
|
19593
|
+
prior = body;
|
|
19594
|
+
last = text;
|
|
18633
19595
|
body += text;
|
|
18634
19596
|
if (minify && isBlock) {
|
|
18635
19597
|
if (spans === null) spans = [];
|
|
@@ -18639,18 +19601,15 @@ const _composeBlockBody = (
|
|
|
18639
19601
|
if (pieces !== null) {
|
|
18640
19602
|
const order = _declarationOrder(pieceTexts);
|
|
18641
19603
|
for (let i = 0; i < pieces.length; i++) {
|
|
18642
|
-
|
|
19604
|
+
prior = body;
|
|
19605
|
+
last = pieces[order === null ? i : order[i]];
|
|
19606
|
+
body += last;
|
|
18643
19607
|
}
|
|
18644
19608
|
}
|
|
18645
19609
|
// Drop the redundant `;` a following `}` — or, at top level, the end of the
|
|
18646
19610
|
// input — makes so, when minifying.
|
|
18647
|
-
if (minify) {
|
|
18648
|
-
|
|
18649
|
-
body.length !== 0 &&
|
|
18650
|
-
body.charCodeAt(body.length - 1) === CC_SEMICOLON
|
|
18651
|
-
) {
|
|
18652
|
-
body = body.slice(0, -1);
|
|
18653
|
-
}
|
|
19611
|
+
if (minify && last.charCodeAt(last.length - 1) === CC_SEMICOLON) {
|
|
19612
|
+
body = prior + last.slice(0, -1);
|
|
18654
19613
|
}
|
|
18655
19614
|
|
|
18656
19615
|
return {
|
|
@@ -19199,7 +20158,7 @@ const _S = {
|
|
|
19199
20158
|
// The walk's current position (`A.node` / `A.parent` read these; module-level
|
|
19200
20159
|
// so the accessor methods' defaults avoid self-referential `this` typing).
|
|
19201
20160
|
/** @type {Node} */
|
|
19202
|
-
_currentNode: /** @type {Node} */ (/** @type {
|
|
20161
|
+
_currentNode: /** @type {Node} */ (/** @type {Node | number} */ (0)),
|
|
19203
20162
|
// The declaration `_inKeywordOnlyValue` last answered for, and its answer: one
|
|
19204
20163
|
// value's identifiers all ask it, and the read is a pointer compare after the
|
|
19205
20164
|
// first. Cleared with the rest of the parse state, so no node outlives it.
|
|
@@ -19499,10 +20458,10 @@ const A = {
|
|
|
19499
20458
|
field(i, field, n = _S._currentNode) {
|
|
19500
20459
|
const k = _nodeIndex(n);
|
|
19501
20460
|
if (i < 0 || i >= A.fieldCount(field, n)) {
|
|
19502
|
-
return
|
|
20461
|
+
return _nodeRef(0);
|
|
19503
20462
|
}
|
|
19504
20463
|
if (_LIST_FIELD[_types[k]] === field) {
|
|
19505
|
-
return
|
|
20464
|
+
return _nodeRef(_flat[_listStarts[k] + i]);
|
|
19506
20465
|
}
|
|
19507
20466
|
return (field === Field.declarations ? _declBodies : _ruleBodies)[
|
|
19508
20467
|
_bodyIdx[k] - 1
|
|
@@ -19519,7 +20478,7 @@ const A = {
|
|
|
19519
20478
|
const item = A.field(i, field, n);
|
|
19520
20479
|
if (A.name(item) === name) return item;
|
|
19521
20480
|
}
|
|
19522
|
-
return
|
|
20481
|
+
return _nodeRef(0);
|
|
19523
20482
|
},
|
|
19524
20483
|
/**
|
|
19525
20484
|
* `important` is a container's bit, cleared when its id is reused; a token's
|
|
@@ -19698,7 +20657,13 @@ const _collectRuleSpans = (spans, item, text, at, path, inner) => {
|
|
|
19698
20657
|
// it — the parent first, so a cut that takes the whole rule is seen before
|
|
19699
20658
|
// anything inside it.
|
|
19700
20659
|
if (path.type(item) === NodeType.QualifiedRule) {
|
|
19701
|
-
spans.push({
|
|
20660
|
+
spans.push({
|
|
20661
|
+
scope: _rootScope(),
|
|
20662
|
+
key: text,
|
|
20663
|
+
at,
|
|
20664
|
+
len: text.length,
|
|
20665
|
+
taken: null
|
|
20666
|
+
});
|
|
19702
20667
|
} else if (
|
|
19703
20668
|
inner !== undefined &&
|
|
19704
20669
|
inner.prelude.charCodeAt(0) === CC_AT_SIGN &&
|
|
@@ -19706,7 +20671,13 @@ const _collectRuleSpans = (spans, item, text, at, path, inner) => {
|
|
|
19706
20671
|
) {
|
|
19707
20672
|
// Not a rule of its own — a condition its body is read under — so it is
|
|
19708
20673
|
// noted only to be cut once the cuts inside it have left it empty.
|
|
19709
|
-
spans.push({
|
|
20674
|
+
spans.push({
|
|
20675
|
+
scope: _rootScope(),
|
|
20676
|
+
key: UNKEYED,
|
|
20677
|
+
at,
|
|
20678
|
+
len: text.length,
|
|
20679
|
+
taken: null
|
|
20680
|
+
});
|
|
19710
20681
|
}
|
|
19711
20682
|
if (inner === undefined) return;
|
|
19712
20683
|
const base = at + inner.bodyAt;
|
|
@@ -19715,7 +20686,8 @@ const _collectRuleSpans = (spans, item, text, at, path, inner) => {
|
|
|
19715
20686
|
scope: _enclosingRuleScope(span.scope, inner.keyPrelude),
|
|
19716
20687
|
key: span.key,
|
|
19717
20688
|
at: base + span.at,
|
|
19718
|
-
len: span.len
|
|
20689
|
+
len: span.len,
|
|
20690
|
+
taken: null
|
|
19719
20691
|
});
|
|
19720
20692
|
}
|
|
19721
20693
|
};
|
|
@@ -19764,6 +20736,7 @@ module.exports._NUMERIC_RE = _NUMERIC_RE;
|
|
|
19764
20736
|
module.exports._atKeywordOf = _atKeywordOf;
|
|
19765
20737
|
module.exports._printedSource = _printedSource;
|
|
19766
20738
|
module.exports._rawName = _rawName;
|
|
20739
|
+
module.exports._setVerbatimLeafPrinter = setVerbatimLeafPrinter;
|
|
19767
20740
|
module.exports._trimCssWhiteSpace = _trimCssWhiteSpace;
|
|
19768
20741
|
module.exports._declarationsOf = _declarationsOf;
|
|
19769
20742
|
module.exports._rulesOf = _rulesOf;
|
|
@@ -19773,6 +20746,33 @@ module.exports._S = _S;
|
|
|
19773
20746
|
module.exports._SEP = _SEP;
|
|
19774
20747
|
module.exports._keepKeywordComments = _keepKeywordComments;
|
|
19775
20748
|
module.exports._TRIM_COMBINATORS = _TRIM_COMBINATORS;
|
|
20749
|
+
/**
|
|
20750
|
+
* The body a reused block composes to, for the rule being printed.
|
|
20751
|
+
* @param {Node} node the rule
|
|
20752
|
+
* @returns {string | undefined} the body, or undefined to compose it
|
|
20753
|
+
*/
|
|
20754
|
+
module.exports._memoizedBlockBody = (node) =>
|
|
20755
|
+
_blockMemoNode === node ? _blockMemoBody : undefined;
|
|
20756
|
+
/**
|
|
20757
|
+
* Done with the rule the walk looked a block up for: keep the body it composed
|
|
20758
|
+
* where the walk asked for one, and forget the rule — node ids are reused.
|
|
20759
|
+
* @param {Node} node the rule
|
|
20760
|
+
* @param {string | null} body its composed body, null to keep none
|
|
20761
|
+
* @returns {void}
|
|
20762
|
+
*/
|
|
20763
|
+
module.exports._memoizeBlockBody = (node, body) => {
|
|
20764
|
+
if (
|
|
20765
|
+
_blockMemoNode === node &&
|
|
20766
|
+
body !== null &&
|
|
20767
|
+
_blockMemoKey !== "" &&
|
|
20768
|
+
_blockMemo !== null
|
|
20769
|
+
) {
|
|
20770
|
+
_blockMemoKeyTable.set(_blockMemoKey, body);
|
|
20771
|
+
}
|
|
20772
|
+
_blockMemoNode = null;
|
|
20773
|
+
_blockMemoBody = undefined;
|
|
20774
|
+
_blockMemoKey = "";
|
|
20775
|
+
};
|
|
19776
20776
|
module.exports._TRIM_CONDITIONS = _TRIM_CONDITIONS;
|
|
19777
20777
|
module.exports._TRIM_MATH = _TRIM_MATH;
|
|
19778
20778
|
module.exports._TRIM_NOTHING = _TRIM_NOTHING;
|