webpack 5.110.2 → 5.111.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/README.md +5 -2
- package/lib/APIPlugin.js +23 -7
- package/lib/AutomaticPrefetchPlugin.js +1 -1
- package/lib/Cache.js +8 -3
- package/lib/CacheFacade.js +1 -1
- package/lib/ChunkGraph.js +20 -18
- package/lib/CircularModulesPlugin.js +157 -55
- package/lib/CleanPlugin.js +2 -2
- package/lib/Compilation.js +321 -303
- package/lib/Compiler.js +209 -46
- package/lib/ConcatenationScope.js +25 -35
- package/lib/ContextModule.js +17 -81
- package/lib/ContextModuleFactory.js +1 -1
- package/lib/CopyPlugin.js +1054 -0
- package/lib/DefinePlugin.js +24 -8
- package/lib/DotenvPlugin.js +41 -13
- package/lib/ExternalModule.js +41 -14
- package/lib/FileSystemInfo.js +17 -14
- package/lib/FlagDependencyExportsPlugin.js +45 -21
- package/lib/FlagDependencyUsagePlugin.js +8 -2
- package/lib/HotModuleReplacementPlugin.js +53 -7
- package/lib/JavascriptMetaInfoPlugin.js +5 -3
- package/lib/MainTemplate.js +5 -1
- package/lib/Module.js +16 -7
- package/lib/MultiCompiler.js +1 -1
- package/lib/MultiWatching.js +1 -1
- package/lib/NormalModule.js +58 -32
- package/lib/NormalModuleFactory.js +1 -1
- package/lib/RuntimeGlobals.js +21 -7
- package/lib/RuntimeModule.js +28 -12
- package/lib/RuntimePlugin.js +33 -3
- package/lib/RuntimeTemplate.js +794 -477
- package/lib/SourceMapDevToolPlugin.js +1 -1
- package/lib/Watching.js +9 -0
- package/lib/WebpackOptionsApply.js +84 -91
- package/lib/asset/AssetGenerator.js +25 -8
- package/lib/asset/AssetModule.js +3 -0
- package/lib/asset/WebManifestParser.js +7 -5
- package/lib/async-modules/AsyncModuleHelpers.js +10 -4
- package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
- package/lib/cache/IdleFileCachePlugin.js +2 -2
- package/lib/cache/PackFileCacheStrategy.js +14 -1
- package/lib/config/browserslistTargetHandler.js +19 -0
- package/lib/config/defaults.js +110 -64
- package/lib/config/normalization.js +100 -20
- package/lib/config/target.js +10 -0
- package/lib/container/ContainerReferencePlugin.js +6 -0
- package/lib/container/RemoteRuntimeModule.js +9 -0
- package/lib/container/declaredRemotes.js +39 -0
- package/lib/css/CssGenerator.js +8 -0
- package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
- package/lib/css/CssLoadingRuntimeModule.js +94 -36
- package/lib/css/CssModule.js +1 -1
- package/lib/css/CssModulesPlugin.js +7 -6
- package/lib/css/CssParser.js +144 -11
- package/lib/css/CssServerStylesRuntimeModule.js +9 -0
- package/lib/css/cssMinify.js +11 -3
- package/lib/css/data.js +2814 -678
- package/lib/css/syntax.js +4082 -248
- package/lib/debug/ProfilingPlugin.js +4 -0
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
- package/lib/dependencies/AMDRuntimeModules.js +18 -0
- package/lib/dependencies/CommonJsExportRequireDependency.js +3 -1
- package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
- package/lib/dependencies/CommonJsImportsParserPlugin.js +1 -0
- package/lib/dependencies/CommonJsPlugin.js +19 -3
- package/lib/dependencies/CommonJsRequireDependency.js +32 -9
- package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
- package/lib/dependencies/CssIcssExportDependency.js +7 -1
- package/lib/dependencies/HarmonyCompatibilityDependency.js +58 -4
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +8 -3
- package/lib/dependencies/HarmonyExportExpressionDependency.js +44 -29
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +171 -32
- package/lib/dependencies/HarmonyExportInitFragment.js +99 -19
- package/lib/dependencies/HarmonyExportSpecifierDependency.js +18 -30
- package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
- package/lib/dependencies/HarmonyImportDependency.js +99 -41
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +20 -3
- package/lib/dependencies/HarmonyImportSpecifierDependency.js +24 -1
- package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
- package/lib/dependencies/HtmlEntryDependency.js +62 -2
- package/lib/dependencies/ImportContextDependency.js +43 -3
- package/lib/dependencies/ImportDependency.js +8 -29
- package/lib/dependencies/ImportMetaMainDependency.js +104 -0
- package/lib/dependencies/ImportMetaPlugin.js +59 -15
- package/lib/dependencies/ImportParserPlugin.js +24 -14
- package/lib/dependencies/RequireHeaderDependency.js +20 -21
- package/lib/dependencies/SystemRuntimeModule.js +9 -0
- package/lib/dependencies/WorkerDependency.js +1 -2
- package/lib/dependencies/WorkletDependency.js +1 -2
- package/lib/dependencies/importOptionsCheck.js +47 -0
- package/lib/dll/DelegatedModule.js +18 -1
- package/lib/dll/DllReferencePlugin.js +11 -8
- package/lib/dll/LibManifestPlugin.js +1 -1
- package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
- package/lib/errors/DotenvFileError.js +29 -0
- package/lib/errors/DuplicateModulesWarning.js +12 -7
- package/lib/errors/EmptyCopyPatternWarning.js +26 -0
- package/lib/errors/ExternalStarReexportError.js +44 -0
- package/lib/errors/ImportedBindingAssignmentError.js +30 -0
- package/lib/errors/MissingSourceMapsWarning.js +36 -0
- package/lib/errors/ModuleParseError.js +2 -1
- package/lib/errors/UnusedAssetsWarning.js +36 -0
- package/lib/errors/UnusedFederationWarning.js +35 -0
- package/lib/errors/UnusedModulesWarning.js +38 -0
- package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
- package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
- package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
- package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
- package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
- package/lib/html/HtmlGenerator.js +11 -5
- package/lib/html/HtmlModulesPlugin.js +360 -84
- package/lib/html/HtmlParser.js +13 -6
- package/lib/html/builtinEmbeddedRenderer.js +107 -0
- package/lib/html/data.js +121 -7
- package/lib/html/htmlMinify.js +84 -113
- package/lib/html/syntax.js +1067 -569
- package/lib/ids/HashedModuleIdsPlugin.js +2 -1
- package/lib/index.js +3 -0
- package/lib/javascript/JavascriptModule.js +1 -0
- package/lib/javascript/JavascriptModulesPlugin.js +52 -22
- package/lib/javascript/JavascriptParser.js +1255 -728
- package/lib/javascript/StartupHelpers.js +27 -3
- package/lib/javascript/data.js +172 -0
- package/lib/javascript/grammar.js +5546 -0
- package/lib/javascript/parser.js +1494 -0
- package/lib/javascript/regexp.js +1762 -0
- package/lib/javascript/regexpData.js +51 -0
- package/lib/javascript/syntax.js +8696 -5225
- package/lib/library/ModuleLibraryPlugin.js +116 -5
- package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
- package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
- package/lib/optimize/ConcatenatedModule.js +133 -25
- package/lib/optimize/ConstExportsPlugin.js +50 -39
- package/lib/optimize/InlineExports.js +20 -123
- package/lib/optimize/MangleExportsPlugin.js +10 -0
- package/lib/optimize/ModuleConcatenationPlugin.js +24 -1
- package/lib/optimize/RealContentHashPlugin.js +68 -9
- package/lib/optimize/SideEffectsFlagPlugin.js +17 -9
- package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
- package/lib/performance/CacheEffectivenessPlugin.js +4 -3
- package/lib/performance/DuplicateModulesPlugin.js +57 -13
- package/lib/performance/EvalUsagePlugin.js +5 -2
- package/lib/performance/MissingSourceMapsPlugin.js +115 -0
- package/lib/performance/PureAnnotationsPlugin.js +6 -3
- package/lib/performance/TopLevelThisPlugin.js +5 -4
- package/lib/performance/UnusedAssetsPlugin.js +109 -0
- package/lib/performance/UnusedFederationPlugin.js +147 -0
- package/lib/performance/UnusedModulesPlugin.js +141 -0
- package/lib/performance/UnusedReexportsPlugin.js +1 -55
- package/lib/performance/isOnlyReexported.js +65 -0
- package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
- package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +2 -0
- package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
- package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
- package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
- package/lib/prefetch/ResourceHintPlugin.js +7 -3
- package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
- package/lib/prefetch/StartupAssetHintRuntimeModule.js +9 -0
- package/lib/rules/RuleSetCompiler.js +211 -4
- package/lib/runtime/AsyncModuleRuntimeModule.js +105 -67
- package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
- package/lib/runtime/BaseUriRuntimeModule.js +9 -0
- package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
- package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +9 -0
- package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
- package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
- package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
- package/lib/runtime/GlobalRuntimeModule.js +9 -0
- package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
- package/lib/runtime/HelperRuntimeModule.js +10 -0
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +338 -217
- package/lib/runtime/NonceRuntimeModule.js +9 -0
- package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
- package/lib/runtime/PublicPathRuntimeModule.js +9 -0
- package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
- package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
- package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
- package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
- package/lib/runtime/SystemContextRuntimeModule.js +9 -0
- package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
- package/lib/schemes/HttpUriPlugin.js +22 -5
- package/lib/serialization/FileMiddleware.js +15 -2
- package/lib/serialization/ObjectMiddleware.js +138 -106
- package/lib/sharing/ConsumeSharedPlugin.js +12 -0
- package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
- package/lib/sharing/ShareRuntimeModule.js +9 -0
- package/lib/sharing/declaredShared.js +47 -0
- package/lib/stats/DefaultStatsFactoryPlugin.js +3 -1
- package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
- package/lib/util/ArrayQueue.js +3 -3
- package/lib/util/SourceProcessor.js +76 -69
- package/lib/util/URLAbsoluteSpecifier.js +3 -1
- package/lib/util/async.js +292 -0
- package/lib/util/buildDiagnostics.js +69 -0
- package/lib/util/conventions.js +5 -1
- package/lib/util/fs.js +27 -1
- package/lib/util/handlerKeys.js +40 -0
- package/lib/util/identifier.js +20 -0
- package/lib/util/internalSerializables.js +6 -0
- package/lib/util/registerExternalSerializer.js +6 -45
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +9 -0
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
- package/package.json +22 -16
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +388 -76
- package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
- package/schemas/plugins/ManifestPlugin.check.js +1 -1
- package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
- package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
- package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
- package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
- package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
- package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
- package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
- package/types.d.ts +1440 -331
- package/lib/dependencies/ExportBindingInitFragment.js +0 -191
- package/lib/errors/EntrypointOverlapWarning.js +0 -45
- package/lib/performance/EntrypointOverlapPlugin.js +0 -122
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
"use strict";
|
|
6
6
|
|
|
7
|
+
const { basename, extname } = require("path");
|
|
7
8
|
const { pathToFileURL } = require("url");
|
|
8
9
|
const { AsyncSeriesHook, AsyncSeriesWaterfallHook } = require("tapable");
|
|
9
10
|
const { RawSource } = require("webpack-sources");
|
|
@@ -19,10 +20,18 @@ const ConstDependency = require("../dependencies/ConstDependency");
|
|
|
19
20
|
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
|
20
21
|
const WebpackError = require("../errors/WebpackError");
|
|
21
22
|
const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
|
|
22
|
-
const
|
|
23
|
+
const ConcatenatedModule = require("../optimize/ConcatenatedModule");
|
|
24
|
+
const {
|
|
25
|
+
compareModulesByFullName,
|
|
26
|
+
compareModulesByIdentifier
|
|
27
|
+
} = require("../util/comparators");
|
|
23
28
|
const createHash = require("../util/createHash");
|
|
24
29
|
const createHooksRegistry = require("../util/createHooksRegistry");
|
|
25
|
-
const {
|
|
30
|
+
const {
|
|
31
|
+
getUndoPath,
|
|
32
|
+
makePathsRelative,
|
|
33
|
+
parseResource
|
|
34
|
+
} = require("../util/identifier");
|
|
26
35
|
const implicitTypeLoaderFallback = require("../util/implicitTypeLoaderFallback");
|
|
27
36
|
const lazyModule = require("../util/lazyModule");
|
|
28
37
|
const memoize = require("../util/memoize");
|
|
@@ -45,20 +54,41 @@ const getHtmlParser = lazyModule(() => require("./HtmlParser"));
|
|
|
45
54
|
// html generation only happens once a build actually has an html module
|
|
46
55
|
const getHtmlGenerator = memoize(() => require("./HtmlGenerator"));
|
|
47
56
|
|
|
57
|
+
// only reached once a page names a CSS chunk of its own
|
|
58
|
+
const getCssModulesPlugin = memoize(() => require("../css/CssModulesPlugin"));
|
|
59
|
+
|
|
48
60
|
/**
|
|
49
61
|
* @import {
|
|
50
62
|
* EntryDescriptionNormalized,
|
|
63
|
+
* FilenameTemplate,
|
|
51
64
|
* OutputHtmlOptions,
|
|
52
65
|
* HtmlFaviconIcon
|
|
53
66
|
* } from "../../declarations/WebpackOptions"
|
|
54
67
|
*/
|
|
68
|
+
/**
|
|
69
|
+
* @import {
|
|
70
|
+
* OutputNormalizedWithDefaults as OutputOptions
|
|
71
|
+
* } from "../config/defaults"
|
|
72
|
+
*/
|
|
55
73
|
/** @typedef {import("../../declarations/WebpackOptions").OutputHtmlOptions["favicon"]} FaviconOption */
|
|
56
74
|
/** @typedef {import("../../declarations/WebpackOptions").OutputHtmlOptions["manifest"]} ManifestOption */
|
|
57
75
|
/** @typedef {Exclude<HtmlFaviconIcon, string>} FaviconIcon */
|
|
76
|
+
/** @import Chunk, { ChunkFilenameTemplate } from "../Chunk" */
|
|
77
|
+
/** @import ChunkGraph from "../ChunkGraph" */
|
|
58
78
|
/** @import Compiler from "../Compiler" */
|
|
79
|
+
/** @import Module from "../Module" */
|
|
80
|
+
/** @typedef {import("../Compilation").EntryData} EntryData */
|
|
59
81
|
/** @import { HtmlModuleBuildInfo } from "./HtmlModule" */
|
|
60
82
|
/** @import HtmlParser from "./HtmlParser" */
|
|
61
|
-
/** @typedef {{ request: string, entryName: string, type: "script" | "script-module" | "modulepreload" | "stylesheet" | "html" | "preload" | "prefetch", css?: boolean }} HtmlEntryInfo */
|
|
83
|
+
/** @typedef {{ request: string, entryName: string, index: number, type: "script" | "script-module" | "modulepreload" | "stylesheet" | "html" | "preload" | "prefetch", css?: boolean }} HtmlEntryInfo */
|
|
84
|
+
/**
|
|
85
|
+
* What one extracted entry needs to be named. Recorded in page order once
|
|
86
|
+
* every page is built, and resolved to filenames once chunks exist.
|
|
87
|
+
* @typedef {object} PendingName
|
|
88
|
+
* @property {string} base name the tag's url (or its page) asks for, before numbering
|
|
89
|
+
* @property {FilenameTemplate} jsTemplate `output.filename` for this entry, as configured
|
|
90
|
+
* @property {string=} freeName name of a page-only entry, which emits nothing and leaves it free
|
|
91
|
+
*/
|
|
62
92
|
|
|
63
93
|
/** @typedef {{ outputName: string }} HtmlTransformHtmlContext */
|
|
64
94
|
/** @typedef {{ outputName: string }} HtmlEmittedContext */
|
|
@@ -222,9 +252,130 @@ const isIntegrityEnabled = (integrity) =>
|
|
|
222
252
|
// `\.html`/`\.css` request matchers for the synthetic `output.html` wrapper.
|
|
223
253
|
const HTML_REQUEST_RE = /\.html(\?|$)/i;
|
|
224
254
|
const CSS_REQUEST_RE = /\.css(\?|$)/i;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
255
|
+
/**
|
|
256
|
+
* The entry a page is the whole of: nothing imports the module and the entry
|
|
257
|
+
* asks for nothing else, so the entry's JavaScript would be the page itself.
|
|
258
|
+
* @param {Compilation} compilation compilation
|
|
259
|
+
* @param {Module} module html module
|
|
260
|
+
* @returns {{ name: string, data: EntryData } | undefined} the page's own entry, when it has one
|
|
261
|
+
*/
|
|
262
|
+
const getPageEntry = (compilation, module) => {
|
|
263
|
+
const { moduleGraph } = compilation;
|
|
264
|
+
for (const connection of moduleGraph.getIncomingConnections(module)) {
|
|
265
|
+
if (connection.originModule) return undefined;
|
|
266
|
+
}
|
|
267
|
+
for (const [name, data] of compilation.entries) {
|
|
268
|
+
if (data.dependencies.length !== 1) continue;
|
|
269
|
+
if (data.includeDependencies.length !== 0) continue;
|
|
270
|
+
if (moduleGraph.getModule(data.dependencies[0]) === module) {
|
|
271
|
+
return { name, data };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return undefined;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The entry of a page that emits nothing but its markup, so its chunk carries
|
|
279
|
+
* no JavaScript. An `output.library` asks for the page's exports, and keeps it.
|
|
280
|
+
* @param {Compilation} compilation compilation
|
|
281
|
+
* @param {Module} module html module
|
|
282
|
+
* @returns {{ name: string, data: EntryData } | undefined} the entry, when the page is all it emits
|
|
283
|
+
*/
|
|
284
|
+
const getPageOnlyEntry = (compilation, module) => {
|
|
285
|
+
const entry = getPageEntry(compilation, module);
|
|
286
|
+
if (entry === undefined) return undefined;
|
|
287
|
+
const { library } = entry.data.options;
|
|
288
|
+
const configured =
|
|
289
|
+
library === undefined ? compilation.outputOptions.library : library;
|
|
290
|
+
return configured === undefined ? entry : undefined;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The basename a tag's url lends to the entry it becomes, so
|
|
295
|
+
* `src="./my-script.js"` emits `my-script.js`. Undefined for an inline body.
|
|
296
|
+
* @param {string} request the tag's request
|
|
297
|
+
* @returns {string | undefined} the basename, when the url carries one
|
|
298
|
+
*/
|
|
299
|
+
const getRequestBaseName = (request) => {
|
|
300
|
+
// An inline `<script>` body is a `data:` request webpack writes itself.
|
|
301
|
+
if (request.startsWith("data:")) return undefined;
|
|
302
|
+
const { path: file } = parseResource(request);
|
|
303
|
+
return basename(file, extname(file)) || undefined;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Claims `base`, numbering it while something else already emits under that
|
|
308
|
+
* name, so two tags sharing a basename stay distinct.
|
|
309
|
+
* @param {string} base preferred name
|
|
310
|
+
* @param {Set<string>} taken names this kind of output already emits under
|
|
311
|
+
* @returns {string} the claimed name
|
|
312
|
+
*/
|
|
313
|
+
const claimName = (base, taken) => {
|
|
314
|
+
let name = base;
|
|
315
|
+
for (let index = 1; taken.has(name); index++) name = `${base}${index}`;
|
|
316
|
+
taken.add(name);
|
|
317
|
+
return name;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Placeholders that tell chunks apart on their own, so a template carrying
|
|
321
|
+
// one needs no `[name]`. Not `[id]`: an extracted entry's id is synthetic.
|
|
322
|
+
const CONTENT_PLACEHOLDER_REGEXP =
|
|
323
|
+
/\[(?:chunkhash|contenthash|fullhash|hash)[\]:]/;
|
|
324
|
+
|
|
325
|
+
// Basename stem of a filename template — what `[name]` stands in for, keeping
|
|
326
|
+
// the directory, extension and query the template asked for.
|
|
327
|
+
const TEMPLATE_STEM_REGEXP = /(^|\/)[^/?]*?((?:\.[^./?]+)*)(\?.*)?$/;
|
|
328
|
+
|
|
329
|
+
const NAME_PLACEHOLDER_REGEXP = /\[name\]/g;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* A filename template usable for a page's extracted entries: it must tell one
|
|
333
|
+
* entry from another, or every entry of the page would be emitted to the same
|
|
334
|
+
* file. A template naming neither the chunk nor its content lends its directory
|
|
335
|
+
* and extension to a `[name]` instead.
|
|
336
|
+
* @param {string} template configured template
|
|
337
|
+
* @param {string} extension extension to fall back to when the template names none
|
|
338
|
+
* @returns {string} a template that tells the page's entries apart
|
|
339
|
+
*/
|
|
340
|
+
const getNamedTemplate = (template, extension) => {
|
|
341
|
+
if (
|
|
342
|
+
template.includes("[name]") ||
|
|
343
|
+
CONTENT_PLACEHOLDER_REGEXP.test(template)
|
|
344
|
+
) {
|
|
345
|
+
return template;
|
|
346
|
+
}
|
|
347
|
+
return template.replace(
|
|
348
|
+
TEMPLATE_STEM_REGEXP,
|
|
349
|
+
(_, dir, ext, query) => `${dir}[name]${ext || extension}${query || ""}`
|
|
350
|
+
);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* The filename one extracted entry emits under, with `name` filling the
|
|
355
|
+
* `[name]` its template carries or the stem it leaves behind.
|
|
356
|
+
* @param {string} template configured template
|
|
357
|
+
* @param {string} extension extension to fall back to when the template names none
|
|
358
|
+
* @param {string} name the name this entry claimed
|
|
359
|
+
* @returns {string} the entry's filename template
|
|
360
|
+
*/
|
|
361
|
+
const fillNamedTemplate = (template, extension, name) =>
|
|
362
|
+
getNamedTemplate(template, extension).replace(NAME_PLACEHOLDER_REGEXP, name);
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The same, for a template `output.filename` states as a function: it is called
|
|
366
|
+
* at emit, and the path it returns read for a stem the same way.
|
|
367
|
+
* @param {FilenameTemplate} template configured template
|
|
368
|
+
* @param {string} extension extension to fall back to when the template names none
|
|
369
|
+
* @param {string} name the name this entry claimed
|
|
370
|
+
* @returns {ChunkFilenameTemplate} the entry's filename template
|
|
371
|
+
*/
|
|
372
|
+
const getEntryTemplate = (template, extension, name) => {
|
|
373
|
+
if (typeof template === "function") {
|
|
374
|
+
return (pathData, assetInfo) =>
|
|
375
|
+
fillNamedTemplate(template(pathData, assetInfo), extension, name);
|
|
376
|
+
}
|
|
377
|
+
return fillNamedTemplate(template, extension, name);
|
|
378
|
+
};
|
|
228
379
|
|
|
229
380
|
/**
|
|
230
381
|
* Requests an `output.html` page must load for an entry: its `dependOn`
|
|
@@ -241,7 +392,12 @@ const collectHtmlEntryRequests = (name, entries) => {
|
|
|
241
392
|
const seenRequests = new Set();
|
|
242
393
|
/** @type {Set<string>} */
|
|
243
394
|
const visited = new Set();
|
|
244
|
-
|
|
395
|
+
/**
|
|
396
|
+
* Collects an entry's non-html requests, `dependOn` entries included.
|
|
397
|
+
* @param {string} entryName entry to walk
|
|
398
|
+
* @returns {void}
|
|
399
|
+
*/
|
|
400
|
+
const walk = (entryName) => {
|
|
245
401
|
if (visited.has(entryName)) return;
|
|
246
402
|
visited.add(entryName);
|
|
247
403
|
const desc = entries[entryName];
|
|
@@ -289,7 +445,7 @@ class HtmlModulesPlugin {
|
|
|
289
445
|
* digest of `content`, with `nonNumericOnlyHash` applied — webpack's
|
|
290
446
|
* standard `[contenthash]` recipe.
|
|
291
447
|
* @param {string | Buffer} content content to hash
|
|
292
|
-
* @param {
|
|
448
|
+
* @param {OutputOptions} outputOptions output options
|
|
293
449
|
* @returns {string} content hash
|
|
294
450
|
*/
|
|
295
451
|
static computeContentHash(content, outputOptions) {
|
|
@@ -311,7 +467,7 @@ class HtmlModulesPlugin {
|
|
|
311
467
|
* initial chunks, `output.htmlChunkFilename` otherwise — the HTML counterpart
|
|
312
468
|
* of `CssModulesPlugin.getChunkFilenameTemplate`.
|
|
313
469
|
* @param {import("../Chunk")} chunk chunk
|
|
314
|
-
* @param {
|
|
470
|
+
* @param {OutputOptions} outputOptions output options
|
|
315
471
|
* @returns {import("../Chunk").ChunkFilenameTemplate} used filename template
|
|
316
472
|
*/
|
|
317
473
|
static getChunkFilenameTemplate(chunk, outputOptions) {
|
|
@@ -480,11 +636,11 @@ class HtmlModulesPlugin {
|
|
|
480
636
|
// `finishMake` creates their entries without scanning the whole module
|
|
481
637
|
// graph), the stylesheet entry names (read in `afterChunks`) and the html
|
|
482
638
|
// options of every emitted page, keyed by its asset name.
|
|
483
|
-
/** @type {WeakMap<import("../Compilation"), { htmlModules: Set<
|
|
639
|
+
/** @type {WeakMap<import("../Compilation"), { htmlModules: Set<Module>, stylesheetEntries: Set<string>, pendingNames: Map<string, PendingName>, htmlAssetOptions: Map<string, OutputHtmlOptions> }>} */
|
|
484
640
|
const compilationState = new WeakMap();
|
|
485
641
|
/**
|
|
486
642
|
* @param {import("../Compilation")} compilation compilation
|
|
487
|
-
* @returns {{ htmlModules: Set<
|
|
643
|
+
* @returns {{ htmlModules: Set<Module>, stylesheetEntries: Set<string>, pendingNames: Map<string, PendingName>, htmlAssetOptions: Map<string, OutputHtmlOptions> }} per-compilation state
|
|
488
644
|
*/
|
|
489
645
|
const getState = (compilation) => {
|
|
490
646
|
let state = compilationState.get(compilation);
|
|
@@ -492,6 +648,7 @@ class HtmlModulesPlugin {
|
|
|
492
648
|
state = {
|
|
493
649
|
htmlModules: new Set(),
|
|
494
650
|
stylesheetEntries: new Set(),
|
|
651
|
+
pendingNames: new Map(),
|
|
495
652
|
htmlAssetOptions: new Map()
|
|
496
653
|
};
|
|
497
654
|
compilationState.set(compilation, state);
|
|
@@ -499,14 +656,21 @@ class HtmlModulesPlugin {
|
|
|
499
656
|
return state;
|
|
500
657
|
};
|
|
501
658
|
compiler.hooks.finishMake.tapAsync(PLUGIN_NAME, (compilation, callback) => {
|
|
502
|
-
const { htmlModules, stylesheetEntries } =
|
|
659
|
+
const { htmlModules, stylesheetEntries, pendingNames } =
|
|
660
|
+
getState(compilation);
|
|
661
|
+
/** @type {Set<string>} */
|
|
662
|
+
const extractedNames = new Set();
|
|
663
|
+
// The page each `type: "html"` link built, so the naming walk below can
|
|
664
|
+
// follow the links in document order instead of in build order.
|
|
665
|
+
/** @type {Map<string, Module>} */
|
|
666
|
+
const linkedPages = new Map();
|
|
503
667
|
|
|
504
668
|
// Collect the entries an HTML module asks for. Only the script chains
|
|
505
669
|
// (`script`, `script-module`) share a runtime via a leader-only
|
|
506
670
|
// `dependOn`; `modulepreload`, `stylesheet` and `html` links are all
|
|
507
671
|
// independent entries (a CSS or page entry must not chain into a JS
|
|
508
672
|
// leader, or the chunk would mix unrelated outputs).
|
|
509
|
-
/** @type {(module:
|
|
673
|
+
/** @type {(module: Module) => { context: string, request: string, name: string, dependOn: string[] | undefined }[]} */
|
|
510
674
|
const collectEntrySpecs = (module) => {
|
|
511
675
|
const { htmlEntries } = /** @type {HtmlModuleBuildInfo} */ (
|
|
512
676
|
module.buildInfo
|
|
@@ -528,10 +692,11 @@ class HtmlModulesPlugin {
|
|
|
528
692
|
leaderName = entry.entryName;
|
|
529
693
|
}
|
|
530
694
|
// Stylesheet entries and `as="style"` preload/prefetch entries emit
|
|
531
|
-
// a CSS chunk, so they
|
|
532
|
-
if (groupKind === "stylesheet" || entry.css) {
|
|
695
|
+
// a CSS chunk, so they are named from `output.cssFilename` alone.
|
|
696
|
+
if (groupKind === "stylesheet" || Boolean(entry.css)) {
|
|
533
697
|
stylesheetEntries.add(entry.entryName);
|
|
534
698
|
}
|
|
699
|
+
extractedNames.add(entry.entryName);
|
|
535
700
|
specs.push({
|
|
536
701
|
context,
|
|
537
702
|
request: entry.request,
|
|
@@ -543,9 +708,70 @@ class HtmlModulesPlugin {
|
|
|
543
708
|
return specs;
|
|
544
709
|
};
|
|
545
710
|
|
|
711
|
+
// Records what a page's entries ask to be named, then walks its links —
|
|
712
|
+
// once every page is built, so the order is the documents', not the builds'.
|
|
713
|
+
/** @type {(module: Module, seen: Set<Module>) => void} */
|
|
714
|
+
const recordNames = (module, seen) => {
|
|
715
|
+
if (seen.has(module)) return;
|
|
716
|
+
seen.add(module);
|
|
717
|
+
const { htmlEntries } = /** @type {HtmlModuleBuildInfo} */ (
|
|
718
|
+
module.buildInfo
|
|
719
|
+
);
|
|
720
|
+
if (!htmlEntries) return;
|
|
721
|
+
const pageEntry = getPageOnlyEntry(compilation, module);
|
|
722
|
+
const namedEntry =
|
|
723
|
+
pageEntry !== undefined && !extractedNames.has(pageEntry.name)
|
|
724
|
+
? pageEntry
|
|
725
|
+
: undefined;
|
|
726
|
+
const { resource } = /** @type {NormalModule} */ (module);
|
|
727
|
+
// An `output.html` page is a wrapper webpack writes around an entry, so
|
|
728
|
+
// what it loads is that entry's own output and takes its name.
|
|
729
|
+
const wrapsEntry =
|
|
730
|
+
namedEntry !== undefined &&
|
|
731
|
+
typeof resource === "string" &&
|
|
732
|
+
resource.startsWith("data:text/html");
|
|
733
|
+
const { filename } = compilation.outputOptions;
|
|
734
|
+
// Kept as configured, a function included, and read for a name once the
|
|
735
|
+
// chunks exist.
|
|
736
|
+
const jsTemplate =
|
|
737
|
+
namedEntry && namedEntry.data.options.filename !== undefined
|
|
738
|
+
? namedEntry.data.options.filename
|
|
739
|
+
: filename;
|
|
740
|
+
// In document order, so of two tags sharing a basename the first a
|
|
741
|
+
// reader sees keeps it. A linked page is named on emit instead.
|
|
742
|
+
/** @type {HtmlEntryInfo[]} */
|
|
743
|
+
const ordered = [];
|
|
744
|
+
/** @type {HtmlEntryInfo[]} */
|
|
745
|
+
const links = [];
|
|
746
|
+
for (const [groupKind, group] of Object.entries(htmlEntries)) {
|
|
747
|
+
for (const entry of group) {
|
|
748
|
+
(groupKind === "html" ? links : ordered).push(entry);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
ordered.sort((a, b) => a.index - b.index);
|
|
752
|
+
for (const entry of ordered) {
|
|
753
|
+
// An inline `<script>` body has no url to be named after, so it
|
|
754
|
+
// keeps the name of its own entry, page hash and tag index.
|
|
755
|
+
const base =
|
|
756
|
+
wrapsEntry && namedEntry
|
|
757
|
+
? namedEntry.name
|
|
758
|
+
: getRequestBaseName(entry.request) || entry.entryName;
|
|
759
|
+
pendingNames.set(entry.entryName, {
|
|
760
|
+
base,
|
|
761
|
+
jsTemplate,
|
|
762
|
+
freeName: namedEntry && namedEntry.name
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
links.sort((a, b) => a.index - b.index);
|
|
766
|
+
for (const link of links) {
|
|
767
|
+
const page = linkedPages.get(link.entryName);
|
|
768
|
+
if (page) recordNames(page, seen);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
|
|
546
772
|
// Push one collected reference as a compilation entry; resolves with
|
|
547
773
|
// the built entry module.
|
|
548
|
-
/** @type {(spec: { context: string, request: string, name: string, dependOn: string[] | undefined }) => Promise<
|
|
774
|
+
/** @type {(spec: { context: string, request: string, name: string, dependOn: string[] | undefined }) => Promise<Module | null>} */
|
|
549
775
|
const addEntry = (spec) =>
|
|
550
776
|
new Promise((resolve, reject) => {
|
|
551
777
|
compilation.addEntry(
|
|
@@ -553,10 +779,8 @@ class HtmlModulesPlugin {
|
|
|
553
779
|
EntryPlugin.createDependency(spec.request, { name: spec.name }),
|
|
554
780
|
{
|
|
555
781
|
name: spec.name,
|
|
556
|
-
//
|
|
557
|
-
//
|
|
558
|
-
// `.css` name via `cssFilenameTemplate` below; `html` page entries
|
|
559
|
-
// emit their file via `renderManifest`.
|
|
782
|
+
// A placeholder `afterChunks` overwrites for every entry it
|
|
783
|
+
// names; only a linked page keeps it, and it emits no JavaScript.
|
|
560
784
|
filename: compilation.outputOptions.chunkFilename || "[name].js",
|
|
561
785
|
dependOn: spec.dependOn
|
|
562
786
|
},
|
|
@@ -570,7 +794,7 @@ class HtmlModulesPlugin {
|
|
|
570
794
|
// every HTML entry already gets. `processed` guards diamonds and cycles.
|
|
571
795
|
/** @type {WeakSet<import("../Module")>} */
|
|
572
796
|
const processed = new WeakSet();
|
|
573
|
-
/** @type {(module:
|
|
797
|
+
/** @type {(module: Module) => Promise<void>} */
|
|
574
798
|
const processEntry = async (module) => {
|
|
575
799
|
if (processed.has(module)) return;
|
|
576
800
|
processed.add(module);
|
|
@@ -578,6 +802,7 @@ class HtmlModulesPlugin {
|
|
|
578
802
|
collectEntrySpecs(module).map(async (spec) => {
|
|
579
803
|
const entryModule = await addEntry(spec);
|
|
580
804
|
if (entryModule && entryModule.type === HTML_MODULE_TYPE) {
|
|
805
|
+
linkedPages.set(spec.name, entryModule);
|
|
581
806
|
await processEntry(entryModule);
|
|
582
807
|
}
|
|
583
808
|
})
|
|
@@ -585,11 +810,15 @@ class HtmlModulesPlugin {
|
|
|
585
810
|
};
|
|
586
811
|
// Seed with the HTML modules seen during make (config entries /
|
|
587
812
|
// imports, fresh or cache-restored); linked pages are reached by
|
|
588
|
-
// recursion above.
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
813
|
+
// recursion above. Sorted because they are tracked in build-completion
|
|
814
|
+
// order, which the names must not depend on.
|
|
815
|
+
const seeds = [...htmlModules].sort(compareModulesByIdentifier);
|
|
816
|
+
Promise.all(seeds.map(processEntry)).then(() => {
|
|
817
|
+
/** @type {Set<Module>} */
|
|
818
|
+
const named = new Set();
|
|
819
|
+
for (const seed of seeds) recordNames(seed, named);
|
|
820
|
+
callback();
|
|
821
|
+
}, callback);
|
|
593
822
|
});
|
|
594
823
|
|
|
595
824
|
// `csp` injects a `<meta http-equiv="Content-Security-Policy">` and
|
|
@@ -613,12 +842,13 @@ class HtmlModulesPlugin {
|
|
|
613
842
|
);
|
|
614
843
|
}
|
|
615
844
|
const { htmlModules } = getState(compilation);
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
845
|
+
/**
|
|
846
|
+
* Records HTML modules as they are built or restored, so `finishMake`
|
|
847
|
+
* creates their entries without scanning the whole module graph.
|
|
848
|
+
* @param {import("../Module")} module built or restored module
|
|
849
|
+
* @returns {void}
|
|
850
|
+
*/
|
|
851
|
+
const trackHtmlModule = (module) => {
|
|
622
852
|
if (module.type !== HTML_MODULE_TYPE) return;
|
|
623
853
|
// enabled here, not on the compilation hook: an integrity build
|
|
624
854
|
// with no html module must not load the generator
|
|
@@ -719,26 +949,6 @@ class HtmlModulesPlugin {
|
|
|
719
949
|
// a content-hashed filename makes a substring hit a real reference.
|
|
720
950
|
/** @type {Set<string>} */
|
|
721
951
|
const deletionCandidates = new Set(inlinedFiles);
|
|
722
|
-
// A wrapper entry's chunk only rebuilds the page's own markup; the
|
|
723
|
-
// page loads the `<script src>` entries the parser split out of it.
|
|
724
|
-
for (const entrypoint of compilation.entrypoints.values()) {
|
|
725
|
-
const chunk = entrypoint.getEntrypointChunk();
|
|
726
|
-
if (!chunk) continue;
|
|
727
|
-
let wrapperOnly = false;
|
|
728
|
-
for (const entryModule of compilation.chunkGraph.getChunkEntryModulesIterable(
|
|
729
|
-
chunk
|
|
730
|
-
)) {
|
|
731
|
-
const { resource } = /** @type {import("../NormalModule")} */ (
|
|
732
|
-
entryModule
|
|
733
|
-
);
|
|
734
|
-
wrapperOnly =
|
|
735
|
-
typeof resource === "string" &&
|
|
736
|
-
HTML_DATA_URL_RE.test(resource);
|
|
737
|
-
if (!wrapperOnly) break;
|
|
738
|
-
}
|
|
739
|
-
if (!wrapperOnly) continue;
|
|
740
|
-
for (const file of chunk.files) deletionCandidates.add(file);
|
|
741
|
-
}
|
|
742
952
|
// Materialize each other asset's source once (it can be expensive to
|
|
743
953
|
// render) and test all candidates against it, rather than
|
|
744
954
|
// re-materializing every asset per candidate.
|
|
@@ -790,26 +1000,57 @@ class HtmlModulesPlugin {
|
|
|
790
1000
|
// entry emits to a distinct file derived from `output.cssFilename`
|
|
791
1001
|
// (or `output.cssChunkFilename` for non-initial CSS chunks).
|
|
792
1002
|
compilation.hooks.afterChunks.tap(PLUGIN_NAME, () => {
|
|
793
|
-
const { stylesheetEntries } = getState(compilation);
|
|
794
|
-
if (
|
|
795
|
-
|
|
1003
|
+
const { stylesheetEntries, pendingNames } = getState(compilation);
|
|
1004
|
+
if (pendingNames.size === 0) return;
|
|
1005
|
+
const { chunkGraph, outputOptions } = compilation;
|
|
1006
|
+
const jsExtension = outputOptions.module ? ".mjs" : ".js";
|
|
1007
|
+
// Claimed in the order the pages recorded them, which is the
|
|
1008
|
+
// documents' order rather than their builds'.
|
|
1009
|
+
const takenJsNames = new Set(compilation.entries.keys());
|
|
1010
|
+
const takenCssNames = new Set(takenJsNames);
|
|
1011
|
+
// An extracted entry emits nothing under its own synthetic name, so
|
|
1012
|
+
// that name never stands in the way of the one its tag asks for.
|
|
1013
|
+
for (const entryName of pendingNames.keys()) {
|
|
1014
|
+
takenJsNames.delete(entryName);
|
|
1015
|
+
takenCssNames.delete(entryName);
|
|
1016
|
+
}
|
|
1017
|
+
for (const { freeName } of pendingNames.values()) {
|
|
1018
|
+
if (freeName === undefined) continue;
|
|
1019
|
+
// A page-only entry emits nothing of its own, so its name is free for
|
|
1020
|
+
// the entries extracted from it — `app.html` keeps `app.js`.
|
|
1021
|
+
takenJsNames.delete(freeName);
|
|
1022
|
+
takenCssNames.delete(freeName);
|
|
1023
|
+
}
|
|
1024
|
+
for (const [entryName, { base, jsTemplate }] of pendingNames) {
|
|
796
1025
|
const entrypoint = compilation.entrypoints.get(entryName);
|
|
797
1026
|
if (!entrypoint) continue;
|
|
798
1027
|
const chunk = entrypoint.getEntrypointChunk();
|
|
799
1028
|
if (!chunk) continue;
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1029
|
+
// A stylesheet entry emits no JavaScript, so it claims no JS name and
|
|
1030
|
+
// leaves that one to a `<script src>` of the same basename.
|
|
1031
|
+
const emitsCss = stylesheetEntries.has(entryName);
|
|
1032
|
+
if (!emitsCss) {
|
|
1033
|
+
// `output.filename` named after the tag's url, the way an entry
|
|
1034
|
+
// point is named. `Compilation` set it from the placeholder above.
|
|
1035
|
+
chunk.filenameTemplate = getEntryTemplate(
|
|
1036
|
+
jsTemplate,
|
|
1037
|
+
jsExtension,
|
|
1038
|
+
claimName(base, takenJsNames)
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
// A `<script src>` whose chunk carries no CSS claims no CSS name, so
|
|
1042
|
+
// a `<link>` of the same basename still gets the plain one.
|
|
1043
|
+
if (
|
|
1044
|
+
!emitsCss &&
|
|
1045
|
+
!getCssModulesPlugin().chunkHasCss(chunk, chunkGraph)
|
|
1046
|
+
) {
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
chunk.cssFilenameTemplate = getEntryTemplate(
|
|
1050
|
+
outputOptions.cssFilename,
|
|
1051
|
+
".css",
|
|
1052
|
+
claimName(base, takenCssNames)
|
|
1053
|
+
);
|
|
813
1054
|
}
|
|
814
1055
|
});
|
|
815
1056
|
compilation.dependencyTemplates.set(
|
|
@@ -927,7 +1168,8 @@ class HtmlModulesPlugin {
|
|
|
927
1168
|
);
|
|
928
1169
|
return new (getHtmlGenerator())(
|
|
929
1170
|
generatorOptions,
|
|
930
|
-
compilation.moduleGraph
|
|
1171
|
+
compilation.moduleGraph,
|
|
1172
|
+
(module) => getPageOnlyEntry(compilation, module) !== undefined
|
|
931
1173
|
);
|
|
932
1174
|
});
|
|
933
1175
|
|
|
@@ -981,15 +1223,15 @@ class HtmlModulesPlugin {
|
|
|
981
1223
|
/** @type {WeakMap<import("webpack-sources").Source, { resolvedContent: string, contentHash: string }>} */
|
|
982
1224
|
const resolvedSentinelHashCache = new WeakMap();
|
|
983
1225
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
) => {
|
|
1226
|
+
/**
|
|
1227
|
+
* Computes an HTML page's emitted filename, chunk-URL-resolved content and
|
|
1228
|
+
* content hash. Links to other pages stay sentinels, so a page's filename
|
|
1229
|
+
* never depends on the filenames of the pages it links to.
|
|
1230
|
+
* @param {NormalModule} module the page's html module
|
|
1231
|
+
* @param {import("../Chunk")} chunk chunk the page is emitted from
|
|
1232
|
+
* @returns {{ resolvedContent: string, contentHash: string, filename: string, info: import("../Compilation").AssetInfo } | undefined} the page's emit, or `undefined` when it generates no html
|
|
1233
|
+
*/
|
|
1234
|
+
const computePageEmit = (module, chunk) => {
|
|
993
1235
|
const { chunkGraph, outputOptions } = compilation;
|
|
994
1236
|
const codeGenResult =
|
|
995
1237
|
/** @type {import("../CodeGenerationResults")} */ (
|
|
@@ -1066,14 +1308,15 @@ class HtmlModulesPlugin {
|
|
|
1066
1308
|
};
|
|
1067
1309
|
};
|
|
1068
1310
|
|
|
1069
|
-
// Emitted filename of a linked `type: "html"` page, keyed by its entry
|
|
1070
|
-
// name. Looked up from the page's own entry chunk so it's independent of
|
|
1071
|
-
// chunk render order; cached for the compilation.
|
|
1072
1311
|
/** @type {Map<string, string>} */
|
|
1073
1312
|
const htmlPageFilenameCache = new Map();
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1313
|
+
/**
|
|
1314
|
+
* Emitted filename of a linked `type: "html"` page, read from the page's
|
|
1315
|
+
* own entry chunk so chunk render order cannot affect it.
|
|
1316
|
+
* @param {string} entryName entry name of the linked page
|
|
1317
|
+
* @returns {string} the page's emitted filename, `"data:,"` when it has none
|
|
1318
|
+
*/
|
|
1319
|
+
const computePageFilenameByEntry = (entryName) => {
|
|
1077
1320
|
const cached = htmlPageFilenameCache.get(entryName);
|
|
1078
1321
|
if (cached !== undefined) return cached;
|
|
1079
1322
|
let filename = "data:,";
|
|
@@ -1204,6 +1447,16 @@ class HtmlModulesPlugin {
|
|
|
1204
1447
|
jsHooks.render.tap(PLUGIN_NAME, (source, renderContext) => {
|
|
1205
1448
|
// No HTML modules ⇒ no sentinels; skip materializing the JS source.
|
|
1206
1449
|
if (htmlModules.size === 0) return source;
|
|
1450
|
+
// Chunks without HTML modules can't carry HTML sentinels — avoid
|
|
1451
|
+
// ConcatSource.source() on the common pure-JS chunk path.
|
|
1452
|
+
if (
|
|
1453
|
+
!HtmlModulesPlugin.chunkHasHtml(
|
|
1454
|
+
renderContext.chunk,
|
|
1455
|
+
compilation.chunkGraph
|
|
1456
|
+
)
|
|
1457
|
+
) {
|
|
1458
|
+
return source;
|
|
1459
|
+
}
|
|
1207
1460
|
const raw = source.source();
|
|
1208
1461
|
if (typeof raw !== "string") return source;
|
|
1209
1462
|
// Every sentinel shares this prefix — one scan answers the common
|
|
@@ -1274,6 +1527,29 @@ class HtmlModulesPlugin {
|
|
|
1274
1527
|
}
|
|
1275
1528
|
);
|
|
1276
1529
|
}
|
|
1530
|
+
|
|
1531
|
+
/**
|
|
1532
|
+
* Returns true, when the chunk has html.
|
|
1533
|
+
* @param {Chunk} chunk chunk
|
|
1534
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
1535
|
+
* @returns {boolean} true, when the chunk has html
|
|
1536
|
+
*/
|
|
1537
|
+
static chunkHasHtml(chunk, chunkGraph) {
|
|
1538
|
+
if (chunkGraph.getChunkModulesIterableBySourceType(chunk, HTML_TYPE)) {
|
|
1539
|
+
return true;
|
|
1540
|
+
}
|
|
1541
|
+
// Scope-hoisted HTML loses HTML_TYPE on the ConcatenatedModule, but its
|
|
1542
|
+
// JS export can still carry unresolved sentinels that need this path.
|
|
1543
|
+
for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
|
|
1544
|
+
if (module instanceof HtmlModule) return true;
|
|
1545
|
+
if (module instanceof ConcatenatedModule) {
|
|
1546
|
+
for (const inner of module.modules) {
|
|
1547
|
+
if (inner instanceof HtmlModule) return true;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
return false;
|
|
1552
|
+
}
|
|
1277
1553
|
}
|
|
1278
1554
|
|
|
1279
1555
|
/**
|