webpack 5.107.2 → 5.108.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/hot/dev-server.js +2 -0
- package/lib/APIPlugin.js +8 -4
- package/lib/CacheFacade.js +7 -0
- package/lib/Chunk.js +4 -0
- package/lib/ChunkGraph.js +30 -9
- package/lib/CircularModulesPlugin.js +190 -0
- package/lib/CleanPlugin.js +2 -1
- package/lib/Compilation.js +396 -65
- package/lib/Compiler.js +25 -11
- package/lib/ConcatenationScope.js +10 -3
- package/lib/ContextExclusionPlugin.js +1 -0
- package/lib/ContextModule.js +92 -47
- package/lib/ContextReplacementPlugin.js +4 -0
- package/lib/DefinePlugin.js +89 -15
- package/lib/Dependency.js +111 -7
- package/lib/EntryOptionPlugin.js +39 -2
- package/lib/EntryPlugin.js +2 -0
- package/lib/ExportsInfo.js +314 -73
- package/lib/ExternalModule.js +82 -34
- package/lib/ExternalModuleFactoryPlugin.js +1 -0
- package/lib/ExternalsPlugin.js +1 -0
- package/lib/FileSystemInfo.js +173 -23
- package/lib/FlagAllModulesAsUsedPlugin.js +1 -5
- package/lib/FlagDependencyExportsPlugin.js +23 -0
- package/lib/FlagDependencyUsagePlugin.js +87 -5
- package/lib/FlagEntryExportAsUsedPlugin.js +2 -0
- package/lib/Generator.js +8 -0
- package/lib/HotModuleReplacementPlugin.js +65 -28
- package/lib/IgnorePlugin.js +1 -0
- package/lib/InitFragment.js +5 -0
- package/lib/JavascriptMetaInfoPlugin.js +7 -5
- package/lib/LazyBarrel.js +361 -0
- package/lib/LoaderTargetPlugin.js +1 -0
- package/lib/Module.js +21 -42
- package/lib/ModuleGraph.js +3 -2
- package/lib/ModuleProfile.js +27 -1
- package/lib/ModuleTemplate.js +2 -0
- package/lib/MultiCompiler.js +2 -0
- package/lib/MultiStats.js +1 -0
- package/lib/MultiWatching.js +2 -0
- package/lib/NodeStuffPlugin.js +22 -17
- package/lib/NormalModule.js +142 -80
- package/lib/NormalModuleReplacementPlugin.js +2 -0
- package/lib/PrefetchPlugin.js +2 -0
- package/lib/ProgressPlugin.js +8 -0
- package/lib/ProvidePlugin.js +1 -0
- package/lib/RawModule.js +20 -16
- package/lib/RecordIdsPlugin.js +1 -0
- package/lib/RuntimeGlobals.js +5 -0
- package/lib/RuntimePlugin.js +58 -26
- package/lib/RuntimeTemplate.js +278 -21
- package/lib/SelfModuleFactory.js +1 -0
- package/lib/SourceMapDevToolPlugin.js +3 -5
- package/lib/Stats.js +1 -0
- package/lib/Template.js +8 -2
- package/lib/TemplatedPathPlugin.js +473 -131
- package/lib/WarnCaseSensitiveModulesPlugin.js +1 -0
- package/lib/WarnDeprecatedOptionPlugin.js +4 -0
- package/lib/WarnNoModeSetPlugin.js +1 -0
- package/lib/WatchIgnorePlugin.js +1 -0
- package/lib/Watching.js +9 -0
- package/lib/WebpackOptionsApply.js +50 -5
- package/lib/asset/AssetBytesGenerator.js +11 -3
- package/lib/asset/AssetGenerator.js +47 -22
- package/lib/asset/AssetModule.js +47 -0
- package/lib/asset/AssetModulesPlugin.js +14 -14
- package/lib/asset/AssetParser.js +2 -2
- package/lib/asset/AssetSourceGenerator.js +8 -0
- package/lib/asset/RawDataUrlModule.js +12 -13
- package/lib/buildChunkGraph.js +64 -7
- package/lib/bun/BunTargetPlugin.js +48 -0
- package/lib/cache/AddBuildDependenciesPlugin.js +1 -0
- package/lib/cache/AddManagedPathsPlugin.js +3 -0
- package/lib/cache/IdleFileCachePlugin.js +4 -0
- package/lib/cache/MemoryWithGcCachePlugin.js +1 -0
- package/lib/cache/PackFileCacheStrategy.js +1 -0
- package/lib/cache/ResolverCachePlugin.js +2 -0
- package/lib/cache/mergeEtags.js +2 -0
- package/lib/cli.js +109 -19
- package/lib/config/browserslistTargetHandler.js +99 -0
- package/lib/config/defaults.js +147 -7
- package/lib/config/defineConfig.js +31 -0
- package/lib/config/normalization.js +5 -0
- package/lib/config/target.js +181 -2
- package/lib/container/ContainerEntryModule.js +15 -14
- package/lib/container/ContainerExposedDependency.js +2 -2
- package/lib/container/ContainerReferencePlugin.js +1 -1
- package/lib/container/FallbackDependency.js +5 -7
- package/lib/container/FallbackModule.js +10 -9
- package/lib/container/RemoteModule.js +20 -10
- package/lib/container/RemoteRuntimeModule.js +14 -12
- package/lib/css/CssGenerator.js +353 -327
- package/lib/css/CssInjectStyleRuntimeModule.js +36 -8
- package/lib/css/CssLoadingRuntimeModule.js +118 -47
- package/lib/css/CssModule.js +52 -23
- package/lib/css/CssModulesPlugin.js +107 -124
- package/lib/css/CssParser.js +2759 -2205
- package/lib/css/syntax.js +2859 -0
- package/lib/debug/ProfilingPlugin.js +2 -0
- package/lib/deno/DenoTargetPlugin.js +47 -0
- package/lib/dependencies/AMDDefineDependency.js +22 -17
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +1 -0
- package/lib/dependencies/AMDRequireArrayDependency.js +7 -11
- package/lib/dependencies/AMDRequireContextDependency.js +7 -11
- package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +1 -0
- package/lib/dependencies/AMDRequireDependency.js +24 -20
- package/lib/dependencies/CachedConstDependency.js +3 -0
- package/lib/dependencies/CommonJsDependencyHelpers.js +1 -0
- package/lib/dependencies/CommonJsExportRequireDependency.js +79 -31
- package/lib/dependencies/CommonJsExportsDependency.js +21 -16
- package/lib/dependencies/CommonJsExportsParserPlugin.js +230 -7
- package/lib/dependencies/CommonJsFullRequireDependency.js +27 -19
- package/lib/dependencies/CommonJsImportsParserPlugin.js +51 -16
- package/lib/dependencies/CommonJsPlugin.js +1 -1
- package/lib/dependencies/CommonJsRequireContextDependency.js +10 -13
- package/lib/dependencies/CommonJsRequireDependency.js +42 -11
- package/lib/dependencies/CommonJsSelfReferenceDependency.js +22 -14
- package/lib/dependencies/ConstDependency.js +16 -11
- package/lib/dependencies/ContextDependency.js +4 -0
- package/lib/dependencies/ContextDependencyTemplateAsId.js +1 -1
- package/lib/dependencies/ContextElementDependency.js +23 -14
- package/lib/dependencies/CreateRequireParserPlugin.js +1 -0
- package/lib/dependencies/CreateScriptUrlDependency.js +5 -7
- package/lib/dependencies/CriticalDependencyWarning.js +1 -0
- package/lib/dependencies/CssIcssExportDependency.js +512 -574
- package/lib/dependencies/CssIcssImportDependency.js +9 -9
- package/lib/dependencies/CssIcssSymbolDependency.js +22 -15
- package/lib/dependencies/CssImportDependency.js +25 -1
- package/lib/dependencies/CssUrlDependency.js +23 -14
- package/lib/dependencies/DllEntryDependency.js +9 -13
- package/lib/dependencies/ExportBindingInitFragment.js +164 -0
- package/lib/dependencies/ExportsInfoDependency.js +12 -12
- package/lib/dependencies/ExternalModuleDependency.js +8 -5
- package/lib/dependencies/ExternalModuleInitFragment.js +7 -5
- package/lib/dependencies/ExternalModuleInitFragmentDependency.js +13 -10
- package/lib/dependencies/HarmonyAcceptDependency.js +11 -11
- package/lib/dependencies/HarmonyAcceptImportDependency.js +1 -0
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +47 -22
- package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +28 -2
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +46 -22
- package/lib/dependencies/HarmonyExportExpressionDependency.js +107 -30
- package/lib/dependencies/HarmonyExportHeaderDependency.js +7 -9
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +109 -31
- package/lib/dependencies/HarmonyExportInitFragment.js +18 -15
- package/lib/dependencies/HarmonyExportSpecifierDependency.js +83 -18
- package/lib/dependencies/HarmonyExports.js +4 -1
- package/lib/dependencies/HarmonyImportDependency.js +24 -0
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +110 -74
- package/lib/dependencies/HarmonyImportGuard.js +254 -0
- package/lib/dependencies/HarmonyImportSideEffectDependency.js +18 -1
- package/lib/dependencies/HarmonyImportSpecifierDependency.js +118 -14
- package/lib/dependencies/HarmonyLinkingError.js +1 -0
- package/lib/dependencies/HarmonyModulesPlugin.js +1 -0
- package/lib/dependencies/{HtmlScriptSrcDependency.js → HtmlEntryDependency.js} +261 -109
- package/lib/dependencies/HtmlInlineHtmlDependency.js +107 -0
- package/lib/dependencies/HtmlInlineScriptDependency.js +17 -13
- package/lib/dependencies/HtmlInlineStyleDependency.js +45 -11
- package/lib/dependencies/ImportContextDependency.js +5 -9
- package/lib/dependencies/ImportDependency.js +44 -2
- package/lib/dependencies/ImportMetaHotAcceptDependency.js +1 -0
- package/lib/dependencies/ImportMetaHotDeclineDependency.js +1 -0
- package/lib/dependencies/ImportMetaPlugin.js +74 -30
- package/lib/dependencies/ImportParserPlugin.js +19 -0
- package/lib/dependencies/ImportWeakDependency.js +1 -0
- package/lib/dependencies/JsonExportsDependency.js +9 -9
- package/lib/dependencies/LoaderImportDependency.js +1 -0
- package/lib/dependencies/LocalModule.js +11 -12
- package/lib/dependencies/LocalModuleDependency.js +11 -13
- package/lib/dependencies/ModuleDecoratorDependency.js +9 -9
- package/lib/dependencies/ModuleDependency.js +7 -0
- package/lib/dependencies/ModuleHotAcceptDependency.js +1 -0
- package/lib/dependencies/ModuleHotDeclineDependency.js +1 -0
- package/lib/dependencies/ModuleInitFragmentDependency.js +15 -11
- package/lib/dependencies/ProvidedDependency.js +31 -27
- package/lib/dependencies/PureExpressionDependency.js +7 -9
- package/lib/dependencies/RequireEnsureDependency.js +12 -13
- package/lib/dependencies/RequireHeaderDependency.js +3 -4
- package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +3 -0
- package/lib/dependencies/RequireResolveContextDependency.js +7 -11
- package/lib/dependencies/RequireResolveDependency.js +1 -0
- package/lib/dependencies/RequireResolveHeaderDependency.js +3 -5
- package/lib/dependencies/RuntimeRequirementsDependency.js +6 -7
- package/lib/dependencies/StaticExportsDependency.js +10 -10
- package/lib/dependencies/SystemPlugin.js +2 -0
- package/lib/dependencies/URLContextDependency.js +5 -7
- package/lib/dependencies/URLDependency.js +14 -16
- package/lib/dependencies/UnsupportedDependency.js +8 -13
- package/lib/dependencies/WebAssemblyExportImportedDependency.js +11 -16
- package/lib/dependencies/WebAssemblyImportDependency.js +14 -16
- package/lib/dependencies/WebpackIsIncludedDependency.js +1 -0
- package/lib/dependencies/WorkerDependency.js +19 -8
- package/lib/dependencies/WorkerPlugin.js +22 -6
- package/lib/dependencies/processExportInfo.js +13 -11
- package/lib/dll/DelegatedModule.js +29 -15
- package/lib/dll/DelegatedModuleFactoryPlugin.js +1 -0
- package/lib/dll/DelegatedPlugin.js +1 -0
- package/lib/dll/DllEntryPlugin.js +3 -0
- package/lib/dll/DllModule.js +3 -2
- package/lib/dll/DllPlugin.js +16 -0
- package/lib/dll/DllReferencePlugin.js +3 -0
- package/lib/dll/LibManifestPlugin.js +1 -0
- package/lib/electron/ElectronTargetPlugin.js +22 -4
- package/lib/errors/ConcurrentCompilationError.js +1 -0
- package/lib/errors/HookWebpackError.js +11 -13
- package/lib/errors/IgnoreErrorModuleFactory.js +1 -0
- package/lib/errors/InvalidDependenciesModuleWarning.js +2 -0
- package/lib/errors/JSONParseError.js +9 -8
- package/lib/errors/ModuleBuildError.js +16 -13
- package/lib/errors/ModuleDependencyError.js +8 -1
- package/lib/errors/ModuleDependencyWarning.js +8 -1
- package/lib/errors/ModuleError.js +5 -11
- package/lib/errors/ModuleHashingError.js +4 -0
- package/lib/errors/ModuleNotFoundError.js +3 -0
- package/lib/errors/ModuleParseError.js +5 -11
- package/lib/errors/ModuleRestoreError.js +2 -0
- package/lib/errors/ModuleStoreError.js +3 -0
- package/lib/errors/ModuleWarning.js +7 -11
- package/lib/errors/NodeStuffInWebError.js +1 -0
- package/lib/errors/NonErrorEmittedError.js +2 -0
- package/lib/errors/UnhandledSchemeError.js +1 -0
- package/lib/esm/ModuleChunkLoadingPlugin.js +0 -1
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +28 -23
- package/lib/hmr/HotModuleReplacement.runtime.js +175 -30
- package/lib/hmr/HotModuleReplacementRuntimeModule.js +7 -0
- package/lib/hmr/JavascriptHotModuleReplacement.runtime.js +173 -26
- package/lib/hmr/LazyCompilationPlugin.js +30 -6
- package/lib/html/HtmlGenerator.js +217 -23
- package/lib/html/HtmlModule.js +40 -0
- package/lib/html/HtmlModulesPlugin.js +219 -55
- package/lib/html/HtmlParser.js +1108 -1099
- package/lib/html/{walkHtmlTokens.js → syntax.js} +4701 -212
- package/lib/ids/IdHelpers.js +4 -2
- package/lib/index.js +19 -0
- package/lib/javascript/BasicEvaluatedExpression.js +1 -0
- package/lib/javascript/EnableChunkLoadingPlugin.js +1 -0
- package/lib/javascript/JavascriptGenerator.js +6 -2
- package/lib/javascript/JavascriptModule.js +54 -0
- package/lib/javascript/JavascriptModulesPlugin.js +257 -102
- package/lib/javascript/JavascriptParser.js +285 -158
- package/lib/json/JsonModule.js +40 -0
- package/lib/json/JsonModulesPlugin.js +7 -0
- package/lib/json/JsonParser.js +4 -2
- package/lib/library/AssignLibraryPlugin.js +5 -3
- package/lib/library/ExportPropertyLibraryPlugin.js +1 -0
- package/lib/library/FalseIIFEUmdWarning.js +1 -0
- package/lib/library/ModuleLibraryPlugin.js +24 -5
- package/lib/library/SystemLibraryPlugin.js +3 -3
- package/lib/node/NodeTargetPlugin.js +1 -0
- package/lib/node/NodeWatchFileSystem.js +1 -0
- package/lib/node/ReadFileChunkLoadingRuntimeModule.js +10 -9
- package/lib/node/RequireChunkLoadingRuntimeModule.js +18 -16
- package/lib/optimize/ConcatenatedModule.js +395 -77
- package/lib/optimize/ConstExportsPlugin.js +211 -0
- package/lib/optimize/InlineExports.js +178 -0
- package/lib/optimize/InnerGraph.js +372 -275
- package/lib/optimize/InnerGraphPlugin.js +196 -99
- package/lib/optimize/ModuleConcatenationPlugin.js +56 -25
- package/lib/optimize/RealContentHashPlugin.js +14 -1
- package/lib/optimize/SideEffectsFlagPlugin.js +234 -64
- package/lib/runtime/AsyncModuleRuntimeModule.js +32 -30
- package/lib/runtime/AutoPublicPathRuntimeModule.js +11 -5
- package/lib/runtime/CompatGetDefaultExportRuntimeModule.js +1 -1
- package/lib/runtime/CreateFakeNamespaceObjectRuntimeModule.js +6 -4
- package/lib/runtime/DefinePropertyGettersRuntimeModule.js +33 -4
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +82 -3
- package/lib/runtime/GetTrustedTypesPolicyRuntimeModule.js +1 -1
- package/lib/runtime/HasOwnPropertyRuntimeModule.js +1 -1
- package/lib/runtime/LoadScriptRuntimeModule.js +18 -13
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +39 -26
- package/lib/runtime/MakeNamespaceObjectRuntimeModule.js +1 -1
- package/lib/runtime/OnChunksLoadedRuntimeModule.js +4 -4
- package/lib/runtime/RelativeUrlRuntimeModule.js +3 -2
- package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +1 -1
- package/lib/runtime/StartupEntrypointRuntimeModule.js +4 -3
- package/lib/runtime/WorkerRuntimeModule.js +33 -0
- package/lib/schemes/HttpUriPlugin.js +3 -2
- package/lib/serialization/AggregateErrorSerializer.js +7 -6
- package/lib/serialization/ArraySerializer.js +4 -8
- package/lib/serialization/BinaryMiddleware.js +538 -468
- package/lib/serialization/DateObjectSerializer.js +2 -2
- package/lib/serialization/ErrorObjectSerializer.js +7 -6
- package/lib/serialization/MapObjectSerializer.js +5 -9
- package/lib/serialization/NullPrototypeObjectSerializer.js +7 -9
- package/lib/serialization/ObjectMiddleware.js +50 -13
- package/lib/serialization/PlainObjectSerializer.js +9 -8
- package/lib/serialization/RegExpObjectSerializer.js +2 -2
- package/lib/serialization/Serializer.js +1 -0
- package/lib/serialization/SetObjectSerializer.js +4 -8
- package/lib/sharing/ConsumeSharedModule.js +6 -7
- package/lib/sharing/ConsumeSharedPlugin.js +1 -0
- package/lib/sharing/ConsumeSharedRuntimeModule.js +46 -41
- package/lib/sharing/ProvideSharedDependency.js +30 -15
- package/lib/sharing/ProvideSharedModule.js +30 -11
- package/lib/sharing/ProvideSharedPlugin.js +4 -2
- package/lib/sharing/SharePlugin.js +3 -0
- package/lib/sharing/ShareRuntimeModule.js +18 -16
- package/lib/sharing/resolveMatchedConfigs.js +2 -1
- package/lib/sharing/utils.js +1 -1
- package/lib/stats/DefaultStatsFactoryPlugin.js +4 -6
- package/lib/stats/DefaultStatsPrinterPlugin.js +14 -14
- package/lib/stats/StatsFactory.js +11 -9
- package/lib/stats/StatsPrinter.js +9 -2
- package/lib/url/URLParserPlugin.js +5 -2
- package/lib/util/AsyncQueue.js +10 -0
- package/lib/util/LazyBucketSortedSet.js +5 -0
- package/lib/util/LazySet.js +6 -4
- package/lib/util/LocConverter.js +11 -1
- package/lib/util/Semaphore.js +1 -0
- package/lib/util/SourceProcessor.js +106 -0
- package/lib/util/TupleSet.js +1 -0
- package/lib/util/WeakTupleMap.js +27 -1
- package/lib/util/chainedImports.js +3 -3
- package/lib/util/comparators.js +2 -2
- package/lib/util/concatenate.js +31 -7
- package/lib/util/createHash.js +0 -1
- package/lib/util/deterministicGrouping.js +19 -12
- package/lib/util/extractSourceMap.js +1 -1
- package/lib/util/findGraphRoots.js +2 -0
- package/lib/util/fs.js +10 -5
- package/lib/util/hash/DebugHash.js +1 -0
- package/lib/util/hash/hash-digest.js +24 -15
- package/lib/util/identifier.js +7 -0
- package/lib/util/internalSerializables.js +11 -2
- package/lib/util/magicComment.js +42 -0
- package/lib/util/makeSerializable.js +1 -0
- package/lib/util/nonNumericOnlyHash.js +30 -1
- package/lib/util/property.js +7 -1
- package/lib/util/publicPathPlaceholder.js +64 -0
- package/lib/util/registerExternalSerializer.js +4 -4
- package/lib/wasm-async/AsyncWasmModule.js +77 -0
- package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +1 -1
- package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -96
- package/lib/wasm-async/AsyncWebAssemblyParser.js +1 -1
- package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +1 -1
- package/lib/wasm-sync/SyncWasmModule.js +39 -0
- package/lib/wasm-sync/UnsupportedWebAssemblyFeatureError.js +1 -0
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +6 -3
- package/lib/wasm-sync/WasmFinalizeExportsPlugin.js +2 -1
- package/lib/wasm-sync/WebAssemblyGenerator.js +1 -0
- package/lib/wasm-sync/WebAssemblyInInitialChunkError.js +2 -0
- package/lib/wasm-sync/WebAssemblyModulesPlugin.js +10 -0
- package/lib/wasm-sync/WebAssemblyParser.js +7 -3
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +45 -39
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +11 -10
- package/package.json +35 -27
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +212 -3
- package/schemas/plugins/HtmlGeneratorOptions.check.js +1 -1
- package/schemas/plugins/HtmlParserOptions.check.d.ts +7 -0
- package/schemas/plugins/HtmlParserOptions.check.js +6 -0
- package/schemas/plugins/HtmlParserOptions.json +3 -0
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssParserOptions.check.js +1 -1
- package/types.d.ts +1456 -290
- package/lib/css/walkCssTokens.js +0 -2020
- package/lib/util/AppendOnlyStackedSet.js +0 -93
package/lib/html/HtmlParser.js
CHANGED
|
@@ -4,43 +4,77 @@
|
|
|
4
4
|
|
|
5
5
|
"use strict";
|
|
6
6
|
|
|
7
|
-
const vm = require("vm");
|
|
8
7
|
const Parser = require("../Parser");
|
|
8
|
+
const {
|
|
9
|
+
TT_EOF,
|
|
10
|
+
TT_FUNCTION,
|
|
11
|
+
TT_STRING,
|
|
12
|
+
TT_URL,
|
|
13
|
+
TT_WHITESPACE,
|
|
14
|
+
TokenStream,
|
|
15
|
+
equalsLowerCase
|
|
16
|
+
} = require("../css/syntax");
|
|
9
17
|
const ConstDependency = require("../dependencies/ConstDependency");
|
|
18
|
+
const HtmlEntryDependency = require("../dependencies/HtmlEntryDependency");
|
|
19
|
+
const HtmlInlineHtmlDependency = require("../dependencies/HtmlInlineHtmlDependency");
|
|
10
20
|
const HtmlInlineScriptDependency = require("../dependencies/HtmlInlineScriptDependency");
|
|
11
21
|
const HtmlInlineStyleDependency = require("../dependencies/HtmlInlineStyleDependency");
|
|
12
|
-
const HtmlScriptSrcDependency = require("../dependencies/HtmlScriptSrcDependency");
|
|
13
22
|
const HtmlSourceDependency = require("../dependencies/HtmlSourceDependency");
|
|
14
23
|
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
|
15
24
|
const CommentCompilationWarning = require("../errors/CommentCompilationWarning");
|
|
16
25
|
const ModuleDependencyError = require("../errors/ModuleDependencyError");
|
|
17
26
|
const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
|
|
18
27
|
const WebpackError = require("../errors/WebpackError");
|
|
28
|
+
const LazySet = require("../util/LazySet");
|
|
19
29
|
const LocConverter = require("../util/LocConverter");
|
|
20
30
|
const createHash = require("../util/createHash");
|
|
21
31
|
const { contextify } = require("../util/identifier");
|
|
22
32
|
const {
|
|
23
33
|
createMagicCommentContext,
|
|
34
|
+
parseMagicComment,
|
|
24
35
|
webpackCommentRegExp
|
|
25
36
|
} = require("../util/magicComment");
|
|
26
|
-
const
|
|
27
|
-
|
|
37
|
+
const {
|
|
38
|
+
NS_SVG,
|
|
39
|
+
NodeType,
|
|
40
|
+
SVG_TAG_ADJUST,
|
|
41
|
+
SourceProcessor,
|
|
42
|
+
buildHtmlAst,
|
|
43
|
+
decodeHtmlEntities,
|
|
44
|
+
decodeHtmlEntitiesWithMap,
|
|
45
|
+
findAttr,
|
|
46
|
+
parseSrcset
|
|
47
|
+
} = require("./syntax");
|
|
48
|
+
|
|
49
|
+
/** @typedef {import("../../declarations/WebpackOptions").HtmlParserOptions} HtmlParserOptions */
|
|
50
|
+
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|
51
|
+
/** @typedef {import("../Module")} Module */
|
|
28
52
|
/** @typedef {import("../Module").BuildInfo} BuildInfo */
|
|
53
|
+
/** @typedef {import("../Compilation").FileSystemDependencies} FileSystemDependencies */
|
|
29
54
|
/** @typedef {import("../Module").BuildMeta} BuildMeta */
|
|
55
|
+
/** @typedef {import("../NormalModule")} NormalModule */
|
|
30
56
|
/** @typedef {import("../Parser").ParserState} ParserState */
|
|
31
57
|
/** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
|
|
58
|
+
/** @typedef {import("./HtmlModule").HtmlModuleBuildInfo} HtmlModuleBuildInfo */
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @typedef {object} HtmlTemplateContext
|
|
62
|
+
* @property {Module} module the html module being transformed
|
|
63
|
+
* @property {string} resource absolute path of the module's resource
|
|
64
|
+
* @property {(dependency: string) => void} addDependency register a file (e.g. a template partial) as a build dependency so editing it triggers a rebuild
|
|
65
|
+
* @property {(dependency: string) => void} addContextDependency register a directory as a build dependency
|
|
66
|
+
* @property {(dependency: string) => void} addMissingDependency register a not-yet-existing path as a build dependency so creating it triggers a rebuild
|
|
67
|
+
* @property {(dependency: string) => void} addBuildDependency register a build dependency (e.g. a template engine config) so changing it invalidates the cache
|
|
68
|
+
* @property {(warning: Error | string) => void} emitWarning report a non-fatal warning on the module
|
|
69
|
+
* @property {(error: Error | string) => void} emitError report an error on the module
|
|
70
|
+
*/
|
|
71
|
+
/** @typedef {(source: string, context: HtmlTemplateContext) => string} HtmlTemplateFunction */
|
|
32
72
|
|
|
33
73
|
const HORIZONTAL_TAB = "\u0009".charCodeAt(0);
|
|
34
74
|
const NEWLINE = "\u000A".charCodeAt(0);
|
|
35
75
|
const FORM_FEED = "\u000C".charCodeAt(0);
|
|
36
76
|
const CARRIAGE_RETURN = "\u000D".charCodeAt(0);
|
|
37
77
|
const SPACE = "\u0020".charCodeAt(0);
|
|
38
|
-
const COMMA = ",".charCodeAt(0);
|
|
39
|
-
const LEFT_PARENTHESIS = "(".charCodeAt(0);
|
|
40
|
-
const RIGHT_PARENTHESIS = ")".charCodeAt(0);
|
|
41
|
-
const SMALL_LETTER_W = "w".charCodeAt(0);
|
|
42
|
-
const SMALL_LETTER_X = "x".charCodeAt(0);
|
|
43
|
-
const SMALL_LETTER_H = "h".charCodeAt(0);
|
|
44
78
|
|
|
45
79
|
/**
|
|
46
80
|
* @param {number} char char
|
|
@@ -61,7 +95,50 @@ function isASCIIWhitespace(char) {
|
|
|
61
95
|
);
|
|
62
96
|
}
|
|
63
97
|
|
|
64
|
-
/** @typedef {
|
|
98
|
+
/** @typedef {import("./syntax").ParsedSource} ParsedSource */
|
|
99
|
+
|
|
100
|
+
// Cheap pre-filter for a `style="..."` attribute: only route it through the
|
|
101
|
+
// CSS pipeline when it can hold a URL-bearing function (`url()` / `src()` /
|
|
102
|
+
// `image()` / `image-set()`), otherwise the processed text equals the input.
|
|
103
|
+
const STYLE_ATTR_URL_REGEXP = /url\(|src\(|image\(|image-set\(/i;
|
|
104
|
+
|
|
105
|
+
// Cheap pre-filter for a `css-url` attribute value (an SVG presentation
|
|
106
|
+
// attribute such as `fill`, `clip-path`, …): only tokenize values that can
|
|
107
|
+
// hold a `url(...)` FuncIRI. Most are plain colors/keywords (`#fff`, `red`).
|
|
108
|
+
const FUNC_IRI_URL_REGEXP = /url\(/i;
|
|
109
|
+
|
|
110
|
+
// Cheap pre-filter for `<iframe srcdoc>` markup: only spin up a nested HTML
|
|
111
|
+
// module when the document can reference an asset — via any attribute (`src=`,
|
|
112
|
+
// `href=`, `style=`, …), a CSS `url(...)`, or a CSS `@import`. Pure
|
|
113
|
+
// formatting/text markup (`<p>hi</p>`) rewrites to itself, so skip it.
|
|
114
|
+
const SRCDOC_ASSET_REGEXP = /[=]|url\(|@import/i;
|
|
115
|
+
|
|
116
|
+
// CSP/fetch attributes copied verbatim onto a synthesized sibling `<link>` /
|
|
117
|
+
// `<script>` (`HtmlEntryDependency`). Fixed output order, independent of
|
|
118
|
+
// source order.
|
|
119
|
+
const COPYABLE_SIBLING_ATTRS = ["nonce", "crossorigin", "referrerpolicy"];
|
|
120
|
+
|
|
121
|
+
const CC_QUOTATION = '"'.charCodeAt(0);
|
|
122
|
+
const CC_APOSTROPHE = "'".charCodeAt(0);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Byte-exact source span of an attribute including the single leading
|
|
126
|
+
* whitespace (` name`, ` name=value`, ` name="value"`), mirroring the
|
|
127
|
+
* tokenizer's end-of-attribute rule (`valueEnd + 1` past the closing quote).
|
|
128
|
+
* @param {string} source HTML source
|
|
129
|
+
* @param {import("./syntax").HtmlAttribute} attr attribute
|
|
130
|
+
* @returns {string} the attribute's source slice
|
|
131
|
+
*/
|
|
132
|
+
const attrSourceSpan = (source, attr) => {
|
|
133
|
+
const end =
|
|
134
|
+
attr.valueStart === -1
|
|
135
|
+
? attr.nameEnd
|
|
136
|
+
: source.charCodeAt(attr.valueStart - 1) === CC_QUOTATION ||
|
|
137
|
+
source.charCodeAt(attr.valueStart - 1) === CC_APOSTROPHE
|
|
138
|
+
? attr.valueEnd + 1
|
|
139
|
+
: attr.valueEnd;
|
|
140
|
+
return source.slice(attr.nameStart - 1, end);
|
|
141
|
+
};
|
|
65
142
|
|
|
66
143
|
// eslint-disable-next-line no-control-regex
|
|
67
144
|
const IGNORE_CHARS_REGEXP = /[\u0000-\u001F\u007F-\u009F\u00A0]/g;
|
|
@@ -101,355 +178,86 @@ const parseSrc = (input) => {
|
|
|
101
178
|
return [[value, start, end]];
|
|
102
179
|
};
|
|
103
180
|
|
|
104
|
-
// HTML `<style>` content is rawtext: it ends at the first `</style>`
|
|
105
|
-
// where the tag name is followed by whitespace, `>` or `/`. The
|
|
106
|
-
// lookahead (rather than a consuming character class) keeps the match
|
|
107
|
-
// from running past the first `>` into a later tag — the
|
|
108
|
-
// `[^>]*` only consumes any (rarely-seen) end-tag attributes before the
|
|
109
|
-
// closing `>` of the end tag itself.
|
|
110
|
-
const STYLE_END_REGEXP = /<\/style(?=[\s/>])[^>]*>/gi;
|
|
111
|
-
|
|
112
|
-
// (Don't use \s, to avoid matching non-breaking space)
|
|
113
|
-
// eslint-disable-next-line no-control-regex
|
|
114
|
-
const LEADING_SPACES_REGEXP = /^[ \t\n\r\u000C]+/;
|
|
115
|
-
// eslint-disable-next-line no-control-regex
|
|
116
|
-
const LEADING_COMMAS_OR_SPACES_REGEXP = /^[, \t\n\r\u000C]+/;
|
|
117
|
-
// eslint-disable-next-line no-control-regex
|
|
118
|
-
const LEADING_NOT_SPACES = /^[^ \t\n\r\u000C]+/;
|
|
119
|
-
const TRAILING_COMMAS_REGEXP = /[,]+$/;
|
|
120
|
-
const NON_NEGATIVE_INTEGER_REGEXP = /^\d+$/;
|
|
121
|
-
// ( Positive or negative or unsigned integers or decimals, without or without exponents.
|
|
122
|
-
// Must include at least one digit.
|
|
123
|
-
// According to spec tests any decimal point must be followed by a digit.
|
|
124
|
-
// No leading plus sign is allowed.)
|
|
125
|
-
// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-floating-point-number
|
|
126
|
-
const FLOATING_POINT_REGEXP =
|
|
127
|
-
/^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
|
|
128
|
-
|
|
129
181
|
/**
|
|
182
|
+
* Extracts the `icon-uri` value of an `msapplication-task` meta content
|
|
183
|
+
* (`name=…;action-uri=…;icon-uri=…`) — the other parts are page URLs, not assets.
|
|
130
184
|
* @param {string} input input
|
|
131
|
-
* @returns {ParsedSource[]} parsed
|
|
185
|
+
* @returns {ParsedSource[]} parsed icon-uri
|
|
132
186
|
*/
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
/** @type {number} */
|
|
148
|
-
let position = 0;
|
|
149
|
-
/** @type {number} */
|
|
150
|
-
let start;
|
|
151
|
-
|
|
152
|
-
/** @type {[string, number, number][]} */
|
|
153
|
-
const candidates = [];
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* @param {RegExp} regExp reg exp to collect characters
|
|
157
|
-
* @returns {string | undefined} characters
|
|
158
|
-
*/
|
|
159
|
-
function collectCharacters(regExp) {
|
|
160
|
-
/** @type {string} */
|
|
161
|
-
let chars;
|
|
162
|
-
const match = regExp.exec(input.slice(Math.max(0, position)));
|
|
163
|
-
|
|
164
|
-
if (match) {
|
|
165
|
-
[chars] = match;
|
|
166
|
-
position += chars.length;
|
|
167
|
-
|
|
168
|
-
return chars;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* @returns {void}
|
|
174
|
-
*/
|
|
175
|
-
function parseDescriptors() {
|
|
176
|
-
// 9. Descriptor parser: Let error be no.
|
|
177
|
-
let pError = false;
|
|
178
|
-
|
|
179
|
-
// 10. Let width be absent.
|
|
180
|
-
// 11. Let density be absent.
|
|
181
|
-
// 12. Let future-compat-h be absent. (We're implementing it now as h)
|
|
182
|
-
/** @type {number | undefined} */
|
|
183
|
-
let width;
|
|
184
|
-
/** @type {number | undefined} */
|
|
185
|
-
let density;
|
|
186
|
-
/** @type {number | undefined} */
|
|
187
|
-
let height;
|
|
188
|
-
/** @type {string | undefined} */
|
|
189
|
-
let desc;
|
|
190
|
-
|
|
191
|
-
// 13. For each descriptor in descriptors, run the appropriate set of steps
|
|
192
|
-
// from the following list:
|
|
193
|
-
for (let i = 0; i < descriptors.length; i++) {
|
|
194
|
-
desc = descriptors[i];
|
|
195
|
-
|
|
196
|
-
const lastChar = desc[desc.length - 1].charCodeAt(0);
|
|
197
|
-
const value = desc.slice(0, Math.max(0, desc.length - 1));
|
|
198
|
-
|
|
199
|
-
// If the descriptor consists of a valid non-negative integer followed by
|
|
200
|
-
// a U+0077 LATIN SMALL LETTER W character
|
|
201
|
-
if (
|
|
202
|
-
NON_NEGATIVE_INTEGER_REGEXP.test(value) &&
|
|
203
|
-
lastChar === SMALL_LETTER_W
|
|
204
|
-
) {
|
|
205
|
-
// If width and density are not both absent, then let error be yes.
|
|
206
|
-
if (width || density) {
|
|
207
|
-
pError = true;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const intVal = Number.parseInt(value, 10);
|
|
211
|
-
|
|
212
|
-
// Apply the rules for parsing non-negative integers to the descriptor.
|
|
213
|
-
// If the result is zero, let error be yes.
|
|
214
|
-
// Otherwise, let width be the result.
|
|
215
|
-
if (intVal === 0) {
|
|
216
|
-
pError = true;
|
|
217
|
-
} else {
|
|
218
|
-
width = intVal;
|
|
187
|
+
const parseMsapplicationTask = (input) => {
|
|
188
|
+
const len = input.length;
|
|
189
|
+
let pos = 0;
|
|
190
|
+
while (pos < len) {
|
|
191
|
+
let sep = input.indexOf(";", pos);
|
|
192
|
+
if (sep === -1) sep = len;
|
|
193
|
+
const eq = input.indexOf("=", pos);
|
|
194
|
+
if (eq !== -1 && eq < sep) {
|
|
195
|
+
const key = input.slice(pos, eq).trim().toLowerCase();
|
|
196
|
+
if (key === "icon-uri") {
|
|
197
|
+
let start = eq + 1;
|
|
198
|
+
let end = sep;
|
|
199
|
+
while (start < end && isASCIIWhitespace(input.charCodeAt(start))) {
|
|
200
|
+
start++;
|
|
219
201
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// a U+0078 LATIN SMALL LETTER X character
|
|
223
|
-
else if (
|
|
224
|
-
FLOATING_POINT_REGEXP.test(value) &&
|
|
225
|
-
lastChar === SMALL_LETTER_X
|
|
226
|
-
) {
|
|
227
|
-
// If width, density and future-compat-h are not all absent, then let error
|
|
228
|
-
// be yes.
|
|
229
|
-
if (width || density || height) {
|
|
230
|
-
pError = true;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const floatVal = Number.parseFloat(value);
|
|
234
|
-
|
|
235
|
-
// Apply the rules for parsing floating-point number values to the descriptor.
|
|
236
|
-
// If the result is less than zero, let error be yes. Otherwise, let density
|
|
237
|
-
// be the result.
|
|
238
|
-
if (floatVal < 0) {
|
|
239
|
-
pError = true;
|
|
240
|
-
} else {
|
|
241
|
-
density = floatVal;
|
|
202
|
+
while (end > start && isASCIIWhitespace(input.charCodeAt(end - 1))) {
|
|
203
|
+
end--;
|
|
242
204
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// a U+0068 LATIN SMALL LETTER H character
|
|
246
|
-
else if (
|
|
247
|
-
NON_NEGATIVE_INTEGER_REGEXP.test(value) &&
|
|
248
|
-
lastChar === SMALL_LETTER_H
|
|
249
|
-
) {
|
|
250
|
-
// If height and density are not both absent, then let error be yes.
|
|
251
|
-
if (height || density) {
|
|
252
|
-
pError = true;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const intVal = Number.parseInt(value, 10);
|
|
256
|
-
|
|
257
|
-
// Apply the rules for parsing non-negative integers to the descriptor.
|
|
258
|
-
// If the result is zero, let error be yes. Otherwise, let future-compat-h
|
|
259
|
-
// be the result.
|
|
260
|
-
if (intVal === 0) {
|
|
261
|
-
pError = true;
|
|
262
|
-
} else {
|
|
263
|
-
height = intVal;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Anything else, Let error be yes.
|
|
267
|
-
} else {
|
|
268
|
-
pError = true;
|
|
205
|
+
if (start === end) return [];
|
|
206
|
+
return [[input.slice(start, end), start, end]];
|
|
269
207
|
}
|
|
270
208
|
}
|
|
271
|
-
|
|
272
|
-
// 15. If error is still no, then append a new image source to candidates whose
|
|
273
|
-
// URL is url, associated with a width width if not absent and a pixel
|
|
274
|
-
// density density if not absent. Otherwise, there is a parse error.
|
|
275
|
-
if (!pError) {
|
|
276
|
-
candidates.push([
|
|
277
|
-
/** @type {string} */ (url),
|
|
278
|
-
start,
|
|
279
|
-
start + /** @type {string} */ (url).length
|
|
280
|
-
]);
|
|
281
|
-
} else {
|
|
282
|
-
throw new Error(
|
|
283
|
-
`Invalid srcset descriptor found in '${input}' at '${desc}'`
|
|
284
|
-
);
|
|
285
|
-
}
|
|
209
|
+
pos = sep + 1;
|
|
286
210
|
}
|
|
211
|
+
return [];
|
|
212
|
+
};
|
|
287
213
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// Do the following, depending on the value of charCode:
|
|
312
|
-
|
|
313
|
-
// Space character
|
|
314
|
-
// If current descriptor is not empty, append current descriptor to
|
|
315
|
-
// descriptors and let current descriptor be the empty string.
|
|
316
|
-
// Set state to after descriptor.
|
|
317
|
-
if (isASCIIWhitespace(charCode)) {
|
|
318
|
-
if (currentDescriptor) {
|
|
319
|
-
descriptors.push(currentDescriptor);
|
|
320
|
-
currentDescriptor = "";
|
|
321
|
-
state = "after descriptor";
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
// U+002C COMMA (,)
|
|
325
|
-
// Advance position to the next character in input. If current descriptor
|
|
326
|
-
// is not empty, append current descriptor to descriptors. Jump to the step
|
|
327
|
-
// labeled descriptor parser.
|
|
328
|
-
else if (charCode === COMMA) {
|
|
329
|
-
position += 1;
|
|
330
|
-
|
|
331
|
-
if (currentDescriptor) {
|
|
332
|
-
descriptors.push(currentDescriptor);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
parseDescriptors();
|
|
336
|
-
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
// U+0028 LEFT PARENTHESIS (()
|
|
340
|
-
// Append charCode to current descriptor. Set state to in parens.
|
|
341
|
-
else if (charCode === LEFT_PARENTHESIS) {
|
|
342
|
-
currentDescriptor += input.charAt(position);
|
|
343
|
-
state = "in parens";
|
|
344
|
-
}
|
|
345
|
-
// EOF
|
|
346
|
-
// If current descriptor is not empty, append current descriptor to
|
|
347
|
-
// descriptors. Jump to the step labeled descriptor parser.
|
|
348
|
-
else if (Number.isNaN(charCode)) {
|
|
349
|
-
if (currentDescriptor) {
|
|
350
|
-
descriptors.push(currentDescriptor);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
parseDescriptors();
|
|
354
|
-
|
|
355
|
-
return;
|
|
356
|
-
|
|
357
|
-
// Anything else
|
|
358
|
-
// Append charCode to current descriptor.
|
|
359
|
-
} else {
|
|
360
|
-
currentDescriptor += input.charAt(position);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
// In parens
|
|
364
|
-
else if (state === "in parens") {
|
|
365
|
-
// U+0029 RIGHT PARENTHESIS ())
|
|
366
|
-
// Append charCode to current descriptor. Set state to in descriptor.
|
|
367
|
-
if (charCode === RIGHT_PARENTHESIS) {
|
|
368
|
-
currentDescriptor += input.charAt(position);
|
|
369
|
-
state = "in descriptor";
|
|
370
|
-
}
|
|
371
|
-
// EOF
|
|
372
|
-
// Append current descriptor to descriptors. Jump to the step labeled
|
|
373
|
-
// descriptor parser.
|
|
374
|
-
else if (Number.isNaN(charCode)) {
|
|
375
|
-
descriptors.push(currentDescriptor);
|
|
376
|
-
parseDescriptors();
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
// Anything else
|
|
380
|
-
// Append charCode to current descriptor.
|
|
381
|
-
else {
|
|
382
|
-
currentDescriptor += input.charAt(position);
|
|
383
|
-
}
|
|
214
|
+
/**
|
|
215
|
+
* Extracts `url(...)` references from a CSS value (an SVG presentation
|
|
216
|
+
* attribute). Reuses webpack's CSS lexer so quoting/escaping match the CSS
|
|
217
|
+
* spec; returns the `parseSrc` shape so the shared emit path maps and rewrites
|
|
218
|
+
* the spans. Unquoted `url(path)` rewrites the content span; quoted
|
|
219
|
+
* `url("path")` rewrites the inner string span (quotes preserved).
|
|
220
|
+
* @param {string} input attribute value (a CSS component-value list)
|
|
221
|
+
* @returns {ParsedSource[]} url references
|
|
222
|
+
*/
|
|
223
|
+
const parseCssUrls = (input) => {
|
|
224
|
+
const ts = new TokenStream(input);
|
|
225
|
+
/** @type {ParsedSource[]} */
|
|
226
|
+
const result = [];
|
|
227
|
+
for (;;) {
|
|
228
|
+
const t = ts.consume();
|
|
229
|
+
if (t.type === TT_EOF) break;
|
|
230
|
+
if (t.type === TT_URL) {
|
|
231
|
+
if (t.contentEnd > t.contentStart) {
|
|
232
|
+
result.push([
|
|
233
|
+
input.slice(t.contentStart, t.contentEnd),
|
|
234
|
+
t.contentStart,
|
|
235
|
+
t.contentEnd
|
|
236
|
+
]);
|
|
384
237
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
238
|
+
} else if (
|
|
239
|
+
t.type === TT_FUNCTION &&
|
|
240
|
+
equalsLowerCase(input.slice(t.start, t.end - 1), "url")
|
|
241
|
+
) {
|
|
242
|
+
let s = ts.consume();
|
|
243
|
+
while (s.type === TT_WHITESPACE) s = ts.consume();
|
|
244
|
+
if (s.type === TT_STRING) {
|
|
245
|
+
const quote = input.charCodeAt(s.start);
|
|
246
|
+
const innerStart = s.start + 1;
|
|
247
|
+
// Drop the closing quote, unless the string is unterminated at EOF.
|
|
248
|
+
const innerEnd =
|
|
249
|
+
input.charCodeAt(s.end - 1) === quote ? s.end - 1 : s.end;
|
|
250
|
+
if (innerEnd > innerStart) {
|
|
251
|
+
result.push([
|
|
252
|
+
input.slice(innerStart, innerEnd),
|
|
253
|
+
innerStart,
|
|
254
|
+
innerEnd
|
|
255
|
+
]);
|
|
401
256
|
}
|
|
402
257
|
}
|
|
403
|
-
|
|
404
|
-
// Advance position to the next character in input.
|
|
405
|
-
position += 1;
|
|
406
258
|
}
|
|
407
259
|
}
|
|
408
|
-
|
|
409
|
-
// 3. Let candidates be an initially empty source set.
|
|
410
|
-
// const candidates = []; // Moved to top
|
|
411
|
-
|
|
412
|
-
// 4. Splitting loop: Collect a sequence of characters that are space
|
|
413
|
-
// characters or U+002C COMMA characters. If any U+002C COMMA characters
|
|
414
|
-
// were collected, that is a parse error.
|
|
415
|
-
|
|
416
|
-
while (true) {
|
|
417
|
-
collectCharacters(LEADING_COMMAS_OR_SPACES_REGEXP);
|
|
418
|
-
|
|
419
|
-
// 5. If position is past the end of input, return candidates and abort these steps.
|
|
420
|
-
if (position >= inputLength) {
|
|
421
|
-
if (candidates.length === 0) {
|
|
422
|
-
throw new Error("Must contain one or more image candidate strings");
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// (we're done, this is the sole return path)
|
|
426
|
-
return candidates;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// 6. Collect a sequence of characters that are not space characters,
|
|
430
|
-
// and let that be url.
|
|
431
|
-
start = position;
|
|
432
|
-
url = collectCharacters(LEADING_NOT_SPACES);
|
|
433
|
-
|
|
434
|
-
// 7. Let descriptors be a new empty list.
|
|
435
|
-
descriptors = [];
|
|
436
|
-
|
|
437
|
-
// 8. If url ends with a U+002C COMMA character (,), follow these sub steps:
|
|
438
|
-
// (1). Remove all trailing U+002C COMMA characters from url. If this removed
|
|
439
|
-
// more than one character, that is a parse error.
|
|
440
|
-
if (url && url.charCodeAt(url.length - 1) === COMMA) {
|
|
441
|
-
url = url.replace(TRAILING_COMMAS_REGEXP, "");
|
|
442
|
-
|
|
443
|
-
// (Jump ahead to step 9 to skip tokenization and just push the candidate).
|
|
444
|
-
parseDescriptors();
|
|
445
|
-
}
|
|
446
|
-
// Otherwise, follow these sub steps:
|
|
447
|
-
else {
|
|
448
|
-
tokenize();
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// 16. Return to the step labeled splitting loop.
|
|
452
|
-
}
|
|
260
|
+
return result;
|
|
453
261
|
};
|
|
454
262
|
|
|
455
263
|
/**
|
|
@@ -471,9 +279,12 @@ const META = new Map([
|
|
|
471
279
|
"msapplication-wide310x150logo",
|
|
472
280
|
"msapplication-square310x310logo",
|
|
473
281
|
"msapplication-config",
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
"twitter:image"
|
|
282
|
+
// Only the `icon-uri` part is an asset, see `parseMsapplicationTask`
|
|
283
|
+
"msapplication-task",
|
|
284
|
+
"twitter:image",
|
|
285
|
+
"twitter:image:src",
|
|
286
|
+
// Legacy preview-image hint
|
|
287
|
+
"thumbnail"
|
|
477
288
|
])
|
|
478
289
|
],
|
|
479
290
|
[
|
|
@@ -511,14 +322,33 @@ const META = new Map([
|
|
|
511
322
|
* @returns {boolean} true when need to parse, otherwise false
|
|
512
323
|
*/
|
|
513
324
|
const filterLinkItemprop = (attributes) => {
|
|
514
|
-
const
|
|
515
|
-
if (!
|
|
325
|
+
const itemprop = getAttributeValue(attributes, "itemprop");
|
|
326
|
+
if (!itemprop) return false;
|
|
516
327
|
const allowedAttributes = META.get("itemprop");
|
|
517
328
|
if (!allowedAttributes) return false;
|
|
518
329
|
|
|
519
|
-
return allowedAttributes.has(
|
|
330
|
+
return allowedAttributes.has(itemprop.trim().toLowerCase());
|
|
520
331
|
};
|
|
521
332
|
|
|
333
|
+
// `<link rel>` values whose `href`/`imagesrcset` webpack treats as a reference.
|
|
334
|
+
const ALLOWED_LINK_RELS = new Set([
|
|
335
|
+
"stylesheet",
|
|
336
|
+
"icon",
|
|
337
|
+
"mask-icon",
|
|
338
|
+
"apple-touch-icon",
|
|
339
|
+
"apple-touch-icon-precomposed",
|
|
340
|
+
"apple-touch-startup-image",
|
|
341
|
+
// TODO the manifest file is emitted as an asset, but its JSON
|
|
342
|
+
// `icons`/`screenshots`/`shortcuts[].icons` URLs aren't parsed — needs a
|
|
343
|
+
// dedicated webmanifest module type (parser + generator), not a table entry.
|
|
344
|
+
"manifest",
|
|
345
|
+
"prefetch",
|
|
346
|
+
"preload",
|
|
347
|
+
"modulepreload",
|
|
348
|
+
// Legacy preview-image hint (`<link rel="image_src" href>`)
|
|
349
|
+
"image_src"
|
|
350
|
+
]);
|
|
351
|
+
|
|
522
352
|
/**
|
|
523
353
|
* @param {Map<string, string>} attributes attributes
|
|
524
354
|
* @returns {boolean} true when need to parse, otherwise false
|
|
@@ -526,21 +356,11 @@ const filterLinkItemprop = (attributes) => {
|
|
|
526
356
|
const filterLinkHref = (attributes) => {
|
|
527
357
|
const rel = getAttributeValue(attributes, "rel");
|
|
528
358
|
if (!rel) return false;
|
|
529
|
-
const usedRels = rel.trim().toLowerCase().split(
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
"apple-touch-icon",
|
|
535
|
-
"apple-touch-icon-precomposed",
|
|
536
|
-
"apple-touch-startup-image",
|
|
537
|
-
"manifest",
|
|
538
|
-
"prefetch",
|
|
539
|
-
"preload",
|
|
540
|
-
"modulepreload"
|
|
541
|
-
];
|
|
542
|
-
|
|
543
|
-
return allowedRels.some((value) => usedRels.includes(value));
|
|
359
|
+
const usedRels = rel.trim().toLowerCase().split(/\s+/);
|
|
360
|
+
for (let i = 0; i < usedRels.length; i++) {
|
|
361
|
+
if (ALLOWED_LINK_RELS.has(usedRels[i])) return true;
|
|
362
|
+
}
|
|
363
|
+
return false;
|
|
544
364
|
};
|
|
545
365
|
|
|
546
366
|
/**
|
|
@@ -559,13 +379,25 @@ const filterMetaContent = (attributes) => {
|
|
|
559
379
|
const [key, allowedNames] = item;
|
|
560
380
|
const name = getAttributeValue(attributes, key);
|
|
561
381
|
if (!name) continue;
|
|
562
|
-
|
|
563
|
-
|
|
382
|
+
// Check every present attribute, not only the first one
|
|
383
|
+
if (allowedNames.has(name.trim().toLowerCase())) return true;
|
|
564
384
|
}
|
|
565
385
|
|
|
566
386
|
return false;
|
|
567
387
|
};
|
|
568
388
|
|
|
389
|
+
/**
|
|
390
|
+
* `<param value>` (obsolete `<object>`/`<applet>` child) is only a URL when
|
|
391
|
+
* `valuetype="ref"`; otherwise it's an opaque string.
|
|
392
|
+
* @param {Map<string, string>} attributes attributes
|
|
393
|
+
* @returns {boolean} true when `value` is a URL reference
|
|
394
|
+
*/
|
|
395
|
+
const filterParamRef = (attributes) => {
|
|
396
|
+
const valuetype = getAttributeValue(attributes, "valuetype");
|
|
397
|
+
if (!valuetype) return false;
|
|
398
|
+
return valuetype.trim().toLowerCase() === "ref";
|
|
399
|
+
};
|
|
400
|
+
|
|
569
401
|
/**
|
|
570
402
|
* @param {Map<string, string>} attributes attributes
|
|
571
403
|
* @returns {boolean} true when the script element opts into ES module semantics
|
|
@@ -619,216 +451,394 @@ const isLinkStylesheet = (attributes) => {
|
|
|
619
451
|
return rel.trim().toLowerCase().split(/\s+/).includes("stylesheet");
|
|
620
452
|
};
|
|
621
453
|
|
|
622
|
-
/** @
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
"
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
454
|
+
/** @typedef {"src" | "srcset" | "css-url" | "msapplication-task" | "script" | "script-module" | "modulepreload" | "stylesheet" | "stylesheet-style" | "stylesheet-style-attribute" | "srcdoc"} SourceType */
|
|
455
|
+
/** Entry types: a `type` whose value is loaded as its own compilation entry chunk. */
|
|
456
|
+
/** @typedef {"script" | "script-module" | "modulepreload" | "stylesheet"} EntrySourceType */
|
|
457
|
+
/** @typedef {SourceType | ((attrs: Map<string, string>, css: boolean) => SourceType)} SourceTypeOrResolver */
|
|
458
|
+
/** @typedef {(attributes: Map<string, string>, value: string) => boolean} SourceFilter */
|
|
459
|
+
/** @typedef {{ tag?: string, attribute: string, type: SourceType, filter?: SourceFilter }} SourceEntry */
|
|
460
|
+
/** A `type` plus optional gates: `filter` (the decoded attribute map + the decoded value; return false to skip — covers both cross-attribute checks and cheap value checks) and `namespace` (restrict to an element namespace, e.g. SVG). */
|
|
461
|
+
/** @typedef {{ type: SourceTypeOrResolver, filter?: SourceFilter, namespace?: number }} SourceItem */
|
|
462
|
+
/** A source whose value is the element's text content (a `<style>`/`<script>` body) rather than an attribute. */
|
|
463
|
+
/** @typedef {{ type: SourceTypeOrResolver, filter?: (attributes: Map<string, string>) => boolean }} ContentSourceItem */
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Builds a null-prototype dictionary from the given property bags
|
|
467
|
+
* (later bags win; `undefined` bags are skipped). A null prototype is
|
|
468
|
+
* essential here: the tables are indexed by HTML tag and attribute
|
|
469
|
+
* names, so a plain object would let names like `__proto__`,
|
|
470
|
+
* `constructor`, or `toString` resolve to inherited values at lookup
|
|
471
|
+
* time — producing bogus dependencies and letting `sources: false` be
|
|
472
|
+
* bypassed.
|
|
473
|
+
* @param {...(Record<string, EXPECTED_ANY> | undefined)} bags property bags
|
|
474
|
+
* @returns {EXPECTED_ANY} null-prototype dictionary
|
|
475
|
+
*/
|
|
476
|
+
const dict = (...bags) => Object.assign(Object.create(null), ...bags);
|
|
477
|
+
|
|
478
|
+
// Shared `SourceItem` singletons used by the built-in defaults —
|
|
479
|
+
// keeping one instance per kind stabilizes V8's hidden classes across
|
|
480
|
+
// lookups in the walk. `srcset` is its own type; the walk picks the
|
|
481
|
+
// `parseSrcset` parser for it and otherwise treats it like `src`.
|
|
482
|
+
/** @type {SourceItem} */
|
|
483
|
+
const PLAIN_SRC = { type: "src", filter: undefined };
|
|
484
|
+
/** @type {SourceItem} */
|
|
485
|
+
const PLAIN_SRCSET = { type: "srcset", filter: undefined };
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* `<link href>` is polymorphic: `rel="modulepreload"` → an ESM
|
|
489
|
+
* preload entry, `rel="stylesheet"` (with `experiments.css`) → a CSS
|
|
490
|
+
* entry, otherwise a plain asset URL.
|
|
491
|
+
* @type {SourceItem}
|
|
492
|
+
*/
|
|
493
|
+
const LINK_HREF = {
|
|
494
|
+
type: (attrs, css) => {
|
|
495
|
+
if (isLinkModulePreload(attrs)) return "modulepreload";
|
|
496
|
+
if (css && isLinkStylesheet(attrs)) return "stylesheet";
|
|
497
|
+
return "src";
|
|
498
|
+
},
|
|
499
|
+
filter: filterLinkUnion
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* `<script src>`: non-JS types (e.g. `application/ld+json`,
|
|
504
|
+
* `importmap`) stay plain asset URLs so the browser keeps seeing them
|
|
505
|
+
* as data blocks; `type="module"` opts into the ESM entry chunk;
|
|
506
|
+
* everything else in `JS_SCRIPT_TYPES` is a classic script.
|
|
507
|
+
* @type {SourceItem}
|
|
508
|
+
*/
|
|
509
|
+
const SCRIPT_SRC = {
|
|
510
|
+
type: (attrs) =>
|
|
511
|
+
isExecutableJsScript(attrs)
|
|
512
|
+
? isModuleScript(attrs)
|
|
513
|
+
? "script-module"
|
|
514
|
+
: "script"
|
|
515
|
+
: "src",
|
|
516
|
+
filter: undefined
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* `<meta content>`: most referenced names hold a single URL (`src`); the
|
|
521
|
+
* `msapplication-task` value is a `;`-delimited list whose `icon-uri` part
|
|
522
|
+
* is the only asset, so it selects the `msapplication-task` parser.
|
|
523
|
+
* @type {SourceItem}
|
|
524
|
+
*/
|
|
525
|
+
const META_CONTENT = {
|
|
526
|
+
type: (attrs) => {
|
|
527
|
+
const name = attrs.get("name");
|
|
528
|
+
return name !== undefined &&
|
|
529
|
+
name.trim().toLowerCase() === "msapplication-task"
|
|
530
|
+
? "msapplication-task"
|
|
531
|
+
: "src";
|
|
532
|
+
},
|
|
533
|
+
filter: filterMetaContent
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// Built-in lookup table, written directly in its final resolved shape:
|
|
537
|
+
// `DEFAULT_SOURCES_BY_TAG[tag][attribute] = item`. No module-load
|
|
538
|
+
// loop, no separate array representation — every parser created with
|
|
539
|
+
// the default `sources` config just references this table and pays
|
|
540
|
+
// zero per-parser work.
|
|
541
|
+
/** @type {Record<string, Record<string, SourceItem>>} */
|
|
542
|
+
const DEFAULT_SOURCES_BY_TAG = dict({
|
|
543
|
+
// Obsolete Java-applet element; `code`/`object` are single class/object URLs.
|
|
544
|
+
applet: dict({ code: PLAIN_SRC, object: PLAIN_SRC }),
|
|
545
|
+
audio: dict({ src: PLAIN_SRC }),
|
|
546
|
+
// Deprecated presentational `background` attribute — an image URL.
|
|
547
|
+
body: dict({ background: PLAIN_SRC }),
|
|
548
|
+
embed: dict({ src: PLAIN_SRC }),
|
|
549
|
+
// `srcdoc` is an entity-encoded HTML document parsed and rewritten as a
|
|
550
|
+
// nested module — its handling lives in the generic source loop so the
|
|
551
|
+
// `sources` option can re-target it (or other tags/attributes) freely.
|
|
552
|
+
iframe: dict({ srcdoc: { type: "srcdoc", filter: undefined } }),
|
|
553
|
+
img: dict({ src: PLAIN_SRC, srcset: PLAIN_SRCSET }),
|
|
554
|
+
input: dict({ src: PLAIN_SRC }),
|
|
555
|
+
link: dict({
|
|
556
|
+
href: LINK_HREF,
|
|
557
|
+
imagesrcset: { type: "srcset", filter: filterLinkHref }
|
|
558
|
+
}),
|
|
559
|
+
meta: dict({ content: META_CONTENT }),
|
|
560
|
+
// MathML `<mglyph src>` references an image.
|
|
561
|
+
mglyph: dict({ src: PLAIN_SRC }),
|
|
562
|
+
// `classid` is a single object URI (`codebase`/`archive` are a base/list, skipped).
|
|
563
|
+
object: dict({ data: PLAIN_SRC, classid: PLAIN_SRC }),
|
|
564
|
+
// Obsolete `<param valuetype="ref" value="url">` child of `<object>`.
|
|
565
|
+
param: dict({ value: { type: "src", filter: filterParamRef } }),
|
|
566
|
+
// `href`/`xlink:href` reference the source of SVG `<script>` elements
|
|
567
|
+
script: dict({
|
|
568
|
+
src: SCRIPT_SRC,
|
|
569
|
+
href: SCRIPT_SRC,
|
|
570
|
+
"xlink:href": SCRIPT_SRC
|
|
571
|
+
}),
|
|
572
|
+
source: dict({ src: PLAIN_SRC, srcset: PLAIN_SRCSET }),
|
|
573
|
+
// Deprecated presentational `background` attribute — an image URL.
|
|
574
|
+
table: dict({ background: PLAIN_SRC }),
|
|
575
|
+
td: dict({ background: PLAIN_SRC }),
|
|
576
|
+
th: dict({ background: PLAIN_SRC }),
|
|
577
|
+
track: dict({ src: PLAIN_SRC }),
|
|
578
|
+
video: dict({ poster: PLAIN_SRC, src: PLAIN_SRC }),
|
|
579
|
+
// SVG. Tag names match the tree builder's adjusted camelCase
|
|
580
|
+
// (`feImage`/`textPath`/`linearGradient`/`radialGradient`). `href`/
|
|
581
|
+
// `xlink:href` reference another element/resource; fragment-only `#id`
|
|
582
|
+
// values are left untouched, so only external `file.svg#id` is rewritten.
|
|
583
|
+
// `color-profile`'s `xlink:href` points at an external ICC profile file.
|
|
584
|
+
"color-profile": dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
585
|
+
feImage: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
586
|
+
filter: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
587
|
+
image: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
588
|
+
linearGradient: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
589
|
+
mpath: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
590
|
+
pattern: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
591
|
+
radialGradient: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
592
|
+
textPath: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC }),
|
|
593
|
+
use: dict({ "xlink:href": PLAIN_SRC, href: PLAIN_SRC })
|
|
594
|
+
// CSS `url(...)` references in SVG presentation attributes (fill, stroke,
|
|
595
|
+
// clip-path, …) are handled separately during the walk (`parseCssUrls`).
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// SVG presentation attributes (`fill`, `stroke`, …) carry CSS `url(...)`
|
|
599
|
+
// FuncIRIs; `parseCssUrls` extracts external refs as assets (internal
|
|
600
|
+
// `url(#id)` is left untouched). They apply to every SVG-namespace element, so
|
|
601
|
+
// the item is `namespace`-gated rather than placed in a tag bucket. The filter
|
|
602
|
+
// is a cheap pre-check skipping the common plain values (`red`, `none`, `#fff`).
|
|
603
|
+
/** @type {SourceItem} */
|
|
604
|
+
const SVG_CSS_URL = {
|
|
605
|
+
type: "css-url",
|
|
606
|
+
filter: (attributes, value) => FUNC_IRI_URL_REGEXP.test(value),
|
|
607
|
+
namespace: NS_SVG
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// The global `style=""` attribute (a CSS declaration list), available on every
|
|
611
|
+
// tag and even with `sources: false`. Like every `stylesheet-style*` source it
|
|
612
|
+
// only emits when `experiments.css` is on (checked where the dependency is
|
|
613
|
+
// created); the filter is a cheap pre-check skipping declarations without a
|
|
614
|
+
// `url()`/`src()`/… .
|
|
615
|
+
/** @type {SourceItem} */
|
|
616
|
+
const STYLE_ATTRIBUTE = {
|
|
617
|
+
type: "stylesheet-style-attribute",
|
|
618
|
+
filter: (attributes, value) => STYLE_ATTR_URL_REGEXP.test(value)
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
// Any-tag sources — applied to every element as a fallback and folded into
|
|
622
|
+
// each tag bucket, so the walk resolves one object per element and reads one
|
|
623
|
+
// property per attribute. `DEFAULT` (svg presentation + style) is active
|
|
624
|
+
// unless `sources: false`; only the always-on style attribute survives `false`.
|
|
625
|
+
/** @type {Record<string, SourceItem>} */
|
|
626
|
+
const DEFAULT_ANY_SOURCES = dict({ style: STYLE_ATTRIBUTE });
|
|
627
|
+
for (const name of [
|
|
628
|
+
"fill",
|
|
629
|
+
"stroke",
|
|
630
|
+
"clip-path",
|
|
631
|
+
"mask",
|
|
632
|
+
"filter",
|
|
633
|
+
"marker",
|
|
634
|
+
"marker-start",
|
|
635
|
+
"marker-mid",
|
|
636
|
+
"marker-end",
|
|
637
|
+
"cursor"
|
|
638
|
+
]) {
|
|
639
|
+
DEFAULT_ANY_SOURCES[name] = SVG_CSS_URL;
|
|
640
|
+
}
|
|
641
|
+
/** @type {Record<string, SourceItem>} */
|
|
642
|
+
const ALWAYS_ANY_SOURCES = dict({ style: STYLE_ATTRIBUTE });
|
|
643
|
+
|
|
644
|
+
// Reserved key holding the any-tag sources inside the per-tag table — the
|
|
645
|
+
// schema forbids an empty `tag`, so it never collides with a real tag. The
|
|
646
|
+
// walk falls back to it for elements without their own bucket.
|
|
647
|
+
const ANY_TAG = "";
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Folds the any-tag sources into every per-tag bucket (tag-specific entries
|
|
651
|
+
* win) and stores them under `ANY_TAG`, so the whole source model is one
|
|
652
|
+
* object and the walk's per-attribute lookup is a single property read.
|
|
653
|
+
* @param {Record<string, Record<string, SourceItem>>} byTag per-tag sources
|
|
654
|
+
* @param {Record<string, SourceItem>} any any-tag sources
|
|
655
|
+
* @returns {Record<string, Record<string, SourceItem>>} folded table
|
|
656
|
+
*/
|
|
657
|
+
const foldAnySources = (byTag, any) => {
|
|
658
|
+
for (const tag of Object.keys(byTag)) {
|
|
659
|
+
byTag[tag] = dict(any, byTag[tag]);
|
|
660
|
+
}
|
|
661
|
+
byTag[ANY_TAG] = any;
|
|
662
|
+
return byTag;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
// Default table (per-tag defaults + any-tag sources folded in) and the
|
|
666
|
+
// `sources: false` table (only the always-on `style=""` attribute). Both are
|
|
667
|
+
// precomputed so the common cases reference them with zero per-parser work.
|
|
668
|
+
/** @type {Record<string, Record<string, SourceItem>>} */
|
|
669
|
+
const DEFAULT_SOURCES_FOLDED = foldAnySources(
|
|
670
|
+
dict(DEFAULT_SOURCES_BY_TAG),
|
|
671
|
+
DEFAULT_ANY_SOURCES
|
|
672
|
+
);
|
|
673
|
+
/** @type {Record<string, Record<string, SourceItem>>} */
|
|
674
|
+
const DISABLED_SOURCES_FOLDED = foldAnySources(dict(), ALWAYS_ANY_SOURCES);
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Inline `<script>` body: classic JS unless `type="module"` opts into ESM.
|
|
678
|
+
* @param {Map<string, string>} attrs attributes
|
|
679
|
+
* @returns {SourceType} the entry type for the inline script
|
|
680
|
+
*/
|
|
681
|
+
const scriptContentType = (attrs) =>
|
|
682
|
+
isModuleScript(attrs) ? "script-module" : "script";
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Inline `<script>` body: only an executable JS block with no external source
|
|
686
|
+
* carries content webpack should bundle.
|
|
687
|
+
* @param {Map<string, string>} attrs attributes
|
|
688
|
+
* @returns {boolean} true when the inline body should be bundled
|
|
689
|
+
*/
|
|
690
|
+
const scriptContentFilter = (attrs) =>
|
|
691
|
+
!attrs.has("src") &&
|
|
692
|
+
!attrs.has("href") &&
|
|
693
|
+
!attrs.has("xlink:href") &&
|
|
694
|
+
isExecutableJsScript(attrs);
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* `<style>` body is a stylesheet only when its `type` is empty or `text/css`.
|
|
698
|
+
* @param {Map<string, string>} attrs attributes
|
|
699
|
+
* @returns {boolean} true when the body is CSS
|
|
700
|
+
*/
|
|
701
|
+
const styleContentFilter = (attrs) => {
|
|
702
|
+
const type = attrs.get("type");
|
|
703
|
+
if (type === undefined) return true;
|
|
704
|
+
const t = type.trim().toLowerCase();
|
|
705
|
+
return t === "" || t === "text/css";
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
// Element-body sources, independent of the `sources` option (even `sources:
|
|
709
|
+
// false`): inline `<script>` bodies are always processed; the `<style>` body
|
|
710
|
+
// only emits when `experiments.css` is on (checked where the dependency is
|
|
711
|
+
// created, like every `stylesheet-style*` source).
|
|
712
|
+
/** @type {Record<string, ContentSourceItem>} */
|
|
713
|
+
const CONTENT_SOURCES = dict({
|
|
714
|
+
script: { type: scriptContentType, filter: scriptContentFilter },
|
|
715
|
+
style: { type: "stylesheet-style", filter: styleContentFilter }
|
|
716
|
+
});
|
|
816
717
|
|
|
817
718
|
class HtmlParser extends Parser {
|
|
818
719
|
/**
|
|
819
720
|
* Creates an instance of HtmlParser.
|
|
820
|
-
* @param {
|
|
821
|
-
* @param {string=} context compilation context used to contextify the HTML module's identifier when seeding the entry-name hash
|
|
822
|
-
* @param {boolean=} outputModule whether `output.module` is enabled; when true, classic `<script src>` tags get `type="module"` auto-inserted so the rewritten src can load the emitted ES module chunk
|
|
823
|
-
* @param {boolean=} css whether `experiments.css` is enabled; when true, inline `<style>` bodies are routed through the CSS pipeline as `data:text/css` modules
|
|
721
|
+
* @param {HtmlParserOptions} options parser options (from `module.parser.html`; always passed by the `createParser` hook)
|
|
824
722
|
*/
|
|
825
|
-
constructor(
|
|
723
|
+
constructor(options) {
|
|
826
724
|
super();
|
|
827
725
|
this.magicCommentContext = createMagicCommentContext();
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
this.
|
|
726
|
+
// Read by HtmlModulesPlugin's `processResult` hook, which transforms
|
|
727
|
+
// the module source before it is stored and parsed.
|
|
728
|
+
/** @type {HtmlTemplateFunction | undefined} */
|
|
729
|
+
this.template = options.template;
|
|
730
|
+
// One source model: a per-tag table that also holds the any-tag sources
|
|
731
|
+
// under `ANY_TAG`. The common cases reference precomputed tables.
|
|
732
|
+
/** @type {Record<string, Record<string, SourceItem>>} */
|
|
733
|
+
this.sourcesByTag = DEFAULT_SOURCES_FOLDED;
|
|
734
|
+
|
|
735
|
+
const sources = options.sources;
|
|
736
|
+
if (sources === undefined || sources === true) return;
|
|
737
|
+
if (sources === false) {
|
|
738
|
+
// Only the always-on `style=""` attribute survives; nothing else is
|
|
739
|
+
// extracted (svg presentation, `<script src>`, `<link>` entries, …).
|
|
740
|
+
this.sourcesByTag = DISABLED_SOURCES_FOLDED;
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// User array — `"..."` anywhere opts the per-tag defaults in as the
|
|
745
|
+
// base; the built-in any-tag sources (svg presentation + style) are
|
|
746
|
+
// always present. A user entry with no `tag` is an any-tag source (e.g.
|
|
747
|
+
// `{ attribute: "data-style", type: "stylesheet-style-attribute" }`).
|
|
748
|
+
// User entries override regardless of position. `dict()` keeps every
|
|
749
|
+
// table null-prototype (see its doc), and per-tag writes rebuild the
|
|
750
|
+
// bucket so the aliased default buckets stay intact.
|
|
751
|
+
/** @type {Record<string, Record<string, SourceItem>>} */
|
|
752
|
+
const byTag = sources.includes("...")
|
|
753
|
+
? dict(DEFAULT_SOURCES_BY_TAG)
|
|
754
|
+
: dict();
|
|
755
|
+
/** @type {Record<string, SourceItem>} */
|
|
756
|
+
const any = dict(DEFAULT_ANY_SOURCES);
|
|
757
|
+
for (const entry of sources) {
|
|
758
|
+
if (entry === "...") continue;
|
|
759
|
+
/** @type {SourceItem} */
|
|
760
|
+
const item = {
|
|
761
|
+
type: entry.type,
|
|
762
|
+
filter: typeof entry.filter === "function" ? entry.filter : undefined
|
|
763
|
+
};
|
|
764
|
+
const attr = entry.attribute.toLowerCase();
|
|
765
|
+
if (entry.tag === undefined) {
|
|
766
|
+
any[attr] = item;
|
|
767
|
+
} else {
|
|
768
|
+
const tag = entry.tag.toLowerCase();
|
|
769
|
+
byTag[tag] = dict(byTag[tag], { [attr]: item });
|
|
770
|
+
// The AST carries adjusted camelCase names for foreign-content
|
|
771
|
+
// tags (e.g. `feImage`) — register the entry under both.
|
|
772
|
+
const adjusted = SVG_TAG_ADJUST[tag];
|
|
773
|
+
if (adjusted !== undefined) {
|
|
774
|
+
byTag[adjusted] = dict(byTag[adjusted], { [attr]: item });
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/** @type {Record<string, Record<string, SourceItem>>} */
|
|
779
|
+
this.sourcesByTag = foldAnySources(byTag, any);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Runs the `template` option over the source and returns the transformed
|
|
784
|
+
* html. Called from HtmlModulesPlugin's `processResult`, where the return
|
|
785
|
+
* value becomes the module's stored source so the parser (which records
|
|
786
|
+
* dependency offsets against it) and the generator (which renders from
|
|
787
|
+
* `module.originalSource()`) stay in agreement.
|
|
788
|
+
* @param {string | Buffer} source the original source
|
|
789
|
+
* @param {NormalModule} module the html module
|
|
790
|
+
* @returns {string | Buffer} the transformed source
|
|
791
|
+
*/
|
|
792
|
+
applyTemplate(source, module) {
|
|
793
|
+
if (!this.template) return source;
|
|
794
|
+
// `processResult` runs after `_doBuild` has initialized these
|
|
795
|
+
// dependency sets, so they are always present here.
|
|
796
|
+
const buildInfo = /** @type {BuildInfo} */ (module.buildInfo);
|
|
797
|
+
const fileDependencies = /** @type {FileSystemDependencies} */ (
|
|
798
|
+
buildInfo.fileDependencies
|
|
799
|
+
);
|
|
800
|
+
const contextDependencies = /** @type {FileSystemDependencies} */ (
|
|
801
|
+
buildInfo.contextDependencies
|
|
802
|
+
);
|
|
803
|
+
const missingDependencies = /** @type {FileSystemDependencies} */ (
|
|
804
|
+
buildInfo.missingDependencies
|
|
805
|
+
);
|
|
806
|
+
const transformed = this.template(
|
|
807
|
+
typeof source === "string" ? source : source.toString("utf8"),
|
|
808
|
+
{
|
|
809
|
+
module,
|
|
810
|
+
resource: module.resource,
|
|
811
|
+
addDependency: (dependency) => {
|
|
812
|
+
fileDependencies.add(dependency);
|
|
813
|
+
},
|
|
814
|
+
addContextDependency: (dependency) => {
|
|
815
|
+
contextDependencies.add(dependency);
|
|
816
|
+
},
|
|
817
|
+
addMissingDependency: (dependency) => {
|
|
818
|
+
missingDependencies.add(dependency);
|
|
819
|
+
},
|
|
820
|
+
addBuildDependency: (dependency) => {
|
|
821
|
+
if (buildInfo.buildDependencies === undefined) {
|
|
822
|
+
buildInfo.buildDependencies = new LazySet();
|
|
823
|
+
}
|
|
824
|
+
buildInfo.buildDependencies.add(dependency);
|
|
825
|
+
},
|
|
826
|
+
emitWarning: (warning) =>
|
|
827
|
+
module.addWarning(
|
|
828
|
+
warning instanceof Error ? warning : new WebpackError(warning)
|
|
829
|
+
),
|
|
830
|
+
emitError: (error) =>
|
|
831
|
+
module.addError(
|
|
832
|
+
error instanceof Error ? error : new WebpackError(error)
|
|
833
|
+
)
|
|
834
|
+
}
|
|
835
|
+
);
|
|
836
|
+
if (typeof transformed !== "string") {
|
|
837
|
+
throw new Error(
|
|
838
|
+
"The `template` html parser option must return a string."
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
return transformed;
|
|
832
842
|
}
|
|
833
843
|
|
|
834
844
|
/**
|
|
@@ -850,6 +860,10 @@ class HtmlParser extends Parser {
|
|
|
850
860
|
const locConverter = new LocConverter(source);
|
|
851
861
|
|
|
852
862
|
const module = state.module;
|
|
863
|
+
const compilation = state.compilation;
|
|
864
|
+
const { hashFunction, module: outputModule } = compilation.outputOptions;
|
|
865
|
+
const context = compilation.compiler.context;
|
|
866
|
+
const css = Boolean(compilation.options.experiments.css);
|
|
853
867
|
|
|
854
868
|
// Stable, per-HTML-module prefix used when generating entry names for
|
|
855
869
|
// script src / modulepreload references so they don't collide across
|
|
@@ -862,28 +876,57 @@ class HtmlParser extends Parser {
|
|
|
862
876
|
/** @type {string} */
|
|
863
877
|
const resource =
|
|
864
878
|
/** @type {EXPECTED_ANY} */ (module).resource || module.identifier();
|
|
865
|
-
const moduleHash = createHash(
|
|
866
|
-
.update(
|
|
879
|
+
const moduleHash = createHash(hashFunction || "md4")
|
|
880
|
+
.update(context ? contextify(context, resource) : resource)
|
|
867
881
|
.digest("hex")
|
|
868
882
|
.slice(0, 8);
|
|
869
883
|
|
|
870
|
-
|
|
884
|
+
// Script src / modulepreload references are collected per-type
|
|
885
|
+
// during the walk; HtmlModulesPlugin later turns them into real
|
|
886
|
+
// entries. `script` and `script-module` entries are chained via a
|
|
887
|
+
// leader-only dependOn so they share a runtime.
|
|
888
|
+
// `<link rel="modulepreload">` entries are kept independent — they
|
|
889
|
+
// must preload without running, so they can never become a runtime
|
|
890
|
+
// leader that other entries would import.
|
|
891
|
+
/**
|
|
892
|
+
* @typedef {object} EntryScriptInfo
|
|
893
|
+
* @property {string} request
|
|
894
|
+
* @property {string} entryName
|
|
895
|
+
* @property {"script" | "script-module" | "modulepreload" | "stylesheet"} type
|
|
896
|
+
*/
|
|
897
|
+
/** @type {EntryScriptInfo[]} */
|
|
898
|
+
const scriptEntries = [];
|
|
899
|
+
/** @type {EntryScriptInfo[]} */
|
|
900
|
+
const scriptModuleEntries = [];
|
|
901
|
+
/** @type {EntryScriptInfo[]} */
|
|
902
|
+
const modulePreloadEntries = [];
|
|
903
|
+
/** @type {EntryScriptInfo[]} */
|
|
904
|
+
const stylesheetEntries = [];
|
|
905
|
+
|
|
906
|
+
// Offset of the first script tag; anchors injected stylesheet `<link>`s
|
|
907
|
+
// before it so a later entry's CSS still loads ahead of every script.
|
|
908
|
+
let firstScriptStart = -1;
|
|
909
|
+
|
|
910
|
+
let nextEntryIndex = 0;
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Tracks the `webpackIgnore` value from the most recent comment that
|
|
914
|
+
* appears before the next tag. Reset whenever a tag is emitted or a
|
|
915
|
+
* comment without a `webpackIgnore` value is encountered.
|
|
916
|
+
* @type {boolean | undefined}
|
|
917
|
+
*/
|
|
918
|
+
let pendingWebpackIgnore;
|
|
871
919
|
|
|
872
|
-
|
|
873
|
-
const pendingAttributes = [];
|
|
920
|
+
const magicCommentContext = this.magicCommentContext;
|
|
874
921
|
|
|
875
922
|
/**
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
879
|
-
* @param {
|
|
880
|
-
* @param {number} nameEnd position right after `<script` (for inserts)
|
|
881
|
-
* @param {"classic" | "esm-script"} kind chunk kind decided by the parser
|
|
882
|
-
* @param {string} input full source string
|
|
883
|
-
* @returns {void}
|
|
923
|
+
* @param {import("./syntax").HtmlAttribute | undefined} typeAttr type attribute
|
|
924
|
+
* @param {number} nameEnd end offset of the tag name
|
|
925
|
+
* @param {string} type type of the script
|
|
926
|
+
* @param {string} input source string
|
|
884
927
|
*/
|
|
885
|
-
const reconcileScriptTypeAttr = (typeAttr, nameEnd,
|
|
886
|
-
if (
|
|
928
|
+
const reconcileScriptTypeAttr = (typeAttr, nameEnd, type, input) => {
|
|
929
|
+
if (outputModule && type === "script") {
|
|
887
930
|
// Chunk is an ES module; upgrade the tag.
|
|
888
931
|
if (typeAttr && typeAttr.valueStart !== -1) {
|
|
889
932
|
module.addPresentationalDependency(
|
|
@@ -897,7 +940,7 @@ class HtmlParser extends Parser {
|
|
|
897
940
|
new ConstDependency(' type="module"', nameEnd)
|
|
898
941
|
);
|
|
899
942
|
}
|
|
900
|
-
} else if (!
|
|
943
|
+
} else if (!outputModule && type === "script-module" && typeAttr) {
|
|
901
944
|
// Chunk is a classic IIFE; drop `type="module"` so the
|
|
902
945
|
// browser doesn't load it under module semantics.
|
|
903
946
|
let attrEnd;
|
|
@@ -911,8 +954,6 @@ class HtmlParser extends Parser {
|
|
|
911
954
|
} else {
|
|
912
955
|
attrEnd = typeAttr.valueEnd;
|
|
913
956
|
}
|
|
914
|
-
// Consume one leading whitespace char so we don't leave a
|
|
915
|
-
// double space between `<script` and the next attribute.
|
|
916
957
|
let attrStart = typeAttr.nameStart;
|
|
917
958
|
if (
|
|
918
959
|
attrStart > 0 &&
|
|
@@ -926,552 +967,520 @@ class HtmlParser extends Parser {
|
|
|
926
967
|
}
|
|
927
968
|
};
|
|
928
969
|
|
|
929
|
-
// Inline `<script>` body extraction is deferred to the matching
|
|
930
|
-
// `closeTag` event so the walker's script-data state machine
|
|
931
|
-
// (including its escaped/double-escaped sub-states) decides where
|
|
932
|
-
// the body ends. This is the spec-compliant way to find the close
|
|
933
|
-
// — a plain `</script>` regex would split too early inside
|
|
934
|
-
// `<!--<script>…</script>-->` patterns.
|
|
935
|
-
/** @type {null | { contentStart: number, attrs: Map<string, string>, typeAttr: AttrToken | undefined, nameEnd: number }} */
|
|
936
|
-
let pendingInlineScript = null;
|
|
937
|
-
|
|
938
|
-
// Script src / modulepreload references are collected per-category
|
|
939
|
-
// during the walk; HtmlModulesPlugin later turns them into real
|
|
940
|
-
// entries. Classic <script src> and <script type="module" src> are
|
|
941
|
-
// chained via a leader-only dependOn so they share a runtime.
|
|
942
|
-
// `<link rel="modulepreload">` entries are kept independent — they
|
|
943
|
-
// must preload without running, so they can never become a runtime
|
|
944
|
-
// leader that other entries would import.
|
|
945
970
|
/**
|
|
946
|
-
* @
|
|
947
|
-
* @
|
|
948
|
-
* @
|
|
949
|
-
* @property {"classic" | "esm-script" | "modulepreload" | "stylesheet"} kind
|
|
971
|
+
* @param {string} mime mime type
|
|
972
|
+
* @param {string} text inline text
|
|
973
|
+
* @returns {string} a `data:` request for the text
|
|
950
974
|
*/
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
/** @type {EntryScriptInfo[]} */
|
|
954
|
-
const esmScriptEntries = [];
|
|
955
|
-
/** @type {EntryScriptInfo[]} */
|
|
956
|
-
const modulePreloadEntries = [];
|
|
957
|
-
/** @type {EntryScriptInfo[]} */
|
|
958
|
-
const stylesheetEntries = [];
|
|
959
|
-
|
|
960
|
-
let nextEntryIndex = 0;
|
|
975
|
+
const dataUri = (mime, text) =>
|
|
976
|
+
`data:${mime};base64,${Buffer.from(text, "utf8").toString("base64")}`;
|
|
961
977
|
|
|
962
978
|
/**
|
|
963
|
-
*
|
|
964
|
-
*
|
|
965
|
-
*
|
|
966
|
-
* @type {boolean | undefined}
|
|
979
|
+
* @param {ConstDependency | HtmlSourceDependency | HtmlEntryDependency | HtmlInlineStyleDependency | HtmlInlineScriptDependency | HtmlInlineHtmlDependency} dep dependency
|
|
980
|
+
* @param {number} start raw start offset
|
|
981
|
+
* @param {number} end raw end offset
|
|
967
982
|
*/
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
983
|
+
const setLoc = (dep, start, end) => {
|
|
984
|
+
const s = locConverter.get(start);
|
|
985
|
+
const e = locConverter.get(end);
|
|
986
|
+
dep.setLoc(s.line, s.column, e.line, e.column);
|
|
987
|
+
};
|
|
971
988
|
|
|
972
989
|
// TODO implement full HTML parser (WASM)
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
input.charCodeAt(start) !== 0x3c /* < */ ||
|
|
982
|
-
input.charCodeAt(start + 1) !== 0x21 /* ! */ ||
|
|
983
|
-
input.charCodeAt(start + 2) !== 0x2d /* - */ ||
|
|
984
|
-
input.charCodeAt(start + 3) !== 0x2d /* - */ ||
|
|
985
|
-
input.charCodeAt(end - 1) !== 0x3e /* > */ ||
|
|
986
|
-
input.charCodeAt(end - 2) !== 0x2d /* - */ ||
|
|
987
|
-
input.charCodeAt(end - 3) !== 0x2d /* - */
|
|
988
|
-
) {
|
|
989
|
-
pendingWebpackIgnore = undefined;
|
|
990
|
-
return end;
|
|
991
|
-
}
|
|
992
|
-
const contentStart = start + 4;
|
|
993
|
-
const contentEnd = end - 3;
|
|
994
|
-
const value = input.slice(contentStart, contentEnd);
|
|
995
|
-
if (!webpackCommentRegExp.test(value)) {
|
|
996
|
-
pendingWebpackIgnore = undefined;
|
|
997
|
-
return end;
|
|
998
|
-
}
|
|
999
|
-
/** @type {Record<string, EXPECTED_ANY>} */
|
|
1000
|
-
let options;
|
|
1001
|
-
try {
|
|
1002
|
-
options = vm.runInContext(
|
|
1003
|
-
`(function(){return {${value}};})()`,
|
|
1004
|
-
magicCommentContext
|
|
1005
|
-
);
|
|
1006
|
-
} catch (err) {
|
|
1007
|
-
const { line: sl, column: sc } = locConverter.get(start);
|
|
1008
|
-
const { line: el, column: ec } = locConverter.get(end);
|
|
1009
|
-
module.addWarning(
|
|
1010
|
-
new CommentCompilationWarning(
|
|
1011
|
-
`Compilation error while processing magic comment(-s): /*${value}*/: ${
|
|
1012
|
-
/** @type {Error} */ (err).message
|
|
1013
|
-
}`,
|
|
1014
|
-
{
|
|
1015
|
-
start: { line: sl, column: sc },
|
|
1016
|
-
end: { line: el, column: ec }
|
|
1017
|
-
}
|
|
1018
|
-
)
|
|
1019
|
-
);
|
|
1020
|
-
pendingWebpackIgnore = undefined;
|
|
1021
|
-
return end;
|
|
1022
|
-
}
|
|
1023
|
-
if (options.webpackIgnore === undefined) {
|
|
1024
|
-
pendingWebpackIgnore = undefined;
|
|
1025
|
-
return end;
|
|
1026
|
-
}
|
|
1027
|
-
if (typeof options.webpackIgnore !== "boolean") {
|
|
1028
|
-
const { line: sl, column: sc } = locConverter.get(start);
|
|
1029
|
-
const { line: el, column: ec } = locConverter.get(end);
|
|
1030
|
-
module.addWarning(
|
|
1031
|
-
new UnsupportedFeatureWarning(
|
|
1032
|
-
`\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
|
|
1033
|
-
{
|
|
1034
|
-
start: { line: sl, column: sc },
|
|
1035
|
-
end: { line: el, column: ec }
|
|
1036
|
-
}
|
|
1037
|
-
)
|
|
1038
|
-
);
|
|
1039
|
-
pendingWebpackIgnore = undefined;
|
|
1040
|
-
return end;
|
|
1041
|
-
}
|
|
1042
|
-
pendingWebpackIgnore = options.webpackIgnore;
|
|
1043
|
-
return end;
|
|
1044
|
-
},
|
|
1045
|
-
attribute: (
|
|
1046
|
-
input,
|
|
1047
|
-
nameStart,
|
|
1048
|
-
nameEnd,
|
|
1049
|
-
valueStart,
|
|
1050
|
-
valueEnd,
|
|
1051
|
-
quoteType
|
|
1052
|
-
) => {
|
|
1053
|
-
pendingAttributes.push({ nameStart, nameEnd, valueStart, valueEnd });
|
|
1054
|
-
if (valueStart === -1) return nameEnd;
|
|
1055
|
-
return quoteType !== walkHtmlTokens.QUOTE_NONE
|
|
1056
|
-
? valueEnd + 1
|
|
1057
|
-
: valueEnd;
|
|
1058
|
-
},
|
|
1059
|
-
closeTag: (input, start, end, nameStart, nameEnd) => {
|
|
1060
|
-
pendingWebpackIgnore = undefined;
|
|
1061
|
-
if (pendingInlineScript) {
|
|
1062
|
-
const elName = input.slice(nameStart, nameEnd).toLowerCase();
|
|
1063
|
-
if (elName === "script") {
|
|
1064
|
-
const ps = pendingInlineScript;
|
|
1065
|
-
pendingInlineScript = null;
|
|
1066
|
-
const contentStart = ps.contentStart;
|
|
1067
|
-
const contentEnd = start; // start of `</script>`
|
|
1068
|
-
const jsContent = input.slice(contentStart, contentEnd);
|
|
1069
|
-
if (jsContent.trim() === "") return end;
|
|
1070
|
-
|
|
1071
|
-
// Base64-encode the JS body so the data URI round-trips
|
|
1072
|
-
// arbitrary JavaScript text, including non-ASCII source
|
|
1073
|
-
// (`decodeDataURI` decodes non-base64 bodies as ASCII,
|
|
1074
|
-
// which would corrupt Unicode string literals or
|
|
1075
|
-
// identifiers).
|
|
1076
|
-
const request = `data:text/javascript;base64,${Buffer.from(
|
|
1077
|
-
jsContent,
|
|
1078
|
-
"utf8"
|
|
1079
|
-
).toString("base64")}`;
|
|
1080
|
-
|
|
1081
|
-
const useEsmEntry = isModuleScript(ps.attrs);
|
|
1082
|
-
const entryName = `__html_${moduleHash}_${nextEntryIndex++}`;
|
|
1083
|
-
/** @type {"classic" | "esm-script"} */
|
|
1084
|
-
const kind = useEsmEntry ? "esm-script" : "classic";
|
|
1085
|
-
const { line: sl, column: sc } = locConverter.get(contentStart);
|
|
1086
|
-
const { line: el, column: ec } = locConverter.get(contentEnd);
|
|
1087
|
-
const dep = new HtmlInlineScriptDependency(
|
|
1088
|
-
request,
|
|
1089
|
-
ps.nameEnd,
|
|
1090
|
-
[contentStart, contentEnd],
|
|
1091
|
-
entryName,
|
|
1092
|
-
useEsmEntry ? "esm" : "commonjs"
|
|
1093
|
-
);
|
|
1094
|
-
dep.setLoc(sl, sc, el, ec);
|
|
1095
|
-
module.addPresentationalDependency(dep);
|
|
1096
|
-
reconcileScriptTypeAttr(ps.typeAttr, ps.nameEnd, kind, input);
|
|
1097
|
-
const collection =
|
|
1098
|
-
kind === "classic" ? classicEntries : esmScriptEntries;
|
|
1099
|
-
collection.push({ request, entryName, kind });
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
return end;
|
|
1103
|
-
},
|
|
1104
|
-
openTag: (input, start, end, nameStart, nameEnd) => {
|
|
1105
|
-
const ignore = pendingWebpackIgnore === true;
|
|
1106
|
-
pendingWebpackIgnore = undefined;
|
|
1107
|
-
if (ignore) {
|
|
1108
|
-
// For `<script>` and `<style>` we don't emit a dependency,
|
|
1109
|
-
// but we must NOT advance past the close tag either: the
|
|
1110
|
-
// walker is already in script-data/rawtext state for
|
|
1111
|
-
// those tags and will consume the body and emit the
|
|
1112
|
-
// matching `closeTag` itself. Returning a position past
|
|
1113
|
-
// `</script>`/`</style>` would leave the walker stuck in
|
|
1114
|
-
// rawtext mode, swallowing later markup.
|
|
1115
|
-
pendingAttributes.length = 0;
|
|
1116
|
-
return end;
|
|
1117
|
-
}
|
|
1118
|
-
const elementName = input.slice(nameStart, nameEnd).toLowerCase();
|
|
1119
|
-
|
|
1120
|
-
// `<style>` is rawtext: capture the inline CSS and hand it to
|
|
1121
|
-
// the CSS pipeline as a virtual `data:text/css` module with
|
|
1122
|
-
// `exportType: "text"`. We use the regex only to discover the
|
|
1123
|
-
// `</style>` position so we can slice the body; the walker is
|
|
1124
|
-
// already in rawtext state for `<style>` and will emit the
|
|
1125
|
-
// matching `closeTag` event itself, so we must return `end`
|
|
1126
|
-
// (the position right after the opening tag's `>`) rather
|
|
1127
|
-
// than advancing past `</style>`. Only `<style>` tags whose
|
|
1128
|
-
// `type` attribute is absent, empty, or `text/css` are
|
|
1129
|
-
// processed; other types are left untouched.
|
|
1130
|
-
if (elementName === "style") {
|
|
1131
|
-
/** @type {string | undefined} */
|
|
1132
|
-
let typeValue;
|
|
1133
|
-
for (const attr of pendingAttributes) {
|
|
1134
|
-
const attrName = input
|
|
1135
|
-
.slice(attr.nameStart, attr.nameEnd)
|
|
1136
|
-
.toLowerCase();
|
|
1137
|
-
if (attrName === "type") {
|
|
1138
|
-
typeValue =
|
|
1139
|
-
attr.valueStart !== -1
|
|
1140
|
-
? input.slice(attr.valueStart, attr.valueEnd)
|
|
1141
|
-
: "";
|
|
1142
|
-
break;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
pendingAttributes.length = 0;
|
|
1146
|
-
|
|
1147
|
-
STYLE_END_REGEXP.lastIndex = end;
|
|
1148
|
-
const closeMatch = STYLE_END_REGEXP.exec(input);
|
|
1149
|
-
if (!closeMatch) return end;
|
|
1150
|
-
const contentStart = end;
|
|
1151
|
-
const contentEnd = closeMatch.index;
|
|
1152
|
-
|
|
1153
|
-
const trimmedType =
|
|
1154
|
-
typeValue !== undefined ? typeValue.trim().toLowerCase() : "";
|
|
990
|
+
// The walker descends into children (and `<template>` content) itself;
|
|
991
|
+
// the Element `exit` clears a pending `webpackIgnore` once an element's
|
|
992
|
+
// children are done (the old `walkChildren` behaviour).
|
|
993
|
+
new SourceProcessor()
|
|
994
|
+
.use({
|
|
995
|
+
[NodeType.Comment]: (n) => {
|
|
996
|
+
const node = /** @type {import("./syntax").HtmlComment} */ (n);
|
|
997
|
+
// Only proper `<!-- ... -->` comments carry magic comments.
|
|
1155
998
|
if (
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
999
|
+
node.end - node.start < 7 ||
|
|
1000
|
+
source.charCodeAt(node.start) !== 0x3c ||
|
|
1001
|
+
source.charCodeAt(node.start + 1) !== 0x21 ||
|
|
1002
|
+
source.charCodeAt(node.start + 2) !== 0x2d ||
|
|
1003
|
+
source.charCodeAt(node.start + 3) !== 0x2d ||
|
|
1004
|
+
source.charCodeAt(node.end - 1) !== 0x3e ||
|
|
1005
|
+
source.charCodeAt(node.end - 2) !== 0x2d ||
|
|
1006
|
+
source.charCodeAt(node.end - 3) !== 0x2d
|
|
1159
1007
|
) {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
// Inline-style processing requires the CSS pipeline; when
|
|
1164
|
-
// `experiments.css` is off, leave the body alone — the
|
|
1165
|
-
// walker is in rawtext mode for `<style>` and will skip
|
|
1166
|
-
// over the body to the matching `</style>` itself.
|
|
1167
|
-
if (!this.css) {
|
|
1168
|
-
return end;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
const cssContent = input.slice(contentStart, contentEnd);
|
|
1172
|
-
if (cssContent.trim() === "") {
|
|
1173
|
-
return end;
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
// URL-encode the CSS body so the data URI parser's `(.*)$`
|
|
1177
|
-
// body group matches even when the source has newlines or
|
|
1178
|
-
// other characters that would otherwise break the regex.
|
|
1179
|
-
const request = `data:text/css,${encodeURIComponent(cssContent)}`;
|
|
1180
|
-
|
|
1181
|
-
const { line: sl, column: sc } = locConverter.get(contentStart);
|
|
1182
|
-
const { line: el, column: ec } = locConverter.get(contentEnd);
|
|
1183
|
-
const dep = new HtmlInlineStyleDependency(request, [
|
|
1184
|
-
contentStart,
|
|
1185
|
-
contentEnd
|
|
1186
|
-
]);
|
|
1187
|
-
dep.setLoc(sl, sc, el, ec);
|
|
1188
|
-
module.addDependency(dep);
|
|
1189
|
-
module.addCodeGenerationDependency(dep);
|
|
1190
|
-
return end;
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
const sources = DEFAULT_SOURCES.get(elementName);
|
|
1194
|
-
|
|
1195
|
-
if (!sources) {
|
|
1196
|
-
pendingAttributes.length = 0;
|
|
1197
|
-
return end;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
/** @type {Map<string, string> | undefined} */
|
|
1201
|
-
let attributesMap;
|
|
1202
|
-
const getAttributesMap = () => {
|
|
1203
|
-
if (attributesMap) return attributesMap;
|
|
1204
|
-
attributesMap = new Map();
|
|
1205
|
-
for (const attr of pendingAttributes) {
|
|
1206
|
-
const name = input
|
|
1207
|
-
.slice(attr.nameStart, attr.nameEnd)
|
|
1208
|
-
.toLowerCase();
|
|
1209
|
-
const value =
|
|
1210
|
-
attr.valueStart !== -1
|
|
1211
|
-
? input.slice(attr.valueStart, attr.valueEnd)
|
|
1212
|
-
: "";
|
|
1213
|
-
attributesMap.set(name, value);
|
|
1008
|
+
pendingWebpackIgnore = undefined;
|
|
1009
|
+
return;
|
|
1214
1010
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
const attributeName = input
|
|
1220
|
-
.slice(attr.nameStart, attr.nameEnd)
|
|
1221
|
-
.toLowerCase();
|
|
1222
|
-
const sourceItem = sources.get(attributeName);
|
|
1223
|
-
|
|
1224
|
-
if (!sourceItem) continue;
|
|
1225
|
-
|
|
1226
|
-
// TODO(html-entities): We should ideally decode entities here using
|
|
1227
|
-
// `walkHtmlTokens.decodeHtmlEntities(input.slice(...))` so that URLs
|
|
1228
|
-
// like `image.png?a=1&b=2` are correctly resolved as `&`.
|
|
1229
|
-
// However, doing so currently breaks `srcset` parsing tests (e.g. `errors.js`)
|
|
1230
|
-
// which explicitly expect whitespace entities like `	` to NOT be decoded
|
|
1231
|
-
// before the srcset parser runs. A follow-up PR should implement selective
|
|
1232
|
-
// decoding for specific URL attributes.
|
|
1233
|
-
const attributeValue =
|
|
1234
|
-
attr.valueStart !== -1
|
|
1235
|
-
? input.slice(attr.valueStart, attr.valueEnd)
|
|
1236
|
-
: "";
|
|
1237
|
-
|
|
1238
|
-
if (!attributeValue) continue;
|
|
1239
|
-
|
|
1240
|
-
if (
|
|
1241
|
-
typeof sourceItem.filter === "function" &&
|
|
1242
|
-
!sourceItem.filter(getAttributesMap())
|
|
1243
|
-
) {
|
|
1244
|
-
continue;
|
|
1011
|
+
const value = node.data;
|
|
1012
|
+
if (!webpackCommentRegExp.test(value)) {
|
|
1013
|
+
pendingWebpackIgnore = undefined;
|
|
1014
|
+
return;
|
|
1245
1015
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
let parsedAttributeValue;
|
|
1249
|
-
|
|
1016
|
+
/** @type {Record<string, EXPECTED_ANY>} */
|
|
1017
|
+
let options;
|
|
1250
1018
|
try {
|
|
1251
|
-
|
|
1019
|
+
options = parseMagicComment(value, magicCommentContext);
|
|
1252
1020
|
} catch (err) {
|
|
1253
|
-
const { line: sl, column: sc } = locConverter.get(
|
|
1254
|
-
const { line: el, column: ec } = locConverter.get(
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1021
|
+
const { line: sl, column: sc } = locConverter.get(node.start);
|
|
1022
|
+
const { line: el, column: ec } = locConverter.get(node.end);
|
|
1023
|
+
module.addWarning(
|
|
1024
|
+
new CommentCompilationWarning(
|
|
1025
|
+
`Compilation error while processing magic comment(-s): /*${value}*/: ${
|
|
1026
|
+
/** @type {Error} */ (err).message
|
|
1027
|
+
}`,
|
|
1028
|
+
{
|
|
1029
|
+
start: { line: sl, column: sc },
|
|
1030
|
+
end: { line: el, column: ec }
|
|
1031
|
+
}
|
|
1032
|
+
)
|
|
1033
|
+
);
|
|
1034
|
+
pendingWebpackIgnore = undefined;
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
if (options.webpackIgnore === undefined) {
|
|
1038
|
+
pendingWebpackIgnore = undefined;
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (typeof options.webpackIgnore !== "boolean") {
|
|
1042
|
+
const { line: sl, column: sc } = locConverter.get(node.start);
|
|
1043
|
+
const { line: el, column: ec } = locConverter.get(node.end);
|
|
1044
|
+
module.addWarning(
|
|
1045
|
+
new UnsupportedFeatureWarning(
|
|
1046
|
+
`\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
|
|
1264
1047
|
{
|
|
1265
1048
|
start: { line: sl, column: sc },
|
|
1266
1049
|
end: { line: el, column: ec }
|
|
1267
1050
|
}
|
|
1268
1051
|
)
|
|
1269
1052
|
);
|
|
1053
|
+
pendingWebpackIgnore = undefined;
|
|
1054
|
+
return;
|
|
1270
1055
|
}
|
|
1056
|
+
pendingWebpackIgnore = options.webpackIgnore;
|
|
1057
|
+
},
|
|
1058
|
+
[NodeType.Doctype]: () => {
|
|
1059
|
+
pendingWebpackIgnore = undefined;
|
|
1060
|
+
},
|
|
1061
|
+
[NodeType.Element]: {
|
|
1062
|
+
// Children (and `<template>` content) are walked by the
|
|
1063
|
+
// processor after `enter`; `exit` clears a pending
|
|
1064
|
+
// `webpackIgnore` once they're done.
|
|
1065
|
+
enter: (n) => {
|
|
1066
|
+
const node = /** @type {import("./syntax").HtmlElement} */ (n);
|
|
1067
|
+
const ignore = pendingWebpackIgnore === true;
|
|
1068
|
+
pendingWebpackIgnore = undefined;
|
|
1069
|
+
|
|
1070
|
+
if (ignore) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1271
1073
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1074
|
+
const elementName = node.tagName;
|
|
1075
|
+
const attrs = node.attributes;
|
|
1076
|
+
|
|
1077
|
+
/** @type {Map<string, string> | undefined} */
|
|
1078
|
+
let attributesMap;
|
|
1079
|
+
const getAttributesMap = () => {
|
|
1080
|
+
if (attributesMap) return attributesMap;
|
|
1081
|
+
attributesMap = new Map();
|
|
1082
|
+
for (const attr of attrs) {
|
|
1083
|
+
// Decoded values — filters and type resolvers compare what
|
|
1084
|
+
// the browser sees (e.g. `rel="icon"` means `icon`)
|
|
1085
|
+
attributesMap.set(
|
|
1086
|
+
attr.name,
|
|
1087
|
+
decodeHtmlEntities(attr.value, true)
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
return attributesMap;
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
// Each matched attribute is a source; the element body (inline
|
|
1094
|
+
// `<style>`/`<script>`) is one more — content rather than an attribute.
|
|
1095
|
+
// All are dispatched by the same `switch (type)`.
|
|
1096
|
+
const sources =
|
|
1097
|
+
this.sourcesByTag[elementName] || this.sourcesByTag[ANY_TAG];
|
|
1098
|
+
const bodyItem = CONTENT_SOURCES[elementName];
|
|
1099
|
+
// Iterate the attributes, then one extra step for the element
|
|
1100
|
+
// body (`i === attrs.length`); `attrs[i]` is only read on the
|
|
1101
|
+
// attribute steps, so the access stays in bounds.
|
|
1102
|
+
for (let i = 0; i < attrs.length + 1; i++) {
|
|
1103
|
+
const content = i === attrs.length;
|
|
1104
|
+
/** @type {SourceType} */
|
|
1105
|
+
let type;
|
|
1106
|
+
/** @type {string} */
|
|
1107
|
+
let value;
|
|
1108
|
+
/** @type {number} */
|
|
1109
|
+
let start;
|
|
1110
|
+
/** @type {number} */
|
|
1111
|
+
let end;
|
|
1112
|
+
/** @type {import("./syntax").HtmlAttribute | undefined} */
|
|
1113
|
+
let attr;
|
|
1114
|
+
if (content) {
|
|
1115
|
+
if (!bodyItem) continue;
|
|
1116
|
+
if (bodyItem.filter && !bodyItem.filter(getAttributesMap())) {
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
/** @type {number | undefined} */
|
|
1120
|
+
let bodyStart;
|
|
1121
|
+
let bodyEnd = node.tagEnd;
|
|
1122
|
+
for (const child of node.children) {
|
|
1123
|
+
if (child.type === NodeType.Text) {
|
|
1124
|
+
if (bodyStart === undefined) bodyStart = child.start;
|
|
1125
|
+
bodyEnd = child.end;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (bodyStart === undefined) continue;
|
|
1129
|
+
value = source.slice(bodyStart, bodyEnd);
|
|
1130
|
+
if (value.trim() === "") continue;
|
|
1131
|
+
start = bodyStart;
|
|
1132
|
+
end = bodyEnd;
|
|
1133
|
+
type =
|
|
1134
|
+
typeof bodyItem.type === "function"
|
|
1135
|
+
? bodyItem.type(getAttributesMap(), css)
|
|
1136
|
+
: bodyItem.type;
|
|
1137
|
+
} else {
|
|
1138
|
+
attr = attrs[i];
|
|
1139
|
+
const item = sources[attr.name];
|
|
1140
|
+
if (!item) continue;
|
|
1141
|
+
if (
|
|
1142
|
+
item.namespace !== undefined &&
|
|
1143
|
+
node.namespace !== item.namespace
|
|
1144
|
+
) {
|
|
1145
|
+
continue;
|
|
1146
|
+
}
|
|
1147
|
+
// Adoption-agency clones carry no offsets; skip blank values.
|
|
1148
|
+
if (attr.valueStart === undefined || attr.valueStart === -1) {
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
value = attr.value;
|
|
1152
|
+
if (!value || !/\S/.test(value)) continue;
|
|
1153
|
+
const filter = item.filter;
|
|
1154
|
+
if (filter) {
|
|
1155
|
+
// The filter also sees the decoded value (the browser tokenizes it
|
|
1156
|
+
// decoded, e.g. `url(`), so a value-only check needn't re-read it.
|
|
1157
|
+
const decoded = value.includes("&")
|
|
1158
|
+
? decodeHtmlEntities(value, true)
|
|
1159
|
+
: value;
|
|
1160
|
+
if (!filter(getAttributesMap(), decoded)) continue;
|
|
1161
|
+
}
|
|
1162
|
+
start = attr.valueStart;
|
|
1163
|
+
end = attr.valueEnd;
|
|
1164
|
+
type =
|
|
1165
|
+
typeof item.type === "function"
|
|
1166
|
+
? item.type(getAttributesMap(), css)
|
|
1167
|
+
: item.type;
|
|
1168
|
+
}
|
|
1169
|
+
// A resolved `type` selects the parse algorithm and the dependency
|
|
1170
|
+
// kind — the only dispatch in the parser.
|
|
1171
|
+
switch (type) {
|
|
1172
|
+
// Inline CSS — a `<style>` body (raw, a full stylesheet) or an
|
|
1173
|
+
// attribute value (decoded, re-escaped on write-back; `*-attribute`
|
|
1174
|
+
// is a declaration list). Needs `experiments.css`.
|
|
1175
|
+
case "stylesheet-style":
|
|
1176
|
+
case "stylesheet-style-attribute": {
|
|
1177
|
+
if (!css) break;
|
|
1178
|
+
const cssText = content
|
|
1179
|
+
? value
|
|
1180
|
+
: decodeHtmlEntities(value, true);
|
|
1181
|
+
if (cssText.trim() === "") break;
|
|
1182
|
+
const dep = new HtmlInlineStyleDependency(
|
|
1183
|
+
dataUri("text/css", cssText),
|
|
1184
|
+
[start, end],
|
|
1185
|
+
content ? false : type === "stylesheet-style-attribute",
|
|
1186
|
+
!content
|
|
1187
|
+
);
|
|
1188
|
+
setLoc(dep, start, end);
|
|
1189
|
+
module.addDependency(dep);
|
|
1190
|
+
module.addCodeGenerationDependency(dep);
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
// `<iframe srcdoc>`: an entity-encoded HTML document. Feed the
|
|
1194
|
+
// decoded markup back through the HTML pipeline as a nested
|
|
1195
|
+
// `data:text/html` module. Per spec the value is a full document
|
|
1196
|
+
// whose URL is `about:srcdoc`, so its base URL is inherited from
|
|
1197
|
+
// this document — asset URLs resolve against this file's context.
|
|
1198
|
+
case "srcdoc": {
|
|
1199
|
+
const htmlText = decodeHtmlEntities(value, true);
|
|
1200
|
+
// Only spin up a nested module when there is markup that can
|
|
1201
|
+
// reference an asset; text/formatting-only markup is identical
|
|
1202
|
+
// after rewriting (see `SRCDOC_ASSET_REGEXP`).
|
|
1371
1203
|
if (
|
|
1372
|
-
|
|
1204
|
+
!htmlText.includes("<") ||
|
|
1205
|
+
!SRCDOC_ASSET_REGEXP.test(htmlText)
|
|
1373
1206
|
) {
|
|
1374
|
-
typeAttr = a;
|
|
1375
1207
|
break;
|
|
1376
1208
|
}
|
|
1209
|
+
const dep = new HtmlInlineHtmlDependency(
|
|
1210
|
+
dataUri("text/html", htmlText),
|
|
1211
|
+
[start, end]
|
|
1212
|
+
);
|
|
1213
|
+
setLoc(dep, start, end);
|
|
1214
|
+
module.addDependency(dep);
|
|
1215
|
+
module.addCodeGenerationDependency(dep);
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
// Inline `<script>` body — bundled as its own entry chunk.
|
|
1219
|
+
case "script":
|
|
1220
|
+
case "script-module":
|
|
1221
|
+
if (content) {
|
|
1222
|
+
const scriptType =
|
|
1223
|
+
/** @type {"script" | "script-module"} */ (type);
|
|
1224
|
+
const request = dataUri("text/javascript", value);
|
|
1225
|
+
const entryName = `__html_${moduleHash}_${nextEntryIndex++}`;
|
|
1226
|
+
const dep = new HtmlInlineScriptDependency(
|
|
1227
|
+
request,
|
|
1228
|
+
node.nameEnd,
|
|
1229
|
+
[start, end],
|
|
1230
|
+
entryName,
|
|
1231
|
+
scriptType === "script-module" ? "esm" : "commonjs"
|
|
1232
|
+
);
|
|
1233
|
+
setLoc(dep, start, end);
|
|
1234
|
+
module.addPresentationalDependency(dep);
|
|
1235
|
+
reconcileScriptTypeAttr(
|
|
1236
|
+
findAttr(attrs, "type"),
|
|
1237
|
+
node.nameEnd,
|
|
1238
|
+
scriptType,
|
|
1239
|
+
source
|
|
1240
|
+
);
|
|
1241
|
+
(scriptType === "script"
|
|
1242
|
+
? scriptEntries
|
|
1243
|
+
: scriptModuleEntries
|
|
1244
|
+
).push({
|
|
1245
|
+
request,
|
|
1246
|
+
entryName,
|
|
1247
|
+
type: scriptType
|
|
1248
|
+
});
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
// falls through — external `<script src>` joins the URL-bearing types
|
|
1252
|
+
case "src":
|
|
1253
|
+
case "srcset":
|
|
1254
|
+
case "css-url":
|
|
1255
|
+
case "msapplication-task":
|
|
1256
|
+
case "modulepreload":
|
|
1257
|
+
case "stylesheet": {
|
|
1258
|
+
// Parse the value into one or more URLs (decoding character
|
|
1259
|
+
// references, mapping spans back to raw offsets), then emit a plain
|
|
1260
|
+
// asset reference or an entry chunk per URL.
|
|
1261
|
+
const a = /** @type {import("./syntax").HtmlAttribute} */ (
|
|
1262
|
+
attr
|
|
1263
|
+
);
|
|
1264
|
+
const parse =
|
|
1265
|
+
type === "srcset"
|
|
1266
|
+
? parseSrcset
|
|
1267
|
+
: type === "css-url"
|
|
1268
|
+
? parseCssUrls
|
|
1269
|
+
: type === "msapplication-task"
|
|
1270
|
+
? parseMsapplicationTask
|
|
1271
|
+
: parseSrc;
|
|
1272
|
+
let text = value;
|
|
1273
|
+
/** @type {number[] | undefined} */
|
|
1274
|
+
let map;
|
|
1275
|
+
if (value.includes("&")) {
|
|
1276
|
+
({ text, map } = decodeHtmlEntitiesWithMap(value, true));
|
|
1277
|
+
if (!/\S/.test(text)) break;
|
|
1278
|
+
}
|
|
1279
|
+
/** @type {ParsedSource[] | undefined} */
|
|
1280
|
+
let parsed;
|
|
1281
|
+
try {
|
|
1282
|
+
parsed = parse(text);
|
|
1283
|
+
} catch (err) {
|
|
1284
|
+
const ds = locConverter.get(start);
|
|
1285
|
+
const de = locConverter.get(end);
|
|
1286
|
+
module.addError(
|
|
1287
|
+
new ModuleDependencyError(
|
|
1288
|
+
module,
|
|
1289
|
+
new WebpackError(
|
|
1290
|
+
`Bad value for attribute "${
|
|
1291
|
+
a.name
|
|
1292
|
+
}" on element "${elementName}": ${
|
|
1293
|
+
/** @type {Error} */ (err).message
|
|
1294
|
+
}`
|
|
1295
|
+
),
|
|
1296
|
+
{
|
|
1297
|
+
start: { line: ds.line, column: ds.column },
|
|
1298
|
+
end: { line: de.line, column: de.column }
|
|
1299
|
+
}
|
|
1300
|
+
)
|
|
1301
|
+
);
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
if (!parsed) break;
|
|
1305
|
+
for (const [url, us, ue] of parsed) {
|
|
1306
|
+
// Internal `url(#id)` / fragment-only refs aren't assets.
|
|
1307
|
+
if (!url || url.startsWith("#")) continue;
|
|
1308
|
+
const s = start + (map ? map[us] : us);
|
|
1309
|
+
const e = start + (map ? map[ue] : ue);
|
|
1310
|
+
// `src`/`srcset`/`css-url`/`msapplication-task` are plain assets;
|
|
1311
|
+
// the rest are narrowed to entry types here and share one
|
|
1312
|
+
// `HtmlEntryDependency`, recorded for HtmlModulesPlugin.
|
|
1313
|
+
if (
|
|
1314
|
+
type === "script" ||
|
|
1315
|
+
type === "script-module" ||
|
|
1316
|
+
type === "modulepreload" ||
|
|
1317
|
+
type === "stylesheet"
|
|
1318
|
+
) {
|
|
1319
|
+
const entryName = `__html_${moduleHash}_${nextEntryIndex++}`;
|
|
1320
|
+
const willBeModuleScript =
|
|
1321
|
+
type === "script-module" ||
|
|
1322
|
+
(outputModule && type === "script");
|
|
1323
|
+
/** @type {EntrySourceType} */
|
|
1324
|
+
const elementKind =
|
|
1325
|
+
type === "modulepreload"
|
|
1326
|
+
? "modulepreload"
|
|
1327
|
+
: type === "stylesheet"
|
|
1328
|
+
? "stylesheet"
|
|
1329
|
+
: willBeModuleScript
|
|
1330
|
+
? "script-module"
|
|
1331
|
+
: "script";
|
|
1332
|
+
const entryCategory =
|
|
1333
|
+
type === "stylesheet"
|
|
1334
|
+
? "css-import"
|
|
1335
|
+
: type === "script-module" || type === "modulepreload"
|
|
1336
|
+
? "esm"
|
|
1337
|
+
: undefined;
|
|
1338
|
+
// Native = the loading element is the tag the template clones
|
|
1339
|
+
// verbatim (`<script>` / `<link>`); a custom element is not.
|
|
1340
|
+
const nativeTag =
|
|
1341
|
+
elementKind === "stylesheet" ||
|
|
1342
|
+
elementKind === "modulepreload"
|
|
1343
|
+
? "link"
|
|
1344
|
+
: "script";
|
|
1345
|
+
// CSP/fetch attributes the template copies onto siblings.
|
|
1346
|
+
let copyableAttrsText = "";
|
|
1347
|
+
let hasOwnCrossOrigin = false;
|
|
1348
|
+
if (node.start >= 0) {
|
|
1349
|
+
for (const copyableName of COPYABLE_SIBLING_ATTRS) {
|
|
1350
|
+
const copyableAttr = findAttr(attrs, copyableName);
|
|
1351
|
+
if (copyableAttr) {
|
|
1352
|
+
copyableAttrsText += attrSourceSpan(
|
|
1353
|
+
source,
|
|
1354
|
+
copyableAttr
|
|
1355
|
+
);
|
|
1356
|
+
if (copyableName === "crossorigin") {
|
|
1357
|
+
hasOwnCrossOrigin = true;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
if (
|
|
1363
|
+
firstScriptStart === -1 &&
|
|
1364
|
+
(type === "script" || type === "script-module")
|
|
1365
|
+
) {
|
|
1366
|
+
firstScriptStart = node.start;
|
|
1367
|
+
}
|
|
1368
|
+
// Sibling-clone attribute edits, captured from the parsed
|
|
1369
|
+
// attributes (offsets relative to the tag) so the template
|
|
1370
|
+
// needn't re-parse the tag text: `integrity` is dropped
|
|
1371
|
+
// (content-specific) and `script-module` clones are forced
|
|
1372
|
+
// to `type="module"` (its value range, else inserted).
|
|
1373
|
+
/** @type {Range | null} */
|
|
1374
|
+
let integrityRange = null;
|
|
1375
|
+
/** @type {Range | null} */
|
|
1376
|
+
let typeValueRange = null;
|
|
1377
|
+
if (node.start >= 0 && elementName === nativeTag) {
|
|
1378
|
+
const integrityAttr = findAttr(attrs, "integrity");
|
|
1379
|
+
if (integrityAttr && integrityAttr.nameStart >= 0) {
|
|
1380
|
+
let rs = integrityAttr.nameStart;
|
|
1381
|
+
while (
|
|
1382
|
+
rs > 0 &&
|
|
1383
|
+
isASCIIWhitespace(source.charCodeAt(rs - 1))
|
|
1384
|
+
) {
|
|
1385
|
+
rs--;
|
|
1386
|
+
}
|
|
1387
|
+
let re;
|
|
1388
|
+
if (integrityAttr.valueStart === -1) {
|
|
1389
|
+
re = integrityAttr.nameEnd;
|
|
1390
|
+
} else {
|
|
1391
|
+
const q = source.charCodeAt(
|
|
1392
|
+
integrityAttr.valueStart - 1
|
|
1393
|
+
);
|
|
1394
|
+
re =
|
|
1395
|
+
q === CC_QUOTATION || q === CC_APOSTROPHE
|
|
1396
|
+
? integrityAttr.valueEnd + 1
|
|
1397
|
+
: integrityAttr.valueEnd;
|
|
1398
|
+
}
|
|
1399
|
+
integrityRange = [rs - node.start, re - node.start];
|
|
1400
|
+
}
|
|
1401
|
+
if (elementKind === "script-module") {
|
|
1402
|
+
const typeAttr = findAttr(attrs, "type");
|
|
1403
|
+
if (typeAttr && typeAttr.valueStart >= 0) {
|
|
1404
|
+
typeValueRange = [
|
|
1405
|
+
typeAttr.valueStart - node.start,
|
|
1406
|
+
typeAttr.valueEnd - node.start
|
|
1407
|
+
];
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
const dep = new HtmlEntryDependency(
|
|
1412
|
+
url,
|
|
1413
|
+
[s, e],
|
|
1414
|
+
entryName,
|
|
1415
|
+
entryCategory,
|
|
1416
|
+
elementKind,
|
|
1417
|
+
node.start,
|
|
1418
|
+
node.tagEnd,
|
|
1419
|
+
elementName === nativeTag,
|
|
1420
|
+
copyableAttrsText,
|
|
1421
|
+
node.nameEnd,
|
|
1422
|
+
hasOwnCrossOrigin,
|
|
1423
|
+
firstScriptStart,
|
|
1424
|
+
integrityRange,
|
|
1425
|
+
typeValueRange
|
|
1426
|
+
);
|
|
1427
|
+
setLoc(dep, s, e);
|
|
1428
|
+
module.addPresentationalDependency(dep);
|
|
1429
|
+
if (
|
|
1430
|
+
elementName === "script" &&
|
|
1431
|
+
(type === "script" || type === "script-module")
|
|
1432
|
+
) {
|
|
1433
|
+
reconcileScriptTypeAttr(
|
|
1434
|
+
findAttr(attrs, "type"),
|
|
1435
|
+
node.nameEnd,
|
|
1436
|
+
type,
|
|
1437
|
+
source
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
(type === "script"
|
|
1441
|
+
? scriptEntries
|
|
1442
|
+
: type === "script-module"
|
|
1443
|
+
? scriptModuleEntries
|
|
1444
|
+
: type === "stylesheet"
|
|
1445
|
+
? stylesheetEntries
|
|
1446
|
+
: modulePreloadEntries
|
|
1447
|
+
).push({ request: url, entryName, type });
|
|
1448
|
+
} else {
|
|
1449
|
+
const dep = new HtmlSourceDependency(url, [s, e]);
|
|
1450
|
+
setLoc(dep, s, e);
|
|
1451
|
+
module.addDependency(dep);
|
|
1452
|
+
module.addCodeGenerationDependency(dep);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
break;
|
|
1377
1456
|
}
|
|
1378
|
-
reconcileScriptTypeAttr(typeAttr, nameEnd, kind, input);
|
|
1379
1457
|
}
|
|
1380
|
-
const collection =
|
|
1381
|
-
kind === "classic"
|
|
1382
|
-
? classicEntries
|
|
1383
|
-
: kind === "esm-script"
|
|
1384
|
-
? esmScriptEntries
|
|
1385
|
-
: kind === "stylesheet"
|
|
1386
|
-
? stylesheetEntries
|
|
1387
|
-
: modulePreloadEntries;
|
|
1388
|
-
collection.push({ request: value, entryName, kind });
|
|
1389
|
-
} else {
|
|
1390
|
-
const dep = new HtmlSourceDependency(value, [
|
|
1391
|
-
sourceStart,
|
|
1392
|
-
sourceEnd
|
|
1393
|
-
]);
|
|
1394
|
-
dep.setLoc(sl, sc, el, ec);
|
|
1395
|
-
module.addDependency(dep);
|
|
1396
|
-
module.addCodeGenerationDependency(dep);
|
|
1397
1458
|
}
|
|
1459
|
+
},
|
|
1460
|
+
exit: () => {
|
|
1461
|
+
pendingWebpackIgnore = undefined;
|
|
1398
1462
|
}
|
|
1399
1463
|
}
|
|
1464
|
+
})
|
|
1465
|
+
.process(source, { ast: buildHtmlAst(source) });
|
|
1400
1466
|
|
|
1401
|
-
|
|
1402
|
-
// tokenizer): its body must never be reparsed as HTML,
|
|
1403
|
-
// regardless of whether the tag has a `src` attribute. The
|
|
1404
|
-
// walker is already in script-data state for `<script>` and
|
|
1405
|
-
// will emit the matching `closeTag` event itself, so we
|
|
1406
|
-
// return `end` and defer body extraction to the closeTag
|
|
1407
|
-
// callback (which gets the spec-correct `</script>` position
|
|
1408
|
-
// even in escaped/double-escaped script-data sub-states).
|
|
1409
|
-
// When the tag has no `src` and its body is non-empty, the
|
|
1410
|
-
// closeTag handler bundles the inline JS as its own entry —
|
|
1411
|
-
// the same pipeline that processes `<script src>` — by
|
|
1412
|
-
// issuing a `data:text/javascript;base64,...` virtual request
|
|
1413
|
-
// and adding a dependency that rewrites the tag to
|
|
1414
|
-
// `<script src="…">` at render time. Only `<script>` tags
|
|
1415
|
-
// whose `type` attribute is absent, empty, or a recognized
|
|
1416
|
-
// JS mimetype are processed as JS; other types (e.g.
|
|
1417
|
-
// `application/ld+json`, `importmap`) are left untouched.
|
|
1418
|
-
if (elementName === "script") {
|
|
1419
|
-
const attrs = getAttributesMap();
|
|
1420
|
-
// Use attribute presence, not value: a `<script src>` with
|
|
1421
|
-
// an empty or valueless `src` still ignores its inline
|
|
1422
|
-
// body in the browser, so we must not bundle the body.
|
|
1423
|
-
const hasSrc = attrs.has("src");
|
|
1424
|
-
|
|
1425
|
-
/** @type {AttrToken | undefined} */
|
|
1426
|
-
let typeAttr;
|
|
1427
|
-
for (const a of pendingAttributes) {
|
|
1428
|
-
if (input.slice(a.nameStart, a.nameEnd).toLowerCase() === "type") {
|
|
1429
|
-
typeAttr = a;
|
|
1430
|
-
break;
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
pendingAttributes.length = 0;
|
|
1434
|
-
|
|
1435
|
-
if (hasSrc || !isExecutableJsScript(attrs)) {
|
|
1436
|
-
// `<script src>` body is ignored by the browser, and
|
|
1437
|
-
// non-JS `<script type>` (e.g. importmap, JSON-LD)
|
|
1438
|
-
// passes through unchanged. Either way the walker
|
|
1439
|
-
// consumes the body and emits the close tag itself.
|
|
1440
|
-
return end;
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
pendingInlineScript = {
|
|
1444
|
-
contentStart: end,
|
|
1445
|
-
attrs,
|
|
1446
|
-
typeAttr,
|
|
1447
|
-
nameEnd
|
|
1448
|
-
};
|
|
1449
|
-
return end;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
pendingAttributes.length = 0;
|
|
1453
|
-
return end;
|
|
1454
|
-
}
|
|
1455
|
-
});
|
|
1456
|
-
|
|
1457
|
-
const buildInfo = /** @type {BuildInfo} */ (module.buildInfo);
|
|
1467
|
+
const buildInfo = /** @type {HtmlModuleBuildInfo} */ (module.buildInfo);
|
|
1458
1468
|
buildInfo.strict = true;
|
|
1459
1469
|
// Hand off the collected entries to HtmlModulesPlugin; it creates the
|
|
1460
|
-
// real compilation entries during the finishMake hook. The
|
|
1461
|
-
// and
|
|
1462
|
-
// they share a runtime; modulepreload
|
|
1463
|
-
// independent entries since `<link rel=modulepreload>`
|
|
1464
|
-
// without running.
|
|
1470
|
+
// real compilation entries during the finishMake hook. The `script`
|
|
1471
|
+
// and `script-module` groups are chained via a leader-only dependOn
|
|
1472
|
+
// so they share a runtime; `modulepreload` and `stylesheet` entries
|
|
1473
|
+
// are emitted as independent entries since `<link rel=modulepreload>`
|
|
1474
|
+
// must preload without running and stylesheets have no runtime.
|
|
1465
1475
|
if (
|
|
1466
|
-
|
|
1467
|
-
|
|
1476
|
+
scriptEntries.length > 0 ||
|
|
1477
|
+
scriptModuleEntries.length > 0 ||
|
|
1468
1478
|
modulePreloadEntries.length > 0 ||
|
|
1469
1479
|
stylesheetEntries.length > 0
|
|
1470
1480
|
) {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
"esm-script": esmScriptEntries,
|
|
1481
|
+
buildInfo.htmlEntryScripts = {
|
|
1482
|
+
script: scriptEntries,
|
|
1483
|
+
"script-module": scriptModuleEntries,
|
|
1475
1484
|
modulepreload: modulePreloadEntries,
|
|
1476
1485
|
stylesheet: stylesheetEntries
|
|
1477
1486
|
};
|