mirror of https://github.com/webpack/webpack.git
feat: HMR support for ESM
Github Actions / lint (push) Waiting to run
Details
Github Actions / validate-legacy-node (push) Waiting to run
Details
Github Actions / benchmark (1/4) (push) Waiting to run
Details
Github Actions / benchmark (2/4) (push) Waiting to run
Details
Github Actions / benchmark (3/4) (push) Waiting to run
Details
Github Actions / benchmark (4/4) (push) Waiting to run
Details
Github Actions / basic (push) Waiting to run
Details
Github Actions / unit (push) Waiting to run
Details
Github Actions / integration (10.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Blocked by required conditions
Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Blocked by required conditions
Details
Github Actions / lint (push) Waiting to run
Details
Github Actions / validate-legacy-node (push) Waiting to run
Details
Github Actions / benchmark (1/4) (push) Waiting to run
Details
Github Actions / benchmark (2/4) (push) Waiting to run
Details
Github Actions / benchmark (3/4) (push) Waiting to run
Details
Github Actions / benchmark (4/4) (push) Waiting to run
Details
Github Actions / basic (push) Waiting to run
Details
Github Actions / unit (push) Waiting to run
Details
Github Actions / integration (10.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (10.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (20.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (22.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, macos-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, macos-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, windows-latest, a) (push) Blocked by required conditions
Details
Github Actions / integration (24.x, windows-latest, b) (push) Blocked by required conditions
Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Blocked by required conditions
Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Blocked by required conditions
Details
This commit is contained in:
parent
605897418a
commit
09fda8730d
|
@ -6,8 +6,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { ConcatSource } = require("webpack-sources");
|
const { ConcatSource } = require("webpack-sources");
|
||||||
const { RuntimeGlobals } = require("..");
|
const { RuntimeGlobals, HotUpdateChunk } = require("..");
|
||||||
const HotUpdateChunk = require("../HotUpdateChunk");
|
|
||||||
const Template = require("../Template");
|
const Template = require("../Template");
|
||||||
const { getAllChunks } = require("../javascript/ChunkHelpers");
|
const { getAllChunks } = require("../javascript/ChunkHelpers");
|
||||||
const {
|
const {
|
||||||
|
@ -72,10 +71,8 @@ class ModuleChunkFormatPlugin {
|
||||||
hooks.renderChunk.tap(PLUGIN_NAME, (modules, renderContext) => {
|
hooks.renderChunk.tap(PLUGIN_NAME, (modules, renderContext) => {
|
||||||
const { chunk, chunkGraph, runtimeTemplate } = renderContext;
|
const { chunk, chunkGraph, runtimeTemplate } = renderContext;
|
||||||
const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
|
const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
|
||||||
|
|
||||||
const source = new ConcatSource();
|
const source = new ConcatSource();
|
||||||
if (hotUpdateChunk) {
|
|
||||||
throw new Error("HMR is not implemented for module chunk format yet");
|
|
||||||
} else {
|
|
||||||
source.add(
|
source.add(
|
||||||
`export const __webpack_id__ = ${JSON.stringify(chunk.id)};\n`
|
`export const __webpack_id__ = ${JSON.stringify(chunk.id)};\n`
|
||||||
);
|
);
|
||||||
|
@ -85,14 +82,16 @@ class ModuleChunkFormatPlugin {
|
||||||
source.add("export const __webpack_modules__ = ");
|
source.add("export const __webpack_modules__ = ");
|
||||||
source.add(modules);
|
source.add(modules);
|
||||||
source.add(";\n");
|
source.add(";\n");
|
||||||
const runtimeModules =
|
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
|
||||||
chunkGraph.getChunkRuntimeModulesInOrder(chunk);
|
|
||||||
if (runtimeModules.length > 0) {
|
if (runtimeModules.length > 0) {
|
||||||
source.add("export const __webpack_runtime__ =\n");
|
source.add("export const __webpack_runtime__ =\n");
|
||||||
source.add(
|
source.add(
|
||||||
Template.renderChunkRuntimeModules(runtimeModules, renderContext)
|
Template.renderChunkRuntimeModules(runtimeModules, renderContext)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (hotUpdateChunk) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
|
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
|
||||||
if (runtimeChunk) {
|
if (runtimeChunk) {
|
||||||
const currentOutputName = compilation
|
const currentOutputName = compilation
|
||||||
|
@ -201,7 +200,6 @@ class ModuleChunkFormatPlugin {
|
||||||
);
|
);
|
||||||
return entrySource;
|
return entrySource;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return source;
|
return source;
|
||||||
});
|
});
|
||||||
hooks.chunkHash.tap(
|
hooks.chunkHash.tap(
|
||||||
|
|
|
@ -63,6 +63,12 @@ class ModuleChunkLoadingPlugin {
|
||||||
compilation.hooks.runtimeRequirementInTree
|
compilation.hooks.runtimeRequirementInTree
|
||||||
.for(RuntimeGlobals.onChunksLoaded)
|
.for(RuntimeGlobals.onChunksLoaded)
|
||||||
.tap(PLUGIN_NAME, handler);
|
.tap(PLUGIN_NAME, handler);
|
||||||
|
compilation.hooks.runtimeRequirementInTree
|
||||||
|
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
|
||||||
|
.tap(PLUGIN_NAME, handler);
|
||||||
|
compilation.hooks.runtimeRequirementInTree
|
||||||
|
.for(RuntimeGlobals.hmrDownloadManifest)
|
||||||
|
.tap(PLUGIN_NAME, handler);
|
||||||
compilation.hooks.runtimeRequirementInTree
|
compilation.hooks.runtimeRequirementInTree
|
||||||
.for(RuntimeGlobals.externalInstallChunk)
|
.for(RuntimeGlobals.externalInstallChunk)
|
||||||
.tap(PLUGIN_NAME, (chunk, set) => {
|
.tap(PLUGIN_NAME, (chunk, set) => {
|
||||||
|
@ -99,6 +105,26 @@ class ModuleChunkLoadingPlugin {
|
||||||
|
|
||||||
set.add(RuntimeGlobals.getChunkScriptFilename);
|
set.add(RuntimeGlobals.getChunkScriptFilename);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
compilation.hooks.runtimeRequirementInTree
|
||||||
|
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
|
||||||
|
.tap(PLUGIN_NAME, (chunk, set) => {
|
||||||
|
if (!isEnabledForChunk(chunk)) return;
|
||||||
|
set.add(RuntimeGlobals.publicPath);
|
||||||
|
set.add(RuntimeGlobals.loadScript);
|
||||||
|
set.add(RuntimeGlobals.getChunkUpdateScriptFilename);
|
||||||
|
set.add(RuntimeGlobals.moduleCache);
|
||||||
|
set.add(RuntimeGlobals.hmrModuleData);
|
||||||
|
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
|
||||||
|
});
|
||||||
|
|
||||||
|
compilation.hooks.runtimeRequirementInTree
|
||||||
|
.for(RuntimeGlobals.hmrDownloadManifest)
|
||||||
|
.tap(PLUGIN_NAME, (chunk, set) => {
|
||||||
|
if (!isEnabledForChunk(chunk)) return;
|
||||||
|
set.add(RuntimeGlobals.publicPath);
|
||||||
|
set.add(RuntimeGlobals.getUpdateManifestFilename);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,9 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
||||||
const withHmr = this._runtimeRequirements.has(
|
const withHmr = this._runtimeRequirements.has(
|
||||||
RuntimeGlobals.hmrDownloadUpdateHandlers
|
RuntimeGlobals.hmrDownloadUpdateHandlers
|
||||||
);
|
);
|
||||||
|
const withHmrManifest = this._runtimeRequirements.has(
|
||||||
|
RuntimeGlobals.hmrDownloadManifest
|
||||||
|
);
|
||||||
const { linkPreload, linkPrefetch } =
|
const { linkPreload, linkPrefetch } =
|
||||||
ModuleChunkLoadingRuntimeModule.getCompilationHooks(compilation);
|
ModuleChunkLoadingRuntimeModule.getCompilationHooks(compilation);
|
||||||
const isNeutralPlatform = runtimeTemplate.isNeutralPlatform();
|
const isNeutralPlatform = runtimeTemplate.isNeutralPlatform();
|
||||||
|
@ -346,7 +349,92 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
||||||
"installedChunks[chunkId] === 0",
|
"installedChunks[chunkId] === 0",
|
||||||
"chunkId"
|
"chunkId"
|
||||||
)};`
|
)};`
|
||||||
: "// no on chunks loaded"
|
: "// no on chunks loaded",
|
||||||
|
withHmr
|
||||||
|
? Template.asString([
|
||||||
|
Template.getFunctionContent(
|
||||||
|
require("../hmr/JavascriptHotModuleReplacement.runtime.js")
|
||||||
|
)
|
||||||
|
.replace(/\$key\$/g, "jsonp")
|
||||||
|
.replace(/\$installedChunks\$/g, "installedChunks")
|
||||||
|
.replace(/\$loadUpdateChunk\$/g, "loadUpdateChunk")
|
||||||
|
.replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
|
||||||
|
.replace(/\$moduleFactories\$/g, RuntimeGlobals.moduleFactories)
|
||||||
|
.replace(
|
||||||
|
/\$ensureChunkHandlers\$/g,
|
||||||
|
RuntimeGlobals.ensureChunkHandlers
|
||||||
|
)
|
||||||
|
.replace(/\$hasOwnProperty\$/g, RuntimeGlobals.hasOwnProperty)
|
||||||
|
.replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
|
||||||
|
.replace(
|
||||||
|
/\$hmrDownloadUpdateHandlers\$/g,
|
||||||
|
RuntimeGlobals.hmrDownloadUpdateHandlers
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
/\$hmrInvalidateModuleHandlers\$/g,
|
||||||
|
RuntimeGlobals.hmrInvalidateModuleHandlers
|
||||||
|
),
|
||||||
|
"",
|
||||||
|
"function loadUpdateChunk(chunkId, updatedModulesList) {",
|
||||||
|
Template.indent([
|
||||||
|
`return new Promise(${runtimeTemplate.basicFunction(
|
||||||
|
"resolve, reject",
|
||||||
|
[
|
||||||
|
"// start update chunk loading",
|
||||||
|
`var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId);`,
|
||||||
|
`var onResolve = ${runtimeTemplate.basicFunction("obj", [
|
||||||
|
"var updatedModules = obj.__webpack_modules__;",
|
||||||
|
"var updatedRuntime = obj.__webpack_runtime__;",
|
||||||
|
"if(updatedRuntime) currentUpdateRuntime.push(updatedRuntime);",
|
||||||
|
"for(var moduleId in updatedModules) {",
|
||||||
|
Template.indent([
|
||||||
|
`if(${RuntimeGlobals.hasOwnProperty}(updatedModules, moduleId)) {`,
|
||||||
|
Template.indent([
|
||||||
|
"currentUpdate[moduleId] = updatedModules[moduleId];",
|
||||||
|
"if(updatedModulesList) updatedModulesList.push(moduleId);"
|
||||||
|
]),
|
||||||
|
"}"
|
||||||
|
]),
|
||||||
|
"}",
|
||||||
|
"resolve(obj);"
|
||||||
|
])};`,
|
||||||
|
`var onReject = ${runtimeTemplate.basicFunction("error", [
|
||||||
|
"var errorMsg = error.message || 'unknown reason';",
|
||||||
|
"error.message = 'Loading hot update chunk ' + chunkId + ' failed.\\n(' + errorMsg + ')';",
|
||||||
|
"error.name = 'ChunkLoadError';",
|
||||||
|
"reject(error);"
|
||||||
|
])}`,
|
||||||
|
`var loadScript = ${runtimeTemplate.basicFunction(
|
||||||
|
"url, onResolve, onReject",
|
||||||
|
[
|
||||||
|
`return ${importFunctionName}(/* webpackIgnore: true */ url).then(onResolve).catch(onReject)`
|
||||||
|
]
|
||||||
|
)}
|
||||||
|
loadScript(url, onResolve, onReject);`
|
||||||
|
]
|
||||||
|
)});`
|
||||||
|
]),
|
||||||
|
"}",
|
||||||
|
""
|
||||||
|
])
|
||||||
|
: "// no HMR",
|
||||||
|
"",
|
||||||
|
withHmrManifest
|
||||||
|
? Template.asString([
|
||||||
|
`${
|
||||||
|
RuntimeGlobals.hmrDownloadManifest
|
||||||
|
} = ${runtimeTemplate.basicFunction("", [
|
||||||
|
'if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API");',
|
||||||
|
`return fetch(${RuntimeGlobals.publicPath} + ${
|
||||||
|
RuntimeGlobals.getUpdateManifestFilename
|
||||||
|
}()).then(${runtimeTemplate.basicFunction("response", [
|
||||||
|
"if(response.status === 404) return; // no update available",
|
||||||
|
'if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText);',
|
||||||
|
"return response.json();"
|
||||||
|
])});`
|
||||||
|
])};`
|
||||||
|
])
|
||||||
|
: "// no HMR manifest"
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,7 +212,12 @@ const describeCases = config => {
|
||||||
link.href = file.name;
|
link.href = file.name;
|
||||||
runner._moduleScope.document.head.appendChild(link);
|
runner._moduleScope.document.head.appendChild(link);
|
||||||
} else {
|
} else {
|
||||||
runner.require(outputDirectory, `./${file.name}`);
|
const result = runner.require(
|
||||||
|
outputDirectory,
|
||||||
|
`./${file.name}`
|
||||||
|
);
|
||||||
|
if (typeof result === "object" && "then" in result)
|
||||||
|
promise = promise.then(() => result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const message = "Hello from async module!";
|
||||||
|
---
|
||||||
|
export const message = "Updated async module!";
|
|
@ -0,0 +1,34 @@
|
||||||
|
import update from "../../update.esm";
|
||||||
|
import.meta.webpackHot.accept(["./async-module", "./lazy-module"]);
|
||||||
|
|
||||||
|
it("should handle HMR with async chunks in ESM format", (done) => {
|
||||||
|
// Initial load of async chunks
|
||||||
|
Promise.all([
|
||||||
|
import("./async-module"),
|
||||||
|
import("./lazy-module")
|
||||||
|
]).then(([asyncModule, lazyModule]) => {
|
||||||
|
expect(asyncModule.message).toBe("Hello from async module!");
|
||||||
|
expect(lazyModule.data.value).toBe(42);
|
||||||
|
|
||||||
|
NEXT(update(done, true, () => {
|
||||||
|
// Re-import after HMR update
|
||||||
|
Promise.all([
|
||||||
|
import("./async-module"),
|
||||||
|
import("./lazy-module")
|
||||||
|
]).then(([updatedAsyncModule, updatedLazyModule]) => {
|
||||||
|
expect(updatedAsyncModule.message).toBe("Updated async module!");
|
||||||
|
expect(updatedLazyModule.data.value).toBe(100);
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
}));
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should support dynamic imports with proper ESM chunk loading", (done) => {
|
||||||
|
// Test that dynamic imports work correctly with ESM chunk format
|
||||||
|
import("./async-module").then((module) => {
|
||||||
|
expect(module.message).toBeDefined();
|
||||||
|
expect(typeof module.message).toBe("string");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
export const data = {
|
||||||
|
type: "lazy",
|
||||||
|
value: 42
|
||||||
|
};
|
||||||
|
---
|
||||||
|
export const data = {
|
||||||
|
type: "lazy",
|
||||||
|
value: 100
|
||||||
|
};
|
|
@ -0,0 +1,17 @@
|
||||||
|
/** @type {import("../../../../types").Configuration} */
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
experiments: {
|
||||||
|
outputModule: true
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
module: true,
|
||||||
|
chunkFormat: "module",
|
||||||
|
filename: "[name].mjs",
|
||||||
|
chunkFilename: "[name].chunk.mjs",
|
||||||
|
enabledLibraryTypes: ["module"]
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
import * as styles from "./style.module.css";
|
||||||
|
import update from "../../update.esm";
|
||||||
|
|
||||||
|
it("should work", async function (done) {
|
||||||
|
expect(styles).toMatchObject({ class: "_style_module_css-class" });
|
||||||
|
|
||||||
|
const styles2 = await import("./style2.module.css");
|
||||||
|
|
||||||
|
expect(styles2).toMatchObject({
|
||||||
|
foo: "_style2_module_css-foo"
|
||||||
|
});
|
||||||
|
|
||||||
|
import.meta.webpackHot.accept(["./style.module.css", "./style2.module.css"], () => {
|
||||||
|
expect(styles).toMatchObject({
|
||||||
|
"class-other": "_style_module_css-class-other"
|
||||||
|
});
|
||||||
|
import("./style2.module.css").then(styles2 => {
|
||||||
|
expect(styles2).toMatchObject({
|
||||||
|
"bar": "_style2_module_css-bar"
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
NEXT(update(done));
|
||||||
|
});
|
||||||
|
|
||||||
|
module.hot.accept();
|
|
@ -0,0 +1,7 @@
|
||||||
|
.class {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
---
|
||||||
|
.class-other {
|
||||||
|
color: blue;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
.foo {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
---
|
||||||
|
.bar {
|
||||||
|
color: blue;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
module.exports = {
|
||||||
|
moduleScope(scope) {
|
||||||
|
const link = scope.window.document.createElement("link");
|
||||||
|
link.rel = "stylesheet";
|
||||||
|
link.href = "https://test.cases/path/bundle.css";
|
||||||
|
scope.window.document.head.appendChild(link);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
/** @type {import("../../../../").Configuration} */
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
experiments: {
|
||||||
|
outputModule: true,
|
||||||
|
css: true
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
module: true,
|
||||||
|
chunkFormat: "module",
|
||||||
|
filename: "[name].mjs",
|
||||||
|
chunkFilename: "[name].chunk.mjs",
|
||||||
|
enabledLibraryTypes: ["module"]
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const asyncData = {
|
||||||
|
loaded: true,
|
||||||
|
content: "Async shared content"
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { sharedData } from "./shared";
|
||||||
|
import update from "../../update.esm";
|
||||||
|
|
||||||
|
it("should handle HMR with runtime chunk in ESM format", (done) => {
|
||||||
|
expect(sharedData.version).toBe("1.0.0");
|
||||||
|
|
||||||
|
import.meta.webpackHot.accept(["./shared"]);
|
||||||
|
|
||||||
|
NEXT(update(done, true, () => {
|
||||||
|
import("./shared").then(updatedModule => {
|
||||||
|
expect(updatedModule.sharedData.version).toBe("2.0.0");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should load async shared module with runtime chunk", (done) => {
|
||||||
|
import("./async-shared").then(module => {
|
||||||
|
expect(module.asyncData.loaded).toBe(true);
|
||||||
|
expect(module.asyncData.content).toBe("Async shared content");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
export const sharedData = {
|
||||||
|
version: "1.0.0",
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
---
|
||||||
|
export const sharedData = {
|
||||||
|
version: "2.0.0",
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
/** @type {import("../../../../types").Configuration} */
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
experiments: {
|
||||||
|
outputModule: true
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
module: true,
|
||||||
|
chunkFormat: "module",
|
||||||
|
filename: "[name].mjs",
|
||||||
|
chunkFilename: "[name].chunk.mjs",
|
||||||
|
enabledLibraryTypes: ["module"]
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false,
|
||||||
|
runtimeChunk: "single"
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { greeting } from "./module.js";
|
||||||
|
import update from "../../update.esm.js";
|
||||||
|
|
||||||
|
import.meta.webpackHot.accept(["./module.js"]);
|
||||||
|
|
||||||
|
it("should update a simple ES module with HMR", (done) => {
|
||||||
|
expect(greeting).toBe("Hello World!");
|
||||||
|
|
||||||
|
NEXT(update(done, true, () => {
|
||||||
|
// After HMR update, we need to re-import the module in ESM
|
||||||
|
import("./module.js").then(updatedModule => {
|
||||||
|
expect(updatedModule.greeting).toBe("Hello HMR!");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have HMR runtime available in ESM output", () => {
|
||||||
|
expect(typeof import.meta.webpackHot.accept).toBe("function");
|
||||||
|
expect(typeof import.meta.webpackHot.decline).toBe("function");
|
||||||
|
expect(typeof import.meta.webpackHot.dispose).toBe("function");
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const greeting = "Hello World!";
|
||||||
|
---
|
||||||
|
export const greeting = "Hello HMR!";
|
|
@ -0,0 +1,17 @@
|
||||||
|
/** @type {import("../../../../types").Configuration} */
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
experiments: {
|
||||||
|
outputModule: true
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
module: true,
|
||||||
|
chunkFormat: "module",
|
||||||
|
filename: "[name].mjs",
|
||||||
|
chunkFilename: "[name].chunk.mjs",
|
||||||
|
enabledLibraryTypes: ["module"]
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,16 @@
|
||||||
|
export function commonFunction(input) {
|
||||||
|
return `Common function processed: ${input}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const commonData = {
|
||||||
|
shared: true
|
||||||
|
};
|
||||||
|
---
|
||||||
|
export function commonFunction(input) {
|
||||||
|
return `Updated common function: ${input}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const commonData = {
|
||||||
|
shared: true,
|
||||||
|
updated: true
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
import update from "../../update.esm";
|
||||||
|
import.meta.webpackHot.accept(["./common/shared", "vendor-lib"]);
|
||||||
|
|
||||||
|
it("should handle HMR with split chunks in ESM format", (done) => {
|
||||||
|
Promise.all([
|
||||||
|
import("./common/shared"),
|
||||||
|
import("vendor-lib")
|
||||||
|
]).then(([commonModule, vendorModule]) => {
|
||||||
|
expect(commonModule.commonFunction("test")).toBe("Common function processed: test");
|
||||||
|
expect(vendorModule.default.version).toBe("1.0.0");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
|
||||||
|
NEXT(update(done, true, () => {
|
||||||
|
// Re-import after HMR update
|
||||||
|
Promise.all([
|
||||||
|
import("./common/shared"),
|
||||||
|
import("vendor-lib")
|
||||||
|
]).then(([commonModule, vendorModule]) => {
|
||||||
|
expect(commonModule.commonFunction("test")).toBe("Updated common function: test");
|
||||||
|
expect(vendorModule.default.version).toBe("2.0.0");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
}));
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
const vendorLib = {
|
||||||
|
version: "1.0.0",
|
||||||
|
init: function() {
|
||||||
|
console.log("Vendor lib initialized");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default vendorLib;
|
||||||
|
---
|
||||||
|
const vendorLib = {
|
||||||
|
version: "2.0.0",
|
||||||
|
init: function() {
|
||||||
|
console.log("Vendor lib initialized v2");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default vendorLib;
|
|
@ -0,0 +1,34 @@
|
||||||
|
/** @type {import("../../../../types").Configuration} */
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
experiments: {
|
||||||
|
outputModule: true
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
module: true,
|
||||||
|
chunkFormat: "module",
|
||||||
|
filename: "[name].mjs",
|
||||||
|
chunkFilename: "[name].chunk.mjs",
|
||||||
|
enabledLibraryTypes: ["module"]
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false,
|
||||||
|
splitChunks: {
|
||||||
|
chunks: "all",
|
||||||
|
minSize: 0,
|
||||||
|
cacheGroups: {
|
||||||
|
common: {
|
||||||
|
test: /common/,
|
||||||
|
name: "common",
|
||||||
|
priority: 10,
|
||||||
|
enforce: true
|
||||||
|
},
|
||||||
|
vendor: {
|
||||||
|
test: /node_modules/,
|
||||||
|
name: "vendor",
|
||||||
|
priority: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,16 @@
|
||||||
|
export default function update(done, options, callback) {
|
||||||
|
return function (err, stats) {
|
||||||
|
if (err) return done(err);
|
||||||
|
import.meta.webpackHot
|
||||||
|
.check(options || true)
|
||||||
|
.then(updatedModules => {
|
||||||
|
if (!updatedModules) {
|
||||||
|
return done(new Error("No update available"));
|
||||||
|
}
|
||||||
|
if (callback) callback(stats);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -222,6 +222,14 @@ class TestRunner {
|
||||||
content: fs.readFileSync(module, "utf-8")
|
content: fs.readFileSync(module, "utf-8")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (module.startsWith("https://test.")) {
|
||||||
|
const realPath = urlToPath(module, currentDirectory);
|
||||||
|
return {
|
||||||
|
subPath: "",
|
||||||
|
modulePath: realPath,
|
||||||
|
content: fs.readFileSync(realPath, "utf-8")
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue