webpack 5.108.4 → 5.109.1
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 +12 -0
- package/lib/APIPlugin.js +48 -29
- package/lib/CacheFacade.js +8 -0
- package/lib/ChunkGraph.js +17 -15
- package/lib/ChunkGroup.js +1 -0
- package/lib/CompatibilityPlugin.js +62 -29
- package/lib/Compilation.js +58 -6
- package/lib/ConstPlugin.js +11 -11
- package/lib/ContextModule.js +239 -6
- package/lib/ContextModuleFactory.js +139 -14
- package/lib/DefinePlugin.js +433 -112
- package/lib/Entrypoint.js +1 -1
- package/lib/ExportsInfo.js +22 -5
- package/lib/ExportsInfoApiPlugin.js +2 -2
- package/lib/ExternalModule.js +107 -5
- package/lib/ExternalModuleFactoryPlugin.js +20 -1
- package/lib/FileSystemInfo.js +256 -100
- package/lib/FlagDependencyUsagePlugin.js +33 -12
- package/lib/HotModuleReplacementPlugin.js +18 -15
- package/lib/InitFragment.js +20 -32
- 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 +175 -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 +67 -107
- package/lib/RuntimeTemplate.js +304 -16
- package/lib/Template.js +23 -3
- 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/container/HoistContainerReferencesPlugin.js +47 -55
- 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 +729 -313
- package/lib/css/syntax.js +1371 -861
- 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/CommonJsExportRequireDependency.js +19 -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/HarmonyEvaluatedImportSpecifierDependency.js +2 -1
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +17 -9
- package/lib/dependencies/HarmonyImportDependency.js +13 -2
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
- package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -7
- 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 +51 -12
- package/lib/html/HtmlGenerator.js +907 -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 +3451 -2762
- package/lib/ids/IdHelpers.js +25 -11
- 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 +704 -355
- package/lib/javascript/JavascriptParserHelpers.js +3 -6
- package/lib/javascript/syntax.js +5037 -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 +29 -2
- package/lib/node/ReadFileCompileWasmPlugin.js +24 -2
- package/lib/optimize/AggressiveMergingPlugin.js +19 -15
- package/lib/optimize/ConcatenatedModule.js +139 -31
- package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
- package/lib/optimize/InnerGraphPlugin.js +50 -30
- package/lib/optimize/ModuleConcatenationPlugin.js +139 -24
- package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
- package/lib/optimize/SplitChunksPlugin.js +146 -44
- package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
- package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
- package/lib/prefetch/ResourceHintPlugin.js +687 -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/BinaryMiddleware.js +270 -938
- package/lib/serialization/FileMiddleware.js +29 -3
- package/lib/stats/DefaultStatsFactoryPlugin.js +229 -194
- package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
- package/lib/stats/DefaultStatsPrinterPlugin.js +36 -168
- package/lib/stats/StatsFactory.js +46 -17
- 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 +12 -11
- 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 +1212 -99
package/lib/html/HtmlParser.js
CHANGED
|
@@ -5,15 +5,6 @@
|
|
|
5
5
|
"use strict";
|
|
6
6
|
|
|
7
7
|
const Parser = require("../Parser");
|
|
8
|
-
const {
|
|
9
|
-
TT_EOF,
|
|
10
|
-
TT_FUNCTION,
|
|
11
|
-
TT_STRING,
|
|
12
|
-
TT_URL,
|
|
13
|
-
TT_WHITESPACE,
|
|
14
|
-
TokenStream,
|
|
15
|
-
equalsLowerCase
|
|
16
|
-
} = require("../css/syntax");
|
|
17
8
|
const ConstDependency = require("../dependencies/ConstDependency");
|
|
18
9
|
const HtmlEntryDependency = require("../dependencies/HtmlEntryDependency");
|
|
19
10
|
const HtmlInlineHtmlDependency = require("../dependencies/HtmlInlineHtmlDependency");
|
|
@@ -25,6 +16,8 @@ const CommentCompilationWarning = require("../errors/CommentCompilationWarning")
|
|
|
25
16
|
const ModuleDependencyError = require("../errors/ModuleDependencyError");
|
|
26
17
|
const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
|
|
27
18
|
const WebpackError = require("../errors/WebpackError");
|
|
19
|
+
const ResourceHintPlugin = require("../prefetch/ResourceHintPlugin");
|
|
20
|
+
const parseResourceHintOptions = require("../prefetch/parseResourceHintOptions");
|
|
28
21
|
const LazySet = require("../util/LazySet");
|
|
29
22
|
const LocConverter = require("../util/LocConverter");
|
|
30
23
|
const createHash = require("../util/createHash");
|
|
@@ -40,8 +33,13 @@ const {
|
|
|
40
33
|
NodeType,
|
|
41
34
|
SVG_TAG_ADJUST,
|
|
42
35
|
SourceProcessor,
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
decodeEntities,
|
|
37
|
+
escapeAttribute,
|
|
38
|
+
escapeText,
|
|
39
|
+
isAsciiWhitespace,
|
|
40
|
+
parseCssUrls,
|
|
41
|
+
parseMsapplicationTask,
|
|
42
|
+
parseSrc,
|
|
45
43
|
parseSrcset
|
|
46
44
|
} = require("./syntax");
|
|
47
45
|
|
|
@@ -69,31 +67,6 @@ const {
|
|
|
69
67
|
*/
|
|
70
68
|
/** @typedef {(source: string, context: HtmlTemplateContext) => string} HtmlTemplateFunction */
|
|
71
69
|
|
|
72
|
-
const HORIZONTAL_TAB = "\u0009".charCodeAt(0);
|
|
73
|
-
const NEWLINE = "\u000A".charCodeAt(0);
|
|
74
|
-
const FORM_FEED = "\u000C".charCodeAt(0);
|
|
75
|
-
const CARRIAGE_RETURN = "\u000D".charCodeAt(0);
|
|
76
|
-
const SPACE = "\u0020".charCodeAt(0);
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @param {number} char char
|
|
80
|
-
* @returns {boolean} true when ASCII whitespace, otherwise false
|
|
81
|
-
*/
|
|
82
|
-
function isASCIIWhitespace(char) {
|
|
83
|
-
return (
|
|
84
|
-
// Horizontal tab
|
|
85
|
-
char === HORIZONTAL_TAB ||
|
|
86
|
-
// New line
|
|
87
|
-
char === NEWLINE ||
|
|
88
|
-
// Form feed
|
|
89
|
-
char === FORM_FEED ||
|
|
90
|
-
// Carriage return
|
|
91
|
-
char === CARRIAGE_RETURN ||
|
|
92
|
-
// Space
|
|
93
|
-
char === SPACE
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
70
|
/** @typedef {import("./syntax").ParsedSource} ParsedSource */
|
|
98
71
|
|
|
99
72
|
// Cheap pre-filter for a `style="..."` attribute: only route it through the
|
|
@@ -116,6 +89,63 @@ const SRCDOC_ASSET_REGEXP = /[=]|url\(|@import/i;
|
|
|
116
89
|
// base per the URL spec, so `<base href>` never rewrites it.
|
|
117
90
|
const ABSOLUTE_URL_SCHEME_REGEXP = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
|
|
118
91
|
|
|
92
|
+
// a `charset=` declaration inside an `http-equiv="content-type"` content value
|
|
93
|
+
const CHARSET_DECLARATION_REGEXP = /charset\s*=/i;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} name meta name
|
|
97
|
+
* @param {string} content meta content
|
|
98
|
+
* @returns {string} meta tag
|
|
99
|
+
*/
|
|
100
|
+
const metaTag = (name, content) => {
|
|
101
|
+
// og: uses property=; all others (including twitter:) use name=
|
|
102
|
+
const attr = name.startsWith("og:")
|
|
103
|
+
? `property="${escapeAttribute(name)}"`
|
|
104
|
+
: `name="${escapeAttribute(name)}"`;
|
|
105
|
+
return `<meta ${attr} content="${escapeAttribute(content)}">`;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {string | { href: string, target?: string }} base base option
|
|
110
|
+
* @returns {string} base tag
|
|
111
|
+
*/
|
|
112
|
+
const baseTag = (base) => {
|
|
113
|
+
const href = typeof base === "string" ? base : base.href;
|
|
114
|
+
const targetAttr =
|
|
115
|
+
typeof base === "object" && base.target
|
|
116
|
+
? ` target="${escapeAttribute(base.target)}"`
|
|
117
|
+
: "";
|
|
118
|
+
return `<base href="${escapeAttribute(href)}"${targetAttr}>`;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Serializes every head tag an `output.html` options object asks for, in
|
|
123
|
+
* spec order: charset, base, meta tags, title. Used where a page is created
|
|
124
|
+
* from scratch (HtmlModulesPlugin's synthetic entry wrapper); authored pages
|
|
125
|
+
* instead merge the options against their existing tags during `parse` —
|
|
126
|
+
* existing tags win.
|
|
127
|
+
* @param {import("../../declarations/WebpackOptions").OutputHtmlOptions} opts html options
|
|
128
|
+
* @returns {string} head tags string
|
|
129
|
+
*/
|
|
130
|
+
const buildHeadTags = (opts) => {
|
|
131
|
+
let out = "";
|
|
132
|
+
const meta = opts.meta;
|
|
133
|
+
if (meta && meta.charset) {
|
|
134
|
+
out += `<meta charset="${escapeAttribute(meta.charset)}">`;
|
|
135
|
+
}
|
|
136
|
+
if (opts.base) out += baseTag(opts.base);
|
|
137
|
+
if (meta) {
|
|
138
|
+
for (const [name, content] of Object.entries(meta)) {
|
|
139
|
+
if (name === "charset") continue;
|
|
140
|
+
out += metaTag(name, content);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (opts.title) {
|
|
144
|
+
out += `<title>${escapeText(opts.title)}</title>`;
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
};
|
|
148
|
+
|
|
119
149
|
// CSP/fetch attributes copied verbatim onto a synthesized sibling `<link>` /
|
|
120
150
|
// `<script>` (`HtmlEntryDependency`). Fixed output order, independent of
|
|
121
151
|
// source order.
|
|
@@ -147,124 +177,34 @@ const attrSourceSpan = (path, source, attr) => {
|
|
|
147
177
|
return source.slice(path.attributeNameStart(attr) - 1, end);
|
|
148
178
|
};
|
|
149
179
|
|
|
150
|
-
// eslint-disable-next-line no-control-regex
|
|
151
|
-
const IGNORE_CHARS_REGEXP = /[\u0000-\u001F\u007F-\u009F\u00A0]/g;
|
|
152
|
-
|
|
153
180
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
181
|
+
* Source span of a tag's `integrity` attribute (offsets relative to
|
|
182
|
+
* `elementStart`, leading whitespace and quotes included), so the template can
|
|
183
|
+
* drop the content-specific author value without re-parsing. Null when absent.
|
|
184
|
+
* @param {import("./syntax").HtmlPath} path walk path (for attribute reads)
|
|
185
|
+
* @param {string} source HTML source
|
|
186
|
+
* @param {number} elementStart offset of the tag's opening `<`
|
|
187
|
+
* @returns {Range | null} the span, or null when there is no `integrity`
|
|
156
188
|
*/
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
let start = 0;
|
|
162
|
-
let end = len;
|
|
163
|
-
|
|
164
|
-
while (start < end) {
|
|
165
|
-
const code = input.charCodeAt(start);
|
|
166
|
-
if (code > 32 && code !== 160) break;
|
|
167
|
-
start++;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (start === end) throw new Error("Must be non-empty");
|
|
171
|
-
|
|
172
|
-
while (end > start) {
|
|
173
|
-
const code = input.charCodeAt(end - 1);
|
|
174
|
-
if (code > 32 && code !== 160) break;
|
|
175
|
-
end--;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
let value = input.slice(start, end);
|
|
179
|
-
|
|
180
|
-
if (IGNORE_CHARS_REGEXP.test(value)) {
|
|
181
|
-
value = value.replace(IGNORE_CHARS_REGEXP, "");
|
|
182
|
-
if (value.length === 0) throw new Error("Must be non-empty");
|
|
189
|
+
const captureIntegrityRange = (path, source, elementStart) => {
|
|
190
|
+
const integrityAttr = path.findAttribute("integrity");
|
|
191
|
+
if (integrityAttr === 0 || path.attributeNameStart(integrityAttr) < 0) {
|
|
192
|
+
return null;
|
|
183
193
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Extracts the `icon-uri` value of an `msapplication-task` meta content
|
|
190
|
-
* (`name=…;action-uri=…;icon-uri=…`) — the other parts are page URLs, not assets.
|
|
191
|
-
* @param {string} input input
|
|
192
|
-
* @returns {ParsedSource[]} parsed icon-uri
|
|
193
|
-
*/
|
|
194
|
-
const parseMsapplicationTask = (input) => {
|
|
195
|
-
const len = input.length;
|
|
196
|
-
let pos = 0;
|
|
197
|
-
while (pos < len) {
|
|
198
|
-
let sep = input.indexOf(";", pos);
|
|
199
|
-
if (sep === -1) sep = len;
|
|
200
|
-
const eq = input.indexOf("=", pos);
|
|
201
|
-
if (eq !== -1 && eq < sep) {
|
|
202
|
-
const key = input.slice(pos, eq).trim().toLowerCase();
|
|
203
|
-
if (key === "icon-uri") {
|
|
204
|
-
let start = eq + 1;
|
|
205
|
-
let end = sep;
|
|
206
|
-
while (start < end && isASCIIWhitespace(input.charCodeAt(start))) {
|
|
207
|
-
start++;
|
|
208
|
-
}
|
|
209
|
-
while (end > start && isASCIIWhitespace(input.charCodeAt(end - 1))) {
|
|
210
|
-
end--;
|
|
211
|
-
}
|
|
212
|
-
if (start === end) return [];
|
|
213
|
-
return [[input.slice(start, end), start, end]];
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
pos = sep + 1;
|
|
194
|
+
let rs = path.attributeNameStart(integrityAttr);
|
|
195
|
+
while (rs > 0 && isAsciiWhitespace(source.charCodeAt(rs - 1))) {
|
|
196
|
+
rs--;
|
|
217
197
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
* `url("path")` rewrites the inner string span (quotes preserved).
|
|
227
|
-
* @param {string} input attribute value (a CSS component-value list)
|
|
228
|
-
* @returns {ParsedSource[]} url references
|
|
229
|
-
*/
|
|
230
|
-
const parseCssUrls = (input) => {
|
|
231
|
-
const ts = new TokenStream(input);
|
|
232
|
-
/** @type {ParsedSource[]} */
|
|
233
|
-
const result = [];
|
|
234
|
-
for (;;) {
|
|
235
|
-
const t = ts.consume();
|
|
236
|
-
if (t.type === TT_EOF) break;
|
|
237
|
-
if (t.type === TT_URL) {
|
|
238
|
-
if (t.contentEnd > t.contentStart) {
|
|
239
|
-
result.push([
|
|
240
|
-
input.slice(t.contentStart, t.contentEnd),
|
|
241
|
-
t.contentStart,
|
|
242
|
-
t.contentEnd
|
|
243
|
-
]);
|
|
244
|
-
}
|
|
245
|
-
} else if (
|
|
246
|
-
t.type === TT_FUNCTION &&
|
|
247
|
-
equalsLowerCase(input.slice(t.start, t.end - 1), "url")
|
|
248
|
-
) {
|
|
249
|
-
let s = ts.consume();
|
|
250
|
-
while (s.type === TT_WHITESPACE) s = ts.consume();
|
|
251
|
-
if (s.type === TT_STRING) {
|
|
252
|
-
const quote = input.charCodeAt(s.start);
|
|
253
|
-
const innerStart = s.start + 1;
|
|
254
|
-
// Drop the closing quote, unless the string is unterminated at EOF.
|
|
255
|
-
const innerEnd =
|
|
256
|
-
input.charCodeAt(s.end - 1) === quote ? s.end - 1 : s.end;
|
|
257
|
-
if (innerEnd > innerStart) {
|
|
258
|
-
result.push([
|
|
259
|
-
input.slice(innerStart, innerEnd),
|
|
260
|
-
innerStart,
|
|
261
|
-
innerEnd
|
|
262
|
-
]);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
198
|
+
let re;
|
|
199
|
+
const ivs = path.attributeValueStart(integrityAttr);
|
|
200
|
+
if (ivs === -1) {
|
|
201
|
+
re = path.attributeNameEnd(integrityAttr);
|
|
202
|
+
} else {
|
|
203
|
+
const q = source.charCodeAt(ivs - 1);
|
|
204
|
+
const ive = path.attributeValueEnd(integrityAttr);
|
|
205
|
+
re = q === CC_QUOTATION || q === CC_APOSTROPHE ? ive + 1 : ive;
|
|
266
206
|
}
|
|
267
|
-
return
|
|
207
|
+
return [rs - elementStart, re - elementStart];
|
|
268
208
|
};
|
|
269
209
|
|
|
270
210
|
/**
|
|
@@ -290,6 +230,8 @@ const META = new Map([
|
|
|
290
230
|
"msapplication-task",
|
|
291
231
|
"twitter:image",
|
|
292
232
|
"twitter:image:src",
|
|
233
|
+
// Twitter player card: URL to the raw video/media stream
|
|
234
|
+
"twitter:player:stream",
|
|
293
235
|
// Legacy preview-image hint
|
|
294
236
|
"thumbnail"
|
|
295
237
|
])
|
|
@@ -345,9 +287,7 @@ const ALLOWED_LINK_RELS = new Set([
|
|
|
345
287
|
"apple-touch-icon",
|
|
346
288
|
"apple-touch-icon-precomposed",
|
|
347
289
|
"apple-touch-startup-image",
|
|
348
|
-
//
|
|
349
|
-
// `icons`/`screenshots`/`shortcuts[].icons` URLs aren't parsed — needs a
|
|
350
|
-
// dedicated webmanifest module type (parser + generator), not a table entry.
|
|
290
|
+
// Routed to the `asset/webmanifest` module type, which parses its icon URLs.
|
|
351
291
|
"manifest",
|
|
352
292
|
"prefetch",
|
|
353
293
|
"preload",
|
|
@@ -458,14 +398,17 @@ const isLinkStylesheet = (attributes) => {
|
|
|
458
398
|
return rel.trim().toLowerCase().split(/\s+/).includes("stylesheet");
|
|
459
399
|
};
|
|
460
400
|
|
|
461
|
-
/** @typedef {"src" | "srcset" | "css-url" | "msapplication-task" | "script" | "script-module" | "modulepreload" | "stylesheet" | "stylesheet-style" | "stylesheet-style-attribute" | "srcdoc"} SourceType */
|
|
401
|
+
/** @typedef {"src" | "srcset" | "css-url" | "msapplication-task" | "script" | "script-module" | "modulepreload" | "stylesheet" | "html" | "preload" | "prefetch" | "stylesheet-style" | "stylesheet-style-attribute" | "srcdoc"} SourceType */
|
|
462
402
|
/** Entry types: a `type` whose value is loaded as its own compilation entry chunk. */
|
|
463
|
-
/** @typedef {"script" | "script-module" | "modulepreload" | "stylesheet"} EntrySourceType */
|
|
403
|
+
/** @typedef {"script" | "script-module" | "modulepreload" | "stylesheet" | "html" | "preload" | "prefetch"} EntrySourceType */
|
|
464
404
|
/** @typedef {SourceType | ((attrs: Map<string, string>, css: boolean) => SourceType)} SourceTypeOrResolver */
|
|
465
405
|
/** @typedef {(attributes: Map<string, string>, value: string) => boolean} SourceFilter */
|
|
466
406
|
/** @typedef {{ tag?: string, attribute: string, type: SourceType, filter?: SourceFilter }} SourceEntry */
|
|
467
407
|
/** A `type` plus optional gates: `filter` (the decoded attribute map + the decoded value; return false to skip — covers both cross-attribute checks and cheap value checks) and `namespace` (restrict to an element namespace, e.g. SVG). */
|
|
468
408
|
/** @typedef {{ type: SourceTypeOrResolver, filter?: SourceFilter, namespace?: number }} SourceItem */
|
|
409
|
+
/** A tag→attribute lookup; a `undefined` value marks a built-in source disabled via `sources` (`type: false`). */
|
|
410
|
+
/** @typedef {Record<string, SourceItem | undefined>} SourceBucket */
|
|
411
|
+
/** @typedef {Record<string, SourceBucket>} SourceTable */
|
|
469
412
|
/** A source whose value is the element's text content (a `<style>`/`<script>` body) rather than an attribute. */
|
|
470
413
|
/** @typedef {{ type: SourceTypeOrResolver, filter?: (attributes: Map<string, string>) => boolean }} ContentSourceItem */
|
|
471
414
|
|
|
@@ -501,6 +444,24 @@ const LINK_HREF = {
|
|
|
501
444
|
type: (attrs, css) => {
|
|
502
445
|
if (isLinkModulePreload(attrs)) return "modulepreload";
|
|
503
446
|
if (css && isLinkStylesheet(attrs)) return "stylesheet";
|
|
447
|
+
// `rel="preload"/"prefetch"` with `as="script"` (or `as="style"` when
|
|
448
|
+
// `experiments.css` is on) becomes a bundled, non-executing entry whose
|
|
449
|
+
// `href` is rewritten to the built chunk; other `as` targets (image,
|
|
450
|
+
// font, fetch, …) stay plain asset URLs.
|
|
451
|
+
const rel = getAttributeValue(attrs, "rel");
|
|
452
|
+
if (rel) {
|
|
453
|
+
const rels = rel.trim().toLowerCase().split(/\s+/);
|
|
454
|
+
const kind = rels.includes("preload")
|
|
455
|
+
? "preload"
|
|
456
|
+
: rels.includes("prefetch")
|
|
457
|
+
? "prefetch"
|
|
458
|
+
: undefined;
|
|
459
|
+
if (kind) {
|
|
460
|
+
const as = getAttributeValue(attrs, "as");
|
|
461
|
+
const asLower = as ? as.trim().toLowerCase() : "";
|
|
462
|
+
if (asLower === "script" || (css && asLower === "style")) return kind;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
504
465
|
return "src";
|
|
505
466
|
},
|
|
506
467
|
filter: filterLinkUnion
|
|
@@ -588,15 +549,22 @@ const DEFAULT_SOURCES_BY_TAG = dict({
|
|
|
588
549
|
// `xlink:href` reference another element/resource; fragment-only `#id`
|
|
589
550
|
// values are left untouched, so only external `file.svg#id` is rewritten.
|
|
590
551
|
// `color-profile`'s `xlink:href` points at an external ICC profile file.
|
|
552
|
+
// `cursor`/`font-face-uri` point at an external image/font file; `altGlyph`/
|
|
553
|
+
// `glyphRef`/`tref` are legacy element references (removed in SVG 2).
|
|
554
|
+
altGlyph: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
591
555
|
"color-profile": dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
556
|
+
cursor: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
592
557
|
feImage: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
593
558
|
filter: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
559
|
+
"font-face-uri": dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
560
|
+
glyphRef: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
594
561
|
image: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
595
562
|
linearGradient: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
596
563
|
mpath: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
597
564
|
pattern: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
598
565
|
radialGradient: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
599
566
|
textPath: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
567
|
+
tref: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
600
568
|
use: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC })
|
|
601
569
|
// CSS `url(...)` references in SVG presentation attributes (fill, stroke,
|
|
602
570
|
// clip-path, …) are handled separately during the walk (`parseCssUrls`).
|
|
@@ -657,9 +625,9 @@ const ANY_TAG = "";
|
|
|
657
625
|
* Folds the any-tag sources into every per-tag bucket (tag-specific entries
|
|
658
626
|
* win) and stores them under `ANY_TAG`, so the whole source model is one
|
|
659
627
|
* object and the walk's per-attribute lookup is a single property read.
|
|
660
|
-
* @param {
|
|
661
|
-
* @param {
|
|
662
|
-
* @returns {
|
|
628
|
+
* @param {SourceTable} byTag per-tag sources
|
|
629
|
+
* @param {SourceBucket} any any-tag sources
|
|
630
|
+
* @returns {SourceTable} folded table
|
|
663
631
|
*/
|
|
664
632
|
const foldAnySources = (byTag, any) => {
|
|
665
633
|
for (const tag of Object.keys(byTag)) {
|
|
@@ -672,12 +640,12 @@ const foldAnySources = (byTag, any) => {
|
|
|
672
640
|
// Default table (per-tag defaults + any-tag sources folded in) and the
|
|
673
641
|
// `sources: false` table (only the always-on `style=""` attribute). Both are
|
|
674
642
|
// precomputed so the common cases reference them with zero per-parser work.
|
|
675
|
-
/** @type {
|
|
643
|
+
/** @type {SourceTable} */
|
|
676
644
|
const DEFAULT_SOURCES_FOLDED = foldAnySources(
|
|
677
645
|
dict(DEFAULT_SOURCES_BY_TAG),
|
|
678
646
|
DEFAULT_ANY_SOURCES
|
|
679
647
|
);
|
|
680
|
-
/** @type {
|
|
648
|
+
/** @type {SourceTable} */
|
|
681
649
|
const DISABLED_SOURCES_FOLDED = foldAnySources(dict(), ALWAYS_ANY_SOURCES);
|
|
682
650
|
|
|
683
651
|
/**
|
|
@@ -734,9 +702,21 @@ class HtmlParser extends Parser {
|
|
|
734
702
|
// the module source before it is stored and parsed.
|
|
735
703
|
/** @type {HtmlTemplateFunction | undefined} */
|
|
736
704
|
this.template = options.template;
|
|
705
|
+
/** @type {import("../../declarations/WebpackOptions").UrlHintRule[] | undefined} */
|
|
706
|
+
this._urlHints = options.urlHints;
|
|
707
|
+
// `as` selects the parse mode — the HTML analog of the CSS parser's `as`.
|
|
708
|
+
// `"document"` (default) parses a full page; any other value is the
|
|
709
|
+
// context element whose inner HTML the source is parsed as (a fragment),
|
|
710
|
+
// so context-sensitive tags (`<tr>`, `<td>`, `<option>`, …) in a partial
|
|
711
|
+
// are kept, not dropped. `"template"` is the neutral generic fragment.
|
|
712
|
+
/** @type {string | undefined} */
|
|
713
|
+
this.fragmentContext =
|
|
714
|
+
options.as === undefined || options.as === "document"
|
|
715
|
+
? undefined
|
|
716
|
+
: options.as;
|
|
737
717
|
// One source model: a per-tag table that also holds the any-tag sources
|
|
738
718
|
// under `ANY_TAG`. The common cases reference precomputed tables.
|
|
739
|
-
/** @type {
|
|
719
|
+
/** @type {SourceTable} */
|
|
740
720
|
this.sourcesByTag = DEFAULT_SOURCES_FOLDED;
|
|
741
721
|
|
|
742
722
|
const sources = options.sources;
|
|
@@ -755,19 +735,25 @@ class HtmlParser extends Parser {
|
|
|
755
735
|
// User entries override regardless of position. `dict()` keeps every
|
|
756
736
|
// table null-prototype (see its doc), and per-tag writes rebuild the
|
|
757
737
|
// bucket so the aliased default buckets stay intact.
|
|
758
|
-
/** @type {
|
|
738
|
+
/** @type {SourceTable} */
|
|
759
739
|
const byTag = sources.includes("...")
|
|
760
740
|
? dict(DEFAULT_SOURCES_BY_TAG)
|
|
761
741
|
: dict();
|
|
762
|
-
/** @type {
|
|
742
|
+
/** @type {SourceBucket} */
|
|
763
743
|
const any = dict(DEFAULT_ANY_SOURCES);
|
|
764
744
|
for (const entry of sources) {
|
|
765
745
|
if (entry === "...") continue;
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
746
|
+
// `type: false` disables a built-in source: writing `undefined` over the
|
|
747
|
+
// attribute makes the walk's `sources[name]` lookup skip it (see below).
|
|
748
|
+
/** @type {SourceItem | undefined} */
|
|
749
|
+
const item =
|
|
750
|
+
entry.type === false
|
|
751
|
+
? undefined
|
|
752
|
+
: {
|
|
753
|
+
type: entry.type,
|
|
754
|
+
filter:
|
|
755
|
+
typeof entry.filter === "function" ? entry.filter : undefined
|
|
756
|
+
};
|
|
771
757
|
const attr = entry.attribute.toLowerCase();
|
|
772
758
|
if (entry.tag === undefined) {
|
|
773
759
|
any[attr] = item;
|
|
@@ -782,7 +768,7 @@ class HtmlParser extends Parser {
|
|
|
782
768
|
}
|
|
783
769
|
}
|
|
784
770
|
}
|
|
785
|
-
/** @type {
|
|
771
|
+
/** @type {SourceTable} */
|
|
786
772
|
this.sourcesByTag = foldAnySources(byTag, any);
|
|
787
773
|
}
|
|
788
774
|
|
|
@@ -871,6 +857,7 @@ class HtmlParser extends Parser {
|
|
|
871
857
|
const { hashFunction, module: outputModule } = compilation.outputOptions;
|
|
872
858
|
const context = compilation.compiler.context;
|
|
873
859
|
const css = Boolean(compilation.options.experiments.css);
|
|
860
|
+
const urlHints = this._urlHints;
|
|
874
861
|
|
|
875
862
|
// Stable, per-HTML-module prefix used when generating entry names for
|
|
876
863
|
// script src / modulepreload references so they don't collide across
|
|
@@ -888,6 +875,43 @@ class HtmlParser extends Parser {
|
|
|
888
875
|
.digest("hex")
|
|
889
876
|
.slice(0, 8);
|
|
890
877
|
|
|
878
|
+
// `output.html` head injection: facts are collected during the walk and
|
|
879
|
+
// become presentational dependencies afterwards — no later asset pass has
|
|
880
|
+
// to re-parse the page. `data:` documents are skipped: synthetic entry
|
|
881
|
+
// wrappers already carry the tags and iframe srcdoc pages must stay
|
|
882
|
+
// untouched.
|
|
883
|
+
const htmlOutputOptions =
|
|
884
|
+
compilation.options && compilation.options.output
|
|
885
|
+
? compilation.options.output.html
|
|
886
|
+
: undefined;
|
|
887
|
+
const headOptions =
|
|
888
|
+
typeof htmlOutputOptions === "object" &&
|
|
889
|
+
(htmlOutputOptions.title !== undefined ||
|
|
890
|
+
htmlOutputOptions.meta !== undefined ||
|
|
891
|
+
htmlOutputOptions.base !== undefined) &&
|
|
892
|
+
!resource.startsWith("data:")
|
|
893
|
+
? htmlOutputOptions
|
|
894
|
+
: undefined;
|
|
895
|
+
let doctypeEnd = -1;
|
|
896
|
+
let htmlTagEnd = -1;
|
|
897
|
+
// insertion offsets just inside the (possibly implicit) head
|
|
898
|
+
let headStart = 0;
|
|
899
|
+
let headLastEnd = 0;
|
|
900
|
+
let headSeen = false;
|
|
901
|
+
let inHead = false;
|
|
902
|
+
// `<template>` contents are inert
|
|
903
|
+
let headTemplateDepth = 0;
|
|
904
|
+
// Depth of open `<template>` elements anywhere in the tree. Template
|
|
905
|
+
// content is an inert, separate fragment per spec, so a `<base>` inside
|
|
906
|
+
// it must NOT set the document base (`headTemplateDepth` only covers
|
|
907
|
+
// templates seen while in `<head>`, so it can't guard body templates).
|
|
908
|
+
let templateDepth = 0;
|
|
909
|
+
let headHasTitle = false;
|
|
910
|
+
let headHasBase = false;
|
|
911
|
+
let headCharsetEnd = -1;
|
|
912
|
+
/** @type {Set<string> | undefined} */
|
|
913
|
+
let headMetaNames;
|
|
914
|
+
|
|
891
915
|
// Script src / modulepreload references are collected per-type
|
|
892
916
|
// during the walk; HtmlModulesPlugin later turns them into real
|
|
893
917
|
// entries. `script` and `script-module` entries are chained via a
|
|
@@ -896,23 +920,37 @@ class HtmlParser extends Parser {
|
|
|
896
920
|
// must preload without running, so they can never become a runtime
|
|
897
921
|
// leader that other entries would import.
|
|
898
922
|
/**
|
|
899
|
-
* @typedef {object}
|
|
923
|
+
* @typedef {object} HtmlEntryInfo
|
|
900
924
|
* @property {string} request
|
|
901
925
|
* @property {string} entryName
|
|
902
|
-
* @property {"script" | "script-module" | "modulepreload" | "stylesheet"} type
|
|
926
|
+
* @property {"script" | "script-module" | "modulepreload" | "stylesheet" | "html" | "preload" | "prefetch"} type
|
|
927
|
+
* @property {boolean=} css entry emits a CSS chunk (`<link rel="preload" as="style">`), so it needs the CSS filename template
|
|
903
928
|
*/
|
|
904
|
-
/** @type {
|
|
929
|
+
/** @type {HtmlEntryInfo[]} */
|
|
905
930
|
const scriptEntries = [];
|
|
906
|
-
/** @type {
|
|
931
|
+
/** @type {HtmlEntryInfo[]} */
|
|
907
932
|
const scriptModuleEntries = [];
|
|
908
|
-
/** @type {
|
|
933
|
+
/** @type {HtmlEntryInfo[]} */
|
|
909
934
|
const modulePreloadEntries = [];
|
|
910
|
-
/** @type {
|
|
935
|
+
/** @type {HtmlEntryInfo[]} */
|
|
911
936
|
const stylesheetEntries = [];
|
|
912
|
-
|
|
913
|
-
//
|
|
914
|
-
|
|
915
|
-
|
|
937
|
+
// `type: "html"` links — each referenced `.html` becomes its own page
|
|
938
|
+
// entry (`<a href="page.html">`), independent (no dependOn).
|
|
939
|
+
/** @type {HtmlEntryInfo[]} */
|
|
940
|
+
const htmlLinkEntries = [];
|
|
941
|
+
// `<link rel="preload"/"prefetch">` of a bundled resource: each becomes an
|
|
942
|
+
// independent, non-executing entry whose `href` is rewritten to the built
|
|
943
|
+
// chunk's URL (JS for `as="script"`, CSS for `as="style"`).
|
|
944
|
+
/** @type {HtmlEntryInfo[]} */
|
|
945
|
+
const preloadEntries = [];
|
|
946
|
+
/** @type {HtmlEntryInfo[]} */
|
|
947
|
+
const prefetchEntries = [];
|
|
948
|
+
|
|
949
|
+
// Offset of the first classic blocking script tag; injected stylesheet
|
|
950
|
+
// `<link>`s stay ahead of it. `defer`/`async`/module scripts don't
|
|
951
|
+
// anchor — they execute after parsing (and after pending stylesheets),
|
|
952
|
+
// so CSS may follow their tags (the order Vite emits).
|
|
953
|
+
let firstBlockingScriptStart = -1;
|
|
916
954
|
|
|
917
955
|
let nextEntryIndex = 0;
|
|
918
956
|
|
|
@@ -940,6 +978,14 @@ class HtmlParser extends Parser {
|
|
|
940
978
|
* @type {boolean | undefined}
|
|
941
979
|
*/
|
|
942
980
|
let pendingWebpackIgnore;
|
|
981
|
+
/** @type {boolean | undefined} */
|
|
982
|
+
let pendingWebpackInline;
|
|
983
|
+
/**
|
|
984
|
+
* Tracks the resource-hint values from the most recent `<!-- webpackPrefetch: … -->`
|
|
985
|
+
* (etc.) comment so the next asset URL attribute picks them up.
|
|
986
|
+
* @type {import("../prefetch/parseResourceHintOptions").ResourceHintOptions | undefined}
|
|
987
|
+
*/
|
|
988
|
+
let pendingHints;
|
|
943
989
|
|
|
944
990
|
const magicCommentContext = this.magicCommentContext;
|
|
945
991
|
|
|
@@ -979,7 +1025,7 @@ class HtmlParser extends Parser {
|
|
|
979
1025
|
let attrStart = path.attributeNameStart(typeAttr);
|
|
980
1026
|
if (
|
|
981
1027
|
attrStart > 0 &&
|
|
982
|
-
|
|
1028
|
+
isAsciiWhitespace(input.charCodeAt(attrStart - 1))
|
|
983
1029
|
) {
|
|
984
1030
|
attrStart -= 1;
|
|
985
1031
|
}
|
|
@@ -1016,10 +1062,7 @@ class HtmlParser extends Parser {
|
|
|
1016
1062
|
* @param {import("./syntax").HtmlAttributeRef} hrefAttr the base's href attribute
|
|
1017
1063
|
*/
|
|
1018
1064
|
const resolveDocumentBase = (path, hrefAttr) => {
|
|
1019
|
-
documentBase =
|
|
1020
|
-
path.attributeValue(hrefAttr),
|
|
1021
|
-
true
|
|
1022
|
-
).trim();
|
|
1065
|
+
documentBase = decodeEntities(path.attributeValue(hrefAttr), true).trim();
|
|
1023
1066
|
if (!documentBase) return;
|
|
1024
1067
|
if (
|
|
1025
1068
|
ABSOLUTE_URL_SCHEME_REGEXP.test(documentBase) ||
|
|
@@ -1074,7 +1117,7 @@ class HtmlParser extends Parser {
|
|
|
1074
1117
|
// the browser sees (e.g. `rel="icon"` means `icon`)
|
|
1075
1118
|
currentAttributesMap.set(
|
|
1076
1119
|
path.attributeName(attr),
|
|
1077
|
-
|
|
1120
|
+
decodeEntities(path.attributeValue(attr), true)
|
|
1078
1121
|
);
|
|
1079
1122
|
}
|
|
1080
1123
|
return currentAttributesMap;
|
|
@@ -1089,6 +1132,10 @@ class HtmlParser extends Parser {
|
|
|
1089
1132
|
[NodeType.Comment]: (path) => {
|
|
1090
1133
|
const start = path.start();
|
|
1091
1134
|
const end = path.end();
|
|
1135
|
+
// comments count toward the head-end insertion anchor
|
|
1136
|
+
if (inHead && headTemplateDepth === 0 && end > headLastEnd) {
|
|
1137
|
+
headLastEnd = end;
|
|
1138
|
+
}
|
|
1092
1139
|
// Only proper `<!-- ... -->` comments carry magic comments.
|
|
1093
1140
|
if (
|
|
1094
1141
|
end - start < 7 ||
|
|
@@ -1101,11 +1148,15 @@ class HtmlParser extends Parser {
|
|
|
1101
1148
|
source.charCodeAt(end - 3) !== 0x2d
|
|
1102
1149
|
) {
|
|
1103
1150
|
pendingWebpackIgnore = undefined;
|
|
1151
|
+
pendingWebpackInline = undefined;
|
|
1152
|
+
pendingHints = undefined;
|
|
1104
1153
|
return;
|
|
1105
1154
|
}
|
|
1106
1155
|
const value = path.data();
|
|
1107
1156
|
if (!webpackCommentRegExp.test(value)) {
|
|
1108
1157
|
pendingWebpackIgnore = undefined;
|
|
1158
|
+
pendingWebpackInline = undefined;
|
|
1159
|
+
pendingHints = undefined;
|
|
1109
1160
|
return;
|
|
1110
1161
|
}
|
|
1111
1162
|
/** @type {Record<string, EXPECTED_ANY>} */
|
|
@@ -1127,13 +1178,26 @@ class HtmlParser extends Parser {
|
|
|
1127
1178
|
)
|
|
1128
1179
|
);
|
|
1129
1180
|
pendingWebpackIgnore = undefined;
|
|
1181
|
+
pendingWebpackInline = undefined;
|
|
1182
|
+
pendingHints = undefined;
|
|
1130
1183
|
return;
|
|
1131
1184
|
}
|
|
1132
|
-
if (
|
|
1185
|
+
if (
|
|
1186
|
+
options.webpackIgnore === undefined &&
|
|
1187
|
+
options.webpackInline === undefined &&
|
|
1188
|
+
options.webpackPrefetch === undefined &&
|
|
1189
|
+
options.webpackPreload === undefined &&
|
|
1190
|
+
options.webpackFetchPriority === undefined
|
|
1191
|
+
) {
|
|
1133
1192
|
pendingWebpackIgnore = undefined;
|
|
1193
|
+
pendingWebpackInline = undefined;
|
|
1194
|
+
pendingHints = undefined;
|
|
1134
1195
|
return;
|
|
1135
1196
|
}
|
|
1136
|
-
if (
|
|
1197
|
+
if (
|
|
1198
|
+
options.webpackIgnore !== undefined &&
|
|
1199
|
+
typeof options.webpackIgnore !== "boolean"
|
|
1200
|
+
) {
|
|
1137
1201
|
const { line: sl, column: sc } = locConverter.get(start);
|
|
1138
1202
|
const { line: el, column: ec } = locConverter.get(end);
|
|
1139
1203
|
module.addWarning(
|
|
@@ -1146,12 +1210,59 @@ class HtmlParser extends Parser {
|
|
|
1146
1210
|
)
|
|
1147
1211
|
);
|
|
1148
1212
|
pendingWebpackIgnore = undefined;
|
|
1213
|
+
pendingWebpackInline = undefined;
|
|
1214
|
+
pendingHints = undefined;
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
if (
|
|
1218
|
+
options.webpackInline !== undefined &&
|
|
1219
|
+
typeof options.webpackInline !== "boolean"
|
|
1220
|
+
) {
|
|
1221
|
+
const { line: sl, column: sc } = locConverter.get(start);
|
|
1222
|
+
const { line: el, column: ec } = locConverter.get(end);
|
|
1223
|
+
module.addWarning(
|
|
1224
|
+
new UnsupportedFeatureWarning(
|
|
1225
|
+
`\`webpackInline\` expected a boolean, but received: ${options.webpackInline}.`,
|
|
1226
|
+
{
|
|
1227
|
+
start: { line: sl, column: sc },
|
|
1228
|
+
end: { line: el, column: ec }
|
|
1229
|
+
}
|
|
1230
|
+
)
|
|
1231
|
+
);
|
|
1232
|
+
pendingWebpackIgnore = undefined;
|
|
1233
|
+
pendingWebpackInline = undefined;
|
|
1234
|
+
pendingHints = undefined;
|
|
1149
1235
|
return;
|
|
1150
1236
|
}
|
|
1151
1237
|
pendingWebpackIgnore = options.webpackIgnore;
|
|
1238
|
+
pendingWebpackInline = options.webpackInline;
|
|
1239
|
+
if (
|
|
1240
|
+
options.webpackPrefetch !== undefined ||
|
|
1241
|
+
options.webpackPreload !== undefined ||
|
|
1242
|
+
options.webpackFetchPriority !== undefined
|
|
1243
|
+
) {
|
|
1244
|
+
const s = locConverter.get(start);
|
|
1245
|
+
const e = locConverter.get(end);
|
|
1246
|
+
const parsedHints = parseResourceHintOptions(options, module, {
|
|
1247
|
+
start: { line: s.line, column: s.column },
|
|
1248
|
+
end: { line: e.line, column: e.column }
|
|
1249
|
+
});
|
|
1250
|
+
pendingHints =
|
|
1251
|
+
parsedHints.prefetch !== undefined ||
|
|
1252
|
+
parsedHints.preload !== undefined ||
|
|
1253
|
+
parsedHints.fetchPriority !== undefined
|
|
1254
|
+
? parsedHints
|
|
1255
|
+
: undefined;
|
|
1256
|
+
} else {
|
|
1257
|
+
pendingHints = undefined;
|
|
1258
|
+
}
|
|
1152
1259
|
},
|
|
1153
|
-
[NodeType.Doctype]: () => {
|
|
1260
|
+
[NodeType.Doctype]: (path) => {
|
|
1261
|
+
// only visited when head injection is active (see `skip` below)
|
|
1262
|
+
if (doctypeEnd === -1) doctypeEnd = path.end();
|
|
1154
1263
|
pendingWebpackIgnore = undefined;
|
|
1264
|
+
pendingWebpackInline = undefined;
|
|
1265
|
+
pendingHints = undefined;
|
|
1155
1266
|
},
|
|
1156
1267
|
[NodeType.Element]: {
|
|
1157
1268
|
// Children (and `<template>` content) are walked by the
|
|
@@ -1159,7 +1270,115 @@ class HtmlParser extends Parser {
|
|
|
1159
1270
|
// `webpackIgnore` once they're done.
|
|
1160
1271
|
enter: (path) => {
|
|
1161
1272
|
const ignore = pendingWebpackIgnore === true;
|
|
1273
|
+
const forceInline = pendingWebpackInline === true;
|
|
1162
1274
|
pendingWebpackIgnore = undefined;
|
|
1275
|
+
pendingWebpackInline = undefined;
|
|
1276
|
+
|
|
1277
|
+
// `<head>` anchors are collected for every page — they place
|
|
1278
|
+
// `inject: "head"` siblings and resource hints; the
|
|
1279
|
+
// title/meta/base facts only when configured. Structural, so it
|
|
1280
|
+
// runs even for `webpackIgnore`d elements.
|
|
1281
|
+
{
|
|
1282
|
+
const name = path.tagName();
|
|
1283
|
+
if (name === "template" && path.namespace() === NS_HTML) {
|
|
1284
|
+
templateDepth++;
|
|
1285
|
+
}
|
|
1286
|
+
if (headTemplateDepth > 0) {
|
|
1287
|
+
if (name === "template" && path.namespace() === NS_HTML) {
|
|
1288
|
+
headTemplateDepth++;
|
|
1289
|
+
}
|
|
1290
|
+
} else if (inHead) {
|
|
1291
|
+
if (headOptions !== undefined && path.namespace() === NS_HTML) {
|
|
1292
|
+
switch (name) {
|
|
1293
|
+
case "title":
|
|
1294
|
+
headHasTitle = true;
|
|
1295
|
+
break;
|
|
1296
|
+
case "base":
|
|
1297
|
+
headHasBase = true;
|
|
1298
|
+
break;
|
|
1299
|
+
case "meta": {
|
|
1300
|
+
if (path.findAttribute("charset") !== 0) {
|
|
1301
|
+
headCharsetEnd = path.tagEnd();
|
|
1302
|
+
} else {
|
|
1303
|
+
const httpEquiv = path.findAttribute("http-equiv");
|
|
1304
|
+
const contentAttr =
|
|
1305
|
+
httpEquiv !== 0 &&
|
|
1306
|
+
decodeEntities(
|
|
1307
|
+
path.attributeValue(httpEquiv),
|
|
1308
|
+
true
|
|
1309
|
+
).toLowerCase() === "content-type"
|
|
1310
|
+
? path.findAttribute("content")
|
|
1311
|
+
: 0;
|
|
1312
|
+
if (
|
|
1313
|
+
contentAttr !== 0 &&
|
|
1314
|
+
CHARSET_DECLARATION_REGEXP.test(
|
|
1315
|
+
decodeEntities(
|
|
1316
|
+
path.attributeValue(contentAttr),
|
|
1317
|
+
true
|
|
1318
|
+
)
|
|
1319
|
+
)
|
|
1320
|
+
) {
|
|
1321
|
+
headCharsetEnd = path.tagEnd();
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
const nameAttr = path.findAttribute("name");
|
|
1325
|
+
const propertyAttr = path.findAttribute("property");
|
|
1326
|
+
if (nameAttr !== 0 || propertyAttr !== 0) {
|
|
1327
|
+
if (headMetaNames === undefined) {
|
|
1328
|
+
headMetaNames = new Set();
|
|
1329
|
+
}
|
|
1330
|
+
if (nameAttr !== 0) {
|
|
1331
|
+
headMetaNames.add(
|
|
1332
|
+
decodeEntities(
|
|
1333
|
+
path.attributeValue(nameAttr),
|
|
1334
|
+
true
|
|
1335
|
+
).toLowerCase()
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
if (propertyAttr !== 0) {
|
|
1339
|
+
headMetaNames.add(
|
|
1340
|
+
decodeEntities(
|
|
1341
|
+
path.attributeValue(propertyAttr),
|
|
1342
|
+
true
|
|
1343
|
+
).toLowerCase()
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
break;
|
|
1348
|
+
}
|
|
1349
|
+
case "template":
|
|
1350
|
+
headTemplateDepth++;
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
} else if (
|
|
1354
|
+
name === "template" &&
|
|
1355
|
+
path.namespace() === NS_HTML
|
|
1356
|
+
) {
|
|
1357
|
+
headTemplateDepth++;
|
|
1358
|
+
}
|
|
1359
|
+
if (path.end() > headLastEnd) headLastEnd = path.end();
|
|
1360
|
+
} else if (!headSeen && path.namespace() === NS_HTML) {
|
|
1361
|
+
if (name === "html") {
|
|
1362
|
+
htmlTagEnd = path.tagEnd();
|
|
1363
|
+
} else if (name === "head") {
|
|
1364
|
+
headSeen = true;
|
|
1365
|
+
inHead = true;
|
|
1366
|
+
// explicit head has real offsets; an implicit one (head
|
|
1367
|
+
// tags are optional in HTML5) anchors after `<html>`,
|
|
1368
|
+
// the doctype, or the document start
|
|
1369
|
+
const tagEnd = path.tagEnd();
|
|
1370
|
+
headStart =
|
|
1371
|
+
tagEnd > 0
|
|
1372
|
+
? tagEnd
|
|
1373
|
+
: htmlTagEnd > 0
|
|
1374
|
+
? htmlTagEnd
|
|
1375
|
+
: doctypeEnd > 0
|
|
1376
|
+
? doctypeEnd
|
|
1377
|
+
: 0;
|
|
1378
|
+
headLastEnd = headStart;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1163
1382
|
|
|
1164
1383
|
if (ignore) {
|
|
1165
1384
|
return;
|
|
@@ -1171,9 +1390,12 @@ class HtmlParser extends Parser {
|
|
|
1171
1390
|
const tagEnd = path.tagEnd();
|
|
1172
1391
|
const nameEnd = path.nameEnd();
|
|
1173
1392
|
|
|
1174
|
-
// The first `<base href>` freezes the base for later URLs
|
|
1393
|
+
// The first `<base href>` freezes the base for later URLs — but
|
|
1394
|
+
// only one in the real document tree, not inside an inert
|
|
1395
|
+
// `<template>` (whose contents are a separate fragment).
|
|
1175
1396
|
if (
|
|
1176
1397
|
documentBase === undefined &&
|
|
1398
|
+
templateDepth === 0 &&
|
|
1177
1399
|
elementName === "base" &&
|
|
1178
1400
|
path.namespace() === NS_HTML
|
|
1179
1401
|
) {
|
|
@@ -1246,7 +1468,7 @@ class HtmlParser extends Parser {
|
|
|
1246
1468
|
// The filter also sees the decoded value (the browser tokenizes it
|
|
1247
1469
|
// decoded, e.g. `url(`), so a value-only check needn't re-read it.
|
|
1248
1470
|
const decoded = value.includes("&")
|
|
1249
|
-
?
|
|
1471
|
+
? decodeEntities(value, true)
|
|
1250
1472
|
: value;
|
|
1251
1473
|
if (!filter(getAttributesMap(), decoded)) continue;
|
|
1252
1474
|
}
|
|
@@ -1266,9 +1488,7 @@ class HtmlParser extends Parser {
|
|
|
1266
1488
|
case "stylesheet-style":
|
|
1267
1489
|
case "stylesheet-style-attribute": {
|
|
1268
1490
|
if (!css) break;
|
|
1269
|
-
const cssText = content
|
|
1270
|
-
? value
|
|
1271
|
-
: decodeHtmlEntities(value, true);
|
|
1491
|
+
const cssText = content ? value : decodeEntities(value, true);
|
|
1272
1492
|
if (cssText.trim() === "") break;
|
|
1273
1493
|
const dep = new HtmlInlineStyleDependency(
|
|
1274
1494
|
dataUri("text/css", cssText),
|
|
@@ -1287,7 +1507,7 @@ class HtmlParser extends Parser {
|
|
|
1287
1507
|
// whose URL is `about:srcdoc`, so its base URL is inherited from
|
|
1288
1508
|
// this document — asset URLs resolve against this file's context.
|
|
1289
1509
|
case "srcdoc": {
|
|
1290
|
-
const htmlText =
|
|
1510
|
+
const htmlText = decodeEntities(value, true);
|
|
1291
1511
|
// Only spin up a nested module when there is markup that can
|
|
1292
1512
|
// reference an asset; text/formatting-only markup is identical
|
|
1293
1513
|
// after rewriting (see `SRCDOC_ASSET_REGEXP`).
|
|
@@ -1346,7 +1566,10 @@ class HtmlParser extends Parser {
|
|
|
1346
1566
|
case "css-url":
|
|
1347
1567
|
case "msapplication-task":
|
|
1348
1568
|
case "modulepreload":
|
|
1349
|
-
case "stylesheet":
|
|
1569
|
+
case "stylesheet":
|
|
1570
|
+
case "html":
|
|
1571
|
+
case "preload":
|
|
1572
|
+
case "prefetch": {
|
|
1350
1573
|
// Parse the value into one or more URLs (decoding character
|
|
1351
1574
|
// references, mapping spans back to raw offsets), then emit a plain
|
|
1352
1575
|
// asset reference or an entry chunk per URL.
|
|
@@ -1362,7 +1585,7 @@ class HtmlParser extends Parser {
|
|
|
1362
1585
|
/** @type {number[] | undefined} */
|
|
1363
1586
|
let map;
|
|
1364
1587
|
if (value.includes("&")) {
|
|
1365
|
-
({ text, map } =
|
|
1588
|
+
({ text, map } = decodeEntities(value, true, true));
|
|
1366
1589
|
if (!/\S/.test(text)) break;
|
|
1367
1590
|
}
|
|
1368
1591
|
/** @type {ParsedSource[] | undefined} */
|
|
@@ -1408,6 +1631,70 @@ class HtmlParser extends Parser {
|
|
|
1408
1631
|
}
|
|
1409
1632
|
const s = start + (map ? map[us] : us);
|
|
1410
1633
|
const e = start + (map ? map[ue] : ue);
|
|
1634
|
+
// `html` links the URL as its own emitted page: the same
|
|
1635
|
+
// `HtmlEntryDependency` as `<script src>` / `<link>`, but the `html`
|
|
1636
|
+
// kind rewrites to the page's filename and emits no sibling tags.
|
|
1637
|
+
// Recorded as an independent entry (no `dependOn`), like an entry point.
|
|
1638
|
+
if (type === "html") {
|
|
1639
|
+
const entryName = `__html_${moduleHash}_${nextEntryIndex++}`;
|
|
1640
|
+
const dep = new HtmlEntryDependency(
|
|
1641
|
+
request,
|
|
1642
|
+
[s, e],
|
|
1643
|
+
entryName,
|
|
1644
|
+
undefined,
|
|
1645
|
+
"html"
|
|
1646
|
+
);
|
|
1647
|
+
setLoc(dep, s, e);
|
|
1648
|
+
module.addPresentationalDependency(dep);
|
|
1649
|
+
htmlLinkEntries.push({ request, entryName, type });
|
|
1650
|
+
continue;
|
|
1651
|
+
}
|
|
1652
|
+
// `rel="preload"/"prefetch"` of a bundled resource: an
|
|
1653
|
+
// independent, non-executing entry whose `href` is rewritten to
|
|
1654
|
+
// the built chunk's URL (CSS for `as="style"`, JS otherwise). No
|
|
1655
|
+
// sibling tags — a resource hint references exactly one URL.
|
|
1656
|
+
if (type === "preload" || type === "prefetch") {
|
|
1657
|
+
const as = getAttributeValue(getAttributesMap(), "as");
|
|
1658
|
+
const asStyle = as
|
|
1659
|
+
? as.trim().toLowerCase() === "style"
|
|
1660
|
+
: false;
|
|
1661
|
+
const entryName = `__html_${moduleHash}_${nextEntryIndex++}`;
|
|
1662
|
+
// Tag metadata so the template can mirror `crossorigin`/
|
|
1663
|
+
// `integrity` onto a native `preload` `<link>` (integrity-
|
|
1664
|
+
// eligible per the SRI spec). `prefetch` gets none — the spec
|
|
1665
|
+
// doesn't list it — so its `integrity` is left uncaptured.
|
|
1666
|
+
const isNativeLink = elementName === "link";
|
|
1667
|
+
const dep = new HtmlEntryDependency(
|
|
1668
|
+
request,
|
|
1669
|
+
[s, e],
|
|
1670
|
+
entryName,
|
|
1671
|
+
asStyle ? "css-import" : undefined,
|
|
1672
|
+
type,
|
|
1673
|
+
elementStart,
|
|
1674
|
+
tagEnd,
|
|
1675
|
+
isNativeLink,
|
|
1676
|
+
"",
|
|
1677
|
+
nameEnd,
|
|
1678
|
+
isNativeLink && path.findAttribute("crossorigin") !== 0,
|
|
1679
|
+
-1,
|
|
1680
|
+
type === "preload" && isNativeLink
|
|
1681
|
+
? captureIntegrityRange(path, source, elementStart)
|
|
1682
|
+
: null,
|
|
1683
|
+
null
|
|
1684
|
+
);
|
|
1685
|
+
setLoc(dep, s, e);
|
|
1686
|
+
module.addPresentationalDependency(dep);
|
|
1687
|
+
(type === "preload"
|
|
1688
|
+
? preloadEntries
|
|
1689
|
+
: prefetchEntries
|
|
1690
|
+
).push({
|
|
1691
|
+
request,
|
|
1692
|
+
entryName,
|
|
1693
|
+
type,
|
|
1694
|
+
css: asStyle
|
|
1695
|
+
});
|
|
1696
|
+
continue;
|
|
1697
|
+
}
|
|
1411
1698
|
// `src`/`srcset`/`css-url`/`msapplication-task` are plain assets;
|
|
1412
1699
|
// the rest are narrowed to entry types here and share one
|
|
1413
1700
|
// `HtmlEntryDependency`, recorded for HtmlModulesPlugin.
|
|
@@ -1461,63 +1748,49 @@ class HtmlParser extends Parser {
|
|
|
1461
1748
|
}
|
|
1462
1749
|
}
|
|
1463
1750
|
}
|
|
1751
|
+
// Blocking-ness of the tag as *emitted*:
|
|
1752
|
+
// `reconcileScriptTypeAttr` upgrades every script entry
|
|
1753
|
+
// to `type="module"` under `output.module` and strips an
|
|
1754
|
+
// authored `type="module"` otherwise — so only non-module
|
|
1755
|
+
// output without `defer`/`async` blocks. Non-native
|
|
1756
|
+
// elements anchor conservatively (their synthesized
|
|
1757
|
+
// siblings may be plain blocking `<script>`s).
|
|
1464
1758
|
if (
|
|
1465
|
-
|
|
1466
|
-
(type === "script" || type === "script-module")
|
|
1759
|
+
firstBlockingScriptStart === -1 &&
|
|
1760
|
+
(type === "script" || type === "script-module") &&
|
|
1761
|
+
!outputModule &&
|
|
1762
|
+
(elementName !== nativeTag ||
|
|
1763
|
+
(path.findAttribute("defer") === 0 &&
|
|
1764
|
+
path.findAttribute("async") === 0))
|
|
1467
1765
|
) {
|
|
1468
|
-
|
|
1766
|
+
firstBlockingScriptStart = elementStart;
|
|
1469
1767
|
}
|
|
1470
1768
|
// Sibling-clone attribute edits, captured from the parsed
|
|
1471
1769
|
// attributes (offsets relative to the tag) so the template
|
|
1472
1770
|
// needn't re-parse the tag text: `integrity` is dropped
|
|
1473
1771
|
// (content-specific) and `script-module` clones are forced
|
|
1474
1772
|
// to `type="module"` (its value range, else inserted).
|
|
1475
|
-
|
|
1476
|
-
|
|
1773
|
+
const integrityRange =
|
|
1774
|
+
elementStart >= 0 && elementName === nativeTag
|
|
1775
|
+
? captureIntegrityRange(path, source, elementStart)
|
|
1776
|
+
: null;
|
|
1477
1777
|
/** @type {Range | null} */
|
|
1478
1778
|
let typeValueRange = null;
|
|
1479
|
-
if (
|
|
1480
|
-
|
|
1779
|
+
if (
|
|
1780
|
+
elementStart >= 0 &&
|
|
1781
|
+
elementName === nativeTag &&
|
|
1782
|
+
elementKind === "script-module"
|
|
1783
|
+
) {
|
|
1784
|
+
const typeAttr = path.findAttribute("type");
|
|
1481
1785
|
if (
|
|
1482
|
-
|
|
1483
|
-
path.
|
|
1786
|
+
typeAttr !== 0 &&
|
|
1787
|
+
path.attributeValueStart(typeAttr) >= 0
|
|
1484
1788
|
) {
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
isASCIIWhitespace(source.charCodeAt(rs - 1))
|
|
1489
|
-
) {
|
|
1490
|
-
rs--;
|
|
1491
|
-
}
|
|
1492
|
-
let re;
|
|
1493
|
-
const ivs = path.attributeValueStart(integrityAttr);
|
|
1494
|
-
if (ivs === -1) {
|
|
1495
|
-
re = path.attributeNameEnd(integrityAttr);
|
|
1496
|
-
} else {
|
|
1497
|
-
const q = source.charCodeAt(ivs - 1);
|
|
1498
|
-
const ive = path.attributeValueEnd(integrityAttr);
|
|
1499
|
-
re =
|
|
1500
|
-
q === CC_QUOTATION || q === CC_APOSTROPHE
|
|
1501
|
-
? ive + 1
|
|
1502
|
-
: ive;
|
|
1503
|
-
}
|
|
1504
|
-
integrityRange = [
|
|
1505
|
-
rs - elementStart,
|
|
1506
|
-
re - elementStart
|
|
1789
|
+
typeValueRange = [
|
|
1790
|
+
path.attributeValueStart(typeAttr) - elementStart,
|
|
1791
|
+
path.attributeValueEnd(typeAttr) - elementStart
|
|
1507
1792
|
];
|
|
1508
1793
|
}
|
|
1509
|
-
if (elementKind === "script-module") {
|
|
1510
|
-
const typeAttr = path.findAttribute("type");
|
|
1511
|
-
if (
|
|
1512
|
-
typeAttr !== 0 &&
|
|
1513
|
-
path.attributeValueStart(typeAttr) >= 0
|
|
1514
|
-
) {
|
|
1515
|
-
typeValueRange = [
|
|
1516
|
-
path.attributeValueStart(typeAttr) - elementStart,
|
|
1517
|
-
path.attributeValueEnd(typeAttr) - elementStart
|
|
1518
|
-
];
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
1794
|
}
|
|
1522
1795
|
const dep = new HtmlEntryDependency(
|
|
1523
1796
|
request,
|
|
@@ -1531,9 +1804,12 @@ class HtmlParser extends Parser {
|
|
|
1531
1804
|
copyableAttrsText,
|
|
1532
1805
|
nameEnd,
|
|
1533
1806
|
hasOwnCrossOrigin,
|
|
1534
|
-
|
|
1807
|
+
firstBlockingScriptStart,
|
|
1535
1808
|
integrityRange,
|
|
1536
|
-
typeValueRange
|
|
1809
|
+
typeValueRange,
|
|
1810
|
+
forceInline,
|
|
1811
|
+
headSeen && headStart > 0 ? headStart : -1,
|
|
1812
|
+
headSeen && headLastEnd > 0 ? headLastEnd : -1
|
|
1537
1813
|
);
|
|
1538
1814
|
setLoc(dep, s, e);
|
|
1539
1815
|
module.addPresentationalDependency(dep);
|
|
@@ -1559,6 +1835,16 @@ class HtmlParser extends Parser {
|
|
|
1559
1835
|
).push({ request, entryName, type });
|
|
1560
1836
|
} else {
|
|
1561
1837
|
const dep = new HtmlSourceDependency(request, [s, e]);
|
|
1838
|
+
ResourceHintPlugin.applyDefaults(
|
|
1839
|
+
dep,
|
|
1840
|
+
ResourceHintPlugin.matchUrlHints(urlHints, request)
|
|
1841
|
+
);
|
|
1842
|
+
// `<!-- webpackPrefetch: true -->` (etc.) immediately
|
|
1843
|
+
// before this tag wins over the project-wide default.
|
|
1844
|
+
if (pendingHints) {
|
|
1845
|
+
ResourceHintPlugin.applyParsedHints(dep, pendingHints);
|
|
1846
|
+
pendingHints = undefined;
|
|
1847
|
+
}
|
|
1562
1848
|
setLoc(dep, s, e);
|
|
1563
1849
|
module.addDependency(dep);
|
|
1564
1850
|
module.addCodeGenerationDependency(dep);
|
|
@@ -1569,15 +1855,76 @@ class HtmlParser extends Parser {
|
|
|
1569
1855
|
}
|
|
1570
1856
|
}
|
|
1571
1857
|
},
|
|
1572
|
-
exit: () => {
|
|
1858
|
+
exit: (path) => {
|
|
1859
|
+
if (path.namespace() === NS_HTML) {
|
|
1860
|
+
const name = path.tagName();
|
|
1861
|
+
if (name === "template") {
|
|
1862
|
+
if (templateDepth > 0) templateDepth--;
|
|
1863
|
+
if (headTemplateDepth > 0) headTemplateDepth--;
|
|
1864
|
+
} else if (name === "head") {
|
|
1865
|
+
inHead = false;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1573
1868
|
pendingWebpackIgnore = undefined;
|
|
1869
|
+
pendingWebpackInline = undefined;
|
|
1870
|
+
pendingHints = undefined;
|
|
1574
1871
|
}
|
|
1575
1872
|
}
|
|
1576
1873
|
})
|
|
1577
1874
|
// Skip AST output this walk never reads: `text` (script/style bodies are
|
|
1578
|
-
// read by offset via `contentEnd`) and `doctype
|
|
1875
|
+
// read by offset via `contentEnd`) and `doctype` — unless head injection
|
|
1876
|
+
// needs the doctype as an insertion anchor; keep `comments` for
|
|
1579
1877
|
// magic comments (`webpackIgnore`).
|
|
1580
|
-
.process(source, {
|
|
1878
|
+
.process(source, {
|
|
1879
|
+
fragmentContext: this.fragmentContext,
|
|
1880
|
+
skip: { text: true, doctype: headOptions === undefined }
|
|
1881
|
+
});
|
|
1882
|
+
|
|
1883
|
+
// `output.html` head injection: existing tags win, injected values are
|
|
1884
|
+
// escaped, insertions at the same offset are merged to keep their order.
|
|
1885
|
+
if (headOptions !== undefined && headSeen) {
|
|
1886
|
+
const { title, meta, base } = headOptions;
|
|
1887
|
+
let startTags = "";
|
|
1888
|
+
if (meta && meta.charset && headCharsetEnd === -1) {
|
|
1889
|
+
startTags += `<meta charset="${escapeAttribute(meta.charset)}">`;
|
|
1890
|
+
}
|
|
1891
|
+
// <base> must follow the charset declaration per spec
|
|
1892
|
+
let baseAfterCharset = "";
|
|
1893
|
+
if (base && !headHasBase) {
|
|
1894
|
+
if (headCharsetEnd !== -1) baseAfterCharset = baseTag(base);
|
|
1895
|
+
else startTags += baseTag(base);
|
|
1896
|
+
}
|
|
1897
|
+
let endTags = "";
|
|
1898
|
+
if (meta) {
|
|
1899
|
+
for (const [name, value] of Object.entries(meta)) {
|
|
1900
|
+
if (
|
|
1901
|
+
name === "charset" ||
|
|
1902
|
+
(headMetaNames !== undefined &&
|
|
1903
|
+
headMetaNames.has(name.toLowerCase()))
|
|
1904
|
+
) {
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
endTags += metaTag(name, value);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
if (title && !headHasTitle) {
|
|
1911
|
+
endTags += `<title>${escapeText(title)}</title>`;
|
|
1912
|
+
}
|
|
1913
|
+
/** @type {[number, string][]} */
|
|
1914
|
+
const inserts = [];
|
|
1915
|
+
if (startTags) inserts.push([headStart, startTags]);
|
|
1916
|
+
if (baseAfterCharset) inserts.push([headCharsetEnd, baseAfterCharset]);
|
|
1917
|
+
if (endTags) inserts.push([headLastEnd, endTags]);
|
|
1918
|
+
inserts.sort((a, b) => a[0] - b[0]);
|
|
1919
|
+
for (let i = 0; i < inserts.length; i++) {
|
|
1920
|
+
const at = inserts[i][0];
|
|
1921
|
+
let tags = inserts[i][1];
|
|
1922
|
+
while (i + 1 < inserts.length && inserts[i + 1][0] === at) {
|
|
1923
|
+
tags += inserts[++i][1];
|
|
1924
|
+
}
|
|
1925
|
+
module.addPresentationalDependency(new ConstDependency(tags, at));
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1581
1928
|
|
|
1582
1929
|
const buildInfo = /** @type {HtmlModuleBuildInfo} */ (module.buildInfo);
|
|
1583
1930
|
buildInfo.strict = true;
|
|
@@ -1587,18 +1934,25 @@ class HtmlParser extends Parser {
|
|
|
1587
1934
|
// and `script-module` groups are chained via a leader-only dependOn
|
|
1588
1935
|
// so they share a runtime; `modulepreload` and `stylesheet` entries
|
|
1589
1936
|
// are emitted as independent entries since `<link rel=modulepreload>`
|
|
1590
|
-
// must preload without running and stylesheets have no runtime.
|
|
1937
|
+
// must preload without running and stylesheets have no runtime. `html`
|
|
1938
|
+
// links are independent page entries too.
|
|
1591
1939
|
if (
|
|
1592
1940
|
scriptEntries.length > 0 ||
|
|
1593
1941
|
scriptModuleEntries.length > 0 ||
|
|
1594
1942
|
modulePreloadEntries.length > 0 ||
|
|
1595
|
-
stylesheetEntries.length > 0
|
|
1943
|
+
stylesheetEntries.length > 0 ||
|
|
1944
|
+
htmlLinkEntries.length > 0 ||
|
|
1945
|
+
preloadEntries.length > 0 ||
|
|
1946
|
+
prefetchEntries.length > 0
|
|
1596
1947
|
) {
|
|
1597
|
-
buildInfo.
|
|
1948
|
+
buildInfo.htmlEntries = {
|
|
1598
1949
|
script: scriptEntries,
|
|
1599
1950
|
"script-module": scriptModuleEntries,
|
|
1600
1951
|
modulepreload: modulePreloadEntries,
|
|
1601
|
-
stylesheet: stylesheetEntries
|
|
1952
|
+
stylesheet: stylesheetEntries,
|
|
1953
|
+
html: htmlLinkEntries,
|
|
1954
|
+
preload: preloadEntries,
|
|
1955
|
+
prefetch: prefetchEntries
|
|
1602
1956
|
};
|
|
1603
1957
|
}
|
|
1604
1958
|
|
|
@@ -1611,4 +1965,6 @@ class HtmlParser extends Parser {
|
|
|
1611
1965
|
}
|
|
1612
1966
|
}
|
|
1613
1967
|
|
|
1968
|
+
HtmlParser.buildHeadTags = buildHeadTags;
|
|
1969
|
+
|
|
1614
1970
|
module.exports = HtmlParser;
|