source: imaps-frontend/node_modules/webpack/lib/HotModuleReplacementPlugin.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 29.1 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { SyncBailHook } = require("tapable");
9const { RawSource } = require("webpack-sources");
10const ChunkGraph = require("./ChunkGraph");
11const Compilation = require("./Compilation");
12const HotUpdateChunk = require("./HotUpdateChunk");
13const NormalModule = require("./NormalModule");
14const RuntimeGlobals = require("./RuntimeGlobals");
15const WebpackError = require("./WebpackError");
16const ConstDependency = require("./dependencies/ConstDependency");
17const ImportMetaHotAcceptDependency = require("./dependencies/ImportMetaHotAcceptDependency");
18const ImportMetaHotDeclineDependency = require("./dependencies/ImportMetaHotDeclineDependency");
19const ModuleHotAcceptDependency = require("./dependencies/ModuleHotAcceptDependency");
20const ModuleHotDeclineDependency = require("./dependencies/ModuleHotDeclineDependency");
21const HotModuleReplacementRuntimeModule = require("./hmr/HotModuleReplacementRuntimeModule");
22const JavascriptParser = require("./javascript/JavascriptParser");
23const {
24 evaluateToIdentifier
25} = require("./javascript/JavascriptParserHelpers");
26const { find, isSubset } = require("./util/SetHelpers");
27const TupleSet = require("./util/TupleSet");
28const { compareModulesById } = require("./util/comparators");
29const {
30 getRuntimeKey,
31 keyToRuntime,
32 forEachRuntime,
33 mergeRuntimeOwned,
34 subtractRuntime,
35 intersectRuntime
36} = require("./util/runtime");
37
38const {
39 JAVASCRIPT_MODULE_TYPE_AUTO,
40 JAVASCRIPT_MODULE_TYPE_DYNAMIC,
41 JAVASCRIPT_MODULE_TYPE_ESM,
42 WEBPACK_MODULE_TYPE_RUNTIME
43} = require("./ModuleTypeConstants");
44
45/** @typedef {import("estree").CallExpression} CallExpression */
46/** @typedef {import("estree").Expression} Expression */
47/** @typedef {import("estree").SpreadElement} SpreadElement */
48/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputNormalized */
49/** @typedef {import("./Chunk")} Chunk */
50/** @typedef {import("./Chunk").ChunkId} ChunkId */
51/** @typedef {import("./ChunkGraph").ModuleId} ModuleId */
52/** @typedef {import("./Compilation").AssetInfo} AssetInfo */
53/** @typedef {import("./Compiler")} Compiler */
54/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
55/** @typedef {import("./Module")} Module */
56/** @typedef {import("./Module").BuildInfo} BuildInfo */
57/** @typedef {import("./RuntimeModule")} RuntimeModule */
58/** @typedef {import("./javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
59/** @typedef {import("./javascript/JavascriptParserHelpers").Range} Range */
60/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
61
62/**
63 * @typedef {object} HMRJavascriptParserHooks
64 * @property {SyncBailHook<[Expression | SpreadElement, string[]], void>} hotAcceptCallback
65 * @property {SyncBailHook<[CallExpression, string[]], void>} hotAcceptWithoutCallback
66 */
67
68/** @typedef {{ updatedChunkIds: Set<ChunkId>, removedChunkIds: Set<ChunkId>, removedModules: Set<Module>, filename: string, assetInfo: AssetInfo }} HotUpdateMainContentByRuntimeItem */
69/** @typedef {Map<string, HotUpdateMainContentByRuntimeItem>} HotUpdateMainContentByRuntime */
70
71/** @type {WeakMap<JavascriptParser, HMRJavascriptParserHooks>} */
72const parserHooksMap = new WeakMap();
73
74const PLUGIN_NAME = "HotModuleReplacementPlugin";
75
76class HotModuleReplacementPlugin {
77 /**
78 * @param {JavascriptParser} parser the parser
79 * @returns {HMRJavascriptParserHooks} the attached hooks
80 */
81 static getParserHooks(parser) {
82 if (!(parser instanceof JavascriptParser)) {
83 throw new TypeError(
84 "The 'parser' argument must be an instance of JavascriptParser"
85 );
86 }
87 let hooks = parserHooksMap.get(parser);
88 if (hooks === undefined) {
89 hooks = {
90 hotAcceptCallback: new SyncBailHook(["expression", "requests"]),
91 hotAcceptWithoutCallback: new SyncBailHook(["expression", "requests"])
92 };
93 parserHooksMap.set(parser, hooks);
94 }
95 return hooks;
96 }
97
98 /**
99 * @param {object=} options options
100 */
101 constructor(options) {
102 this.options = options || {};
103 }
104
105 /**
106 * Apply the plugin
107 * @param {Compiler} compiler the compiler instance
108 * @returns {void}
109 */
110 apply(compiler) {
111 const { _backCompat: backCompat } = compiler;
112 if (compiler.options.output.strictModuleErrorHandling === undefined)
113 compiler.options.output.strictModuleErrorHandling = true;
114 const runtimeRequirements = [RuntimeGlobals.module];
115
116 /**
117 * @param {JavascriptParser} parser the parser
118 * @param {typeof ModuleHotAcceptDependency} ParamDependency dependency
119 * @returns {(expr: CallExpression) => boolean | undefined} callback
120 */
121 const createAcceptHandler = (parser, ParamDependency) => {
122 const { hotAcceptCallback, hotAcceptWithoutCallback } =
123 HotModuleReplacementPlugin.getParserHooks(parser);
124
125 return expr => {
126 const module = parser.state.module;
127 const dep = new ConstDependency(
128 `${module.moduleArgument}.hot.accept`,
129 /** @type {Range} */ (expr.callee.range),
130 runtimeRequirements
131 );
132 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
133 module.addPresentationalDependency(dep);
134 /** @type {BuildInfo} */
135 (module.buildInfo).moduleConcatenationBailout =
136 "Hot Module Replacement";
137
138 if (expr.arguments.length >= 1) {
139 const arg = parser.evaluateExpression(expr.arguments[0]);
140 /** @type {BasicEvaluatedExpression[]} */
141 let params = [];
142 if (arg.isString()) {
143 params = [arg];
144 } else if (arg.isArray()) {
145 params =
146 /** @type {BasicEvaluatedExpression[]} */
147 (arg.items).filter(param => param.isString());
148 }
149 /** @type {string[]} */
150 const requests = [];
151 if (params.length > 0) {
152 for (const [idx, param] of params.entries()) {
153 const request = /** @type {string} */ (param.string);
154 const dep = new ParamDependency(
155 request,
156 /** @type {Range} */ (param.range)
157 );
158 dep.optional = true;
159 dep.loc = Object.create(
160 /** @type {DependencyLocation} */ (expr.loc)
161 );
162 dep.loc.index = idx;
163 module.addDependency(dep);
164 requests.push(request);
165 }
166 if (expr.arguments.length > 1) {
167 hotAcceptCallback.call(expr.arguments[1], requests);
168 for (let i = 1; i < expr.arguments.length; i++) {
169 parser.walkExpression(expr.arguments[i]);
170 }
171 return true;
172 }
173 hotAcceptWithoutCallback.call(expr, requests);
174 return true;
175 }
176 }
177 parser.walkExpressions(expr.arguments);
178 return true;
179 };
180 };
181
182 /**
183 * @param {JavascriptParser} parser the parser
184 * @param {typeof ModuleHotDeclineDependency} ParamDependency dependency
185 * @returns {(expr: CallExpression) => boolean | undefined} callback
186 */
187 const createDeclineHandler = (parser, ParamDependency) => expr => {
188 const module = parser.state.module;
189 const dep = new ConstDependency(
190 `${module.moduleArgument}.hot.decline`,
191 /** @type {Range} */ (expr.callee.range),
192 runtimeRequirements
193 );
194 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
195 module.addPresentationalDependency(dep);
196 /** @type {BuildInfo} */
197 (module.buildInfo).moduleConcatenationBailout = "Hot Module Replacement";
198 if (expr.arguments.length === 1) {
199 const arg = parser.evaluateExpression(expr.arguments[0]);
200 /** @type {BasicEvaluatedExpression[]} */
201 let params = [];
202 if (arg.isString()) {
203 params = [arg];
204 } else if (arg.isArray()) {
205 params =
206 /** @type {BasicEvaluatedExpression[]} */
207 (arg.items).filter(param => param.isString());
208 }
209 for (const [idx, param] of params.entries()) {
210 const dep = new ParamDependency(
211 /** @type {string} */ (param.string),
212 /** @type {Range} */ (param.range)
213 );
214 dep.optional = true;
215 dep.loc = Object.create(/** @type {DependencyLocation} */ (expr.loc));
216 dep.loc.index = idx;
217 module.addDependency(dep);
218 }
219 }
220 return true;
221 };
222
223 /**
224 * @param {JavascriptParser} parser the parser
225 * @returns {(expr: Expression) => boolean | undefined} callback
226 */
227 const createHMRExpressionHandler = parser => expr => {
228 const module = parser.state.module;
229 const dep = new ConstDependency(
230 `${module.moduleArgument}.hot`,
231 /** @type {Range} */ (expr.range),
232 runtimeRequirements
233 );
234 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
235 module.addPresentationalDependency(dep);
236 /** @type {BuildInfo} */
237 (module.buildInfo).moduleConcatenationBailout = "Hot Module Replacement";
238 return true;
239 };
240
241 /**
242 * @param {JavascriptParser} parser the parser
243 * @returns {void}
244 */
245 const applyModuleHot = parser => {
246 parser.hooks.evaluateIdentifier.for("module.hot").tap(
247 {
248 name: PLUGIN_NAME,
249 before: "NodeStuffPlugin"
250 },
251 expr =>
252 evaluateToIdentifier(
253 "module.hot",
254 "module",
255 () => ["hot"],
256 true
257 )(expr)
258 );
259 parser.hooks.call
260 .for("module.hot.accept")
261 .tap(
262 PLUGIN_NAME,
263 createAcceptHandler(parser, ModuleHotAcceptDependency)
264 );
265 parser.hooks.call
266 .for("module.hot.decline")
267 .tap(
268 PLUGIN_NAME,
269 createDeclineHandler(parser, ModuleHotDeclineDependency)
270 );
271 parser.hooks.expression
272 .for("module.hot")
273 .tap(PLUGIN_NAME, createHMRExpressionHandler(parser));
274 };
275
276 /**
277 * @param {JavascriptParser} parser the parser
278 * @returns {void}
279 */
280 const applyImportMetaHot = parser => {
281 parser.hooks.evaluateIdentifier
282 .for("import.meta.webpackHot")
283 .tap(PLUGIN_NAME, expr =>
284 evaluateToIdentifier(
285 "import.meta.webpackHot",
286 "import.meta",
287 () => ["webpackHot"],
288 true
289 )(expr)
290 );
291 parser.hooks.call
292 .for("import.meta.webpackHot.accept")
293 .tap(
294 PLUGIN_NAME,
295 createAcceptHandler(parser, ImportMetaHotAcceptDependency)
296 );
297 parser.hooks.call
298 .for("import.meta.webpackHot.decline")
299 .tap(
300 PLUGIN_NAME,
301 createDeclineHandler(parser, ImportMetaHotDeclineDependency)
302 );
303 parser.hooks.expression
304 .for("import.meta.webpackHot")
305 .tap(PLUGIN_NAME, createHMRExpressionHandler(parser));
306 };
307
308 compiler.hooks.compilation.tap(
309 PLUGIN_NAME,
310 (compilation, { normalModuleFactory }) => {
311 // This applies the HMR plugin only to the targeted compiler
312 // It should not affect child compilations
313 if (compilation.compiler !== compiler) return;
314
315 // #region module.hot.* API
316 compilation.dependencyFactories.set(
317 ModuleHotAcceptDependency,
318 normalModuleFactory
319 );
320 compilation.dependencyTemplates.set(
321 ModuleHotAcceptDependency,
322 new ModuleHotAcceptDependency.Template()
323 );
324 compilation.dependencyFactories.set(
325 ModuleHotDeclineDependency,
326 normalModuleFactory
327 );
328 compilation.dependencyTemplates.set(
329 ModuleHotDeclineDependency,
330 new ModuleHotDeclineDependency.Template()
331 );
332 // #endregion
333
334 // #region import.meta.webpackHot.* API
335 compilation.dependencyFactories.set(
336 ImportMetaHotAcceptDependency,
337 normalModuleFactory
338 );
339 compilation.dependencyTemplates.set(
340 ImportMetaHotAcceptDependency,
341 new ImportMetaHotAcceptDependency.Template()
342 );
343 compilation.dependencyFactories.set(
344 ImportMetaHotDeclineDependency,
345 normalModuleFactory
346 );
347 compilation.dependencyTemplates.set(
348 ImportMetaHotDeclineDependency,
349 new ImportMetaHotDeclineDependency.Template()
350 );
351 // #endregion
352
353 let hotIndex = 0;
354 /** @type {Record<string, string>} */
355 const fullHashChunkModuleHashes = {};
356 /** @type {Record<string, string>} */
357 const chunkModuleHashes = {};
358
359 compilation.hooks.record.tap(PLUGIN_NAME, (compilation, records) => {
360 if (records.hash === compilation.hash) return;
361 const chunkGraph = compilation.chunkGraph;
362 records.hash = compilation.hash;
363 records.hotIndex = hotIndex;
364 records.fullHashChunkModuleHashes = fullHashChunkModuleHashes;
365 records.chunkModuleHashes = chunkModuleHashes;
366 records.chunkHashes = {};
367 records.chunkRuntime = {};
368 for (const chunk of compilation.chunks) {
369 const chunkId = /** @type {ChunkId} */ (chunk.id);
370 records.chunkHashes[chunkId] = chunk.hash;
371 records.chunkRuntime[chunkId] = getRuntimeKey(chunk.runtime);
372 }
373 records.chunkModuleIds = {};
374 for (const chunk of compilation.chunks) {
375 records.chunkModuleIds[/** @type {ChunkId} */ (chunk.id)] =
376 Array.from(
377 chunkGraph.getOrderedChunkModulesIterable(
378 chunk,
379 compareModulesById(chunkGraph)
380 ),
381 m => chunkGraph.getModuleId(m)
382 );
383 }
384 });
385 /** @type {TupleSet<[Module, Chunk]>} */
386 const updatedModules = new TupleSet();
387 /** @type {TupleSet<[Module, Chunk]>} */
388 const fullHashModules = new TupleSet();
389 /** @type {TupleSet<[Module, RuntimeSpec]>} */
390 const nonCodeGeneratedModules = new TupleSet();
391 compilation.hooks.fullHash.tap(PLUGIN_NAME, hash => {
392 const chunkGraph = compilation.chunkGraph;
393 const records = compilation.records;
394 for (const chunk of compilation.chunks) {
395 /**
396 * @param {Module} module module
397 * @returns {string} module hash
398 */
399 const getModuleHash = module => {
400 if (
401 compilation.codeGenerationResults.has(module, chunk.runtime)
402 ) {
403 return compilation.codeGenerationResults.getHash(
404 module,
405 chunk.runtime
406 );
407 }
408 nonCodeGeneratedModules.add(module, chunk.runtime);
409 return chunkGraph.getModuleHash(module, chunk.runtime);
410 };
411 const fullHashModulesInThisChunk =
412 chunkGraph.getChunkFullHashModulesSet(chunk);
413 if (fullHashModulesInThisChunk !== undefined) {
414 for (const module of fullHashModulesInThisChunk) {
415 fullHashModules.add(module, chunk);
416 }
417 }
418 const modules = chunkGraph.getChunkModulesIterable(chunk);
419 if (modules !== undefined) {
420 if (records.chunkModuleHashes) {
421 if (fullHashModulesInThisChunk !== undefined) {
422 for (const module of modules) {
423 const key = `${chunk.id}|${module.identifier()}`;
424 const hash = getModuleHash(module);
425 if (
426 fullHashModulesInThisChunk.has(
427 /** @type {RuntimeModule} */ (module)
428 )
429 ) {
430 if (records.fullHashChunkModuleHashes[key] !== hash) {
431 updatedModules.add(module, chunk);
432 }
433 fullHashChunkModuleHashes[key] = hash;
434 } else {
435 if (records.chunkModuleHashes[key] !== hash) {
436 updatedModules.add(module, chunk);
437 }
438 chunkModuleHashes[key] = hash;
439 }
440 }
441 } else {
442 for (const module of modules) {
443 const key = `${chunk.id}|${module.identifier()}`;
444 const hash = getModuleHash(module);
445 if (records.chunkModuleHashes[key] !== hash) {
446 updatedModules.add(module, chunk);
447 }
448 chunkModuleHashes[key] = hash;
449 }
450 }
451 } else if (fullHashModulesInThisChunk !== undefined) {
452 for (const module of modules) {
453 const key = `${chunk.id}|${module.identifier()}`;
454 const hash = getModuleHash(module);
455 if (
456 fullHashModulesInThisChunk.has(
457 /** @type {RuntimeModule} */ (module)
458 )
459 ) {
460 fullHashChunkModuleHashes[key] = hash;
461 } else {
462 chunkModuleHashes[key] = hash;
463 }
464 }
465 } else {
466 for (const module of modules) {
467 const key = `${chunk.id}|${module.identifier()}`;
468 const hash = getModuleHash(module);
469 chunkModuleHashes[key] = hash;
470 }
471 }
472 }
473 }
474
475 hotIndex = records.hotIndex || 0;
476 if (updatedModules.size > 0) hotIndex++;
477
478 hash.update(`${hotIndex}`);
479 });
480 compilation.hooks.processAssets.tap(
481 {
482 name: PLUGIN_NAME,
483 stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
484 },
485 () => {
486 const chunkGraph = compilation.chunkGraph;
487 const records = compilation.records;
488 if (records.hash === compilation.hash) return;
489 if (
490 !records.chunkModuleHashes ||
491 !records.chunkHashes ||
492 !records.chunkModuleIds
493 ) {
494 return;
495 }
496 for (const [module, chunk] of fullHashModules) {
497 const key = `${chunk.id}|${module.identifier()}`;
498 const hash = nonCodeGeneratedModules.has(module, chunk.runtime)
499 ? chunkGraph.getModuleHash(module, chunk.runtime)
500 : compilation.codeGenerationResults.getHash(
501 module,
502 chunk.runtime
503 );
504 if (records.chunkModuleHashes[key] !== hash) {
505 updatedModules.add(module, chunk);
506 }
507 chunkModuleHashes[key] = hash;
508 }
509
510 /** @type {HotUpdateMainContentByRuntime} */
511 const hotUpdateMainContentByRuntime = new Map();
512 let allOldRuntime;
513 for (const key of Object.keys(records.chunkRuntime)) {
514 const runtime = keyToRuntime(records.chunkRuntime[key]);
515 allOldRuntime = mergeRuntimeOwned(allOldRuntime, runtime);
516 }
517 forEachRuntime(allOldRuntime, runtime => {
518 const { path: filename, info: assetInfo } =
519 compilation.getPathWithInfo(
520 /** @type {NonNullable<OutputNormalized["hotUpdateMainFilename"]>} */
521 (compilation.outputOptions.hotUpdateMainFilename),
522 {
523 hash: records.hash,
524 runtime
525 }
526 );
527 hotUpdateMainContentByRuntime.set(
528 /** @type {string} */ (runtime),
529 {
530 updatedChunkIds: new Set(),
531 removedChunkIds: new Set(),
532 removedModules: new Set(),
533 filename,
534 assetInfo
535 }
536 );
537 });
538 if (hotUpdateMainContentByRuntime.size === 0) return;
539
540 // Create a list of all active modules to verify which modules are removed completely
541 /** @type {Map<number|string, Module>} */
542 const allModules = new Map();
543 for (const module of compilation.modules) {
544 const id =
545 /** @type {ModuleId} */
546 (chunkGraph.getModuleId(module));
547 allModules.set(id, module);
548 }
549
550 // List of completely removed modules
551 /** @type {Set<string | number>} */
552 const completelyRemovedModules = new Set();
553
554 for (const key of Object.keys(records.chunkHashes)) {
555 const oldRuntime = keyToRuntime(records.chunkRuntime[key]);
556 /** @type {Module[]} */
557 const remainingModules = [];
558 // Check which modules are removed
559 for (const id of records.chunkModuleIds[key]) {
560 const module = allModules.get(id);
561 if (module === undefined) {
562 completelyRemovedModules.add(id);
563 } else {
564 remainingModules.push(module);
565 }
566 }
567
568 /** @type {ChunkId | null} */
569 let chunkId;
570 let newModules;
571 let newRuntimeModules;
572 let newFullHashModules;
573 let newDependentHashModules;
574 let newRuntime;
575 let removedFromRuntime;
576 const currentChunk = find(
577 compilation.chunks,
578 chunk => `${chunk.id}` === key
579 );
580 if (currentChunk) {
581 chunkId = currentChunk.id;
582 newRuntime = intersectRuntime(
583 currentChunk.runtime,
584 allOldRuntime
585 );
586 if (newRuntime === undefined) continue;
587 newModules = chunkGraph
588 .getChunkModules(currentChunk)
589 .filter(module => updatedModules.has(module, currentChunk));
590 newRuntimeModules = Array.from(
591 chunkGraph.getChunkRuntimeModulesIterable(currentChunk)
592 ).filter(module => updatedModules.has(module, currentChunk));
593 const fullHashModules =
594 chunkGraph.getChunkFullHashModulesIterable(currentChunk);
595 newFullHashModules =
596 fullHashModules &&
597 Array.from(fullHashModules).filter(module =>
598 updatedModules.has(module, currentChunk)
599 );
600 const dependentHashModules =
601 chunkGraph.getChunkDependentHashModulesIterable(currentChunk);
602 newDependentHashModules =
603 dependentHashModules &&
604 Array.from(dependentHashModules).filter(module =>
605 updatedModules.has(module, currentChunk)
606 );
607 removedFromRuntime = subtractRuntime(oldRuntime, newRuntime);
608 } else {
609 // chunk has completely removed
610 chunkId = `${Number(key)}` === key ? Number(key) : key;
611 removedFromRuntime = oldRuntime;
612 newRuntime = oldRuntime;
613 }
614 if (removedFromRuntime) {
615 // chunk was removed from some runtimes
616 forEachRuntime(removedFromRuntime, runtime => {
617 const item =
618 /** @type {HotUpdateMainContentByRuntimeItem} */
619 (
620 hotUpdateMainContentByRuntime.get(
621 /** @type {string} */ (runtime)
622 )
623 );
624 item.removedChunkIds.add(/** @type {ChunkId} */ (chunkId));
625 });
626 // dispose modules from the chunk in these runtimes
627 // where they are no longer in this runtime
628 for (const module of remainingModules) {
629 const moduleKey = `${key}|${module.identifier()}`;
630 const oldHash = records.chunkModuleHashes[moduleKey];
631 const runtimes = chunkGraph.getModuleRuntimes(module);
632 if (oldRuntime === newRuntime && runtimes.has(newRuntime)) {
633 // Module is still in the same runtime combination
634 const hash = nonCodeGeneratedModules.has(module, newRuntime)
635 ? chunkGraph.getModuleHash(module, newRuntime)
636 : compilation.codeGenerationResults.getHash(
637 module,
638 newRuntime
639 );
640 if (hash !== oldHash) {
641 if (module.type === WEBPACK_MODULE_TYPE_RUNTIME) {
642 newRuntimeModules = newRuntimeModules || [];
643 newRuntimeModules.push(
644 /** @type {RuntimeModule} */ (module)
645 );
646 } else {
647 newModules = newModules || [];
648 newModules.push(module);
649 }
650 }
651 } else {
652 // module is no longer in this runtime combination
653 // We (incorrectly) assume that it's not in an overlapping runtime combination
654 // and dispose it from the main runtimes the chunk was removed from
655 forEachRuntime(removedFromRuntime, runtime => {
656 // If the module is still used in this runtime, do not dispose it
657 // This could create a bad runtime state where the module is still loaded,
658 // but no chunk which contains it. This means we don't receive further HMR updates
659 // to this module and that's bad.
660 // TODO force load one of the chunks which contains the module
661 for (const moduleRuntime of runtimes) {
662 if (typeof moduleRuntime === "string") {
663 if (moduleRuntime === runtime) return;
664 } else if (
665 moduleRuntime !== undefined &&
666 moduleRuntime.has(/** @type {string} */ (runtime))
667 )
668 return;
669 }
670 const item =
671 /** @type {HotUpdateMainContentByRuntimeItem} */ (
672 hotUpdateMainContentByRuntime.get(
673 /** @type {string} */ (runtime)
674 )
675 );
676 item.removedModules.add(module);
677 });
678 }
679 }
680 }
681 if (
682 (newModules && newModules.length > 0) ||
683 (newRuntimeModules && newRuntimeModules.length > 0)
684 ) {
685 const hotUpdateChunk = new HotUpdateChunk();
686 if (backCompat)
687 ChunkGraph.setChunkGraphForChunk(hotUpdateChunk, chunkGraph);
688 hotUpdateChunk.id = chunkId;
689 hotUpdateChunk.runtime = currentChunk
690 ? currentChunk.runtime
691 : newRuntime;
692 if (currentChunk) {
693 for (const group of currentChunk.groupsIterable)
694 hotUpdateChunk.addGroup(group);
695 }
696 chunkGraph.attachModules(hotUpdateChunk, newModules || []);
697 chunkGraph.attachRuntimeModules(
698 hotUpdateChunk,
699 newRuntimeModules || []
700 );
701 if (newFullHashModules) {
702 chunkGraph.attachFullHashModules(
703 hotUpdateChunk,
704 newFullHashModules
705 );
706 }
707 if (newDependentHashModules) {
708 chunkGraph.attachDependentHashModules(
709 hotUpdateChunk,
710 newDependentHashModules
711 );
712 }
713 const renderManifest = compilation.getRenderManifest({
714 chunk: hotUpdateChunk,
715 hash: records.hash,
716 fullHash: records.hash,
717 outputOptions: compilation.outputOptions,
718 moduleTemplates: compilation.moduleTemplates,
719 dependencyTemplates: compilation.dependencyTemplates,
720 codeGenerationResults: compilation.codeGenerationResults,
721 runtimeTemplate: compilation.runtimeTemplate,
722 moduleGraph: compilation.moduleGraph,
723 chunkGraph
724 });
725 for (const entry of renderManifest) {
726 /** @type {string} */
727 let filename;
728 /** @type {AssetInfo} */
729 let assetInfo;
730 if ("filename" in entry) {
731 filename = entry.filename;
732 assetInfo = entry.info;
733 } else {
734 ({ path: filename, info: assetInfo } =
735 compilation.getPathWithInfo(
736 entry.filenameTemplate,
737 entry.pathOptions
738 ));
739 }
740 const source = entry.render();
741 compilation.additionalChunkAssets.push(filename);
742 compilation.emitAsset(filename, source, {
743 hotModuleReplacement: true,
744 ...assetInfo
745 });
746 if (currentChunk) {
747 currentChunk.files.add(filename);
748 compilation.hooks.chunkAsset.call(currentChunk, filename);
749 }
750 }
751 forEachRuntime(newRuntime, runtime => {
752 const item =
753 /** @type {HotUpdateMainContentByRuntimeItem} */ (
754 hotUpdateMainContentByRuntime.get(
755 /** @type {string} */ (runtime)
756 )
757 );
758 item.updatedChunkIds.add(/** @type {ChunkId} */ (chunkId));
759 });
760 }
761 }
762 const completelyRemovedModulesArray = Array.from(
763 completelyRemovedModules
764 );
765 const hotUpdateMainContentByFilename = new Map();
766 for (const {
767 removedChunkIds,
768 removedModules,
769 updatedChunkIds,
770 filename,
771 assetInfo
772 } of hotUpdateMainContentByRuntime.values()) {
773 const old = hotUpdateMainContentByFilename.get(filename);
774 if (
775 old &&
776 (!isSubset(old.removedChunkIds, removedChunkIds) ||
777 !isSubset(old.removedModules, removedModules) ||
778 !isSubset(old.updatedChunkIds, updatedChunkIds))
779 ) {
780 compilation.warnings.push(
781 new WebpackError(`HotModuleReplacementPlugin
782The configured output.hotUpdateMainFilename doesn't lead to unique filenames per runtime and HMR update differs between runtimes.
783This might lead to incorrect runtime behavior of the applied update.
784To fix this, make sure to include [runtime] in the output.hotUpdateMainFilename option, or use the default config.`)
785 );
786 for (const chunkId of removedChunkIds)
787 old.removedChunkIds.add(chunkId);
788 for (const chunkId of removedModules)
789 old.removedModules.add(chunkId);
790 for (const chunkId of updatedChunkIds)
791 old.updatedChunkIds.add(chunkId);
792 continue;
793 }
794 hotUpdateMainContentByFilename.set(filename, {
795 removedChunkIds,
796 removedModules,
797 updatedChunkIds,
798 assetInfo
799 });
800 }
801 for (const [
802 filename,
803 { removedChunkIds, removedModules, updatedChunkIds, assetInfo }
804 ] of hotUpdateMainContentByFilename) {
805 const hotUpdateMainJson = {
806 c: Array.from(updatedChunkIds),
807 r: Array.from(removedChunkIds),
808 m:
809 removedModules.size === 0
810 ? completelyRemovedModulesArray
811 : completelyRemovedModulesArray.concat(
812 Array.from(
813 removedModules,
814 m =>
815 /** @type {ModuleId} */ (chunkGraph.getModuleId(m))
816 )
817 )
818 };
819
820 const source = new RawSource(JSON.stringify(hotUpdateMainJson));
821 compilation.emitAsset(filename, source, {
822 hotModuleReplacement: true,
823 ...assetInfo
824 });
825 }
826 }
827 );
828
829 compilation.hooks.additionalTreeRuntimeRequirements.tap(
830 PLUGIN_NAME,
831 (chunk, runtimeRequirements) => {
832 runtimeRequirements.add(RuntimeGlobals.hmrDownloadManifest);
833 runtimeRequirements.add(RuntimeGlobals.hmrDownloadUpdateHandlers);
834 runtimeRequirements.add(RuntimeGlobals.interceptModuleExecution);
835 runtimeRequirements.add(RuntimeGlobals.moduleCache);
836 compilation.addRuntimeModule(
837 chunk,
838 new HotModuleReplacementRuntimeModule()
839 );
840 }
841 );
842
843 normalModuleFactory.hooks.parser
844 .for(JAVASCRIPT_MODULE_TYPE_AUTO)
845 .tap(PLUGIN_NAME, parser => {
846 applyModuleHot(parser);
847 applyImportMetaHot(parser);
848 });
849 normalModuleFactory.hooks.parser
850 .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
851 .tap(PLUGIN_NAME, parser => {
852 applyModuleHot(parser);
853 });
854 normalModuleFactory.hooks.parser
855 .for(JAVASCRIPT_MODULE_TYPE_ESM)
856 .tap(PLUGIN_NAME, parser => {
857 applyImportMetaHot(parser);
858 });
859 normalModuleFactory.hooks.module.tap(PLUGIN_NAME, module => {
860 module.hot = true;
861 return module;
862 });
863
864 NormalModule.getCompilationHooks(compilation).loader.tap(
865 PLUGIN_NAME,
866 context => {
867 context.hot = true;
868 }
869 );
870 }
871 );
872 }
873}
874
875module.exports = HotModuleReplacementPlugin;
Note: See TracBrowser for help on using the repository browser.