123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const { SyncBailHook } = require("tapable");
- const { RawSource } = require("webpack-sources");
- const ChunkGraph = require("./ChunkGraph");
- const Compilation = require("./Compilation");
- const HotUpdateChunk = require("./HotUpdateChunk");
- const NormalModule = require("./NormalModule");
- const RuntimeGlobals = require("./RuntimeGlobals");
- const WebpackError = require("./WebpackError");
- const ConstDependency = require("./dependencies/ConstDependency");
- const ImportMetaHotAcceptDependency = require("./dependencies/ImportMetaHotAcceptDependency");
- const ImportMetaHotDeclineDependency = require("./dependencies/ImportMetaHotDeclineDependency");
- const ModuleHotAcceptDependency = require("./dependencies/ModuleHotAcceptDependency");
- const ModuleHotDeclineDependency = require("./dependencies/ModuleHotDeclineDependency");
- const HotModuleReplacementRuntimeModule = require("./hmr/HotModuleReplacementRuntimeModule");
- const JavascriptParser = require("./javascript/JavascriptParser");
- const {
- evaluateToIdentifier
- } = require("./javascript/JavascriptParserHelpers");
- const { find, isSubset } = require("./util/SetHelpers");
- const TupleSet = require("./util/TupleSet");
- const { compareModulesById } = require("./util/comparators");
- const {
- getRuntimeKey,
- keyToRuntime,
- forEachRuntime,
- mergeRuntimeOwned,
- subtractRuntime,
- intersectRuntime
- } = require("./util/runtime");
- const {
- JAVASCRIPT_MODULE_TYPE_AUTO,
- JAVASCRIPT_MODULE_TYPE_DYNAMIC,
- JAVASCRIPT_MODULE_TYPE_ESM,
- WEBPACK_MODULE_TYPE_RUNTIME
- } = require("./ModuleTypeConstants");
- /** @typedef {import("estree").CallExpression} CallExpression */
- /** @typedef {import("estree").Expression} Expression */
- /** @typedef {import("./Chunk")} Chunk */
- /** @typedef {import("./Chunk").ChunkId} ChunkId */
- /** @typedef {import("./Compilation").AssetInfo} AssetInfo */
- /** @typedef {import("./Compiler")} Compiler */
- /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
- /** @typedef {import("./Module")} Module */
- /** @typedef {import("./Module").BuildInfo} BuildInfo */
- /** @typedef {import("./RuntimeModule")} RuntimeModule */
- /** @typedef {import("./javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
- /** @typedef {import("./javascript/JavascriptParserHelpers").Range} Range */
- /** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
- /**
- * @typedef {object} HMRJavascriptParserHooks
- * @property {SyncBailHook<[TODO, string[]], void>} hotAcceptCallback
- * @property {SyncBailHook<[TODO, string[]], void>} hotAcceptWithoutCallback
- */
- /** @typedef {Map<string, { updatedChunkIds: Set<ChunkId>, removedChunkIds: Set<ChunkId>, removedModules: Set<Module>, filename: string, assetInfo: AssetInfo }>} HotUpdateMainContentByRuntime */
- /** @type {WeakMap<JavascriptParser, HMRJavascriptParserHooks>} */
- const parserHooksMap = new WeakMap();
- const PLUGIN_NAME = "HotModuleReplacementPlugin";
- class HotModuleReplacementPlugin {
- /**
- * @param {JavascriptParser} parser the parser
- * @returns {HMRJavascriptParserHooks} the attached hooks
- */
- static getParserHooks(parser) {
- if (!(parser instanceof JavascriptParser)) {
- throw new TypeError(
- "The 'parser' argument must be an instance of JavascriptParser"
- );
- }
- let hooks = parserHooksMap.get(parser);
- if (hooks === undefined) {
- hooks = {
- hotAcceptCallback: new SyncBailHook(["expression", "requests"]),
- hotAcceptWithoutCallback: new SyncBailHook(["expression", "requests"])
- };
- parserHooksMap.set(parser, hooks);
- }
- return hooks;
- }
- /**
- * @param {object=} options options
- */
- constructor(options) {
- this.options = options || {};
- }
- /**
- * Apply the plugin
- * @param {Compiler} compiler the compiler instance
- * @returns {void}
- */
- apply(compiler) {
- const { _backCompat: backCompat } = compiler;
- if (compiler.options.output.strictModuleErrorHandling === undefined)
- compiler.options.output.strictModuleErrorHandling = true;
- const runtimeRequirements = [RuntimeGlobals.module];
- /**
- * @param {JavascriptParser} parser the parser
- * @param {typeof ModuleHotAcceptDependency} ParamDependency dependency
- * @returns {(expr: CallExpression) => boolean | undefined} callback
- */
- const createAcceptHandler = (parser, ParamDependency) => {
- const { hotAcceptCallback, hotAcceptWithoutCallback } =
- HotModuleReplacementPlugin.getParserHooks(parser);
- return expr => {
- const module = parser.state.module;
- const dep = new ConstDependency(
- `${module.moduleArgument}.hot.accept`,
- /** @type {Range} */ (expr.callee.range),
- runtimeRequirements
- );
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- module.addPresentationalDependency(dep);
- /** @type {BuildInfo} */
- (module.buildInfo).moduleConcatenationBailout =
- "Hot Module Replacement";
- if (expr.arguments.length >= 1) {
- const arg = parser.evaluateExpression(expr.arguments[0]);
- /** @type {BasicEvaluatedExpression[]} */
- let params = [];
- if (arg.isString()) {
- params = [arg];
- } else if (arg.isArray()) {
- params =
- /** @type {BasicEvaluatedExpression[]} */
- (arg.items).filter(param => param.isString());
- }
- /** @type {string[]} */
- let requests = [];
- if (params.length > 0) {
- params.forEach((param, idx) => {
- const request = /** @type {string} */ (param.string);
- const dep = new ParamDependency(
- request,
- /** @type {Range} */ (param.range)
- );
- dep.optional = true;
- dep.loc = Object.create(
- /** @type {DependencyLocation} */ (expr.loc)
- );
- dep.loc.index = idx;
- module.addDependency(dep);
- requests.push(request);
- });
- if (expr.arguments.length > 1) {
- hotAcceptCallback.call(expr.arguments[1], requests);
- for (let i = 1; i < expr.arguments.length; i++) {
- parser.walkExpression(expr.arguments[i]);
- }
- return true;
- } else {
- hotAcceptWithoutCallback.call(expr, requests);
- return true;
- }
- }
- }
- parser.walkExpressions(expr.arguments);
- return true;
- };
- };
- /**
- * @param {JavascriptParser} parser the parser
- * @param {typeof ModuleHotDeclineDependency} ParamDependency dependency
- * @returns {(expr: CallExpression) => boolean | undefined} callback
- */
- const createDeclineHandler = (parser, ParamDependency) => expr => {
- const module = parser.state.module;
- const dep = new ConstDependency(
- `${module.moduleArgument}.hot.decline`,
- /** @type {Range} */ (expr.callee.range),
- runtimeRequirements
- );
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- module.addPresentationalDependency(dep);
- /** @type {BuildInfo} */
- (module.buildInfo).moduleConcatenationBailout = "Hot Module Replacement";
- if (expr.arguments.length === 1) {
- const arg = parser.evaluateExpression(expr.arguments[0]);
- /** @type {BasicEvaluatedExpression[]} */
- let params = [];
- if (arg.isString()) {
- params = [arg];
- } else if (arg.isArray()) {
- params =
- /** @type {BasicEvaluatedExpression[]} */
- (arg.items).filter(param => param.isString());
- }
- params.forEach((param, idx) => {
- const dep = new ParamDependency(
- /** @type {string} */ (param.string),
- /** @type {Range} */ (param.range)
- );
- dep.optional = true;
- dep.loc = Object.create(/** @type {DependencyLocation} */ (expr.loc));
- dep.loc.index = idx;
- module.addDependency(dep);
- });
- }
- return true;
- };
- /**
- * @param {JavascriptParser} parser the parser
- * @returns {(expr: Expression) => boolean | undefined} callback
- */
- const createHMRExpressionHandler = parser => expr => {
- const module = parser.state.module;
- const dep = new ConstDependency(
- `${module.moduleArgument}.hot`,
- /** @type {Range} */ (expr.range),
- runtimeRequirements
- );
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- module.addPresentationalDependency(dep);
- /** @type {BuildInfo} */
- (module.buildInfo).moduleConcatenationBailout = "Hot Module Replacement";
- return true;
- };
- /**
- * @param {JavascriptParser} parser the parser
- * @returns {void}
- */
- const applyModuleHot = parser => {
- parser.hooks.evaluateIdentifier.for("module.hot").tap(
- {
- name: PLUGIN_NAME,
- before: "NodeStuffPlugin"
- },
- expr => {
- return evaluateToIdentifier(
- "module.hot",
- "module",
- () => ["hot"],
- true
- )(expr);
- }
- );
- parser.hooks.call
- .for("module.hot.accept")
- .tap(
- PLUGIN_NAME,
- createAcceptHandler(parser, ModuleHotAcceptDependency)
- );
- parser.hooks.call
- .for("module.hot.decline")
- .tap(
- PLUGIN_NAME,
- createDeclineHandler(parser, ModuleHotDeclineDependency)
- );
- parser.hooks.expression
- .for("module.hot")
- .tap(PLUGIN_NAME, createHMRExpressionHandler(parser));
- };
- /**
- * @param {JavascriptParser} parser the parser
- * @returns {void}
- */
- const applyImportMetaHot = parser => {
- parser.hooks.evaluateIdentifier
- .for("import.meta.webpackHot")
- .tap(PLUGIN_NAME, expr => {
- return evaluateToIdentifier(
- "import.meta.webpackHot",
- "import.meta",
- () => ["webpackHot"],
- true
- )(expr);
- });
- parser.hooks.call
- .for("import.meta.webpackHot.accept")
- .tap(
- PLUGIN_NAME,
- createAcceptHandler(parser, ImportMetaHotAcceptDependency)
- );
- parser.hooks.call
- .for("import.meta.webpackHot.decline")
- .tap(
- PLUGIN_NAME,
- createDeclineHandler(parser, ImportMetaHotDeclineDependency)
- );
- parser.hooks.expression
- .for("import.meta.webpackHot")
- .tap(PLUGIN_NAME, createHMRExpressionHandler(parser));
- };
- compiler.hooks.compilation.tap(
- PLUGIN_NAME,
- (compilation, { normalModuleFactory }) => {
- // This applies the HMR plugin only to the targeted compiler
- // It should not affect child compilations
- if (compilation.compiler !== compiler) return;
- //#region module.hot.* API
- compilation.dependencyFactories.set(
- ModuleHotAcceptDependency,
- normalModuleFactory
- );
- compilation.dependencyTemplates.set(
- ModuleHotAcceptDependency,
- new ModuleHotAcceptDependency.Template()
- );
- compilation.dependencyFactories.set(
- ModuleHotDeclineDependency,
- normalModuleFactory
- );
- compilation.dependencyTemplates.set(
- ModuleHotDeclineDependency,
- new ModuleHotDeclineDependency.Template()
- );
- //#endregion
- //#region import.meta.webpackHot.* API
- compilation.dependencyFactories.set(
- ImportMetaHotAcceptDependency,
- normalModuleFactory
- );
- compilation.dependencyTemplates.set(
- ImportMetaHotAcceptDependency,
- new ImportMetaHotAcceptDependency.Template()
- );
- compilation.dependencyFactories.set(
- ImportMetaHotDeclineDependency,
- normalModuleFactory
- );
- compilation.dependencyTemplates.set(
- ImportMetaHotDeclineDependency,
- new ImportMetaHotDeclineDependency.Template()
- );
- //#endregion
- let hotIndex = 0;
- /** @type {Record<string, string>} */
- const fullHashChunkModuleHashes = {};
- /** @type {Record<string, string>} */
- const chunkModuleHashes = {};
- compilation.hooks.record.tap(PLUGIN_NAME, (compilation, records) => {
- if (records.hash === compilation.hash) return;
- const chunkGraph = compilation.chunkGraph;
- records.hash = compilation.hash;
- records.hotIndex = hotIndex;
- records.fullHashChunkModuleHashes = fullHashChunkModuleHashes;
- records.chunkModuleHashes = chunkModuleHashes;
- records.chunkHashes = {};
- records.chunkRuntime = {};
- for (const chunk of compilation.chunks) {
- const chunkId = /** @type {ChunkId} */ (chunk.id);
- records.chunkHashes[chunkId] = chunk.hash;
- records.chunkRuntime[chunkId] = getRuntimeKey(chunk.runtime);
- }
- records.chunkModuleIds = {};
- for (const chunk of compilation.chunks) {
- records.chunkModuleIds[/** @type {ChunkId} */ (chunk.id)] =
- Array.from(
- chunkGraph.getOrderedChunkModulesIterable(
- chunk,
- compareModulesById(chunkGraph)
- ),
- m => chunkGraph.getModuleId(m)
- );
- }
- });
- /** @type {TupleSet<[Module, Chunk]>} */
- const updatedModules = new TupleSet();
- /** @type {TupleSet<[Module, Chunk]>} */
- const fullHashModules = new TupleSet();
- /** @type {TupleSet<[Module, RuntimeSpec]>} */
- const nonCodeGeneratedModules = new TupleSet();
- compilation.hooks.fullHash.tap(PLUGIN_NAME, hash => {
- const chunkGraph = compilation.chunkGraph;
- const records = compilation.records;
- for (const chunk of compilation.chunks) {
- /**
- * @param {Module} module module
- * @returns {string} module hash
- */
- const getModuleHash = module => {
- if (
- compilation.codeGenerationResults.has(module, chunk.runtime)
- ) {
- return compilation.codeGenerationResults.getHash(
- module,
- chunk.runtime
- );
- } else {
- nonCodeGeneratedModules.add(module, chunk.runtime);
- return chunkGraph.getModuleHash(module, chunk.runtime);
- }
- };
- const fullHashModulesInThisChunk =
- chunkGraph.getChunkFullHashModulesSet(chunk);
- if (fullHashModulesInThisChunk !== undefined) {
- for (const module of fullHashModulesInThisChunk) {
- fullHashModules.add(module, chunk);
- }
- }
- const modules = chunkGraph.getChunkModulesIterable(chunk);
- if (modules !== undefined) {
- if (records.chunkModuleHashes) {
- if (fullHashModulesInThisChunk !== undefined) {
- for (const module of modules) {
- const key = `${chunk.id}|${module.identifier()}`;
- const hash = getModuleHash(module);
- if (
- fullHashModulesInThisChunk.has(
- /** @type {RuntimeModule} */ (module)
- )
- ) {
- if (records.fullHashChunkModuleHashes[key] !== hash) {
- updatedModules.add(module, chunk);
- }
- fullHashChunkModuleHashes[key] = hash;
- } else {
- if (records.chunkModuleHashes[key] !== hash) {
- updatedModules.add(module, chunk);
- }
- chunkModuleHashes[key] = hash;
- }
- }
- } else {
- for (const module of modules) {
- const key = `${chunk.id}|${module.identifier()}`;
- const hash = getModuleHash(module);
- if (records.chunkModuleHashes[key] !== hash) {
- updatedModules.add(module, chunk);
- }
- chunkModuleHashes[key] = hash;
- }
- }
- } else {
- if (fullHashModulesInThisChunk !== undefined) {
- for (const module of modules) {
- const key = `${chunk.id}|${module.identifier()}`;
- const hash = getModuleHash(module);
- if (
- fullHashModulesInThisChunk.has(
- /** @type {RuntimeModule} */ (module)
- )
- ) {
- fullHashChunkModuleHashes[key] = hash;
- } else {
- chunkModuleHashes[key] = hash;
- }
- }
- } else {
- for (const module of modules) {
- const key = `${chunk.id}|${module.identifier()}`;
- const hash = getModuleHash(module);
- chunkModuleHashes[key] = hash;
- }
- }
- }
- }
- }
- hotIndex = records.hotIndex || 0;
- if (updatedModules.size > 0) hotIndex++;
- hash.update(`${hotIndex}`);
- });
- compilation.hooks.processAssets.tap(
- {
- name: PLUGIN_NAME,
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
- },
- () => {
- const chunkGraph = compilation.chunkGraph;
- const records = compilation.records;
- if (records.hash === compilation.hash) return;
- if (
- !records.chunkModuleHashes ||
- !records.chunkHashes ||
- !records.chunkModuleIds
- ) {
- return;
- }
- for (const [module, chunk] of fullHashModules) {
- const key = `${chunk.id}|${module.identifier()}`;
- const hash = nonCodeGeneratedModules.has(module, chunk.runtime)
- ? chunkGraph.getModuleHash(module, chunk.runtime)
- : compilation.codeGenerationResults.getHash(
- module,
- chunk.runtime
- );
- if (records.chunkModuleHashes[key] !== hash) {
- updatedModules.add(module, chunk);
- }
- chunkModuleHashes[key] = hash;
- }
- /** @type {HotUpdateMainContentByRuntime} */
- const hotUpdateMainContentByRuntime = new Map();
- let allOldRuntime;
- for (const key of Object.keys(records.chunkRuntime)) {
- const runtime = keyToRuntime(records.chunkRuntime[key]);
- allOldRuntime = mergeRuntimeOwned(allOldRuntime, runtime);
- }
- forEachRuntime(allOldRuntime, runtime => {
- const { path: filename, info: assetInfo } =
- compilation.getPathWithInfo(
- compilation.outputOptions.hotUpdateMainFilename,
- {
- hash: records.hash,
- runtime
- }
- );
- hotUpdateMainContentByRuntime.set(
- /** @type {string} */ (runtime),
- {
- updatedChunkIds: new Set(),
- removedChunkIds: new Set(),
- removedModules: new Set(),
- filename,
- assetInfo
- }
- );
- });
- if (hotUpdateMainContentByRuntime.size === 0) return;
- // Create a list of all active modules to verify which modules are removed completely
- /** @type {Map<number|string, Module>} */
- const allModules = new Map();
- for (const module of compilation.modules) {
- const id = chunkGraph.getModuleId(module);
- allModules.set(id, module);
- }
- // List of completely removed modules
- /** @type {Set<string | number>} */
- const completelyRemovedModules = new Set();
- for (const key of Object.keys(records.chunkHashes)) {
- const oldRuntime = keyToRuntime(records.chunkRuntime[key]);
- /** @type {Module[]} */
- const remainingModules = [];
- // Check which modules are removed
- for (const id of records.chunkModuleIds[key]) {
- const module = allModules.get(id);
- if (module === undefined) {
- completelyRemovedModules.add(id);
- } else {
- remainingModules.push(module);
- }
- }
- /** @type {ChunkId | null} */
- let chunkId;
- let newModules;
- let newRuntimeModules;
- let newFullHashModules;
- let newDependentHashModules;
- let newRuntime;
- let removedFromRuntime;
- const currentChunk = find(
- compilation.chunks,
- chunk => `${chunk.id}` === key
- );
- if (currentChunk) {
- chunkId = currentChunk.id;
- newRuntime = intersectRuntime(
- currentChunk.runtime,
- allOldRuntime
- );
- if (newRuntime === undefined) continue;
- newModules = chunkGraph
- .getChunkModules(currentChunk)
- .filter(module => updatedModules.has(module, currentChunk));
- newRuntimeModules = Array.from(
- chunkGraph.getChunkRuntimeModulesIterable(currentChunk)
- ).filter(module => updatedModules.has(module, currentChunk));
- const fullHashModules =
- chunkGraph.getChunkFullHashModulesIterable(currentChunk);
- newFullHashModules =
- fullHashModules &&
- Array.from(fullHashModules).filter(module =>
- updatedModules.has(module, currentChunk)
- );
- const dependentHashModules =
- chunkGraph.getChunkDependentHashModulesIterable(currentChunk);
- newDependentHashModules =
- dependentHashModules &&
- Array.from(dependentHashModules).filter(module =>
- updatedModules.has(module, currentChunk)
- );
- removedFromRuntime = subtractRuntime(oldRuntime, newRuntime);
- } else {
- // chunk has completely removed
- chunkId = `${+key}` === key ? +key : key;
- removedFromRuntime = oldRuntime;
- newRuntime = oldRuntime;
- }
- if (removedFromRuntime) {
- // chunk was removed from some runtimes
- forEachRuntime(removedFromRuntime, runtime => {
- const item = hotUpdateMainContentByRuntime.get(
- /** @type {string} */ (runtime)
- );
- item.removedChunkIds.add(/** @type {ChunkId} */ (chunkId));
- });
- // dispose modules from the chunk in these runtimes
- // where they are no longer in this runtime
- for (const module of remainingModules) {
- const moduleKey = `${key}|${module.identifier()}`;
- const oldHash = records.chunkModuleHashes[moduleKey];
- const runtimes = chunkGraph.getModuleRuntimes(module);
- if (oldRuntime === newRuntime && runtimes.has(newRuntime)) {
- // Module is still in the same runtime combination
- const hash = nonCodeGeneratedModules.has(module, newRuntime)
- ? chunkGraph.getModuleHash(module, newRuntime)
- : compilation.codeGenerationResults.getHash(
- module,
- newRuntime
- );
- if (hash !== oldHash) {
- if (module.type === WEBPACK_MODULE_TYPE_RUNTIME) {
- newRuntimeModules = newRuntimeModules || [];
- newRuntimeModules.push(
- /** @type {RuntimeModule} */ (module)
- );
- } else {
- newModules = newModules || [];
- newModules.push(module);
- }
- }
- } else {
- // module is no longer in this runtime combination
- // We (incorrectly) assume that it's not in an overlapping runtime combination
- // and dispose it from the main runtimes the chunk was removed from
- forEachRuntime(removedFromRuntime, runtime => {
- // If the module is still used in this runtime, do not dispose it
- // This could create a bad runtime state where the module is still loaded,
- // but no chunk which contains it. This means we don't receive further HMR updates
- // to this module and that's bad.
- // TODO force load one of the chunks which contains the module
- for (const moduleRuntime of runtimes) {
- if (typeof moduleRuntime === "string") {
- if (moduleRuntime === runtime) return;
- } else if (moduleRuntime !== undefined) {
- if (
- moduleRuntime.has(/** @type {string} */ (runtime))
- )
- return;
- }
- }
- const item = hotUpdateMainContentByRuntime.get(
- /** @type {string} */ (runtime)
- );
- item.removedModules.add(module);
- });
- }
- }
- }
- if (
- (newModules && newModules.length > 0) ||
- (newRuntimeModules && newRuntimeModules.length > 0)
- ) {
- const hotUpdateChunk = new HotUpdateChunk();
- if (backCompat)
- ChunkGraph.setChunkGraphForChunk(hotUpdateChunk, chunkGraph);
- hotUpdateChunk.id = chunkId;
- hotUpdateChunk.runtime = newRuntime;
- if (currentChunk) {
- for (const group of currentChunk.groupsIterable)
- hotUpdateChunk.addGroup(group);
- }
- chunkGraph.attachModules(hotUpdateChunk, newModules || []);
- chunkGraph.attachRuntimeModules(
- hotUpdateChunk,
- newRuntimeModules || []
- );
- if (newFullHashModules) {
- chunkGraph.attachFullHashModules(
- hotUpdateChunk,
- newFullHashModules
- );
- }
- if (newDependentHashModules) {
- chunkGraph.attachDependentHashModules(
- hotUpdateChunk,
- newDependentHashModules
- );
- }
- const renderManifest = compilation.getRenderManifest({
- chunk: hotUpdateChunk,
- hash: records.hash,
- fullHash: records.hash,
- outputOptions: compilation.outputOptions,
- moduleTemplates: compilation.moduleTemplates,
- dependencyTemplates: compilation.dependencyTemplates,
- codeGenerationResults: compilation.codeGenerationResults,
- runtimeTemplate: compilation.runtimeTemplate,
- moduleGraph: compilation.moduleGraph,
- chunkGraph
- });
- for (const entry of renderManifest) {
- /** @type {string} */
- let filename;
- /** @type {AssetInfo} */
- let assetInfo;
- if ("filename" in entry) {
- filename = entry.filename;
- assetInfo = entry.info;
- } else {
- ({ path: filename, info: assetInfo } =
- compilation.getPathWithInfo(
- entry.filenameTemplate,
- entry.pathOptions
- ));
- }
- const source = entry.render();
- compilation.additionalChunkAssets.push(filename);
- compilation.emitAsset(filename, source, {
- hotModuleReplacement: true,
- ...assetInfo
- });
- if (currentChunk) {
- currentChunk.files.add(filename);
- compilation.hooks.chunkAsset.call(currentChunk, filename);
- }
- }
- forEachRuntime(newRuntime, runtime => {
- const item = hotUpdateMainContentByRuntime.get(
- /** @type {string} */ (runtime)
- );
- item.updatedChunkIds.add(/** @type {ChunkId} */ (chunkId));
- });
- }
- }
- const completelyRemovedModulesArray = Array.from(
- completelyRemovedModules
- );
- const hotUpdateMainContentByFilename = new Map();
- for (const {
- removedChunkIds,
- removedModules,
- updatedChunkIds,
- filename,
- assetInfo
- } of hotUpdateMainContentByRuntime.values()) {
- const old = hotUpdateMainContentByFilename.get(filename);
- if (
- old &&
- (!isSubset(old.removedChunkIds, removedChunkIds) ||
- !isSubset(old.removedModules, removedModules) ||
- !isSubset(old.updatedChunkIds, updatedChunkIds))
- ) {
- compilation.warnings.push(
- new WebpackError(`HotModuleReplacementPlugin
- The configured output.hotUpdateMainFilename doesn't lead to unique filenames per runtime and HMR update differs between runtimes.
- This might lead to incorrect runtime behavior of the applied update.
- To fix this, make sure to include [runtime] in the output.hotUpdateMainFilename option, or use the default config.`)
- );
- for (const chunkId of removedChunkIds)
- old.removedChunkIds.add(chunkId);
- for (const chunkId of removedModules)
- old.removedModules.add(chunkId);
- for (const chunkId of updatedChunkIds)
- old.updatedChunkIds.add(chunkId);
- continue;
- }
- hotUpdateMainContentByFilename.set(filename, {
- removedChunkIds,
- removedModules,
- updatedChunkIds,
- assetInfo
- });
- }
- for (const [
- filename,
- { removedChunkIds, removedModules, updatedChunkIds, assetInfo }
- ] of hotUpdateMainContentByFilename) {
- const hotUpdateMainJson = {
- c: Array.from(updatedChunkIds),
- r: Array.from(removedChunkIds),
- m:
- removedModules.size === 0
- ? completelyRemovedModulesArray
- : completelyRemovedModulesArray.concat(
- Array.from(removedModules, m =>
- chunkGraph.getModuleId(m)
- )
- )
- };
- const source = new RawSource(JSON.stringify(hotUpdateMainJson));
- compilation.emitAsset(filename, source, {
- hotModuleReplacement: true,
- ...assetInfo
- });
- }
- }
- );
- compilation.hooks.additionalTreeRuntimeRequirements.tap(
- PLUGIN_NAME,
- (chunk, runtimeRequirements) => {
- runtimeRequirements.add(RuntimeGlobals.hmrDownloadManifest);
- runtimeRequirements.add(RuntimeGlobals.hmrDownloadUpdateHandlers);
- runtimeRequirements.add(RuntimeGlobals.interceptModuleExecution);
- runtimeRequirements.add(RuntimeGlobals.moduleCache);
- compilation.addRuntimeModule(
- chunk,
- new HotModuleReplacementRuntimeModule()
- );
- }
- );
- normalModuleFactory.hooks.parser
- .for(JAVASCRIPT_MODULE_TYPE_AUTO)
- .tap(PLUGIN_NAME, parser => {
- applyModuleHot(parser);
- applyImportMetaHot(parser);
- });
- normalModuleFactory.hooks.parser
- .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
- .tap(PLUGIN_NAME, parser => {
- applyModuleHot(parser);
- });
- normalModuleFactory.hooks.parser
- .for(JAVASCRIPT_MODULE_TYPE_ESM)
- .tap(PLUGIN_NAME, parser => {
- applyImportMetaHot(parser);
- });
- NormalModule.getCompilationHooks(compilation).loader.tap(
- PLUGIN_NAME,
- context => {
- context.hot = true;
- }
- );
- }
- );
- }
- }
- module.exports = HotModuleReplacementPlugin;
|