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