UNPKG

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