UNPKG

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