@depup/webpack 5.110.3-depup.0 → 5.111.1-depup.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 +7 -8
- package/changes.json +7 -11
- package/lib/APIPlugin.js +26 -11
- package/lib/AutomaticPrefetchPlugin.js +1 -1
- package/lib/Cache.js +8 -3
- package/lib/CacheFacade.js +1 -1
- package/lib/CircularModulesPlugin.js +157 -55
- package/lib/CleanPlugin.js +2 -2
- package/lib/CompatibilityPlugin.js +3 -4
- package/lib/Compilation.js +336 -337
- package/lib/Compiler.js +213 -51
- package/lib/ConstPlugin.js +16 -132
- package/lib/CopyPlugin.js +1054 -0
- package/lib/DefinePlugin.js +24 -8
- package/lib/DependencyTemplate.js +2 -1
- package/lib/DotenvPlugin.js +24 -28
- package/lib/ErrorHelpers.js +113 -0
- package/lib/ExportsInfo.js +20 -16
- package/lib/ExternalModule.js +47 -22
- package/lib/FileSystemInfo.js +17 -14
- package/lib/FlagDependencyExportsPlugin.js +53 -34
- package/lib/FlagDependencyUsagePlugin.js +9 -13
- package/lib/Generator.js +24 -2
- package/lib/HotModuleReplacementPlugin.js +55 -7
- package/lib/IgnorePlugin.js +1 -1
- package/lib/JavascriptMetaInfoPlugin.js +5 -3
- package/lib/LoaderOptionsPlugin.js +1 -1
- package/lib/MainTemplate.js +5 -1
- package/lib/Module.js +19 -10
- package/lib/ModuleGraph.js +6 -9
- package/lib/MultiCompiler.js +7 -17
- package/lib/MultiWatching.js +1 -1
- package/lib/NormalModule.js +135 -50
- package/lib/NormalModuleFactory.js +1 -1
- package/lib/RuntimeGlobals.js +21 -7
- package/lib/RuntimeModule.js +28 -12
- package/lib/RuntimePlugin.js +30 -3
- package/lib/RuntimeTemplate.js +813 -518
- package/lib/SourceMapDevToolPlugin.js +54 -104
- package/lib/TemplatedPathPlugin.js +16 -59
- package/lib/Watching.js +9 -0
- package/lib/WebpackOptionsApply.js +84 -91
- package/lib/asset/AssetBytesGenerator.js +7 -1
- package/lib/asset/AssetGenerator.js +50 -24
- package/lib/asset/AssetModule.js +3 -0
- package/lib/asset/AssetSourceGenerator.js +7 -1
- package/lib/asset/WebManifestParser.js +7 -5
- package/lib/buildChunkGraph.js +11 -15
- package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
- package/lib/cache/IdleFileCachePlugin.js +2 -2
- package/lib/cache/MemoryCachePlugin.js +4 -4
- package/lib/cache/MemoryWithGcCachePlugin.js +7 -8
- package/lib/cache/PackFileCacheStrategy.js +17 -19
- package/lib/cache/getLazyHashedEtag.js +3 -5
- package/lib/config/browserslistTargetHandler.js +17 -4
- package/lib/config/defaults.js +138 -128
- package/lib/config/getClaimedAssetTypes.js +10 -11
- package/lib/config/normalization.js +82 -12
- package/lib/config/target.js +13 -4
- package/lib/container/ContainerReferencePlugin.js +6 -0
- package/lib/container/RemoteRuntimeModule.js +9 -0
- package/lib/container/declaredRemotes.js +39 -0
- package/lib/{ContextExclusionPlugin.js → context/ContextExclusionPlugin.js} +1 -1
- package/lib/{ContextModule.js → context/ContextModule.js} +54 -115
- package/lib/{ContextModuleFactory.js → context/ContextModuleFactory.js} +19 -21
- package/lib/{ContextReplacementPlugin.js → context/ContextReplacementPlugin.js} +4 -4
- package/lib/css/CssGenerator.js +27 -11
- 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 +162 -31
- package/lib/css/CssServerStylesRuntimeModule.js +9 -0
- package/lib/css/cssMinify.js +43 -32
- package/lib/css/data.js +3514 -869
- package/lib/css/syntax.js +6370 -1220
- package/lib/debug/ProfilingPlugin.js +7 -4
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
- package/lib/dependencies/AMDRuntimeModules.js +18 -0
- package/lib/dependencies/CommonJsExportRequireDependency.js +13 -12
- package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
- package/lib/dependencies/CommonJsImportsParserPlugin.js +5 -5
- package/lib/dependencies/CommonJsPlugin.js +19 -3
- package/lib/dependencies/CommonJsRequireDependency.js +14 -5
- package/lib/dependencies/CommonJsSelfReferenceDependency.js +2 -4
- package/lib/dependencies/ContextDependency.js +1 -1
- package/lib/dependencies/ContextDependencyHelpers.js +3 -9
- package/lib/dependencies/ContextElementDependency.js +1 -1
- package/lib/dependencies/CssIcssExportDependency.js +13 -9
- package/lib/dependencies/CssIcssImportDependency.js +3 -6
- package/lib/dependencies/CssUrlDependency.js +3 -5
- package/lib/dependencies/HarmonyCompatibilityDependency.js +67 -22
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +3 -4
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +10 -7
- package/lib/dependencies/HarmonyExportExpressionDependency.js +47 -33
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +74 -17
- package/lib/dependencies/HarmonyExportInitFragment.js +110 -23
- package/lib/dependencies/HarmonyExportSpecifierDependency.js +25 -58
- package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
- package/lib/dependencies/HarmonyImportDependency.js +6 -1
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +111 -17
- package/lib/dependencies/HarmonyImportSideEffectDependency.js +3 -4
- package/lib/dependencies/HarmonyImportSpecifierDependency.js +40 -3
- package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
- package/lib/dependencies/HtmlEntryDependency.js +124 -102
- package/lib/dependencies/HtmlInlineScriptDependency.js +3 -4
- package/lib/dependencies/HtmlInlineStyleDependency.js +3 -4
- package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +1 -1
- package/lib/dependencies/ImportMetaGlobHelpers.js +1 -1
- package/lib/dependencies/ImportMetaMainDependency.js +104 -0
- package/lib/dependencies/ImportMetaPlugin.js +67 -25
- package/lib/dependencies/ImportParserPlugin.js +1 -1
- package/lib/dependencies/LoaderPlugin.js +26 -1
- package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
- package/lib/dependencies/SystemRuntimeModule.js +9 -0
- package/lib/dependencies/URLContextDependency.js +1 -1
- package/lib/dependencies/URLDependency.js +6 -5
- package/lib/dependencies/WorkerAndWorkletPlugin.js +15 -21
- package/lib/dependencies/WorkerDependency.js +7 -7
- package/lib/dependencies/WorkletDependency.js +7 -11
- 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/DuplicateModulesWarning.js +12 -7
- package/lib/errors/EmptyCopyPatternWarning.js +26 -0
- package/lib/errors/ExternalStarReexportError.js +44 -0
- package/lib/errors/HookWebpackError.js +3 -2
- 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/hmr/LazyCompilationPlugin.js +3 -5
- package/lib/html/HtmlGenerator.js +72 -87
- package/lib/html/HtmlModulesPlugin.js +418 -200
- package/lib/html/HtmlParser.js +110 -147
- package/lib/html/builtinEmbeddedRenderer.js +107 -0
- package/lib/html/data.js +142 -7
- package/lib/html/htmlMinify.js +92 -122
- package/lib/html/syntax.js +1618 -1391
- package/lib/ids/HashedModuleIdsPlugin.js +2 -1
- package/lib/index.js +49 -3
- package/lib/javascript/ChunkFormatHelpers.js +4 -3
- package/lib/javascript/JavascriptGenerator.js +8 -1
- package/lib/javascript/JavascriptModule.js +1 -0
- package/lib/javascript/JavascriptModulesPlugin.js +73 -72
- package/lib/javascript/JavascriptParser.js +1970 -900
- 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 +1500 -0
- package/lib/javascript/regexp.js +1762 -0
- package/lib/javascript/regexpData.js +51 -0
- package/lib/javascript/syntax.js +9061 -5262
- package/lib/json/JsonGenerator.js +7 -1
- package/lib/library/ModuleLibraryPlugin.js +171 -132
- package/lib/loaders/LoaderRunner.js +137 -9
- package/lib/node/NodeWatchFileSystem.js +3 -9
- package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
- package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
- package/lib/optimize/AggressiveMergingPlugin.js +3 -5
- package/lib/optimize/ConcatenatedModule.js +178 -120
- package/lib/optimize/ConstExportsPlugin.js +50 -39
- package/lib/optimize/FlagIncludedChunksPlugin.js +3 -11
- package/lib/optimize/InlineExports.js +20 -123
- package/lib/optimize/InnerGraphPlugin.js +7 -13
- package/lib/optimize/LimitChunkCountPlugin.js +5 -8
- package/lib/optimize/MangleExportsPlugin.js +13 -4
- package/lib/optimize/ModuleConcatenationPlugin.js +27 -5
- package/lib/optimize/RealContentHashPlugin.js +68 -9
- package/lib/optimize/SideEffectsFlagPlugin.js +24 -28
- package/lib/optimize/SplitChunksPlugin.js +2 -4
- package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
- package/lib/performance/BroadContextsPlugin.js +1 -1
- package/lib/performance/CacheEffectivenessPlugin.js +7 -7
- 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/SizeLimitsPlugin.js +2 -4
- 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 +8 -8
- 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 +18 -23
- package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
- package/lib/prefetch/StartupAssetHintRuntimeModule.js +16 -10
- package/lib/rules/RuleSetCompiler.js +211 -4
- package/lib/runtime/AsyncModuleRuntimeModule.js +98 -66
- 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 +12 -4
- 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 +11 -4
- package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
- package/lib/runtime/HelperRuntimeModule.js +10 -0
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +305 -224
- 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/DataUriPlugin.js +3 -6
- package/lib/schemes/VirtualUrlPlugin.js +1 -1
- package/lib/serialization/BinaryMiddleware.js +9 -9
- package/lib/serialization/FileMiddleware.js +16 -16
- package/lib/serialization/ObjectMiddleware.js +168 -139
- 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 +6 -5
- package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
- package/lib/typescript/TypeScriptPlugin.js +2 -3
- package/lib/url/URLParserPlugin.js +6 -8
- package/lib/util/LocConverter.js +4 -7
- package/lib/util/SourceProcessor.js +91 -91
- package/lib/util/async.js +292 -0
- package/lib/util/buildDiagnostics.js +69 -0
- package/lib/util/chainedImports.js +3 -4
- package/lib/util/concatenate.js +4 -4
- package/lib/util/createHooksRegistry.js +1 -2
- package/lib/util/dataURL.js +2 -3
- package/lib/util/deterministicGrouping.js +8 -13
- package/lib/util/findGraphRoots.js +6 -13
- package/lib/util/fs.js +27 -1
- package/lib/util/globUtils.js +1 -1
- package/lib/util/handlerKeys.js +40 -0
- package/lib/util/hash/wasm-hash.js +3 -4
- package/lib/util/identifier.js +20 -0
- package/lib/util/internalSerializables.js +5 -1
- package/lib/util/registerExternalSerializer.js +6 -45
- package/lib/util/semver.js +5 -3
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
- package/lib/wasm-async/AsyncWebAssemblyGenerator.js +6 -1
- package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +5 -1
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +12 -4
- package/lib/wasm-sync/WebAssemblyGenerator.js +6 -1
- package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +5 -1
- package/lib/wasm-sync/WebAssemblyUtils.js +2 -4
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
- package/package.json +55 -46
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +379 -66
- 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 +1723 -604
- 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 */
|
|
@@ -118,10 +148,9 @@ const createCompilationHooks = () => ({
|
|
|
118
148
|
|
|
119
149
|
const PLUGIN_NAME = "HtmlModulesPlugin";
|
|
120
150
|
|
|
121
|
-
// Built-in default favicon
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
// Resolved from `__filename` (a sibling of this module) — no `path` needed.
|
|
151
|
+
// Built-in default favicon, referenced as a `file:` request so the wrapper's
|
|
152
|
+
// `<link rel="icon">` flows through the normal asset pipeline: emitted hashed,
|
|
153
|
+
// with the right publicPath and no direct IO.
|
|
125
154
|
const DEFAULT_FAVICON = new URL("./favicon.svg", pathToFileURL(__filename))
|
|
126
155
|
.href;
|
|
127
156
|
|
|
@@ -222,9 +251,130 @@ const isIntegrityEnabled = (integrity) =>
|
|
|
222
251
|
// `\.html`/`\.css` request matchers for the synthetic `output.html` wrapper.
|
|
223
252
|
const HTML_REQUEST_RE = /\.html(\?|$)/i;
|
|
224
253
|
const CSS_REQUEST_RE = /\.css(\?|$)/i;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
254
|
+
/**
|
|
255
|
+
* The entry a page is the whole of: nothing imports the module and the entry
|
|
256
|
+
* asks for nothing else, so the entry's JavaScript would be the page itself.
|
|
257
|
+
* @param {Compilation} compilation compilation
|
|
258
|
+
* @param {Module} module html module
|
|
259
|
+
* @returns {{ name: string, data: EntryData } | undefined} the page's own entry, when it has one
|
|
260
|
+
*/
|
|
261
|
+
const getPageEntry = (compilation, module) => {
|
|
262
|
+
const { moduleGraph } = compilation;
|
|
263
|
+
for (const connection of moduleGraph.getIncomingConnections(module)) {
|
|
264
|
+
if (connection.originModule) return undefined;
|
|
265
|
+
}
|
|
266
|
+
for (const [name, data] of compilation.entries) {
|
|
267
|
+
if (data.dependencies.length !== 1) continue;
|
|
268
|
+
if (data.includeDependencies.length !== 0) continue;
|
|
269
|
+
if (moduleGraph.getModule(data.dependencies[0]) === module) {
|
|
270
|
+
return { name, data };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return undefined;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The entry of a page that emits nothing but its markup, so its chunk carries
|
|
278
|
+
* no JavaScript. An `output.library` asks for the page's exports, and keeps it.
|
|
279
|
+
* @param {Compilation} compilation compilation
|
|
280
|
+
* @param {Module} module html module
|
|
281
|
+
* @returns {{ name: string, data: EntryData } | undefined} the entry, when the page is all it emits
|
|
282
|
+
*/
|
|
283
|
+
const getPageOnlyEntry = (compilation, module) => {
|
|
284
|
+
const entry = getPageEntry(compilation, module);
|
|
285
|
+
if (entry === undefined) return undefined;
|
|
286
|
+
const { library } = entry.data.options;
|
|
287
|
+
const configured =
|
|
288
|
+
library === undefined ? compilation.outputOptions.library : library;
|
|
289
|
+
return configured === undefined ? entry : undefined;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The basename a tag's url lends to the entry it becomes, so
|
|
294
|
+
* `src="./my-script.js"` emits `my-script.js`. Undefined for an inline body.
|
|
295
|
+
* @param {string} request the tag's request
|
|
296
|
+
* @returns {string | undefined} the basename, when the url carries one
|
|
297
|
+
*/
|
|
298
|
+
const getRequestBaseName = (request) => {
|
|
299
|
+
// An inline `<script>` body is a `data:` request webpack writes itself.
|
|
300
|
+
if (request.startsWith("data:")) return undefined;
|
|
301
|
+
const { path: file } = parseResource(request);
|
|
302
|
+
return basename(file, extname(file)) || undefined;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Claims `base`, numbering it while something else already emits under that
|
|
307
|
+
* name, so two tags sharing a basename stay distinct.
|
|
308
|
+
* @param {string} base preferred name
|
|
309
|
+
* @param {Set<string>} taken names this kind of output already emits under
|
|
310
|
+
* @returns {string} the claimed name
|
|
311
|
+
*/
|
|
312
|
+
const claimName = (base, taken) => {
|
|
313
|
+
let name = base;
|
|
314
|
+
for (let index = 1; taken.has(name); index++) name = `${base}${index}`;
|
|
315
|
+
taken.add(name);
|
|
316
|
+
return name;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// Placeholders that tell chunks apart on their own, so a template carrying
|
|
320
|
+
// one needs no `[name]`. Not `[id]`: an extracted entry's id is synthetic.
|
|
321
|
+
const CONTENT_PLACEHOLDER_REGEXP =
|
|
322
|
+
/\[(?:chunkhash|contenthash|fullhash|hash)[\]:]/;
|
|
323
|
+
|
|
324
|
+
// Basename stem of a filename template — what `[name]` stands in for, keeping
|
|
325
|
+
// the directory, extension and query the template asked for.
|
|
326
|
+
const TEMPLATE_STEM_REGEXP = /(^|\/)[^/?]*?((?:\.[^./?]+)*)(\?.*)?$/;
|
|
327
|
+
|
|
328
|
+
const NAME_PLACEHOLDER_REGEXP = /\[name\]/g;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* A filename template usable for a page's extracted entries: it must tell one
|
|
332
|
+
* entry from another, or every entry of the page would be emitted to the same
|
|
333
|
+
* file. A template naming neither the chunk nor its content lends its directory
|
|
334
|
+
* and extension to a `[name]` instead.
|
|
335
|
+
* @param {string} template configured template
|
|
336
|
+
* @param {string} extension extension to fall back to when the template names none
|
|
337
|
+
* @returns {string} a template that tells the page's entries apart
|
|
338
|
+
*/
|
|
339
|
+
const getNamedTemplate = (template, extension) => {
|
|
340
|
+
if (
|
|
341
|
+
template.includes("[name]") ||
|
|
342
|
+
CONTENT_PLACEHOLDER_REGEXP.test(template)
|
|
343
|
+
) {
|
|
344
|
+
return template;
|
|
345
|
+
}
|
|
346
|
+
return template.replace(
|
|
347
|
+
TEMPLATE_STEM_REGEXP,
|
|
348
|
+
(_, dir, ext, query) => `${dir}[name]${ext || extension}${query || ""}`
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The filename one extracted entry emits under, with `name` filling the
|
|
354
|
+
* `[name]` its template carries or the stem it leaves behind.
|
|
355
|
+
* @param {string} template configured template
|
|
356
|
+
* @param {string} extension extension to fall back to when the template names none
|
|
357
|
+
* @param {string} name the name this entry claimed
|
|
358
|
+
* @returns {string} the entry's filename template
|
|
359
|
+
*/
|
|
360
|
+
const fillNamedTemplate = (template, extension, name) =>
|
|
361
|
+
getNamedTemplate(template, extension).replace(NAME_PLACEHOLDER_REGEXP, name);
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* The same, for a template `output.filename` states as a function: it is called
|
|
365
|
+
* at emit, and the path it returns read for a stem the same way.
|
|
366
|
+
* @param {FilenameTemplate} template configured template
|
|
367
|
+
* @param {string} extension extension to fall back to when the template names none
|
|
368
|
+
* @param {string} name the name this entry claimed
|
|
369
|
+
* @returns {ChunkFilenameTemplate} the entry's filename template
|
|
370
|
+
*/
|
|
371
|
+
const getEntryTemplate = (template, extension, name) => {
|
|
372
|
+
if (typeof template === "function") {
|
|
373
|
+
return (pathData, assetInfo) =>
|
|
374
|
+
fillNamedTemplate(template(pathData, assetInfo), extension, name);
|
|
375
|
+
}
|
|
376
|
+
return fillNamedTemplate(template, extension, name);
|
|
377
|
+
};
|
|
228
378
|
|
|
229
379
|
/**
|
|
230
380
|
* Requests an `output.html` page must load for an entry: its `dependOn`
|
|
@@ -294,7 +444,7 @@ class HtmlModulesPlugin {
|
|
|
294
444
|
* digest of `content`, with `nonNumericOnlyHash` applied — webpack's
|
|
295
445
|
* standard `[contenthash]` recipe.
|
|
296
446
|
* @param {string | Buffer} content content to hash
|
|
297
|
-
* @param {
|
|
447
|
+
* @param {OutputOptions} outputOptions output options
|
|
298
448
|
* @returns {string} content hash
|
|
299
449
|
*/
|
|
300
450
|
static computeContentHash(content, outputOptions) {
|
|
@@ -316,7 +466,7 @@ class HtmlModulesPlugin {
|
|
|
316
466
|
* initial chunks, `output.htmlChunkFilename` otherwise — the HTML counterpart
|
|
317
467
|
* of `CssModulesPlugin.getChunkFilenameTemplate`.
|
|
318
468
|
* @param {import("../Chunk")} chunk chunk
|
|
319
|
-
* @param {
|
|
469
|
+
* @param {OutputOptions} outputOptions output options
|
|
320
470
|
* @returns {import("../Chunk").ChunkFilenameTemplate} used filename template
|
|
321
471
|
*/
|
|
322
472
|
static getChunkFilenameTemplate(chunk, outputOptions) {
|
|
@@ -355,10 +505,9 @@ class HtmlModulesPlugin {
|
|
|
355
505
|
// several entries, so those can only be applied per emitted asset.
|
|
356
506
|
/** @type {Map<string, OutputHtmlOptions>} */
|
|
357
507
|
const htmlOptionsByEntry = new Map();
|
|
358
|
-
// SRI sentinels are emitted
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
// `.html` pages never reach the entry hook, hence the global seed.
|
|
508
|
+
// SRI sentinels are emitted before the owning entry is known, so one page
|
|
509
|
+
// asking for integrity turns them on for all and the rest drop them on emit.
|
|
510
|
+
// Authored `.html` pages never reach the entry hook, hence the global seed.
|
|
362
511
|
let anyIntegrity = isIntegrityEnabled(
|
|
363
512
|
globalHtmlOptions && globalHtmlOptions.integrity
|
|
364
513
|
);
|
|
@@ -391,13 +540,9 @@ class HtmlModulesPlugin {
|
|
|
391
540
|
return options;
|
|
392
541
|
};
|
|
393
542
|
|
|
394
|
-
// `output.html`
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
// injected first so the page loads the shared/runtime chunks before
|
|
398
|
-
// the entry's own (see `collectHtmlEntryRequests`). `crossOriginLoading`
|
|
399
|
-
// and SRI are applied to the injected tags centrally in
|
|
400
|
-
// HtmlEntryDependency.
|
|
543
|
+
// `output.html` wraps a non-HTML entry in a synthetic HTML module, so the
|
|
544
|
+
// existing pipeline injects its chunks. `dependOn` ancestors go first, so the
|
|
545
|
+
// page loads shared and runtime chunks before the entry's own.
|
|
401
546
|
EntryOptionPlugin.getHooks(compiler).entry.tap(
|
|
402
547
|
PLUGIN_NAME,
|
|
403
548
|
(context, name, desc) => {
|
|
@@ -472,24 +617,20 @@ class HtmlModulesPlugin {
|
|
|
472
617
|
/** @type {Map<string, { content: string, source: import("webpack-sources").RawSource }>} */
|
|
473
618
|
const sentinelResolvedSourceCache = new Map();
|
|
474
619
|
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
// and
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
|
|
485
|
-
// `finishMake` creates their entries without scanning the whole module
|
|
486
|
-
// graph), the stylesheet entry names (read in `afterChunks`) and the html
|
|
487
|
-
// options of every emitted page, keyed by its asset name.
|
|
488
|
-
/** @type {WeakMap<import("../Compilation"), { htmlModules: Set<import("../Module")>, stylesheetEntries: Set<string>, htmlAssetOptions: Map<string, OutputHtmlOptions> }>} */
|
|
620
|
+
// WHY: the `script` and `script-module` groups are chained through a
|
|
621
|
+
// leader-only `dependOn`, so they share a runtime: the group's first entry
|
|
622
|
+
// owns it and every later one sets `dependOn: [leader]`. A modulepreload
|
|
623
|
+
// entry is emitted independently, with no `dependOn`, so a later script can
|
|
624
|
+
// never import it as a runtime leader — which is what keeps the "preload but
|
|
625
|
+
// don't execute" contract of `<link rel="modulepreload">` intact.
|
|
626
|
+
// Per-compilation state: the HTML modules seen during make, so `finishMake`
|
|
627
|
+
// creates their entries without scanning the module graph, plus the
|
|
628
|
+
// stylesheet entry names and each emitted page's html options.
|
|
629
|
+
/** @type {WeakMap<import("../Compilation"), { htmlModules: Set<Module>, stylesheetEntries: Set<string>, pendingNames: Map<string, PendingName>, htmlAssetOptions: Map<string, OutputHtmlOptions> }>} */
|
|
489
630
|
const compilationState = new WeakMap();
|
|
490
631
|
/**
|
|
491
632
|
* @param {import("../Compilation")} compilation compilation
|
|
492
|
-
* @returns {{ htmlModules: Set<
|
|
633
|
+
* @returns {{ htmlModules: Set<Module>, stylesheetEntries: Set<string>, pendingNames: Map<string, PendingName>, htmlAssetOptions: Map<string, OutputHtmlOptions> }} per-compilation state
|
|
493
634
|
*/
|
|
494
635
|
const getState = (compilation) => {
|
|
495
636
|
let state = compilationState.get(compilation);
|
|
@@ -497,6 +638,7 @@ class HtmlModulesPlugin {
|
|
|
497
638
|
state = {
|
|
498
639
|
htmlModules: new Set(),
|
|
499
640
|
stylesheetEntries: new Set(),
|
|
641
|
+
pendingNames: new Map(),
|
|
500
642
|
htmlAssetOptions: new Map()
|
|
501
643
|
};
|
|
502
644
|
compilationState.set(compilation, state);
|
|
@@ -504,14 +646,19 @@ class HtmlModulesPlugin {
|
|
|
504
646
|
return state;
|
|
505
647
|
};
|
|
506
648
|
compiler.hooks.finishMake.tapAsync(PLUGIN_NAME, (compilation, callback) => {
|
|
507
|
-
const { htmlModules, stylesheetEntries } =
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
|
|
649
|
+
const { htmlModules, stylesheetEntries, pendingNames } =
|
|
650
|
+
getState(compilation);
|
|
651
|
+
/** @type {Set<string>} */
|
|
652
|
+
const extractedNames = new Set();
|
|
653
|
+
// The page each `type: "html"` link built, so the naming walk below can
|
|
654
|
+
// follow the links in document order instead of in build order.
|
|
655
|
+
/** @type {Map<string, Module>} */
|
|
656
|
+
const linkedPages = new Map();
|
|
657
|
+
|
|
658
|
+
// Collect the entries an HTML module asks for. Only the script chains share
|
|
659
|
+
// a runtime, through a leader-only `dependOn`; chaining a CSS or page entry
|
|
660
|
+
// into a JS leader would mix unrelated outputs into one chunk.
|
|
661
|
+
/** @type {(module: Module) => { context: string, request: string, name: string, dependOn: string[] | undefined }[]} */
|
|
515
662
|
const collectEntrySpecs = (module) => {
|
|
516
663
|
const { htmlEntries } = /** @type {HtmlModuleBuildInfo} */ (
|
|
517
664
|
module.buildInfo
|
|
@@ -533,10 +680,11 @@ class HtmlModulesPlugin {
|
|
|
533
680
|
leaderName = entry.entryName;
|
|
534
681
|
}
|
|
535
682
|
// Stylesheet entries and `as="style"` preload/prefetch entries emit
|
|
536
|
-
// a CSS chunk, so they
|
|
537
|
-
if (groupKind === "stylesheet" || entry.css) {
|
|
683
|
+
// a CSS chunk, so they are named from `output.cssFilename` alone.
|
|
684
|
+
if (groupKind === "stylesheet" || Boolean(entry.css)) {
|
|
538
685
|
stylesheetEntries.add(entry.entryName);
|
|
539
686
|
}
|
|
687
|
+
extractedNames.add(entry.entryName);
|
|
540
688
|
specs.push({
|
|
541
689
|
context,
|
|
542
690
|
request: entry.request,
|
|
@@ -548,9 +696,70 @@ class HtmlModulesPlugin {
|
|
|
548
696
|
return specs;
|
|
549
697
|
};
|
|
550
698
|
|
|
699
|
+
// Records what a page's entries ask to be named, then walks its links —
|
|
700
|
+
// once every page is built, so the order is the documents', not the builds'.
|
|
701
|
+
/** @type {(module: Module, seen: Set<Module>) => void} */
|
|
702
|
+
const recordNames = (module, seen) => {
|
|
703
|
+
if (seen.has(module)) return;
|
|
704
|
+
seen.add(module);
|
|
705
|
+
const { htmlEntries } = /** @type {HtmlModuleBuildInfo} */ (
|
|
706
|
+
module.buildInfo
|
|
707
|
+
);
|
|
708
|
+
if (!htmlEntries) return;
|
|
709
|
+
const pageEntry = getPageOnlyEntry(compilation, module);
|
|
710
|
+
const namedEntry =
|
|
711
|
+
pageEntry !== undefined && !extractedNames.has(pageEntry.name)
|
|
712
|
+
? pageEntry
|
|
713
|
+
: undefined;
|
|
714
|
+
const { resource } = /** @type {NormalModule} */ (module);
|
|
715
|
+
// An `output.html` page is a wrapper webpack writes around an entry, so
|
|
716
|
+
// what it loads is that entry's own output and takes its name.
|
|
717
|
+
const wrapsEntry =
|
|
718
|
+
namedEntry !== undefined &&
|
|
719
|
+
typeof resource === "string" &&
|
|
720
|
+
resource.startsWith("data:text/html");
|
|
721
|
+
const { filename } = compilation.outputOptions;
|
|
722
|
+
// Kept as configured, a function included, and read for a name once the
|
|
723
|
+
// chunks exist.
|
|
724
|
+
const jsTemplate =
|
|
725
|
+
namedEntry && namedEntry.data.options.filename !== undefined
|
|
726
|
+
? namedEntry.data.options.filename
|
|
727
|
+
: filename;
|
|
728
|
+
// In document order, so of two tags sharing a basename the first a
|
|
729
|
+
// reader sees keeps it. A linked page is named on emit instead.
|
|
730
|
+
/** @type {HtmlEntryInfo[]} */
|
|
731
|
+
const ordered = [];
|
|
732
|
+
/** @type {HtmlEntryInfo[]} */
|
|
733
|
+
const links = [];
|
|
734
|
+
for (const [groupKind, group] of Object.entries(htmlEntries)) {
|
|
735
|
+
for (const entry of group) {
|
|
736
|
+
(groupKind === "html" ? links : ordered).push(entry);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
ordered.sort((a, b) => a.index - b.index);
|
|
740
|
+
for (const entry of ordered) {
|
|
741
|
+
// An inline `<script>` body has no url to be named after, so it
|
|
742
|
+
// keeps the name of its own entry, page hash and tag index.
|
|
743
|
+
const base =
|
|
744
|
+
wrapsEntry && namedEntry
|
|
745
|
+
? namedEntry.name
|
|
746
|
+
: getRequestBaseName(entry.request) || entry.entryName;
|
|
747
|
+
pendingNames.set(entry.entryName, {
|
|
748
|
+
base,
|
|
749
|
+
jsTemplate,
|
|
750
|
+
freeName: namedEntry && namedEntry.name
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
links.sort((a, b) => a.index - b.index);
|
|
754
|
+
for (const link of links) {
|
|
755
|
+
const page = linkedPages.get(link.entryName);
|
|
756
|
+
if (page) recordNames(page, seen);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
551
760
|
// Push one collected reference as a compilation entry; resolves with
|
|
552
761
|
// the built entry module.
|
|
553
|
-
/** @type {(spec: { context: string, request: string, name: string, dependOn: string[] | undefined }) => Promise<
|
|
762
|
+
/** @type {(spec: { context: string, request: string, name: string, dependOn: string[] | undefined }) => Promise<Module | null>} */
|
|
554
763
|
const addEntry = (spec) =>
|
|
555
764
|
new Promise((resolve, reject) => {
|
|
556
765
|
compilation.addEntry(
|
|
@@ -558,10 +767,8 @@ class HtmlModulesPlugin {
|
|
|
558
767
|
EntryPlugin.createDependency(spec.request, { name: spec.name }),
|
|
559
768
|
{
|
|
560
769
|
name: spec.name,
|
|
561
|
-
//
|
|
562
|
-
//
|
|
563
|
-
// `.css` name via `cssFilenameTemplate` below; `html` page entries
|
|
564
|
-
// emit their file via `renderManifest`.
|
|
770
|
+
// A placeholder `afterChunks` overwrites for every entry it
|
|
771
|
+
// names; only a linked page keeps it, and it emits no JavaScript.
|
|
565
772
|
filename: compilation.outputOptions.chunkFilename || "[name].js",
|
|
566
773
|
dependOn: spec.dependOn
|
|
567
774
|
},
|
|
@@ -575,7 +782,7 @@ class HtmlModulesPlugin {
|
|
|
575
782
|
// every HTML entry already gets. `processed` guards diamonds and cycles.
|
|
576
783
|
/** @type {WeakSet<import("../Module")>} */
|
|
577
784
|
const processed = new WeakSet();
|
|
578
|
-
/** @type {(module:
|
|
785
|
+
/** @type {(module: Module) => Promise<void>} */
|
|
579
786
|
const processEntry = async (module) => {
|
|
580
787
|
if (processed.has(module)) return;
|
|
581
788
|
processed.add(module);
|
|
@@ -583,27 +790,30 @@ class HtmlModulesPlugin {
|
|
|
583
790
|
collectEntrySpecs(module).map(async (spec) => {
|
|
584
791
|
const entryModule = await addEntry(spec);
|
|
585
792
|
if (entryModule && entryModule.type === HTML_MODULE_TYPE) {
|
|
793
|
+
linkedPages.set(spec.name, entryModule);
|
|
586
794
|
await processEntry(entryModule);
|
|
587
795
|
}
|
|
588
796
|
})
|
|
589
797
|
);
|
|
590
798
|
};
|
|
591
|
-
// Seed with the HTML modules seen during make
|
|
592
|
-
//
|
|
593
|
-
//
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
799
|
+
// Seed with the HTML modules seen during make; linked pages are reached by
|
|
800
|
+
// the recursion above. Sorted because they are tracked in build-completion
|
|
801
|
+
// order, which the generated names must not depend on.
|
|
802
|
+
const seeds = [...htmlModules].sort(compareModulesByIdentifier);
|
|
803
|
+
Promise.all(seeds.map(processEntry)).then(() => {
|
|
804
|
+
/** @type {Set<Module>} */
|
|
805
|
+
const named = new Set();
|
|
806
|
+
for (const seed of seeds) recordNames(seed, named);
|
|
807
|
+
callback();
|
|
808
|
+
}, callback);
|
|
598
809
|
});
|
|
599
810
|
|
|
600
811
|
// `csp` injects a `<meta http-equiv="Content-Security-Policy">` and
|
|
601
|
-
// `integrity` resolves the SRI sentinels
|
|
602
|
-
// content is final
|
|
603
|
-
|
|
604
|
-
// `"auto"` marks implicit enablement
|
|
605
|
-
//
|
|
606
|
-
// (e.g. html-webpack-plugin's template loader in its child compiler).
|
|
812
|
+
// `integrity` resolves the SRI sentinels, both per page once its inline
|
|
813
|
+
// content is final, so an entry's `html` can override them.
|
|
814
|
+
|
|
815
|
+
// `"auto"` marks implicit enablement, the one case where loaders win over
|
|
816
|
+
// the built-in type — see `applyExperimentsDefaults`.
|
|
607
817
|
// TODO webpack 6: html defaults to `true`, drop this implicit-only fallback.
|
|
608
818
|
const implicitlyEnabled = compiler.options.experiments.html === "auto";
|
|
609
819
|
compiler.hooks.compilation.tap(
|
|
@@ -636,13 +846,12 @@ class HtmlModulesPlugin {
|
|
|
636
846
|
compilation.hooks.succeedModule.tap(PLUGIN_NAME, trackHtmlModule);
|
|
637
847
|
compilation.hooks.stillValidModule.tap(PLUGIN_NAME, trackHtmlModule);
|
|
638
848
|
|
|
639
|
-
// Resolve integrity and inline sentinels after `RealContentHashPlugin
|
|
640
|
-
// so final bytes are in place. Only HTML pages carry
|
|
641
|
-
|
|
642
|
-
// SRI
|
|
643
|
-
//
|
|
644
|
-
//
|
|
645
|
-
// so a CDN deployment doesn't ship no-op integrity attributes.
|
|
849
|
+
// Resolve integrity and inline sentinels after `RealContentHashPlugin`,
|
|
850
|
+
// so final bytes are in place. Only HTML pages carry them.
|
|
851
|
+
|
|
852
|
+
// SRI takes effect only on a cross-origin subresource fetched with CORS,
|
|
853
|
+
// so without `output.crossOriginLoading` the browser ignores `integrity`.
|
|
854
|
+
// Warn once, rather than let a CDN deployment ship no-op attributes.
|
|
646
855
|
if (anyIntegrity && !compilation.outputOptions.crossOriginLoading) {
|
|
647
856
|
compilation.warnings.push(
|
|
648
857
|
new WebpackError(
|
|
@@ -688,12 +897,9 @@ class HtmlModulesPlugin {
|
|
|
688
897
|
inlinedFiles
|
|
689
898
|
);
|
|
690
899
|
}
|
|
691
|
-
// `injectTags
|
|
692
|
-
//
|
|
693
|
-
//
|
|
694
|
-
// render every edit — placement, attribute rewrites, moves,
|
|
695
|
-
// removals, CSP hashes/nonce/meta — in one pass. Injected inline
|
|
696
|
-
// `<script>`/`<style>` are hashed like the page's own.
|
|
900
|
+
// `injectTags`, `transformTags` and CSP all run over a single parse:
|
|
901
|
+
// collect the tags and anchors once, let the hooks act, then render
|
|
902
|
+
// every edit in one pass.
|
|
697
903
|
const injected = await hooks.injectTags.promise([], {
|
|
698
904
|
outputName: name,
|
|
699
905
|
html: resolved
|
|
@@ -725,26 +931,6 @@ class HtmlModulesPlugin {
|
|
|
725
931
|
// a content-hashed filename makes a substring hit a real reference.
|
|
726
932
|
/** @type {Set<string>} */
|
|
727
933
|
const deletionCandidates = new Set(inlinedFiles);
|
|
728
|
-
// A wrapper entry's chunk only rebuilds the page's own markup; the
|
|
729
|
-
// page loads the `<script src>` entries the parser split out of it.
|
|
730
|
-
for (const entrypoint of compilation.entrypoints.values()) {
|
|
731
|
-
const chunk = entrypoint.getEntrypointChunk();
|
|
732
|
-
if (!chunk) continue;
|
|
733
|
-
let wrapperOnly = false;
|
|
734
|
-
for (const entryModule of compilation.chunkGraph.getChunkEntryModulesIterable(
|
|
735
|
-
chunk
|
|
736
|
-
)) {
|
|
737
|
-
const { resource } = /** @type {import("../NormalModule")} */ (
|
|
738
|
-
entryModule
|
|
739
|
-
);
|
|
740
|
-
wrapperOnly =
|
|
741
|
-
typeof resource === "string" &&
|
|
742
|
-
HTML_DATA_URL_RE.test(resource);
|
|
743
|
-
if (!wrapperOnly) break;
|
|
744
|
-
}
|
|
745
|
-
if (!wrapperOnly) continue;
|
|
746
|
-
for (const file of chunk.files) deletionCandidates.add(file);
|
|
747
|
-
}
|
|
748
934
|
// Materialize each other asset's source once (it can be expensive to
|
|
749
935
|
// render) and test all candidates against it, rather than
|
|
750
936
|
// re-materializing every asset per candidate.
|
|
@@ -787,45 +973,70 @@ class HtmlModulesPlugin {
|
|
|
787
973
|
}
|
|
788
974
|
);
|
|
789
975
|
|
|
790
|
-
//
|
|
791
|
-
//
|
|
792
|
-
//
|
|
793
|
-
// Compilation only flows `options.filename` to `chunk.filenameTemplate`,
|
|
794
|
-
// which controls JS emit — there's no entry-level `cssFilename`.
|
|
795
|
-
// Set it ourselves after chunks are created so each stylesheet
|
|
796
|
-
// entry emits to a distinct file derived from `output.cssFilename`
|
|
797
|
-
// (or `output.cssChunkFilename` for non-initial CSS chunks).
|
|
976
|
+
// There is no entry-level `cssFilename`: Compilation flows
|
|
977
|
+
// `options.filename` only to `chunk.filenameTemplate`, which drives JS
|
|
978
|
+
// emit. So set `chunk.cssFilenameTemplate` here, once chunks exist.
|
|
798
979
|
compilation.hooks.afterChunks.tap(PLUGIN_NAME, () => {
|
|
799
|
-
const { stylesheetEntries } = getState(compilation);
|
|
800
|
-
if (
|
|
801
|
-
|
|
980
|
+
const { stylesheetEntries, pendingNames } = getState(compilation);
|
|
981
|
+
if (pendingNames.size === 0) return;
|
|
982
|
+
const { chunkGraph, outputOptions } = compilation;
|
|
983
|
+
const jsExtension = outputOptions.module ? ".mjs" : ".js";
|
|
984
|
+
// Claimed in the order the pages recorded them, which is the
|
|
985
|
+
// documents' order rather than their builds'.
|
|
986
|
+
const takenJsNames = new Set(compilation.entries.keys());
|
|
987
|
+
const takenCssNames = new Set(takenJsNames);
|
|
988
|
+
// An extracted entry emits nothing under its own synthetic name, so
|
|
989
|
+
// that name never stands in the way of the one its tag asks for.
|
|
990
|
+
for (const entryName of pendingNames.keys()) {
|
|
991
|
+
takenJsNames.delete(entryName);
|
|
992
|
+
takenCssNames.delete(entryName);
|
|
993
|
+
}
|
|
994
|
+
for (const { freeName } of pendingNames.values()) {
|
|
995
|
+
if (freeName === undefined) continue;
|
|
996
|
+
// A page-only entry emits nothing of its own, so its name is free for
|
|
997
|
+
// the entries extracted from it — `app.html` keeps `app.js`.
|
|
998
|
+
takenJsNames.delete(freeName);
|
|
999
|
+
takenCssNames.delete(freeName);
|
|
1000
|
+
}
|
|
1001
|
+
for (const [entryName, { base, jsTemplate }] of pendingNames) {
|
|
802
1002
|
const entrypoint = compilation.entrypoints.get(entryName);
|
|
803
1003
|
if (!entrypoint) continue;
|
|
804
1004
|
const chunk = entrypoint.getEntrypointChunk();
|
|
805
1005
|
if (!chunk) continue;
|
|
806
|
-
//
|
|
807
|
-
//
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1006
|
+
// A stylesheet entry emits no JavaScript, so it claims no JS name and
|
|
1007
|
+
// leaves that one to a `<script src>` of the same basename.
|
|
1008
|
+
const emitsCss = stylesheetEntries.has(entryName);
|
|
1009
|
+
if (!emitsCss) {
|
|
1010
|
+
// `output.filename` named after the tag's url, the way an entry
|
|
1011
|
+
// point is named. `Compilation` set it from the placeholder above.
|
|
1012
|
+
chunk.filenameTemplate = getEntryTemplate(
|
|
1013
|
+
jsTemplate,
|
|
1014
|
+
jsExtension,
|
|
1015
|
+
claimName(base, takenJsNames)
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
// A `<script src>` whose chunk carries no CSS claims no CSS name, so
|
|
1019
|
+
// a `<link>` of the same basename still gets the plain one.
|
|
1020
|
+
if (
|
|
1021
|
+
!emitsCss &&
|
|
1022
|
+
!getCssModulesPlugin().chunkHasCss(chunk, chunkGraph)
|
|
1023
|
+
) {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
chunk.cssFilenameTemplate = getEntryTemplate(
|
|
1027
|
+
outputOptions.cssFilename,
|
|
1028
|
+
".css",
|
|
1029
|
+
claimName(base, takenCssNames)
|
|
1030
|
+
);
|
|
819
1031
|
}
|
|
820
1032
|
});
|
|
821
1033
|
compilation.dependencyTemplates.set(
|
|
822
1034
|
StaticExportsDependency,
|
|
823
1035
|
new StaticExportsDependency.Template()
|
|
824
1036
|
);
|
|
825
|
-
// `ConstDependency`
|
|
826
|
-
// `
|
|
827
|
-
//
|
|
828
|
-
// generator runs the insertion.
|
|
1037
|
+
// HtmlParser uses `ConstDependency` to insert ` type="module"` into a
|
|
1038
|
+
// rewritten `<script>` under `output.module`; its template has to be
|
|
1039
|
+
// registered for the HTML generator to run that insertion.
|
|
829
1040
|
compilation.dependencyTemplates.set(
|
|
830
1041
|
ConstDependency,
|
|
831
1042
|
new ConstDependency.Template()
|
|
@@ -857,10 +1068,9 @@ class HtmlModulesPlugin {
|
|
|
857
1068
|
HtmlEntryDependency,
|
|
858
1069
|
new HtmlEntryDependency.Template()
|
|
859
1070
|
);
|
|
860
|
-
// Inline `<script>` content
|
|
861
|
-
//
|
|
862
|
-
//
|
|
863
|
-
// template rewrites the original tag to `<script src=…>`.
|
|
1071
|
+
// Inline `<script>` content becomes its own entry through a
|
|
1072
|
+
// `data:text/javascript,...` request, the same pipeline as
|
|
1073
|
+
// `<script src>`; the template then rewrites the tag.
|
|
864
1074
|
compilation.dependencyFactories.set(
|
|
865
1075
|
HtmlInlineScriptDependency,
|
|
866
1076
|
normalModuleFactory
|
|
@@ -869,11 +1079,9 @@ class HtmlModulesPlugin {
|
|
|
869
1079
|
HtmlInlineScriptDependency,
|
|
870
1080
|
new HtmlInlineScriptDependency.Template()
|
|
871
1081
|
);
|
|
872
|
-
// Inline `<style>` content
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
// generation data (`css-text` channel set by CssGenerator
|
|
876
|
-
// when `exportType` is `"text"`).
|
|
1082
|
+
// Inline `<style>` content goes through the CSS pipeline as a
|
|
1083
|
+
// `data:text/css` module; the template reads the processed text
|
|
1084
|
+
// from the `css-text` code-generation channel.
|
|
877
1085
|
compilation.dependencyFactories.set(
|
|
878
1086
|
HtmlInlineStyleDependency,
|
|
879
1087
|
normalModuleFactory
|
|
@@ -933,7 +1141,8 @@ class HtmlModulesPlugin {
|
|
|
933
1141
|
);
|
|
934
1142
|
return new (getHtmlGenerator())(
|
|
935
1143
|
generatorOptions,
|
|
936
|
-
compilation.moduleGraph
|
|
1144
|
+
compilation.moduleGraph,
|
|
1145
|
+
(module) => getPageOnlyEntry(compilation, module) !== undefined
|
|
937
1146
|
);
|
|
938
1147
|
});
|
|
939
1148
|
|
|
@@ -942,10 +1151,9 @@ class HtmlModulesPlugin {
|
|
|
942
1151
|
(result, module) => {
|
|
943
1152
|
if (module.type === HTML_MODULE_TYPE) {
|
|
944
1153
|
const [source, ...rest] = removeBOMFromResult(result);
|
|
945
|
-
//
|
|
946
|
-
//
|
|
947
|
-
//
|
|
948
|
-
// and the generator's render base in sync.
|
|
1154
|
+
// Run here, where the returned source becomes the module's stored
|
|
1155
|
+
// source, so the parser's dependency offsets and the generator's
|
|
1156
|
+
// render base stay in sync.
|
|
949
1157
|
const parser = /** @type {HtmlParser} */ (module.parser);
|
|
950
1158
|
|
|
951
1159
|
return [parser.applyTemplate(source, module), ...rest];
|
|
@@ -955,35 +1163,20 @@ class HtmlModulesPlugin {
|
|
|
955
1163
|
}
|
|
956
1164
|
);
|
|
957
1165
|
|
|
958
|
-
//
|
|
959
|
-
//
|
|
960
|
-
// `
|
|
961
|
-
//
|
|
962
|
-
//
|
|
963
|
-
//
|
|
964
|
-
//
|
|
965
|
-
//
|
|
966
|
-
//
|
|
967
|
-
//
|
|
968
|
-
|
|
969
|
-
// `
|
|
970
|
-
//
|
|
971
|
-
//
|
|
972
|
-
// intentionally omitted so `[name]` resolves to the HTML
|
|
973
|
-
// source's basename (e.g. `page` for `./page.html`) rather
|
|
974
|
-
// than the importing chunk's name (e.g. `main`). A per-module
|
|
975
|
-
// content hash is computed from the rewritten HTML so the
|
|
976
|
-
// template's `[contenthash]` placeholder works; the
|
|
977
|
-
// compilation hash is also forwarded so `[fullhash]` /
|
|
978
|
-
// `[hash]` work in user-supplied templates.
|
|
979
|
-
|
|
980
|
-
// Sentinel-resolved content and its content hash depend only on
|
|
981
|
-
// the HTML-type module source — not on the chunk or output
|
|
982
|
-
// filename — yet `renderManifest` runs per `(chunk, module)`.
|
|
983
|
-
// Memoize so a module landing in multiple chunks resolves and
|
|
984
|
-
// hashes its sentinels once. Scoped to this compilation because
|
|
985
|
-
// sentinel resolution embeds chunk filenames, which change
|
|
986
|
-
// across rebuilds; weakly keyed by source so entries release.
|
|
1166
|
+
// WHY: emit extracted `.html` files for any HTML module that opted in.
|
|
1167
|
+
// `HtmlGenerator#_shouldExtract` computes that opt-in —
|
|
1168
|
+
// `module.generator.html.extract: true` always extracts, `false` never,
|
|
1169
|
+
// and unset extracts only a compilation entry — and the iteration below
|
|
1170
|
+
// picks up only modules whose generator reported the `html` source type,
|
|
1171
|
+
// so that decision is honoured implicitly. The filename comes from
|
|
1172
|
+
// `output.htmlFilename` for initial chunks and `output.htmlChunkFilename`
|
|
1173
|
+
// otherwise, mirroring the CSS pipeline, and the path data follows the
|
|
1174
|
+
// asset-module pattern with `chunk` deliberately left out, so `[name]`
|
|
1175
|
+
// resolves to the HTML source's basename rather than the chunk's.
|
|
1176
|
+
|
|
1177
|
+
// `renderManifest` runs per `(chunk, module)`, but resolved content and
|
|
1178
|
+
// its hash depend only on the module source, so memoize. Scoped to this
|
|
1179
|
+
// compilation, since resolution embeds chunk filenames.
|
|
987
1180
|
/** @type {WeakMap<import("webpack-sources").Source, { resolvedContent: string, contentHash: string }>} */
|
|
988
1181
|
const resolvedSentinelHashCache = new WeakMap();
|
|
989
1182
|
|
|
@@ -1005,15 +1198,12 @@ class HtmlModulesPlugin {
|
|
|
1005
1198
|
if (!placeholderSource) return undefined;
|
|
1006
1199
|
let cached = resolvedSentinelHashCache.get(placeholderSource);
|
|
1007
1200
|
if (cached === undefined) {
|
|
1008
|
-
// Resolve chunk-URL sentinels
|
|
1009
|
-
// `[contenthash]`
|
|
1010
|
-
//
|
|
1011
|
-
|
|
1012
|
-
//
|
|
1013
|
-
//
|
|
1014
|
-
// Asset URL sentinels (from `HtmlEntryDependency` resource-hint
|
|
1015
|
-
// tags) resolve here too — deferred from template.apply so it
|
|
1016
|
-
// doesn't race with asset-module codegen.
|
|
1201
|
+
// Resolve chunk-URL sentinels before hashing, so the page's
|
|
1202
|
+
// `[contenthash]` tracks the referenced chunks. An inlined chunk has
|
|
1203
|
+
// no URL, so its sentinel carries the chunk content hash instead.
|
|
1204
|
+
|
|
1205
|
+
// Asset URL sentinels resolve here rather than in `template.apply`,
|
|
1206
|
+
// which would race with asset-module codegen.
|
|
1017
1207
|
const resolvedContent = getHtmlGenerator().resolveAssetUrlSentinels(
|
|
1018
1208
|
getHtmlGenerator().embedInlineChunkHashes(
|
|
1019
1209
|
getHtmlGenerator().resolveChunkUrlSentinels(
|
|
@@ -1109,9 +1299,8 @@ class HtmlModulesPlugin {
|
|
|
1109
1299
|
compilation.hooks.renderManifest.tap(
|
|
1110
1300
|
PLUGIN_NAME,
|
|
1111
1301
|
(result, { chunk }) => {
|
|
1112
|
-
// HMR's `HotUpdateChunk`s
|
|
1113
|
-
//
|
|
1114
|
-
// for them would create stray hot-update HTML files.
|
|
1302
|
+
// HMR's `HotUpdateChunk`s reach this hook but are not output chunks,
|
|
1303
|
+
// so extracting `.html` for them would emit stray hot-update files.
|
|
1115
1304
|
// `CssModulesPlugin` early-returns for the same reason.
|
|
1116
1305
|
if (chunk instanceof HotUpdateChunk) return result;
|
|
1117
1306
|
const { chunkGraph } = compilation;
|
|
@@ -1125,12 +1314,13 @@ class HtmlModulesPlugin {
|
|
|
1125
1314
|
const outputOptions = compilation.outputOptions;
|
|
1126
1315
|
for (const module of modules) {
|
|
1127
1316
|
const normalModule = /** @type {NormalModule} */ (module);
|
|
1128
|
-
// `<iframe srcdoc>`
|
|
1129
|
-
// can write the processed markup back
|
|
1130
|
-
//
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1133
|
-
//
|
|
1317
|
+
// WHY: an `<iframe srcdoc>` module exposes `html` only so that
|
|
1318
|
+
// `HtmlInlineHtmlDependency` can write the processed markup back
|
|
1319
|
+
// into the host attribute; it is never a standalone page.
|
|
1320
|
+
// `generator.extract: "inline"` marks it so no `.html` file is
|
|
1321
|
+
// emitted — without it they would collide on the `data:text/html`
|
|
1322
|
+
// to chunk-name path, every srcdoc module in a chunk emitting
|
|
1323
|
+
// `<chunk>.html`. An O(1) flag check rather than a scan.
|
|
1134
1324
|
const generatorOptions = normalModule.generatorOptions;
|
|
1135
1325
|
if (generatorOptions && generatorOptions.extract === "inline") {
|
|
1136
1326
|
continue;
|
|
@@ -1140,11 +1330,9 @@ class HtmlModulesPlugin {
|
|
|
1140
1330
|
if (!emit) continue;
|
|
1141
1331
|
const { resolvedContent, contentHash, filename, info } = emit;
|
|
1142
1332
|
|
|
1143
|
-
// Resolve
|
|
1144
|
-
//
|
|
1145
|
-
//
|
|
1146
|
-
// a subdirectory. A relative `<base href>` prepends `../`s so the
|
|
1147
|
-
// base can't misdirect the rewritten URLs (see `HtmlParser`).
|
|
1333
|
+
// Resolve the remaining `[webpack/auto]` placeholders against the
|
|
1334
|
+
// emitted page's own location, so URLs stay relative to `output.path`
|
|
1335
|
+
// from a subdirectory. A relative `<base href>` prepends `../`s.
|
|
1148
1336
|
const basePrefix =
|
|
1149
1337
|
/** @type {HtmlModuleBuildInfo} */ (normalModule.buildInfo)
|
|
1150
1338
|
.baseUrlPrefix || "";
|
|
@@ -1166,12 +1354,9 @@ class HtmlModulesPlugin {
|
|
|
1166
1354
|
.split(autoPlaceholder)
|
|
1167
1355
|
.join(undoPath);
|
|
1168
1356
|
const finalSource = new RawSource(finalContent);
|
|
1169
|
-
//
|
|
1170
|
-
//
|
|
1171
|
-
//
|
|
1172
|
-
// the asset cache key and the post-undo-path content in the hash so the
|
|
1173
|
-
// asset cache can't reuse one variant's bytes at another variant's URL.
|
|
1174
|
-
// Unchanged content reuses the memoized hash instead of re-digesting.
|
|
1357
|
+
// One module can land in several chunks whose filename shapes give
|
|
1358
|
+
// different undo paths, so the emitted filename joins the cache key
|
|
1359
|
+
// and the post-undo content the hash — else variants alias.
|
|
1175
1360
|
const finalContentHash =
|
|
1176
1361
|
finalContent === resolvedContent
|
|
1177
1362
|
? contentHash
|
|
@@ -1211,6 +1396,16 @@ class HtmlModulesPlugin {
|
|
|
1211
1396
|
jsHooks.render.tap(PLUGIN_NAME, (source, renderContext) => {
|
|
1212
1397
|
// No HTML modules ⇒ no sentinels; skip materializing the JS source.
|
|
1213
1398
|
if (htmlModules.size === 0) return source;
|
|
1399
|
+
// Chunks without HTML modules can't carry HTML sentinels — avoid
|
|
1400
|
+
// ConcatSource.source() on the common pure-JS chunk path.
|
|
1401
|
+
if (
|
|
1402
|
+
!HtmlModulesPlugin.chunkHasHtml(
|
|
1403
|
+
renderContext.chunk,
|
|
1404
|
+
compilation.chunkGraph
|
|
1405
|
+
)
|
|
1406
|
+
) {
|
|
1407
|
+
return source;
|
|
1408
|
+
}
|
|
1214
1409
|
const raw = source.source();
|
|
1215
1410
|
if (typeof raw !== "string") return source;
|
|
1216
1411
|
// Every sentinel shares this prefix — one scan answers the common
|
|
@@ -1281,6 +1476,29 @@ class HtmlModulesPlugin {
|
|
|
1281
1476
|
}
|
|
1282
1477
|
);
|
|
1283
1478
|
}
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
* Returns true, when the chunk has html.
|
|
1482
|
+
* @param {Chunk} chunk chunk
|
|
1483
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
1484
|
+
* @returns {boolean} true, when the chunk has html
|
|
1485
|
+
*/
|
|
1486
|
+
static chunkHasHtml(chunk, chunkGraph) {
|
|
1487
|
+
if (chunkGraph.getChunkModulesIterableBySourceType(chunk, HTML_TYPE)) {
|
|
1488
|
+
return true;
|
|
1489
|
+
}
|
|
1490
|
+
// Scope-hoisted HTML loses HTML_TYPE on the ConcatenatedModule, but its
|
|
1491
|
+
// JS export can still carry unresolved sentinels that need this path.
|
|
1492
|
+
for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
|
|
1493
|
+
if (module instanceof HtmlModule) return true;
|
|
1494
|
+
if (module instanceof ConcatenatedModule) {
|
|
1495
|
+
for (const inner of module.modules) {
|
|
1496
|
+
if (inner instanceof HtmlModule) return true;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1284
1502
|
}
|
|
1285
1503
|
|
|
1286
1504
|
/**
|