webpack 5.108.4 → 5.109.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/APIPlugin.js +48 -29
- package/lib/CacheFacade.js +8 -0
- package/lib/ChunkGroup.js +1 -0
- package/lib/CompatibilityPlugin.js +31 -29
- package/lib/Compilation.js +49 -4
- package/lib/ConstPlugin.js +11 -11
- package/lib/ContextModule.js +239 -6
- package/lib/ContextModuleFactory.js +139 -14
- package/lib/DefinePlugin.js +396 -101
- package/lib/Entrypoint.js +1 -1
- package/lib/ExportsInfoApiPlugin.js +2 -2
- package/lib/ExternalModule.js +107 -5
- package/lib/ExternalModuleFactoryPlugin.js +20 -1
- package/lib/FileSystemInfo.js +130 -74
- package/lib/HotModuleReplacementPlugin.js +18 -15
- package/lib/Module.js +3 -0
- package/lib/ModuleFilenameHelpers.js +2 -2
- package/lib/ModuleTypeConstants.js +8 -1
- package/lib/MultiCompiler.js +13 -2
- package/lib/MultiWatching.js +1 -0
- package/lib/NodeStuffPlugin.js +51 -33
- package/lib/NormalModule.js +169 -401
- package/lib/NormalModuleFactory.js +76 -2
- package/lib/ProgressPlugin.js +211 -119
- package/lib/ProvidePlugin.js +2 -2
- package/lib/RuntimeGlobals.js +47 -0
- package/lib/RuntimePlugin.js +33 -0
- package/lib/RuntimeTemplate.js +304 -16
- package/lib/UseStrictPlugin.js +1 -1
- package/lib/WebpackIsIncludedPlugin.js +1 -1
- package/lib/WebpackOptionsApply.js +24 -19
- package/lib/asset/AssetGenerator.js +110 -28
- package/lib/asset/AssetModulesPlugin.js +92 -2
- package/lib/asset/WebManifestGenerator.js +164 -0
- package/lib/asset/WebManifestParser.js +130 -0
- package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
- package/lib/async-modules/isGeneratorLowered.js +26 -0
- package/lib/cache/PackFileCacheStrategy.js +9 -3
- package/lib/config/browserslistTargetHandler.js +40 -0
- package/lib/config/defaults.js +219 -29
- package/lib/config/normalization.js +26 -0
- package/lib/config/target.js +9 -1
- package/lib/css/CssGenerator.js +40 -31
- package/lib/css/CssLoadingRuntimeModule.js +5 -1
- package/lib/css/CssModulesPlugin.js +13 -0
- package/lib/css/CssParser.js +594 -263
- package/lib/css/syntax.js +875 -462
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
- package/lib/dependencies/AMDPlugin.js +2 -2
- package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
- package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
- package/lib/dependencies/CommonJsExportsDependency.js +47 -2
- package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
- package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
- package/lib/dependencies/CommonJsPlugin.js +2 -2
- package/lib/dependencies/CommonJsRequireDependency.js +26 -1
- package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
- package/lib/dependencies/ContextDependencyHelpers.js +3 -3
- package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
- package/lib/dependencies/CssIcssExportDependency.js +162 -71
- package/lib/dependencies/CssUrlDependency.js +35 -4
- package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
- package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
- package/lib/dependencies/HarmonyImportDependency.js +12 -1
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
- package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
- package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
- package/lib/dependencies/HtmlEntryDependency.js +797 -172
- package/lib/dependencies/HtmlSourceDependency.js +26 -0
- package/lib/dependencies/ImportDependency.js +43 -1
- package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
- package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
- package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
- package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
- package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
- package/lib/dependencies/ImportMetaPlugin.js +532 -180
- package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
- package/lib/dependencies/ImportParserPlugin.js +80 -17
- package/lib/dependencies/ImportPhase.js +10 -8
- package/lib/dependencies/LoaderPlugin.js +5 -1
- package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
- package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
- package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
- package/lib/dependencies/SystemPlugin.js +2 -4
- package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
- package/lib/dependencies/URLDependency.js +141 -21
- package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
- package/lib/dependencies/WorkerDependency.js +86 -18
- package/lib/dependencies/WorkletDependency.js +278 -0
- package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
- package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
- package/lib/errors/LoaderLoadingError.js +20 -0
- package/lib/errors/ModuleParseError.js +39 -22
- package/lib/errors/NodeStuffInWebError.js +1 -1
- package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
- package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
- package/lib/hmr/lazyCompilationBackend.js +32 -8
- package/lib/html/HtmlGenerator.js +887 -15
- package/lib/html/HtmlModule.js +2 -2
- package/lib/html/HtmlModulesPlugin.js +610 -166
- package/lib/html/HtmlParser.js +603 -247
- package/lib/html/favicon.svg +1 -0
- package/lib/html/syntax.js +1264 -661
- package/lib/ids/IdHelpers.js +1 -1
- package/lib/index.js +6 -0
- package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
- package/lib/javascript/BasicEvaluatedExpression.js +366 -48
- package/lib/javascript/JavascriptGenerator.js +197 -49
- package/lib/javascript/JavascriptModulesPlugin.js +178 -24
- package/lib/javascript/JavascriptParser.js +701 -355
- package/lib/javascript/JavascriptParserHelpers.js +3 -6
- package/lib/javascript/syntax.js +4380 -0
- package/lib/library/ModuleLibraryPlugin.js +38 -1
- package/lib/loaders/LoaderRunner.js +714 -0
- package/lib/loaders/loadLoader.js +100 -0
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +19 -0
- package/lib/node/ReadFileCompileWasmPlugin.js +14 -0
- package/lib/optimize/ConcatenatedModule.js +120 -16
- package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
- package/lib/optimize/InnerGraphPlugin.js +50 -30
- package/lib/optimize/ModuleConcatenationPlugin.js +69 -3
- package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
- package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
- package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
- package/lib/prefetch/ResourceHintPlugin.js +663 -0
- package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
- package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
- package/lib/prefetch/parseResourceHintOptions.js +103 -0
- package/lib/rules/RuleSetCompiler.js +114 -0
- package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
- package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
- package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
- package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
- package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
- package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
- package/lib/schemes/HttpUriPlugin.js +40 -1
- package/lib/schemes/VirtualUrlPlugin.js +1 -2
- package/lib/serialization/FileMiddleware.js +29 -3
- package/lib/stats/DefaultStatsFactoryPlugin.js +32 -3
- package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
- package/lib/stats/DefaultStatsPrinterPlugin.js +12 -2
- package/lib/stats/StatsFactory.js +15 -8
- package/lib/url/URLParserPlugin.js +66 -13
- package/lib/util/AsyncQueue.js +122 -72
- package/lib/util/SetHelpers.js +1 -1
- package/lib/util/StackedMap.js +122 -48
- package/lib/util/binarySearchBounds.js +2 -2
- package/lib/util/concatenate.js +40 -14
- package/lib/util/createHooksRegistry.js +13 -5
- package/lib/util/dataURL.js +2 -2
- package/lib/util/deterministicGrouping.js +7 -5
- package/lib/util/formatSize.js +6 -2
- package/lib/util/globUtils.js +710 -0
- package/lib/util/identifier.js +23 -6
- package/lib/util/implicitTypeLoaderFallback.js +62 -0
- package/lib/util/internalSerializables.js +88 -71
- package/lib/util/magicComment.js +121 -25
- package/lib/util/numberHash.js +2 -2
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
- package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
- package/lib/web/FetchCompileWasmPlugin.js +14 -0
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
- package/lib/webpack.js +65 -0
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
- package/module.d.ts +21 -0
- package/package.json +13 -12
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +759 -36
- package/schemas/plugins/HtmlParserOptions.check.js +1 -1
- package/schemas/plugins/ProgressPlugin.check.js +1 -1
- package/schemas/plugins/ProgressPlugin.json +17 -1
- package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
- package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
- package/schemas/plugins/container/ExternalsType.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssParserOptions.check.js +1 -1
- package/types.d.ts +1213 -96
package/lib/css/CssParser.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
8
|
const path = require("path");
|
|
9
|
-
const vm = require("vm");
|
|
10
9
|
const { CSS_MODULE_TYPE_AUTO } = require("../ModuleTypeConstants");
|
|
11
10
|
const Parser = require("../Parser");
|
|
12
11
|
const ConstDependency = require("../dependencies/ConstDependency");
|
|
@@ -20,12 +19,12 @@ const CommentCompilationWarning = require("../errors/CommentCompilationWarning")
|
|
|
20
19
|
const ModuleDependencyWarning = require("../errors/ModuleDependencyWarning");
|
|
21
20
|
const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
|
|
22
21
|
const WebpackError = require("../errors/WebpackError");
|
|
22
|
+
const ResourceHintPlugin = require("../prefetch/ResourceHintPlugin");
|
|
23
23
|
const LocConverter = require("../util/LocConverter");
|
|
24
|
-
const binarySearchBounds = require("../util/binarySearchBounds");
|
|
25
24
|
const { parseResource } = require("../util/identifier");
|
|
26
25
|
const {
|
|
27
26
|
createMagicCommentContext,
|
|
28
|
-
|
|
27
|
+
parseCommentOptionsInRange
|
|
29
28
|
} = require("../util/magicComment");
|
|
30
29
|
const topologicalSort = require("../util/topologicalSort");
|
|
31
30
|
const {
|
|
@@ -34,6 +33,12 @@ const {
|
|
|
34
33
|
SourceProcessor,
|
|
35
34
|
buildSkipSet,
|
|
36
35
|
equalsLowerCase,
|
|
36
|
+
isDashedIdentifier,
|
|
37
|
+
isWhitespace,
|
|
38
|
+
normalizeUrl,
|
|
39
|
+
rangeEquals,
|
|
40
|
+
rangeEqualsLowerCase,
|
|
41
|
+
toLowerCaseIfNeeded,
|
|
37
42
|
unescapeIdentifier
|
|
38
43
|
} = require("./syntax");
|
|
39
44
|
|
|
@@ -75,6 +80,8 @@ const CC_LINE_FEED = "\n".charCodeAt(0);
|
|
|
75
80
|
const CC_CARRIAGE_RETURN = "\r".charCodeAt(0);
|
|
76
81
|
const CC_FORM_FEED = "\f".charCodeAt(0);
|
|
77
82
|
const CC_LEFT_CURLY = "{".charCodeAt(0);
|
|
83
|
+
const CC_LOWER_V = "v".charCodeAt(0);
|
|
84
|
+
const CC_UPPER_V = "V".charCodeAt(0);
|
|
78
85
|
|
|
79
86
|
// A parsed CSS comment. `loc` is computed on demand — only magic-comment error
|
|
80
87
|
// warnings read it, so comment-heavy CSS skips the per-comment line/col work.
|
|
@@ -82,22 +89,37 @@ const CC_LEFT_CURLY = "{".charCodeAt(0);
|
|
|
82
89
|
/** @typedef {{ value: string, range: Range }} Comment */
|
|
83
90
|
|
|
84
91
|
// Newlines (CSS Syntax 3 §3.3) — listed explicitly since there's no preprocessing stage.
|
|
85
|
-
const STRING_MULTILINE = /\\[\n\r\f]/g;
|
|
86
92
|
// https://www.w3.org/TR/css-syntax-3/#whitespace
|
|
87
|
-
const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
|
|
88
93
|
// Pure-mode markers: `cssmodules-pure-ignore` opts a single rule out of the purity check, `cssmodules-pure-no-check` (before the first rule) opts the whole file out.
|
|
89
94
|
const PURE_IGNORE_RE = /^\s*cssmodules-pure-ignore(?:\s|$)/;
|
|
90
95
|
const PURE_NO_CHECK_RE = /^\s*cssmodules-pure-no-check(?:\s|$)/;
|
|
91
|
-
const UNESCAPE = /\\([0-9a-f]{1,6}[ \t\n\r\f]?|[\s\S])/gi;
|
|
92
96
|
const IMAGE_SET_FUNCTION = /^(?:-\w+-)?image-set$/i;
|
|
93
|
-
// `parseCommentOptions` fast path: a whole-comment `webpackXxx: <bool|number|null>`; every other (and every failing) shape falls back to the `vm.runInContext` path.
|
|
94
|
-
const MAGIC_COMMENT_FAST_PATH =
|
|
95
|
-
/^\s*(webpack[A-Z][A-Za-z]+)\s*:\s*(true|false|null|-?\d+(?:\.\d+)?)\s*$/;
|
|
96
97
|
const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(?:-\w+-)?keyframes$/;
|
|
97
98
|
const VENDOR_PREFIX = /^-\w+-/;
|
|
98
99
|
const COMPOSES_PROPERTY = /^(?:composes|compose-with)$/i;
|
|
100
|
+
// Functional view-transition pseudo-elements whose `(<name> .class…)` argument names are scoped like `view-transition-name`/`-class` values.
|
|
101
|
+
const VIEW_TRANSITION_PART_PSEUDO =
|
|
102
|
+
/^view-transition-(?:group|image-pair|old|new)$/i;
|
|
99
103
|
const IS_MODULES = /\.modules?\.[^.]+$/i;
|
|
100
104
|
|
|
105
|
+
// `@font-face` src extension → preload `type`; the emission guesses `as="font"`.
|
|
106
|
+
const FONT_MIME_TYPES = new Map([
|
|
107
|
+
["woff2", "font/woff2"],
|
|
108
|
+
["woff", "font/woff"],
|
|
109
|
+
["ttf", "font/ttf"],
|
|
110
|
+
["otf", "font/otf"],
|
|
111
|
+
["eot", "application/vnd.ms-fontobject"]
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {string} request font url (may carry a query/hash)
|
|
116
|
+
* @returns {string | undefined} preload `type` for a known font extension
|
|
117
|
+
*/
|
|
118
|
+
const fontMimeType = (request) => {
|
|
119
|
+
const match = /\.([a-z0-9]+)(?:[?#]|$)/i.exec(request);
|
|
120
|
+
return match ? FONT_MIME_TYPES.get(match[1].toLowerCase()) : undefined;
|
|
121
|
+
};
|
|
122
|
+
|
|
101
123
|
// Skip options for a non-CSS-Modules parse: drop the selector prelude (never
|
|
102
124
|
// walked without modules) plus value / function-arg leaves nothing reads (the
|
|
103
125
|
// `Ident` visitor no-ops, the `Declaration` visitor returns early, no ICSS).
|
|
@@ -120,6 +142,20 @@ const SKIP_NON_MODULES = {
|
|
|
120
142
|
]),
|
|
121
143
|
selectorPrelude: true
|
|
122
144
|
};
|
|
145
|
+
// Like SKIP_NON_MODULES but keeps selector preludes and the `Colon` / `Ident`
|
|
146
|
+
// tokens inside function-arg lists, so `@custom-selector` `:--name` references
|
|
147
|
+
// (including nested ones like `:is(:--name)`) survive a non-modules parse.
|
|
148
|
+
const SKIP_NON_MODULES_KEEP_SELECTORS = {
|
|
149
|
+
types: buildSkipSet([
|
|
150
|
+
NodeType.Number,
|
|
151
|
+
NodeType.Dimension,
|
|
152
|
+
NodeType.Percentage,
|
|
153
|
+
NodeType.Hash,
|
|
154
|
+
NodeType.Delim,
|
|
155
|
+
NodeType.Whitespace
|
|
156
|
+
]),
|
|
157
|
+
selectorPrelude: false
|
|
158
|
+
};
|
|
123
159
|
const CSS_COMMENT = /\/\*((?!\*\/)[\s\S]*?)\*\//g;
|
|
124
160
|
// `@value` recognizers (postcss-modules-values shape): the import form `<names> from <source>`, and the `<importName> as <localName>` alias inside it.
|
|
125
161
|
const VALUE_IMPORT_FORM = /from(\/\*|\s)(?:[\s\S]+)$/i;
|
|
@@ -149,110 +185,6 @@ const matchAll = (regexp, str) => {
|
|
|
149
185
|
return result;
|
|
150
186
|
};
|
|
151
187
|
|
|
152
|
-
/**
|
|
153
|
-
* Returns normalized url.
|
|
154
|
-
* @param {string} str url string
|
|
155
|
-
* @param {boolean} isString is url wrapped in quotes
|
|
156
|
-
* @returns {string} normalized url
|
|
157
|
-
*/
|
|
158
|
-
const normalizeUrl = (str, isString) => {
|
|
159
|
-
// Fast paths: skip the regex engine for the common URL with no escape and
|
|
160
|
-
// no edge whitespace (e.g. `./img.png`). Each guard is equivalent to the
|
|
161
|
-
// regex being a no-op.
|
|
162
|
-
// Remove escaped newlines from a string-token url like `url("im\<newline>g.png")`.
|
|
163
|
-
if (isString && str.includes("\\")) {
|
|
164
|
-
str = str.replace(STRING_MULTILINE, "");
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Remove unnecessary spaces from `url(" img.png ")`
|
|
168
|
-
if (
|
|
169
|
-
str.length !== 0 &&
|
|
170
|
-
(isCssWhitespace(str.charCodeAt(0)) ||
|
|
171
|
-
isCssWhitespace(str.charCodeAt(str.length - 1)))
|
|
172
|
-
) {
|
|
173
|
-
str = str.replace(TRIM_WHITE_SPACES, "");
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Unescape
|
|
177
|
-
if (str.includes("\\")) {
|
|
178
|
-
str = str.replace(UNESCAPE, (match) => {
|
|
179
|
-
if (match.length > 2) {
|
|
180
|
-
return String.fromCharCode(Number.parseInt(match.slice(1).trim(), 16));
|
|
181
|
-
}
|
|
182
|
-
return match[1];
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (/^data:/i.test(str)) {
|
|
187
|
-
return str;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (str.includes("%")) {
|
|
191
|
-
// Convert `url('%2E/img.png')` -> `url('./img.png')`
|
|
192
|
-
try {
|
|
193
|
-
str = decodeURIComponent(str);
|
|
194
|
-
} catch (_err) {
|
|
195
|
-
// Ignore
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return str;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
// `escapeIdentifier` / `unescapeIdentifier` live in ./syntax.js; they're re-exported at the bottom for back-compat callers.
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* A custom property name (`<dashed-ident>`): a `--`-prefixed identifier other than bare `--`.
|
|
206
|
-
* @param {string} identifier identifier
|
|
207
|
-
* @returns {boolean} true when identifier is dashed, otherwise false
|
|
208
|
-
*/
|
|
209
|
-
const isDashedIdentifier = (identifier) =>
|
|
210
|
-
identifier.startsWith("--") && identifier.length >= 3;
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* `s.toLowerCase()` that returns `s` itself (no allocation) when it can't
|
|
214
|
-
* change — no ASCII uppercase and no non-ASCII (whose Unicode case mapping is
|
|
215
|
-
* left to the real `toLowerCase`).
|
|
216
|
-
* @param {string} s string
|
|
217
|
-
* @returns {string} lowercased string
|
|
218
|
-
*/
|
|
219
|
-
const toLowerCaseIfNeeded = (s) => {
|
|
220
|
-
for (let i = 0; i < s.length; i++) {
|
|
221
|
-
const c = s.charCodeAt(i);
|
|
222
|
-
if ((c >= 65 && c <= 90) || c > 127) return s.toLowerCase();
|
|
223
|
-
}
|
|
224
|
-
return s;
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Case-sensitive equality of a source range against a literal — no slice.
|
|
229
|
-
* @param {string} input source
|
|
230
|
-
* @param {number} start range start
|
|
231
|
-
* @param {number} end range end (exclusive)
|
|
232
|
-
* @param {string} lit literal to match
|
|
233
|
-
* @returns {boolean} true when the range equals `lit`
|
|
234
|
-
*/
|
|
235
|
-
const rangeEquals = (input, start, end, lit) =>
|
|
236
|
-
end - start === lit.length && input.startsWith(lit, start);
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* ASCII case-insensitive equality of a source range against a lowercase ASCII literal — no slice.
|
|
240
|
-
* @param {string} input source
|
|
241
|
-
* @param {number} start range start
|
|
242
|
-
* @param {number} end range end (exclusive)
|
|
243
|
-
* @param {string} lit lowercase ASCII literal to match
|
|
244
|
-
* @returns {boolean} true when the range equals `lit` ignoring ASCII case
|
|
245
|
-
*/
|
|
246
|
-
const rangeEqualsLowerCase = (input, start, end, lit) => {
|
|
247
|
-
if (end - start !== lit.length) return false;
|
|
248
|
-
for (let i = 0; i < lit.length; i++) {
|
|
249
|
-
let c = input.charCodeAt(start + i);
|
|
250
|
-
if (c >= 65 && c <= 90) c |= 0x20;
|
|
251
|
-
if (c !== lit.charCodeAt(i)) return false;
|
|
252
|
-
}
|
|
253
|
-
return true;
|
|
254
|
-
};
|
|
255
|
-
|
|
256
188
|
/**
|
|
257
189
|
* Range-keyed index over a known-properties table: ASCII-case-folded 31-hash
|
|
258
190
|
* of the name bytes → canonical key(s). Lets the Declaration visitor answer
|
|
@@ -312,16 +244,6 @@ const knownPropertyForRange = (index, input, start, end) => {
|
|
|
312
244
|
return undefined;
|
|
313
245
|
};
|
|
314
246
|
|
|
315
|
-
/**
|
|
316
|
-
* `binarySearchBounds` comparator for `getComments` — hoisted so the lookup
|
|
317
|
-
* doesn't allocate a fresh closure per call.
|
|
318
|
-
* @param {Comment} comment comment
|
|
319
|
-
* @param {number} needle needle (byte offset)
|
|
320
|
-
* @returns {number} comparison
|
|
321
|
-
*/
|
|
322
|
-
const compareCommentStart = (comment, needle) =>
|
|
323
|
-
/** @type {Range} */ (comment.range)[0] - needle;
|
|
324
|
-
|
|
325
247
|
/** @type {Record<string, number>} */
|
|
326
248
|
const PREDEFINED_COUNTER_STYLES = {
|
|
327
249
|
decimal: 1,
|
|
@@ -561,6 +483,22 @@ const buildKnownProperties = (options = {}) => {
|
|
|
561
483
|
"spell-out": 1,
|
|
562
484
|
...PREDEFINED_COUNTER_STYLES
|
|
563
485
|
});
|
|
486
|
+
knownProperties.set("view-transition-name", {
|
|
487
|
+
none: Infinity,
|
|
488
|
+
auto: Infinity,
|
|
489
|
+
"match-element": Infinity,
|
|
490
|
+
...GLOBAL_VALUES
|
|
491
|
+
});
|
|
492
|
+
knownProperties.set("view-transition-group", {
|
|
493
|
+
normal: Infinity,
|
|
494
|
+
contain: Infinity,
|
|
495
|
+
nearest: Infinity,
|
|
496
|
+
...GLOBAL_VALUES
|
|
497
|
+
});
|
|
498
|
+
knownProperties.set("view-transition-class", {
|
|
499
|
+
none: Infinity,
|
|
500
|
+
...GLOBAL_VALUES
|
|
501
|
+
});
|
|
564
502
|
}
|
|
565
503
|
|
|
566
504
|
if (options.grid) {
|
|
@@ -606,24 +544,8 @@ const getKnownProperties = (options = {}) => {
|
|
|
606
544
|
return table;
|
|
607
545
|
};
|
|
608
546
|
|
|
609
|
-
const EMPTY_COMMENT_OPTIONS = {
|
|
610
|
-
options: null,
|
|
611
|
-
errors: null
|
|
612
|
-
};
|
|
613
|
-
|
|
614
547
|
// Byte-level source-cursor scans for computing replacement / strip ranges on raw source after parsing.
|
|
615
548
|
|
|
616
|
-
/**
|
|
617
|
-
* @param {number} cc char code
|
|
618
|
-
* @returns {boolean} true when `cc` is CSS whitespace (space/tab/newline/CR/FF)
|
|
619
|
-
*/
|
|
620
|
-
const isCssWhitespace = (cc) =>
|
|
621
|
-
cc === CC_SPACE ||
|
|
622
|
-
cc === CC_TAB ||
|
|
623
|
-
cc === CC_LINE_FEED ||
|
|
624
|
-
cc === CC_CARRIAGE_RETURN ||
|
|
625
|
-
cc === CC_FORM_FEED;
|
|
626
|
-
|
|
627
549
|
/**
|
|
628
550
|
* Skip trailing whitespace + at most one newline (CRLF-aware).
|
|
629
551
|
* @param {string} input source
|
|
@@ -943,6 +865,8 @@ class CssParser extends Parser {
|
|
|
943
865
|
animation: true,
|
|
944
866
|
container: true,
|
|
945
867
|
customIdents: true,
|
|
868
|
+
customMedia: true,
|
|
869
|
+
customSelectors: true,
|
|
946
870
|
dashedIdents: true,
|
|
947
871
|
function: true,
|
|
948
872
|
grid: true,
|
|
@@ -1012,6 +936,43 @@ class CssParser extends Parser {
|
|
|
1012
936
|
/** @type {Comment[]} */
|
|
1013
937
|
const comments = [];
|
|
1014
938
|
|
|
939
|
+
const urlHints = this.options.urlHints;
|
|
940
|
+
const fontPreload = this.options.fontPreload;
|
|
941
|
+
/**
|
|
942
|
+
* Apply `parser.css.urlHints` defaults + `webpackPrefetch` /
|
|
943
|
+
* `webpackPreload` / `webpackFetchPriority` / `webpackAs` /
|
|
944
|
+
* `webpackType` / `webpackMedia` magic-comment overrides to a fresh
|
|
945
|
+
* `CssUrlDependency`. Magic comments win over the project-wide default.
|
|
946
|
+
* @param {CssUrlDependency} dep dep
|
|
947
|
+
* @param {string} request asset request
|
|
948
|
+
* @param {Record<string, EXPECTED_ANY> | null | undefined} options parsed comment options
|
|
949
|
+
* @param {import("../Dependency").DependencyLocation} loc location for warnings
|
|
950
|
+
* @returns {void}
|
|
951
|
+
*/
|
|
952
|
+
const applyResourceHintDefaults = (dep, request, options, loc) => {
|
|
953
|
+
// `fontPreload` heuristic: seed `preload`/`as`/`type` for the first url
|
|
954
|
+
// of each `@font-face` (the nearest enclosing at-rule) as the lowest
|
|
955
|
+
// default, so `urlHints` rules and magic comments below still override.
|
|
956
|
+
if (fontPreload && atRuleStateStack.length > 0) {
|
|
957
|
+
const top = atRuleStateStack[atRuleStateStack.length - 1];
|
|
958
|
+
if (top.name === "@font-face" && !top.fontPreloaded) {
|
|
959
|
+
top.fontPreloaded = true;
|
|
960
|
+
dep.preload = true;
|
|
961
|
+
dep.asAttribute = "font";
|
|
962
|
+
const type = fontMimeType(request);
|
|
963
|
+
if (type) dep.typeAttribute = type;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
ResourceHintPlugin.applyResourceHints(
|
|
967
|
+
dep,
|
|
968
|
+
urlHints,
|
|
969
|
+
request,
|
|
970
|
+
options,
|
|
971
|
+
state.module,
|
|
972
|
+
loc
|
|
973
|
+
);
|
|
974
|
+
};
|
|
975
|
+
|
|
1015
976
|
const module = state.module;
|
|
1016
977
|
|
|
1017
978
|
// All `:export`-style exports are collected into a single
|
|
@@ -1162,14 +1123,21 @@ class CssParser extends Parser {
|
|
|
1162
1123
|
};
|
|
1163
1124
|
|
|
1164
1125
|
/**
|
|
1165
|
-
* Apply the magic comments in `range`: warn on any compilation error, validate `webpackIgnore`, and
|
|
1126
|
+
* Apply the magic comments in `range`: warn on any compilation error, validate `webpackIgnore`, and return both the parsed options (for resource-hint / other magic-comment consumers) and whether the resource is ignored.
|
|
1166
1127
|
* @param {Range} range byte range to scan for magic comments
|
|
1167
1128
|
* @param {number} warnStart start offset of the loc for an invalid-`webpackIgnore` warning (computed lazily)
|
|
1168
1129
|
* @param {number} warnEnd end offset of that loc
|
|
1169
|
-
* @returns {boolean}
|
|
1130
|
+
* @returns {{ ignored: boolean, options: Record<string, EXPECTED_ANY> | null }} parsed options and `webpackIgnore` flag
|
|
1170
1131
|
*/
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1132
|
+
const magicCommentsIn = (range, warnStart, warnEnd) => {
|
|
1133
|
+
/** @type {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: Comment })[] | null }} */
|
|
1134
|
+
const { options, errors } = parseCommentOptionsInRange(
|
|
1135
|
+
/** @type {(Comment & { range: [number, number], value: string })[]} */ (
|
|
1136
|
+
comments
|
|
1137
|
+
),
|
|
1138
|
+
range,
|
|
1139
|
+
this.magicCommentContext
|
|
1140
|
+
);
|
|
1173
1141
|
if (errors) {
|
|
1174
1142
|
for (const e of errors) {
|
|
1175
1143
|
state.module.addWarning(
|
|
@@ -1180,6 +1148,7 @@ class CssParser extends Parser {
|
|
|
1180
1148
|
);
|
|
1181
1149
|
}
|
|
1182
1150
|
}
|
|
1151
|
+
let ignored = false;
|
|
1183
1152
|
if (options && options.webpackIgnore !== undefined) {
|
|
1184
1153
|
if (typeof options.webpackIgnore !== "boolean") {
|
|
1185
1154
|
// Loc is computed lazily here — it's only needed for this rare
|
|
@@ -1191,15 +1160,38 @@ class CssParser extends Parser {
|
|
|
1191
1160
|
)
|
|
1192
1161
|
);
|
|
1193
1162
|
} else {
|
|
1194
|
-
|
|
1163
|
+
ignored = options.webpackIgnore;
|
|
1195
1164
|
}
|
|
1196
1165
|
}
|
|
1197
|
-
return
|
|
1166
|
+
return { ignored, options };
|
|
1198
1167
|
};
|
|
1168
|
+
/**
|
|
1169
|
+
* Backwards-compatible boolean shortcut of {@link magicCommentsIn} for call sites that only need the ignore flag.
|
|
1170
|
+
* @param {Range} range byte range to scan for magic comments
|
|
1171
|
+
* @param {number} warnStart start offset of the loc for an invalid-`webpackIgnore` warning
|
|
1172
|
+
* @param {number} warnEnd end offset of that loc
|
|
1173
|
+
* @returns {boolean} true when `webpackIgnore: true`
|
|
1174
|
+
*/
|
|
1175
|
+
const webpackIgnored = (range, warnStart, warnEnd) =>
|
|
1176
|
+
magicCommentsIn(range, warnStart, warnEnd).ignored;
|
|
1199
1177
|
|
|
1200
1178
|
// Closure-scope alias for `source` used by AST-walking helpers for substring extraction.
|
|
1201
1179
|
const input = source;
|
|
1202
1180
|
|
|
1181
|
+
// `@custom-media` / `@custom-selector` are build-time only (no engine ships them), so they're resolved by file-local substitution. The `includes` gates keep files without them at zero overhead; definitions may follow their uses (names are stylesheet-global), so uses are collected during the walk and resolved after it.
|
|
1182
|
+
const mayHaveCustomMedia =
|
|
1183
|
+
this.options.customMedia && input.includes("@custom-media");
|
|
1184
|
+
const mayHaveCustomSelectors =
|
|
1185
|
+
this.options.customSelectors && input.includes("@custom-selector");
|
|
1186
|
+
/** @type {Map<string, { text: string, kind: ("condition" | "type" | "unsupported") }> | undefined} */
|
|
1187
|
+
let customMediaDefs;
|
|
1188
|
+
/** @type {{ name: string, start: number, end: number, invalid: boolean, leading: boolean }[] | undefined} */
|
|
1189
|
+
let customMediaUses;
|
|
1190
|
+
/** @type {Map<string, string> | undefined} */
|
|
1191
|
+
let customSelectorDefs;
|
|
1192
|
+
/** @type {{ name: string, start: number, end: number }[] | undefined} */
|
|
1193
|
+
let customSelectorUses;
|
|
1194
|
+
|
|
1203
1195
|
/**
|
|
1204
1196
|
* Unescape a CSS identifier from a source byte range — for value spans not
|
|
1205
1197
|
* backed by a single token (string contents, `--` dashed-ident bodies,
|
|
@@ -1393,7 +1385,7 @@ class CssParser extends Parser {
|
|
|
1393
1385
|
modeData ? modeData === "local" : mode === "local";
|
|
1394
1386
|
|
|
1395
1387
|
/**
|
|
1396
|
-
* Comment visitor (`NodeType.Comment`): push every comment (in source order) onto the local `comments`, read back by `advanceCommentCursor` (pure-mode flags) and `
|
|
1388
|
+
* Comment visitor (`NodeType.Comment`): push every comment (in source order) onto the local `comments`, read back by `advanceCommentCursor` (pure-mode flags) and `parseCommentOptionsInRange` (magic comments).
|
|
1397
1389
|
* @param {import("./syntax").CssPath} path walk path at the comment node
|
|
1398
1390
|
*/
|
|
1399
1391
|
const commentVisitor = (path) => {
|
|
@@ -1424,88 +1416,6 @@ class CssParser extends Parser {
|
|
|
1424
1416
|
};
|
|
1425
1417
|
})();
|
|
1426
1418
|
|
|
1427
|
-
const magicCommentContext = this.magicCommentContext;
|
|
1428
|
-
|
|
1429
|
-
/**
|
|
1430
|
-
* Comments fully inside `range`, via binary search over the source-ordered `comments`.
|
|
1431
|
-
* @param {Range} range range
|
|
1432
|
-
* @returns {Comment[]} comments in the range
|
|
1433
|
-
*/
|
|
1434
|
-
const getComments = (range) => {
|
|
1435
|
-
// No comments: skip the binary search + result-array allocation (the common case — most CSS has no magic comments).
|
|
1436
|
-
if (comments.length === 0) return [];
|
|
1437
|
-
const [start, end] = range;
|
|
1438
|
-
let idx = binarySearchBounds.ge(comments, start, compareCommentStart);
|
|
1439
|
-
/** @type {Comment[]} */
|
|
1440
|
-
const commentsInRange = [];
|
|
1441
|
-
while (comments[idx] && comments[idx].range[1] <= end) {
|
|
1442
|
-
commentsInRange.push(comments[idx]);
|
|
1443
|
-
idx++;
|
|
1444
|
-
}
|
|
1445
|
-
return commentsInRange;
|
|
1446
|
-
};
|
|
1447
|
-
|
|
1448
|
-
/**
|
|
1449
|
-
* Parse webpack magic-comment options from any comment inside `range`.
|
|
1450
|
-
* @param {Range} range range of the comment
|
|
1451
|
-
* @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: Comment })[] | null }} result
|
|
1452
|
-
*/
|
|
1453
|
-
const parseCommentOptions = (range) => {
|
|
1454
|
-
const found = getComments(range);
|
|
1455
|
-
if (found.length === 0) {
|
|
1456
|
-
return EMPTY_COMMENT_OPTIONS;
|
|
1457
|
-
}
|
|
1458
|
-
/** @type {Record<string, EXPECTED_ANY>} */
|
|
1459
|
-
const options = {};
|
|
1460
|
-
/** @type {(Error & { comment: Comment })[]} */
|
|
1461
|
-
const errors = [];
|
|
1462
|
-
for (const c of found) {
|
|
1463
|
-
const { value } = c;
|
|
1464
|
-
if (value && webpackCommentRegExp.test(value)) {
|
|
1465
|
-
// Fast path for the common `webpackXxx: <bool|number|null>` pair, keeping it out of `vm.runInContext`.
|
|
1466
|
-
const fast = MAGIC_COMMENT_FAST_PATH.exec(value);
|
|
1467
|
-
if (fast !== null) {
|
|
1468
|
-
const key = fast[1];
|
|
1469
|
-
const raw = fast[2];
|
|
1470
|
-
options[key] =
|
|
1471
|
-
raw === "true"
|
|
1472
|
-
? true
|
|
1473
|
-
: raw === "false"
|
|
1474
|
-
? false
|
|
1475
|
-
: raw === "null"
|
|
1476
|
-
? null
|
|
1477
|
-
: Number(raw);
|
|
1478
|
-
continue;
|
|
1479
|
-
}
|
|
1480
|
-
// try compile only if webpack options comment is present
|
|
1481
|
-
try {
|
|
1482
|
-
for (let [key, val] of Object.entries(
|
|
1483
|
-
vm.runInContext(
|
|
1484
|
-
`(function(){return {${value}};})()`,
|
|
1485
|
-
magicCommentContext
|
|
1486
|
-
)
|
|
1487
|
-
)) {
|
|
1488
|
-
if (typeof val === "object" && val !== null) {
|
|
1489
|
-
val =
|
|
1490
|
-
val.constructor.name === "RegExp"
|
|
1491
|
-
? new RegExp(val)
|
|
1492
|
-
: JSON.parse(JSON.stringify(val));
|
|
1493
|
-
}
|
|
1494
|
-
options[key] = val;
|
|
1495
|
-
}
|
|
1496
|
-
} catch (err) {
|
|
1497
|
-
const newErr = new Error(
|
|
1498
|
-
String(/** @type {Error} */ (err).message)
|
|
1499
|
-
);
|
|
1500
|
-
newErr.stack = String(/** @type {Error} */ (err).stack);
|
|
1501
|
-
Object.assign(newErr, { comment: c });
|
|
1502
|
-
errors.push(/** @type {(Error & { comment: Comment })} */ (newErr));
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
return { options, errors };
|
|
1507
|
-
};
|
|
1508
|
-
|
|
1509
1419
|
// CSS modules stuff
|
|
1510
1420
|
|
|
1511
1421
|
/**
|
|
@@ -2000,7 +1910,7 @@ class CssParser extends Parser {
|
|
|
2000
1910
|
|
|
2001
1911
|
/**
|
|
2002
1912
|
* Per-at-rule scope frames; `exit` reads `hasBlock` to pick the block-cleanup vs `suppressNextRulePrelude` branch.
|
|
2003
|
-
* @type {{ savedAnchor: boolean, savedLocalIdentifiers: string[], name: string, hasBlock: boolean, endsWithSemicolon: boolean }[]}
|
|
1913
|
+
* @type {{ savedAnchor: boolean, savedLocalIdentifiers: string[], name: string, hasBlock: boolean, endsWithSemicolon: boolean, fontPreloaded: boolean }[]}
|
|
2004
1914
|
*/
|
|
2005
1915
|
const atRuleStateStack = [];
|
|
2006
1916
|
|
|
@@ -2052,7 +1962,7 @@ class CssParser extends Parser {
|
|
|
2052
1962
|
if (isLocal) {
|
|
2053
1963
|
while (
|
|
2054
1964
|
trailStart > 0 &&
|
|
2055
|
-
|
|
1965
|
+
isWhitespace(source.charCodeAt(trailStart - 1))
|
|
2056
1966
|
) {
|
|
2057
1967
|
trailStart--;
|
|
2058
1968
|
}
|
|
@@ -2138,6 +2048,37 @@ class CssParser extends Parser {
|
|
|
2138
2048
|
);
|
|
2139
2049
|
};
|
|
2140
2050
|
|
|
2051
|
+
/**
|
|
2052
|
+
* Scope a `::view-transition-*()` pseudo-element's `(<pt-name> .<pt-class>…)` argument: each non-dashed custom-ident (the part name and its classes) is localized like a `view-transition-name` value; `*` and dashed idents are left untouched. Keeps the pseudo consistent with the scoped `view-transition-name`/`-class` declarations.
|
|
2053
|
+
* @param {AstNode[]} cvs the pseudo-element's argument component values
|
|
2054
|
+
* @returns {void}
|
|
2055
|
+
*/
|
|
2056
|
+
const walkViewTransitionPart = (cvs) => {
|
|
2057
|
+
// customIdents off: leave the whole `(…)` untouched (both name and classes), consistent with unscoped `view-transition-name`/`-class` declarations.
|
|
2058
|
+
if (!this.options.customIdents) return;
|
|
2059
|
+
for (const cv of cvs) {
|
|
2060
|
+
if (A.type(cv) !== NodeType.Ident) continue;
|
|
2061
|
+
const name = A.unescaped(cv);
|
|
2062
|
+
if (isDashedIdentifier(name)) continue;
|
|
2063
|
+
const start = A.start(cv);
|
|
2064
|
+
const end = A.end(cv);
|
|
2065
|
+
const { line: sl, column: sc } = locConverter.get(start);
|
|
2066
|
+
const { line: el, column: ec } = locConverter.get(end);
|
|
2067
|
+
addCssExport(
|
|
2068
|
+
sl,
|
|
2069
|
+
sc,
|
|
2070
|
+
el,
|
|
2071
|
+
ec,
|
|
2072
|
+
name,
|
|
2073
|
+
getReexport(name),
|
|
2074
|
+
[start, end],
|
|
2075
|
+
true,
|
|
2076
|
+
CssIcssExportDependency.EXPORT_MODE.ONCE
|
|
2077
|
+
);
|
|
2078
|
+
pure.markLocal();
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
|
|
2141
2082
|
/**
|
|
2142
2083
|
* Walk component values as a selector list, emitting ID / attribute deps and recursing into `:not()`/`:is()`/`:local()`/`:global()` wrappers; `localMode` is the sub-tree mode and `topLevel` controls whether commas reset it (only outside parentheses).
|
|
2143
2084
|
* @param {AstNode[]} values component values to walk
|
|
@@ -2221,10 +2162,22 @@ class CssParser extends Parser {
|
|
|
2221
2162
|
}
|
|
2222
2163
|
break;
|
|
2223
2164
|
}
|
|
2224
|
-
case NodeType.Function:
|
|
2165
|
+
case NodeType.Function: {
|
|
2166
|
+
// `::view-transition-group(name .class…)` etc.: scope the part name + classes as custom idents (the `v` char guard skips the slice for the common `:is`/`:not`/… functions).
|
|
2167
|
+
const fnNameStart = A.nameStart(v);
|
|
2168
|
+
if (
|
|
2169
|
+
segmentMode === "local" &&
|
|
2170
|
+
(input.charCodeAt(fnNameStart) === CC_LOWER_V ||
|
|
2171
|
+
input.charCodeAt(fnNameStart) === CC_UPPER_V) &&
|
|
2172
|
+
VIEW_TRANSITION_PART_PSEUDO.test(A.unescapedName(v))
|
|
2173
|
+
) {
|
|
2174
|
+
walkViewTransitionPart(A.children(v));
|
|
2175
|
+
break;
|
|
2176
|
+
}
|
|
2225
2177
|
// Any other function (`:not(…)`, `:is(…)`, …): recurse with the segment mode preserved (only `:local(…)` / `:global(…)`, handled above, switch mode).
|
|
2226
2178
|
walkSelectorList(A.children(v), segmentMode, false);
|
|
2227
2179
|
break;
|
|
2180
|
+
}
|
|
2228
2181
|
case NodeType.Hash: {
|
|
2229
2182
|
if (A.typeFlag(v) !== "id" || segmentMode !== "local") {
|
|
2230
2183
|
break;
|
|
@@ -2347,6 +2300,8 @@ class CssParser extends Parser {
|
|
|
2347
2300
|
name === "@namespace" ||
|
|
2348
2301
|
name === "@value" ||
|
|
2349
2302
|
name === "@scope" ||
|
|
2303
|
+
(mayHaveCustomMedia && name === "@custom-media") ||
|
|
2304
|
+
(mayHaveCustomSelectors && name === "@custom-selector") ||
|
|
2350
2305
|
(isModules &&
|
|
2351
2306
|
((this.options.animation &&
|
|
2352
2307
|
OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name)) ||
|
|
@@ -2378,7 +2333,10 @@ class CssParser extends Parser {
|
|
|
2378
2333
|
const t = A.type(currentStructural);
|
|
2379
2334
|
if (t === NodeType.AtRule) {
|
|
2380
2335
|
return (
|
|
2381
|
-
currentAtRuleName !== "@value" &&
|
|
2336
|
+
currentAtRuleName !== "@value" &&
|
|
2337
|
+
currentAtRuleName !== "@import" &&
|
|
2338
|
+
currentAtRuleName !== "@custom-media" &&
|
|
2339
|
+
currentAtRuleName !== "@custom-selector"
|
|
2382
2340
|
);
|
|
2383
2341
|
}
|
|
2384
2342
|
if (t === NodeType.Declaration) {
|
|
@@ -2671,7 +2629,7 @@ class CssParser extends Parser {
|
|
|
2671
2629
|
let resumeAt = A.end(decl);
|
|
2672
2630
|
if (source.charCodeAt(A.end(decl)) === CC_SEMICOLON) {
|
|
2673
2631
|
resumeAt = A.end(decl) + 1;
|
|
2674
|
-
while (
|
|
2632
|
+
while (isWhitespace(source.charCodeAt(resumeAt))) resumeAt++;
|
|
2675
2633
|
}
|
|
2676
2634
|
module.addPresentationalDependency(
|
|
2677
2635
|
new ConstDependency("", [A.nameStart(decl), resumeAt])
|
|
@@ -2689,15 +2647,12 @@ class CssParser extends Parser {
|
|
|
2689
2647
|
const first = A.children(fn)[nextNonWhitespace(A.children(fn), 0)];
|
|
2690
2648
|
if (!first || A.type(first) !== NodeType.String) return;
|
|
2691
2649
|
const string = /** @type {Token} */ (first);
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
) {
|
|
2699
|
-
return;
|
|
2700
|
-
}
|
|
2650
|
+
const { ignored, options } = magicCommentsIn(
|
|
2651
|
+
[lastTokenEndForComments, A.start(fn)],
|
|
2652
|
+
A.start(string),
|
|
2653
|
+
A.end(string)
|
|
2654
|
+
);
|
|
2655
|
+
if (ignored) return;
|
|
2701
2656
|
const value = normalizeUrl(
|
|
2702
2657
|
input.slice(A.start(string) + 1, A.end(string) - 1),
|
|
2703
2658
|
true
|
|
@@ -2710,6 +2665,12 @@ class CssParser extends Parser {
|
|
|
2710
2665
|
"string"
|
|
2711
2666
|
);
|
|
2712
2667
|
setDepLoc(dep, A.start(string), A.end(string));
|
|
2668
|
+
applyResourceHintDefaults(
|
|
2669
|
+
dep,
|
|
2670
|
+
value,
|
|
2671
|
+
options,
|
|
2672
|
+
rangeLoc(A.start(string), A.end(string))
|
|
2673
|
+
);
|
|
2713
2674
|
module.addDependency(dep);
|
|
2714
2675
|
module.addCodeGenerationDependency(dep);
|
|
2715
2676
|
} else if (IMAGE_SET_FUNCTION.test(fname)) {
|
|
@@ -2734,21 +2695,24 @@ class CssParser extends Parser {
|
|
|
2734
2695
|
true
|
|
2735
2696
|
);
|
|
2736
2697
|
if (value.length === 0) continue;
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
) {
|
|
2744
|
-
continue;
|
|
2745
|
-
}
|
|
2698
|
+
const { ignored, options } = magicCommentsIn(
|
|
2699
|
+
[start, A.end(string)],
|
|
2700
|
+
A.start(string),
|
|
2701
|
+
A.end(string)
|
|
2702
|
+
);
|
|
2703
|
+
if (ignored) continue;
|
|
2746
2704
|
const dep = new CssUrlDependency(
|
|
2747
2705
|
value,
|
|
2748
2706
|
[A.start(string), A.end(string)],
|
|
2749
2707
|
"url"
|
|
2750
2708
|
);
|
|
2751
2709
|
setDepLoc(dep, A.start(string), A.end(string));
|
|
2710
|
+
applyResourceHintDefaults(
|
|
2711
|
+
dep,
|
|
2712
|
+
value,
|
|
2713
|
+
options,
|
|
2714
|
+
rangeLoc(A.start(string), A.end(string))
|
|
2715
|
+
);
|
|
2752
2716
|
module.addDependency(dep);
|
|
2753
2717
|
module.addCodeGenerationDependency(dep);
|
|
2754
2718
|
}
|
|
@@ -3072,6 +3036,336 @@ class CssParser extends Parser {
|
|
|
3072
3036
|
module.addPresentationalDependency(dep);
|
|
3073
3037
|
};
|
|
3074
3038
|
|
|
3039
|
+
/**
|
|
3040
|
+
* The lone dashed-ident name of a `( … )` block, or `undefined` — `(--foo)` yields `--foo`, `(--foo: 1px)` / `(min-width: 0)` yield `undefined`. `hasExtra` reports whether any non-whitespace token follows the ident (a boolean-context violation).
|
|
3041
|
+
* @param {SimpleBlock} block parenthesised block node
|
|
3042
|
+
* @param {{ hasExtra: boolean }} out extra-token flag output
|
|
3043
|
+
* @returns {string | undefined} the dashed-ident name, or undefined
|
|
3044
|
+
*/
|
|
3045
|
+
const loneDashedIdentOfBlock = (block, out) => {
|
|
3046
|
+
out.hasExtra = false;
|
|
3047
|
+
let ident;
|
|
3048
|
+
for (const k of A.children(block)) {
|
|
3049
|
+
if (A.type(k) === NodeType.Whitespace) continue;
|
|
3050
|
+
if (ident !== undefined) {
|
|
3051
|
+
out.hasExtra = true;
|
|
3052
|
+
return ident;
|
|
3053
|
+
}
|
|
3054
|
+
if (A.type(k) !== NodeType.Ident) return undefined;
|
|
3055
|
+
const v = A.value(k);
|
|
3056
|
+
if (!isDashedIdentifier(v)) return undefined;
|
|
3057
|
+
ident = v;
|
|
3058
|
+
}
|
|
3059
|
+
return ident;
|
|
3060
|
+
};
|
|
3061
|
+
|
|
3062
|
+
/**
|
|
3063
|
+
* Collect one `@custom-media --name <value>` definition (last-wins) and drop the rule. Classifies the value: `"condition"` (a media-in-parens, or comma list → `(a or b)`) inlines anywhere; `"type"` (a media type like `screen` / `not all` / `screen and (…)`) inlines only at a query's start; `true` / `false` / nested custom-media values are `"unsupported"` (warned on use — the ecosystem disagrees on these).
|
|
3064
|
+
* @param {AtRule} at the `@custom-media` at-rule
|
|
3065
|
+
*/
|
|
3066
|
+
const collectCustomMedia = (at) => {
|
|
3067
|
+
const start = A.start(at);
|
|
3068
|
+
const end = A.end(at);
|
|
3069
|
+
const ruleEnd = input.charCodeAt(end) === CC_SEMICOLON ? end + 1 : end;
|
|
3070
|
+
module.addPresentationalDependency(
|
|
3071
|
+
new ConstDependency("", [start, ruleEnd])
|
|
3072
|
+
);
|
|
3073
|
+
|
|
3074
|
+
const prelude = A.prelude(at);
|
|
3075
|
+
let i = 0;
|
|
3076
|
+
while (i < prelude.length && A.type(prelude[i]) === NodeType.Whitespace) {
|
|
3077
|
+
i++;
|
|
3078
|
+
}
|
|
3079
|
+
if (i >= prelude.length || A.type(prelude[i]) !== NodeType.Ident) return;
|
|
3080
|
+
const name = A.value(prelude[i]);
|
|
3081
|
+
if (!isDashedIdentifier(name)) return;
|
|
3082
|
+
i++;
|
|
3083
|
+
|
|
3084
|
+
/**
|
|
3085
|
+
* @param {string} text resolved text
|
|
3086
|
+
* @param {"condition" | "type" | "unsupported"} kind value kind
|
|
3087
|
+
*/
|
|
3088
|
+
const store = (text, kind) => {
|
|
3089
|
+
(customMediaDefs || (customMediaDefs = new Map())).set(name, {
|
|
3090
|
+
text,
|
|
3091
|
+
kind
|
|
3092
|
+
});
|
|
3093
|
+
};
|
|
3094
|
+
const extra = { hasExtra: false };
|
|
3095
|
+
|
|
3096
|
+
// Value span after the name, trimmed.
|
|
3097
|
+
let vs = i;
|
|
3098
|
+
let ve = prelude.length;
|
|
3099
|
+
while (vs < ve && A.type(prelude[vs]) === NodeType.Whitespace) vs++;
|
|
3100
|
+
while (ve > vs && A.type(prelude[ve - 1]) === NodeType.Whitespace) ve--;
|
|
3101
|
+
if (vs >= ve) {
|
|
3102
|
+
store("", "unsupported");
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
let hasComma = false;
|
|
3107
|
+
for (let k = vs; k < ve; k++) {
|
|
3108
|
+
if (A.type(prelude[k]) === NodeType.Comma) {
|
|
3109
|
+
hasComma = true;
|
|
3110
|
+
break;
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
if (!hasComma) {
|
|
3115
|
+
const first = prelude[vs];
|
|
3116
|
+
const valueText = input.slice(A.start(first), A.end(prelude[ve - 1]));
|
|
3117
|
+
if (A.type(first) === NodeType.SimpleBlock) {
|
|
3118
|
+
// A lone `(--x)` is an unresolved nested custom media; anything else is a media-in-parens.
|
|
3119
|
+
const nested =
|
|
3120
|
+
loneDashedIdentOfBlock(
|
|
3121
|
+
/** @type {SimpleBlock} */ (first),
|
|
3122
|
+
extra
|
|
3123
|
+
) !== undefined;
|
|
3124
|
+
store(nested ? "" : valueText, nested ? "unsupported" : "condition");
|
|
3125
|
+
return;
|
|
3126
|
+
}
|
|
3127
|
+
if (A.type(first) !== NodeType.Ident) {
|
|
3128
|
+
store("", "unsupported");
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
3131
|
+
if (
|
|
3132
|
+
rangeEqualsLowerCase(input, A.start(first), A.end(first), "true") ||
|
|
3133
|
+
rangeEqualsLowerCase(input, A.start(first), A.end(first), "false")
|
|
3134
|
+
) {
|
|
3135
|
+
store("", "unsupported");
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
if (rangeEqualsLowerCase(input, A.start(first), A.end(first), "not")) {
|
|
3139
|
+
// `not (…)` is a media condition (wrap so it stays a media-in-parens); `not <type>` is a media type.
|
|
3140
|
+
let j = vs + 1;
|
|
3141
|
+
while (j < ve && A.type(prelude[j]) === NodeType.Whitespace) j++;
|
|
3142
|
+
if (j < ve && A.type(prelude[j]) === NodeType.SimpleBlock) {
|
|
3143
|
+
store(`(${valueText})`, "condition");
|
|
3144
|
+
} else {
|
|
3145
|
+
store(valueText, "type");
|
|
3146
|
+
}
|
|
3147
|
+
return;
|
|
3148
|
+
}
|
|
3149
|
+
// `screen`, `only screen`, `screen and (…)`, …
|
|
3150
|
+
store(valueText, "type");
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
// Comma list → `(seg1 or seg2 …)`; supported only when every segment is a media-in-parens.
|
|
3155
|
+
/** @type {string[]} */
|
|
3156
|
+
const segments = [];
|
|
3157
|
+
let ok = true;
|
|
3158
|
+
/** @type {AstNode[]} */
|
|
3159
|
+
let seg = [];
|
|
3160
|
+
const flush = () => {
|
|
3161
|
+
let a = 0;
|
|
3162
|
+
let b = seg.length;
|
|
3163
|
+
while (a < b && A.type(seg[a]) === NodeType.Whitespace) a++;
|
|
3164
|
+
while (b > a && A.type(seg[b - 1]) === NodeType.Whitespace) b--;
|
|
3165
|
+
if (a >= b) {
|
|
3166
|
+
ok = false;
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
const f = seg[a];
|
|
3170
|
+
const text = input.slice(A.start(f), A.end(seg[b - 1]));
|
|
3171
|
+
if (
|
|
3172
|
+
b - a === 1 &&
|
|
3173
|
+
A.type(f) === NodeType.SimpleBlock &&
|
|
3174
|
+
loneDashedIdentOfBlock(/** @type {SimpleBlock} */ (f), extra) ===
|
|
3175
|
+
undefined
|
|
3176
|
+
) {
|
|
3177
|
+
segments.push(text);
|
|
3178
|
+
} else if (
|
|
3179
|
+
A.type(f) === NodeType.Ident &&
|
|
3180
|
+
rangeEqualsLowerCase(input, A.start(f), A.end(f), "not")
|
|
3181
|
+
) {
|
|
3182
|
+
segments.push(`(${text})`);
|
|
3183
|
+
} else {
|
|
3184
|
+
ok = false;
|
|
3185
|
+
}
|
|
3186
|
+
};
|
|
3187
|
+
for (let k = vs; k < ve; k++) {
|
|
3188
|
+
if (A.type(prelude[k]) === NodeType.Comma) {
|
|
3189
|
+
flush();
|
|
3190
|
+
seg = [];
|
|
3191
|
+
} else {
|
|
3192
|
+
seg.push(prelude[k]);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
flush();
|
|
3196
|
+
if (ok && segments.length > 0) {
|
|
3197
|
+
store(
|
|
3198
|
+
segments.length === 1 ? segments[0] : `(${segments.join(" or ")})`,
|
|
3199
|
+
"condition"
|
|
3200
|
+
);
|
|
3201
|
+
} else {
|
|
3202
|
+
store("", "unsupported");
|
|
3203
|
+
}
|
|
3204
|
+
};
|
|
3205
|
+
|
|
3206
|
+
/**
|
|
3207
|
+
* Record every `(--name)` custom-media reference in a media prelude (recursing into nested groups/functions) for post-walk resolution; `invalid` flags a non-boolean-context use like `(--name: 1px)`, `leading` marks a reference that is the first term of a top-level query (the only spot a media-type value may be inlined).
|
|
3208
|
+
* @param {readonly AstNode[]} tokens prelude (or nested block) tokens
|
|
3209
|
+
* @param {boolean} topLevel whether `tokens` is the media prelude itself (vs. a nested group)
|
|
3210
|
+
*/
|
|
3211
|
+
const scanCustomMediaUses = (tokens, topLevel) => {
|
|
3212
|
+
const extra = { hasExtra: false };
|
|
3213
|
+
let leading = true;
|
|
3214
|
+
for (const t of tokens) {
|
|
3215
|
+
const tt = A.type(t);
|
|
3216
|
+
if (tt === NodeType.Whitespace) continue;
|
|
3217
|
+
if (topLevel && tt === NodeType.Comma) {
|
|
3218
|
+
leading = true;
|
|
3219
|
+
continue;
|
|
3220
|
+
}
|
|
3221
|
+
if (tt === NodeType.SimpleBlock) {
|
|
3222
|
+
const name = loneDashedIdentOfBlock(
|
|
3223
|
+
/** @type {SimpleBlock} */ (t),
|
|
3224
|
+
extra
|
|
3225
|
+
);
|
|
3226
|
+
if (name !== undefined) {
|
|
3227
|
+
(customMediaUses || (customMediaUses = [])).push({
|
|
3228
|
+
name,
|
|
3229
|
+
start: A.start(t),
|
|
3230
|
+
end: A.end(t),
|
|
3231
|
+
invalid: extra.hasExtra,
|
|
3232
|
+
leading: Boolean(topLevel && leading)
|
|
3233
|
+
});
|
|
3234
|
+
} else {
|
|
3235
|
+
scanCustomMediaUses(A.children(t), false);
|
|
3236
|
+
}
|
|
3237
|
+
} else if (tt === NodeType.Function) {
|
|
3238
|
+
scanCustomMediaUses(A.children(t), false);
|
|
3239
|
+
}
|
|
3240
|
+
if (topLevel) leading = false;
|
|
3241
|
+
}
|
|
3242
|
+
};
|
|
3243
|
+
|
|
3244
|
+
/**
|
|
3245
|
+
* Collect one `@custom-selector :--name <selector-list>` definition (last-wins) and drop the rule.
|
|
3246
|
+
* @param {AtRule} at the `@custom-selector` at-rule
|
|
3247
|
+
*/
|
|
3248
|
+
const collectCustomSelector = (at) => {
|
|
3249
|
+
const start = A.start(at);
|
|
3250
|
+
const end = A.end(at);
|
|
3251
|
+
const ruleEnd = input.charCodeAt(end) === CC_SEMICOLON ? end + 1 : end;
|
|
3252
|
+
module.addPresentationalDependency(
|
|
3253
|
+
new ConstDependency("", [start, ruleEnd])
|
|
3254
|
+
);
|
|
3255
|
+
|
|
3256
|
+
const prelude = A.prelude(at);
|
|
3257
|
+
let i = 0;
|
|
3258
|
+
while (i < prelude.length && A.type(prelude[i]) === NodeType.Whitespace) {
|
|
3259
|
+
i++;
|
|
3260
|
+
}
|
|
3261
|
+
if (i + 1 >= prelude.length || A.type(prelude[i]) !== NodeType.Colon) {
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
const nameNode = prelude[i + 1];
|
|
3265
|
+
if (
|
|
3266
|
+
A.type(nameNode) !== NodeType.Ident ||
|
|
3267
|
+
A.end(prelude[i]) !== A.start(nameNode)
|
|
3268
|
+
) {
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
const name = A.value(nameNode);
|
|
3272
|
+
if (!isDashedIdentifier(name)) return;
|
|
3273
|
+
const list = input.slice(A.end(nameNode), end).trim();
|
|
3274
|
+
if (list.length === 0) return;
|
|
3275
|
+
(customSelectorDefs || (customSelectorDefs = new Map())).set(name, list);
|
|
3276
|
+
};
|
|
3277
|
+
|
|
3278
|
+
/**
|
|
3279
|
+
* Record every `:--name` custom-selector reference in a selector prelude (recursing into `:is(…)` etc.) for post-walk expansion. A `:--name` is a colon immediately followed by a dashed ident.
|
|
3280
|
+
* @param {readonly AstNode[]} tokens selector prelude (or nested) tokens
|
|
3281
|
+
*/
|
|
3282
|
+
const scanCustomSelectorUses = (tokens) => {
|
|
3283
|
+
for (let k = 0; k < tokens.length; k++) {
|
|
3284
|
+
const t = tokens[k];
|
|
3285
|
+
const tt = A.type(t);
|
|
3286
|
+
if (tt === NodeType.Colon) {
|
|
3287
|
+
const next = tokens[k + 1];
|
|
3288
|
+
if (
|
|
3289
|
+
next &&
|
|
3290
|
+
A.type(next) === NodeType.Ident &&
|
|
3291
|
+
A.end(t) === A.start(next) &&
|
|
3292
|
+
isDashedIdentifier(A.value(next))
|
|
3293
|
+
) {
|
|
3294
|
+
(customSelectorUses || (customSelectorUses = [])).push({
|
|
3295
|
+
name: A.value(next),
|
|
3296
|
+
start: A.start(t),
|
|
3297
|
+
end: A.end(next)
|
|
3298
|
+
});
|
|
3299
|
+
}
|
|
3300
|
+
} else if (tt === NodeType.Function || tt === NodeType.SimpleBlock) {
|
|
3301
|
+
scanCustomSelectorUses(A.children(t));
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
};
|
|
3305
|
+
|
|
3306
|
+
/**
|
|
3307
|
+
* Rewrite the collected `@custom-media` / `@custom-selector` uses now that all (possibly later-defined) definitions are known; warns on invalid, unknown, or unsupported custom-media uses.
|
|
3308
|
+
*/
|
|
3309
|
+
const resolveCustomMediaAndSelectors = () => {
|
|
3310
|
+
if (customMediaUses) {
|
|
3311
|
+
for (const use of customMediaUses) {
|
|
3312
|
+
if (use.invalid) {
|
|
3313
|
+
this._emitWarning(
|
|
3314
|
+
state,
|
|
3315
|
+
`Custom media query '${use.name}' must be used in a boolean context`,
|
|
3316
|
+
locConverter,
|
|
3317
|
+
use.start,
|
|
3318
|
+
use.end
|
|
3319
|
+
);
|
|
3320
|
+
continue;
|
|
3321
|
+
}
|
|
3322
|
+
const def = customMediaDefs && customMediaDefs.get(use.name);
|
|
3323
|
+
if (!def) {
|
|
3324
|
+
this._emitWarning(
|
|
3325
|
+
state,
|
|
3326
|
+
`Unknown custom media query '${use.name}'`,
|
|
3327
|
+
locConverter,
|
|
3328
|
+
use.start,
|
|
3329
|
+
use.end
|
|
3330
|
+
);
|
|
3331
|
+
continue;
|
|
3332
|
+
}
|
|
3333
|
+
if (def.kind === "unsupported") {
|
|
3334
|
+
this._emitWarning(
|
|
3335
|
+
state,
|
|
3336
|
+
`Custom media query '${use.name}' uses an unsupported value ('true'/'false' or a nested custom media) and was left unresolved`,
|
|
3337
|
+
locConverter,
|
|
3338
|
+
use.start,
|
|
3339
|
+
use.end
|
|
3340
|
+
);
|
|
3341
|
+
continue;
|
|
3342
|
+
}
|
|
3343
|
+
if (def.kind === "type" && !use.leading) {
|
|
3344
|
+
this._emitWarning(
|
|
3345
|
+
state,
|
|
3346
|
+
`Custom media query '${use.name}' resolves to a media type and can only be used at the start of a media query`,
|
|
3347
|
+
locConverter,
|
|
3348
|
+
use.start,
|
|
3349
|
+
use.end
|
|
3350
|
+
);
|
|
3351
|
+
continue;
|
|
3352
|
+
}
|
|
3353
|
+
module.addPresentationalDependency(
|
|
3354
|
+
new ConstDependency(def.text, [use.start, use.end])
|
|
3355
|
+
);
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
if (customSelectorUses) {
|
|
3359
|
+
for (const use of customSelectorUses) {
|
|
3360
|
+
const list = customSelectorDefs && customSelectorDefs.get(use.name);
|
|
3361
|
+
if (list === undefined) continue;
|
|
3362
|
+
module.addPresentationalDependency(
|
|
3363
|
+
new ConstDependency(`:is(${list})`, [use.start, use.end])
|
|
3364
|
+
);
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
|
|
3075
3369
|
/**
|
|
3076
3370
|
* Export the localizable idents / strings in a known property's value (`animation-name: foo`, grid line-names / template-areas, …). Top-level only, except `grid-template` recurses into `repeat(…)` and `[line-name]` blocks.
|
|
3077
3371
|
* @param {Declaration} decl the declaration
|
|
@@ -3082,6 +3376,10 @@ class CssParser extends Parser {
|
|
|
3082
3376
|
/** @type {Record<string, number>} */
|
|
3083
3377
|
let parsedKeywords = Object.create(null);
|
|
3084
3378
|
const isGridProperty = Boolean(declPropertyName.startsWith("grid"));
|
|
3379
|
+
// Only `view-transition-*` legitimately takes a dashed-ident value; other known properties keep the historical plain-ident handling.
|
|
3380
|
+
const isViewTransitionProperty = Boolean(
|
|
3381
|
+
declPropertyName.startsWith("view-transition")
|
|
3382
|
+
);
|
|
3085
3383
|
const isGridTemplate = isGridProperty
|
|
3086
3384
|
? Boolean(
|
|
3087
3385
|
declPropertyName === "grid" ||
|
|
@@ -3141,6 +3439,13 @@ class CssParser extends Parser {
|
|
|
3141
3439
|
break;
|
|
3142
3440
|
}
|
|
3143
3441
|
const identifier = A.value(cv);
|
|
3442
|
+
// `view-transition-name: --foo` — a dashed name scopes as a custom property under `dashedIdents`, not as a plain ident export.
|
|
3443
|
+
if (isViewTransitionProperty && isDashedIdentifier(identifier)) {
|
|
3444
|
+
if (this.options.dashedIdents) {
|
|
3445
|
+
emitDashedIdentExport(A.start(cv), A.end(cv));
|
|
3446
|
+
}
|
|
3447
|
+
break;
|
|
3448
|
+
}
|
|
3144
3449
|
// Values are almost always lowercase already — avoid the copy.
|
|
3145
3450
|
const keyword = toLowerCaseIfNeeded(identifier);
|
|
3146
3451
|
parsedKeywords[keyword] =
|
|
@@ -3300,6 +3605,18 @@ class CssParser extends Parser {
|
|
|
3300
3605
|
handleImportAtRule(at, topLevel);
|
|
3301
3606
|
break;
|
|
3302
3607
|
}
|
|
3608
|
+
case "@custom-media": {
|
|
3609
|
+
if (mayHaveCustomMedia) collectCustomMedia(at);
|
|
3610
|
+
break;
|
|
3611
|
+
}
|
|
3612
|
+
case "@custom-selector": {
|
|
3613
|
+
if (mayHaveCustomSelectors) collectCustomSelector(at);
|
|
3614
|
+
break;
|
|
3615
|
+
}
|
|
3616
|
+
case "@media": {
|
|
3617
|
+
if (mayHaveCustomMedia) scanCustomMediaUses(A.prelude(at), true);
|
|
3618
|
+
break;
|
|
3619
|
+
}
|
|
3303
3620
|
default: {
|
|
3304
3621
|
if (!isModules) break;
|
|
3305
3622
|
if (name === "@value") {
|
|
@@ -3357,7 +3674,8 @@ class CssParser extends Parser {
|
|
|
3357
3674
|
this.options.dashedIdents &&
|
|
3358
3675
|
isModules &&
|
|
3359
3676
|
!isProcessedByLocalAtRule &&
|
|
3360
|
-
effectiveLocalMode
|
|
3677
|
+
effectiveLocalMode &&
|
|
3678
|
+
!(mayHaveCustomMedia && name === "@media")
|
|
3361
3679
|
);
|
|
3362
3680
|
dashed.emit = dashed.active;
|
|
3363
3681
|
|
|
@@ -3410,7 +3728,8 @@ class CssParser extends Parser {
|
|
|
3410
3728
|
savedLocalIdentifiers,
|
|
3411
3729
|
name,
|
|
3412
3730
|
hasBlock: atHasBlock,
|
|
3413
|
-
endsWithSemicolon: source.charCodeAt(A.end(at)) === CC_SEMICOLON
|
|
3731
|
+
endsWithSemicolon: source.charCodeAt(A.end(at)) === CC_SEMICOLON,
|
|
3732
|
+
fontPreloaded: false
|
|
3414
3733
|
});
|
|
3415
3734
|
},
|
|
3416
3735
|
// At-rule exit: pure-frame finalization, `suppressNextRulePrelude`, scope restore, top-level reset.
|
|
@@ -3440,6 +3759,8 @@ class CssParser extends Parser {
|
|
|
3440
3759
|
const rule = /** @type {QualifiedRule} */ (node);
|
|
3441
3760
|
const topLevel = path.parent === null;
|
|
3442
3761
|
advanceCommentCursor(A.start(rule));
|
|
3762
|
+
// `:--name` custom-selector references (dashed idents never collide with the `:import`/`:export` ICSS preludes below).
|
|
3763
|
+
if (mayHaveCustomSelectors) scanCustomSelectorUses(A.prelude(rule));
|
|
3443
3764
|
// `:import(…) { … }` / `:export { … }` ICSS pseudo-rules are processed inline at top level; nested ones bail out.
|
|
3444
3765
|
if (isModules) {
|
|
3445
3766
|
const rulePrelude = A.prelude(rule);
|
|
@@ -3673,15 +3994,12 @@ class CssParser extends Parser {
|
|
|
3673
3994
|
) {
|
|
3674
3995
|
return;
|
|
3675
3996
|
}
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
) {
|
|
3683
|
-
return;
|
|
3684
|
-
}
|
|
3997
|
+
const { ignored, options: urlComments } = magicCommentsIn(
|
|
3998
|
+
[lastTokenEndForComments, A.end(node)],
|
|
3999
|
+
lastTokenEndForComments,
|
|
4000
|
+
A.end(node)
|
|
4001
|
+
);
|
|
4002
|
+
if (ignored) return;
|
|
3685
4003
|
let value = normalizeUrl(
|
|
3686
4004
|
input.slice(A.contentStart(url), A.contentEnd(url)),
|
|
3687
4005
|
false
|
|
@@ -3717,6 +4035,12 @@ class CssParser extends Parser {
|
|
|
3717
4035
|
"url"
|
|
3718
4036
|
);
|
|
3719
4037
|
setDepLoc(dep, A.start(node), A.end(node));
|
|
4038
|
+
applyResourceHintDefaults(
|
|
4039
|
+
dep,
|
|
4040
|
+
value,
|
|
4041
|
+
urlComments,
|
|
4042
|
+
rangeLoc(A.start(node), A.end(node))
|
|
4043
|
+
);
|
|
3720
4044
|
module.addDependency(dep);
|
|
3721
4045
|
module.addCodeGenerationDependency(dep);
|
|
3722
4046
|
},
|
|
@@ -3842,11 +4166,18 @@ class CssParser extends Parser {
|
|
|
3842
4166
|
// and ICSS-captured values).
|
|
3843
4167
|
new SourceProcessor({
|
|
3844
4168
|
as: /** @type {"stylesheet" | "block-contents"} */ (this.options.as),
|
|
3845
|
-
skip
|
|
4169
|
+
// Non-modules parses skip selector preludes, but `@custom-selector` expansion needs them, so keep them when the file may use one.
|
|
4170
|
+
skip: isModules
|
|
4171
|
+
? undefined
|
|
4172
|
+
: mayHaveCustomSelectors
|
|
4173
|
+
? SKIP_NON_MODULES_KEEP_SELECTORS
|
|
4174
|
+
: SKIP_NON_MODULES
|
|
3846
4175
|
})
|
|
3847
4176
|
.use(/** @type {VisitorMap} */ (visitors))
|
|
3848
4177
|
.process(source, { locConverter });
|
|
3849
4178
|
|
|
4179
|
+
if (customMediaUses || customSelectorUses) resolveCustomMediaAndSelectors();
|
|
4180
|
+
|
|
3850
4181
|
/** @type {BuildInfo} */
|
|
3851
4182
|
(module.buildInfo).strict = true;
|
|
3852
4183
|
|