| [9af201e] | 1 | /*
|
|---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
|---|
| 3 | */
|
|---|
| 4 |
|
|---|
| 5 | "use strict";
|
|---|
| 6 |
|
|---|
| 7 | const { RawSource } = require("webpack-sources");
|
|---|
| 8 | const EntryPlugin = require("../EntryPlugin");
|
|---|
| 9 | const HotUpdateChunk = require("../HotUpdateChunk");
|
|---|
| 10 | const { HTML_TYPE } = require("../ModuleSourceTypeConstants");
|
|---|
| 11 | const { HTML_MODULE_TYPE } = require("../ModuleTypeConstants");
|
|---|
| 12 | const NormalModule = require("../NormalModule");
|
|---|
| 13 | const ConstDependency = require("../dependencies/ConstDependency");
|
|---|
| 14 | const HtmlInlineScriptDependency = require("../dependencies/HtmlInlineScriptDependency");
|
|---|
| 15 | const HtmlInlineStyleDependency = require("../dependencies/HtmlInlineStyleDependency");
|
|---|
| 16 | const HtmlScriptSrcDependency = require("../dependencies/HtmlScriptSrcDependency");
|
|---|
| 17 | const HtmlSourceDependency = require("../dependencies/HtmlSourceDependency");
|
|---|
| 18 | const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
|---|
| 19 | const { compareModulesByFullName } = require("../util/comparators");
|
|---|
| 20 | const removeBOM = require("../util/removeBOM");
|
|---|
| 21 | const HtmlGenerator = require("./HtmlGenerator");
|
|---|
| 22 | const HtmlParser = require("./HtmlParser");
|
|---|
| 23 |
|
|---|
| 24 | /** @typedef {import("../Compiler")} Compiler */
|
|---|
| 25 | /** @typedef {{ request: string, entryName: string, kind: "classic" | "esm-script" | "modulepreload" | "stylesheet" }} EntryScriptInfo */
|
|---|
| 26 |
|
|---|
| 27 | const PLUGIN_NAME = "HtmlModulesPlugin";
|
|---|
| 28 |
|
|---|
| 29 | /**
|
|---|
| 30 | * @param {string} name definition name in `schemas/WebpackOptions.json`
|
|---|
| 31 | * @returns {EXPECTED_OBJECT} a schema referencing `#/definitions/<name>`
|
|---|
| 32 | */
|
|---|
| 33 | const getSchema = (name) => {
|
|---|
| 34 | const { definitions } = require("../../schemas/WebpackOptions.json");
|
|---|
| 35 |
|
|---|
| 36 | return {
|
|---|
| 37 | definitions,
|
|---|
| 38 | oneOf: [{ $ref: `#/definitions/${name}` }]
|
|---|
| 39 | };
|
|---|
| 40 | };
|
|---|
| 41 |
|
|---|
| 42 | const generatorValidationOptions = {
|
|---|
| 43 | name: "Html Modules Plugin",
|
|---|
| 44 | baseDataPath: "generator"
|
|---|
| 45 | };
|
|---|
| 46 |
|
|---|
| 47 | class HtmlModulesPlugin {
|
|---|
| 48 | /**
|
|---|
| 49 | * Applies the plugin by registering its hooks on the compiler.
|
|---|
| 50 | * @param {Compiler} compiler the compiler instance
|
|---|
| 51 | * @returns {void}
|
|---|
| 52 | */
|
|---|
| 53 | apply(compiler) {
|
|---|
| 54 | // `<script src>` and `<link rel="modulepreload">` references collected
|
|---|
| 55 | // by HtmlParser become real compilation entries here. The classic
|
|---|
| 56 | // and esm-script groups are chained via a leader-only dependOn so
|
|---|
| 57 | // they share a runtime — the first entry of the group owns it and
|
|---|
| 58 | // every subsequent entry sets `dependOn: [leader]`. Modulepreload
|
|---|
| 59 | // entries are emitted as independent entries (no dependOn) so they
|
|---|
| 60 | // can never be imported as a runtime leader by a later script —
|
|---|
| 61 | // that's what keeps the "preload but don't execute" contract of
|
|---|
| 62 | // `<link rel="modulepreload">` intact.
|
|---|
| 63 | /** @type {WeakMap<import("../Compilation"), Set<string>>} */
|
|---|
| 64 | const stylesheetEntriesPerCompilation = new WeakMap();
|
|---|
| 65 | compiler.hooks.finishMake.tapAsync(PLUGIN_NAME, (compilation, callback) => {
|
|---|
| 66 | /** @type {Promise<void>[]} */
|
|---|
| 67 | const promises = [];
|
|---|
| 68 | /** @type {Set<string>} */
|
|---|
| 69 | const stylesheetEntries = new Set();
|
|---|
| 70 | stylesheetEntriesPerCompilation.set(compilation, stylesheetEntries);
|
|---|
| 71 |
|
|---|
| 72 | for (const module of compilation.modules) {
|
|---|
| 73 | if (module.type !== HTML_MODULE_TYPE) continue;
|
|---|
| 74 | const buildInfo = module.buildInfo;
|
|---|
| 75 | const htmlEntryScripts =
|
|---|
| 76 | buildInfo &&
|
|---|
| 77 | /** @type {Record<string, EntryScriptInfo[]> | undefined} */
|
|---|
| 78 | (buildInfo.htmlEntryScripts);
|
|---|
| 79 | if (!htmlEntryScripts) continue;
|
|---|
| 80 |
|
|---|
| 81 | const context = /** @type {string} */ (module.context);
|
|---|
| 82 |
|
|---|
| 83 | for (const [groupKind, group] of Object.entries(htmlEntryScripts)) {
|
|---|
| 84 | // Only the script chains (`classic`, `esm-script`) need a
|
|---|
| 85 | // shared runtime via leader-only `dependOn` — the others
|
|---|
| 86 | // either preload without executing (`modulepreload`) or
|
|---|
| 87 | // produce CSS chunks (`stylesheet`) which have no runtime
|
|---|
| 88 | // to share. CSS entries must NOT chain into a JS leader
|
|---|
| 89 | // either, because the resulting chunk would mix a CSS
|
|---|
| 90 | // stylesheet with a JS runtime.
|
|---|
| 91 | const isChainGroup =
|
|---|
| 92 | groupKind !== "modulepreload" && groupKind !== "stylesheet";
|
|---|
| 93 | /** @type {string | undefined} */
|
|---|
| 94 | let leaderName;
|
|---|
| 95 | for (const entry of group) {
|
|---|
| 96 | const dependOn =
|
|---|
| 97 | isChainGroup && leaderName !== undefined
|
|---|
| 98 | ? [leaderName]
|
|---|
| 99 | : undefined;
|
|---|
| 100 | if (isChainGroup && leaderName === undefined) {
|
|---|
| 101 | leaderName = entry.entryName;
|
|---|
| 102 | }
|
|---|
| 103 | if (groupKind === "stylesheet") {
|
|---|
| 104 | stylesheetEntries.add(entry.entryName);
|
|---|
| 105 | }
|
|---|
| 106 | promises.push(
|
|---|
| 107 | new Promise((resolve, reject) => {
|
|---|
| 108 | compilation.addEntry(
|
|---|
| 109 | context,
|
|---|
| 110 | EntryPlugin.createDependency(entry.request, {
|
|---|
| 111 | name: entry.entryName
|
|---|
| 112 | }),
|
|---|
| 113 | {
|
|---|
| 114 | name: entry.entryName,
|
|---|
| 115 | // Each script src / modulepreload entry gets its own
|
|---|
| 116 | // filename derived from the synthetic entry name so it
|
|---|
| 117 | // doesn't collide with the user's `output.filename`.
|
|---|
| 118 | // For CSS entries the JS `filename` is irrelevant (no
|
|---|
| 119 | // `.js` is emitted) — the CSS file's name is set on the
|
|---|
| 120 | // chunk via `cssFilenameTemplate` in `afterChunks` below.
|
|---|
| 121 | filename:
|
|---|
| 122 | compilation.outputOptions.chunkFilename || "[name].js",
|
|---|
| 123 | dependOn
|
|---|
| 124 | },
|
|---|
| 125 | (err) => {
|
|---|
| 126 | if (err) reject(err);
|
|---|
| 127 | else resolve();
|
|---|
| 128 | }
|
|---|
| 129 | );
|
|---|
| 130 | })
|
|---|
| 131 | );
|
|---|
| 132 | }
|
|---|
| 133 | }
|
|---|
| 134 | }
|
|---|
| 135 |
|
|---|
| 136 | Promise.all(promises).then(
|
|---|
| 137 | () => callback(),
|
|---|
| 138 | (err) => callback(err)
|
|---|
| 139 | );
|
|---|
| 140 | });
|
|---|
| 141 |
|
|---|
| 142 | compiler.hooks.compilation.tap(
|
|---|
| 143 | PLUGIN_NAME,
|
|---|
| 144 | (compilation, { normalModuleFactory }) => {
|
|---|
| 145 | // CSS entries created by `<link rel="stylesheet">` in HTML need
|
|---|
| 146 | // their `.css` filename set via `chunk.cssFilenameTemplate`
|
|---|
| 147 | // (the field `CssModulesPlugin.getChunkFilenameTemplate` reads).
|
|---|
| 148 | // Compilation only flows `options.filename` to `chunk.filenameTemplate`,
|
|---|
| 149 | // which controls JS emit — there's no entry-level `cssFilename`.
|
|---|
| 150 | // Set it ourselves after chunks are created so each stylesheet
|
|---|
| 151 | // entry emits to a distinct file derived from `output.cssFilename`
|
|---|
| 152 | // (or `output.cssChunkFilename` for non-initial CSS chunks).
|
|---|
| 153 | compilation.hooks.afterChunks.tap(PLUGIN_NAME, () => {
|
|---|
| 154 | const stylesheetEntries =
|
|---|
| 155 | stylesheetEntriesPerCompilation.get(compilation);
|
|---|
| 156 | if (!stylesheetEntries || stylesheetEntries.size === 0) return;
|
|---|
| 157 | for (const entryName of stylesheetEntries) {
|
|---|
| 158 | const entrypoint = compilation.entrypoints.get(entryName);
|
|---|
| 159 | if (!entrypoint) continue;
|
|---|
| 160 | const chunk = entrypoint.getEntrypointChunk();
|
|---|
| 161 | if (!chunk) continue;
|
|---|
| 162 | // Each html-derived stylesheet entry uses the
|
|---|
| 163 | // `cssChunkFilename` template — even though the entry
|
|---|
| 164 | // chunk technically `canBeInitial()`, we deliberately
|
|---|
| 165 | // avoid `cssFilename` here because that template often
|
|---|
| 166 | // has no per-entry placeholder (it's derived from
|
|---|
| 167 | // `output.filename`, which can be a literal like
|
|---|
| 168 | // `bundle0.js`), and multiple `<link rel="stylesheet">`
|
|---|
| 169 | // tags would then collide on the same emitted `.css`
|
|---|
| 170 | // file. `cssChunkFilename` is derived from
|
|---|
| 171 | // `output.chunkFilename` which webpack auto-extends
|
|---|
| 172 | // with `[id].` when needed, guaranteeing uniqueness.
|
|---|
| 173 | chunk.cssFilenameTemplate =
|
|---|
| 174 | compilation.outputOptions.cssChunkFilename;
|
|---|
| 175 | }
|
|---|
| 176 | });
|
|---|
| 177 | compilation.dependencyFactories.set(
|
|---|
| 178 | HtmlSourceDependency,
|
|---|
| 179 | normalModuleFactory
|
|---|
| 180 | );
|
|---|
| 181 | compilation.dependencyTemplates.set(
|
|---|
| 182 | HtmlSourceDependency,
|
|---|
| 183 | new HtmlSourceDependency.Template()
|
|---|
| 184 | );
|
|---|
| 185 | compilation.dependencyFactories.set(
|
|---|
| 186 | HtmlScriptSrcDependency,
|
|---|
| 187 | normalModuleFactory
|
|---|
| 188 | );
|
|---|
| 189 | compilation.dependencyTemplates.set(
|
|---|
| 190 | HtmlScriptSrcDependency,
|
|---|
| 191 | new HtmlScriptSrcDependency.Template()
|
|---|
| 192 | );
|
|---|
| 193 | // Inline `<script>` content is bundled as its own entry — the
|
|---|
| 194 | // same pipeline that handles `<script src>` — via a
|
|---|
| 195 | // `data:text/javascript,...` request. The dependency
|
|---|
| 196 | // template rewrites the original tag to `<script src=…>`.
|
|---|
| 197 | compilation.dependencyFactories.set(
|
|---|
| 198 | HtmlInlineScriptDependency,
|
|---|
| 199 | normalModuleFactory
|
|---|
| 200 | );
|
|---|
| 201 | compilation.dependencyTemplates.set(
|
|---|
| 202 | HtmlInlineScriptDependency,
|
|---|
| 203 | new HtmlInlineScriptDependency.Template()
|
|---|
| 204 | );
|
|---|
| 205 | // Inline `<style>` content is routed through the CSS pipeline
|
|---|
| 206 | // as a `data:text/css` module. The dependency template reads
|
|---|
| 207 | // the processed CSS text from the CSS module's code
|
|---|
| 208 | // generation data (`css-text` channel set by CssGenerator
|
|---|
| 209 | // when `exportType` is `"text"`).
|
|---|
| 210 | compilation.dependencyFactories.set(
|
|---|
| 211 | HtmlInlineStyleDependency,
|
|---|
| 212 | normalModuleFactory
|
|---|
| 213 | );
|
|---|
| 214 | compilation.dependencyTemplates.set(
|
|---|
| 215 | HtmlInlineStyleDependency,
|
|---|
| 216 | new HtmlInlineStyleDependency.Template()
|
|---|
| 217 | );
|
|---|
| 218 | compilation.dependencyTemplates.set(
|
|---|
| 219 | StaticExportsDependency,
|
|---|
| 220 | new StaticExportsDependency.Template()
|
|---|
| 221 | );
|
|---|
| 222 | // `ConstDependency` is used by HtmlParser to insert
|
|---|
| 223 | // ` type="module"` into the rewritten <script> tag when
|
|---|
| 224 | // `output.module` is on. Register its template so the HTML
|
|---|
| 225 | // generator runs the insertion.
|
|---|
| 226 | compilation.dependencyTemplates.set(
|
|---|
| 227 | ConstDependency,
|
|---|
| 228 | new ConstDependency.Template()
|
|---|
| 229 | );
|
|---|
| 230 | const cssEnabled = Boolean(
|
|---|
| 231 | compiler.options.experiments && compiler.options.experiments.css
|
|---|
| 232 | );
|
|---|
| 233 | normalModuleFactory.hooks.createParser
|
|---|
| 234 | .for(HTML_MODULE_TYPE)
|
|---|
| 235 | .tap(
|
|---|
| 236 | PLUGIN_NAME,
|
|---|
| 237 | () =>
|
|---|
| 238 | new HtmlParser(
|
|---|
| 239 | compilation.outputOptions.hashFunction,
|
|---|
| 240 | compiler.context,
|
|---|
| 241 | compilation.outputOptions.module,
|
|---|
| 242 | cssEnabled
|
|---|
| 243 | )
|
|---|
| 244 | );
|
|---|
| 245 |
|
|---|
| 246 | normalModuleFactory.hooks.createGenerator
|
|---|
| 247 | .for(HTML_MODULE_TYPE)
|
|---|
| 248 | .tap(PLUGIN_NAME, (generatorOptions) => {
|
|---|
| 249 | compiler.validate(
|
|---|
| 250 | () => getSchema("HtmlGeneratorOptions"),
|
|---|
| 251 | generatorOptions,
|
|---|
| 252 | generatorValidationOptions,
|
|---|
| 253 | (options) =>
|
|---|
| 254 | require("../../schemas/plugins/HtmlGeneratorOptions.check")(
|
|---|
| 255 | options
|
|---|
| 256 | )
|
|---|
| 257 | );
|
|---|
| 258 | return new HtmlGenerator(generatorOptions, compilation.moduleGraph);
|
|---|
| 259 | });
|
|---|
| 260 |
|
|---|
| 261 | NormalModule.getCompilationHooks(compilation).processResult.tap(
|
|---|
| 262 | PLUGIN_NAME,
|
|---|
| 263 | (result, module) => {
|
|---|
| 264 | if (module.type === HTML_MODULE_TYPE) {
|
|---|
| 265 | const [source, ...rest] = result;
|
|---|
| 266 |
|
|---|
| 267 | return [removeBOM(source), ...rest];
|
|---|
| 268 | }
|
|---|
| 269 |
|
|---|
| 270 | return result;
|
|---|
| 271 | }
|
|---|
| 272 | );
|
|---|
| 273 |
|
|---|
| 274 | // Emit extracted `.html` files for any HTML module that opted
|
|---|
| 275 | // into extraction. The opt-in is computed by
|
|---|
| 276 | // `HtmlGenerator#_shouldExtract`: `module.generator.html.extract:
|
|---|
| 277 | // true` always extracts, `false` never extracts, and when
|
|---|
| 278 | // `extract` is unset the generator extracts iff the HTML module
|
|---|
| 279 | // is a compilation entry — the iteration below picks up only
|
|---|
| 280 | // modules whose generator reported the `html` source type, so
|
|---|
| 281 | // that decision is honored implicitly. The HTML content is read
|
|---|
| 282 | // from the generator's secondary `"html"` source type (see
|
|---|
| 283 | // HtmlGenerator#generate). The filename template comes from
|
|---|
| 284 | // `output.htmlFilename` (initial chunks) or
|
|---|
| 285 | // `output.htmlChunkFilename` (non-initial chunks), mirroring
|
|---|
| 286 | // the CSS pipeline. Path data follows the asset-module pattern —
|
|---|
| 287 | // `module` + a relative source `filename`, with `chunk`
|
|---|
| 288 | // intentionally omitted so `[name]` resolves to the HTML
|
|---|
| 289 | // source's basename (e.g. `page` for `./page.html`) rather
|
|---|
| 290 | // than the importing chunk's name (e.g. `main`). A per-module
|
|---|
| 291 | // content hash is computed from the rewritten HTML so the
|
|---|
| 292 | // template's `[contenthash]` placeholder works; the
|
|---|
| 293 | // compilation hash is also forwarded so `[fullhash]` /
|
|---|
| 294 | // `[hash]` work in user-supplied templates.
|
|---|
| 295 | const {
|
|---|
| 296 | getUndoPath,
|
|---|
| 297 | makePathsRelative
|
|---|
| 298 | } = require("../util/identifier");
|
|---|
| 299 | const createHash = require("../util/createHash");
|
|---|
| 300 | const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
|
|---|
| 301 | const CssUrlDependency = require("../dependencies/CssUrlDependency");
|
|---|
| 302 |
|
|---|
| 303 | const autoPlaceholder = CssUrlDependency.PUBLIC_PATH_AUTO;
|
|---|
| 304 |
|
|---|
| 305 | compilation.hooks.renderManifest.tap(
|
|---|
| 306 | PLUGIN_NAME,
|
|---|
| 307 | (result, { chunk, codeGenerationResults, hash: compilationHash }) => {
|
|---|
| 308 | // HMR's `HotUpdateChunk`s flow through the same hook
|
|---|
| 309 | // but aren't real output chunks — extracting `.html`
|
|---|
| 310 | // for them would create stray hot-update HTML files.
|
|---|
| 311 | // `CssModulesPlugin` early-returns for the same reason.
|
|---|
| 312 | if (chunk instanceof HotUpdateChunk) return result;
|
|---|
| 313 | const { chunkGraph } = compilation;
|
|---|
| 314 | const modules =
|
|---|
| 315 | chunkGraph.getOrderedChunkModulesIterableBySourceType(
|
|---|
| 316 | chunk,
|
|---|
| 317 | HTML_TYPE,
|
|---|
| 318 | compareModulesByFullName(compilation.compiler)
|
|---|
| 319 | );
|
|---|
| 320 | if (!modules) return result;
|
|---|
| 321 | const outputOptions = compilation.outputOptions;
|
|---|
| 322 | for (const module of modules) {
|
|---|
| 323 | const normalModule = /** @type {NormalModule} */ (module);
|
|---|
| 324 | const codeGenResult = codeGenerationResults.get(
|
|---|
| 325 | module,
|
|---|
| 326 | chunk.runtime
|
|---|
| 327 | );
|
|---|
| 328 | const placeholderSource = codeGenResult.sources.get(HTML_TYPE);
|
|---|
| 329 | if (!placeholderSource) continue;
|
|---|
| 330 |
|
|---|
| 331 | const filenameTemplate = chunk.canBeInitial()
|
|---|
| 332 | ? outputOptions.htmlFilename
|
|---|
| 333 | : outputOptions.htmlChunkFilename;
|
|---|
| 334 |
|
|---|
| 335 | const sourceFilename = makePathsRelative(
|
|---|
| 336 | compiler.context,
|
|---|
| 337 | /** @type {string} */
|
|---|
| 338 | (normalModule.getResource() || normalModule.resource),
|
|---|
| 339 | compiler.root
|
|---|
| 340 | ).replace(/^\.\//, "");
|
|---|
| 341 |
|
|---|
| 342 | const placeholderContent = /** @type {string} */ (
|
|---|
| 343 | placeholderSource.source()
|
|---|
| 344 | );
|
|---|
| 345 | const hashInput = createHash(outputOptions.hashFunction);
|
|---|
| 346 | if (outputOptions.hashSalt) {
|
|---|
| 347 | hashInput.update(outputOptions.hashSalt);
|
|---|
| 348 | }
|
|---|
| 349 | hashInput.update(placeholderContent);
|
|---|
| 350 | const fullContentHash = /** @type {string} */ (
|
|---|
| 351 | hashInput.digest(outputOptions.hashDigest)
|
|---|
| 352 | );
|
|---|
| 353 | const contentHash = nonNumericOnlyHash(
|
|---|
| 354 | fullContentHash,
|
|---|
| 355 | outputOptions.hashDigestLength
|
|---|
| 356 | );
|
|---|
| 357 |
|
|---|
| 358 | const { path: filename, info } = compilation.getPathWithInfo(
|
|---|
| 359 | /** @type {import("../TemplatedPathPlugin").TemplatePath} */
|
|---|
| 360 | (filenameTemplate),
|
|---|
| 361 | {
|
|---|
| 362 | module,
|
|---|
| 363 | runtime: chunk.runtime,
|
|---|
| 364 | chunkGraph,
|
|---|
| 365 | contentHash,
|
|---|
| 366 | contentHashType: HTML_TYPE,
|
|---|
| 367 | filename: sourceFilename,
|
|---|
| 368 | hash: compilationHash
|
|---|
| 369 | }
|
|---|
| 370 | );
|
|---|
| 371 |
|
|---|
| 372 | // Resolve any remaining `[webpack/auto]` placeholders to
|
|---|
| 373 | // an undo path computed from the emitted HTML's location.
|
|---|
| 374 | // Without this, an `output.htmlFilename` that emits into
|
|---|
| 375 | // a subdirectory (e.g. `pages/[name].html`) would leave
|
|---|
| 376 | // asset URLs like `image.png` and chunk URLs like
|
|---|
| 377 | // `main.js` root-relative, so the browser would resolve
|
|---|
| 378 | // them under the HTML's directory instead of the
|
|---|
| 379 | // `output.path` root.
|
|---|
| 380 | const undoPath = getUndoPath(
|
|---|
| 381 | filename,
|
|---|
| 382 | /** @type {string} */ (outputOptions.path),
|
|---|
| 383 | false
|
|---|
| 384 | );
|
|---|
| 385 | const finalContent = placeholderContent
|
|---|
| 386 | .split(autoPlaceholder)
|
|---|
| 387 | .join(undoPath);
|
|---|
| 388 | const finalSource = new RawSource(finalContent);
|
|---|
| 389 | // The same HTML module can land in multiple chunks
|
|---|
| 390 | // with different `output.htmlFilename` /
|
|---|
| 391 | // `output.htmlChunkFilename` shapes, which means
|
|---|
| 392 | // different `undoPath`s and therefore different
|
|---|
| 393 | // final content for the same module id. Include
|
|---|
| 394 | // the emitted filename in the asset cache key and
|
|---|
| 395 | // the post-undo-path content in the hash, so the
|
|---|
| 396 | // asset cache can't reuse one variant's bytes at
|
|---|
| 397 | // another variant's URL.
|
|---|
| 398 | const finalHash = createHash(outputOptions.hashFunction);
|
|---|
| 399 | if (outputOptions.hashSalt) {
|
|---|
| 400 | finalHash.update(outputOptions.hashSalt);
|
|---|
| 401 | }
|
|---|
| 402 | finalHash.update(finalContent);
|
|---|
| 403 | const finalContentHash = nonNumericOnlyHash(
|
|---|
| 404 | /** @type {string} */ (
|
|---|
| 405 | finalHash.digest(outputOptions.hashDigest)
|
|---|
| 406 | ),
|
|---|
| 407 | outputOptions.hashDigestLength
|
|---|
| 408 | );
|
|---|
| 409 |
|
|---|
| 410 | result.push({
|
|---|
| 411 | render: () => finalSource,
|
|---|
| 412 | filename,
|
|---|
| 413 | info,
|
|---|
| 414 | auxiliary: true,
|
|---|
| 415 | identifier: `htmlModule${chunkGraph.getModuleId(
|
|---|
| 416 | module
|
|---|
| 417 | )}|${filename}`,
|
|---|
| 418 | hash: finalContentHash
|
|---|
| 419 | });
|
|---|
| 420 | }
|
|---|
| 421 | return result;
|
|---|
| 422 | }
|
|---|
| 423 | );
|
|---|
| 424 | }
|
|---|
| 425 | );
|
|---|
| 426 | }
|
|---|
| 427 | }
|
|---|
| 428 |
|
|---|
| 429 | module.exports = HtmlModulesPlugin;
|
|---|