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";
|
||||
|
||||
const { ConcatSource } = require("webpack-sources");
|
||||
const { RuntimeGlobals } = require("..");
|
||||
const HotUpdateChunk = require("../HotUpdateChunk");
|
||||
const { RuntimeGlobals, HotUpdateChunk } = require("..");
|
||||
const Template = require("../Template");
|
||||
const { getAllChunks } = require("../javascript/ChunkHelpers");
|
||||
const {
|
||||
|
@ -72,30 +71,47 @@ class ModuleChunkFormatPlugin {
|
|||
hooks.renderChunk.tap(PLUGIN_NAME, (modules, renderContext) => {
|
||||
const { chunk, chunkGraph, runtimeTemplate } = renderContext;
|
||||
const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
|
||||
|
||||
const source = new ConcatSource();
|
||||
source.add(
|
||||
`export const __webpack_id__ = ${JSON.stringify(chunk.id)};\n`
|
||||
);
|
||||
source.add(
|
||||
`export const __webpack_ids__ = ${JSON.stringify(chunk.ids)};\n`
|
||||
);
|
||||
source.add("export const __webpack_modules__ = ");
|
||||
source.add(modules);
|
||||
source.add(";\n");
|
||||
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
|
||||
if (runtimeModules.length > 0) {
|
||||
source.add("export const __webpack_runtime__ =\n");
|
||||
source.add(
|
||||
Template.renderChunkRuntimeModules(runtimeModules, renderContext)
|
||||
);
|
||||
}
|
||||
if (hotUpdateChunk) {
|
||||
throw new Error("HMR is not implemented for module chunk format yet");
|
||||
} else {
|
||||
source.add(
|
||||
`export const __webpack_id__ = ${JSON.stringify(chunk.id)};\n`
|
||||
);
|
||||
source.add(
|
||||
`export const __webpack_ids__ = ${JSON.stringify(chunk.ids)};\n`
|
||||
);
|
||||
source.add("export const __webpack_modules__ = ");
|
||||
source.add(modules);
|
||||
source.add(";\n");
|
||||
const runtimeModules =
|
||||
chunkGraph.getChunkRuntimeModulesInOrder(chunk);
|
||||
if (runtimeModules.length > 0) {
|
||||
source.add("export const __webpack_runtime__ =\n");
|
||||
source.add(
|
||||
Template.renderChunkRuntimeModules(runtimeModules, renderContext)
|
||||
);
|
||||
}
|
||||
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
|
||||
if (runtimeChunk) {
|
||||
const currentOutputName = compilation
|
||||
return source;
|
||||
}
|
||||
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
|
||||
if (runtimeChunk) {
|
||||
const currentOutputName = compilation
|
||||
.getPath(
|
||||
getChunkFilenameTemplate(chunk, compilation.outputOptions),
|
||||
{
|
||||
chunk,
|
||||
contentHashType: "javascript"
|
||||
}
|
||||
)
|
||||
.replace(/^\/+/g, "")
|
||||
.split("/");
|
||||
|
||||
/**
|
||||
* @param {Chunk} chunk the chunk
|
||||
* @returns {string} the relative path
|
||||
*/
|
||||
const getRelativePath = chunk => {
|
||||
const baseOutputName = currentOutputName.slice();
|
||||
const chunkOutputName = compilation
|
||||
.getPath(
|
||||
getChunkFilenameTemplate(chunk, compilation.outputOptions),
|
||||
{
|
||||
|
@ -106,101 +122,83 @@ class ModuleChunkFormatPlugin {
|
|||
.replace(/^\/+/g, "")
|
||||
.split("/");
|
||||
|
||||
/**
|
||||
* @param {Chunk} chunk the chunk
|
||||
* @returns {string} the relative path
|
||||
*/
|
||||
const getRelativePath = chunk => {
|
||||
const baseOutputName = currentOutputName.slice();
|
||||
const chunkOutputName = compilation
|
||||
.getPath(
|
||||
getChunkFilenameTemplate(chunk, compilation.outputOptions),
|
||||
{
|
||||
chunk,
|
||||
contentHashType: "javascript"
|
||||
}
|
||||
)
|
||||
.replace(/^\/+/g, "")
|
||||
.split("/");
|
||||
|
||||
// remove common parts except filename
|
||||
while (
|
||||
baseOutputName.length > 1 &&
|
||||
chunkOutputName.length > 1 &&
|
||||
baseOutputName[0] === chunkOutputName[0]
|
||||
) {
|
||||
baseOutputName.shift();
|
||||
chunkOutputName.shift();
|
||||
}
|
||||
const last = chunkOutputName.join("/");
|
||||
// create final path
|
||||
return getUndoPath(baseOutputName.join("/"), last, true) + last;
|
||||
};
|
||||
|
||||
const entrySource = new ConcatSource();
|
||||
entrySource.add(source);
|
||||
entrySource.add(";\n\n// load runtime\n");
|
||||
entrySource.add(
|
||||
`import ${RuntimeGlobals.require} from ${JSON.stringify(
|
||||
getRelativePath(/** @type {Chunk} */ (runtimeChunk))
|
||||
)};\n`
|
||||
);
|
||||
|
||||
const startupSource = new ConcatSource();
|
||||
startupSource.add(
|
||||
`var __webpack_exec__ = ${runtimeTemplate.returningFunction(
|
||||
`${RuntimeGlobals.require}(${RuntimeGlobals.entryModuleId} = moduleId)`,
|
||||
"moduleId"
|
||||
)}\n`
|
||||
);
|
||||
|
||||
const loadedChunks = new Set();
|
||||
let index = 0;
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const [module, entrypoint] = entries[i];
|
||||
if (!chunkGraph.getModuleSourceTypes(module).has("javascript")) {
|
||||
continue;
|
||||
}
|
||||
const final = i + 1 === entries.length;
|
||||
const moduleId = chunkGraph.getModuleId(module);
|
||||
const chunks = getAllChunks(
|
||||
/** @type {Entrypoint} */ (entrypoint),
|
||||
/** @type {Chunk} */ (runtimeChunk),
|
||||
undefined
|
||||
);
|
||||
for (const chunk of chunks) {
|
||||
if (loadedChunks.has(chunk) || !chunkHasJs(chunk, chunkGraph))
|
||||
continue;
|
||||
loadedChunks.add(chunk);
|
||||
startupSource.add(
|
||||
`import * as __webpack_chunk_${index}__ from ${JSON.stringify(
|
||||
getRelativePath(chunk)
|
||||
)};\n`
|
||||
);
|
||||
startupSource.add(
|
||||
`${RuntimeGlobals.externalInstallChunk}(__webpack_chunk_${index}__);\n`
|
||||
);
|
||||
index++;
|
||||
}
|
||||
startupSource.add(
|
||||
`${
|
||||
final ? `var ${RuntimeGlobals.exports} = ` : ""
|
||||
}__webpack_exec__(${JSON.stringify(moduleId)});\n`
|
||||
);
|
||||
// remove common parts except filename
|
||||
while (
|
||||
baseOutputName.length > 1 &&
|
||||
chunkOutputName.length > 1 &&
|
||||
baseOutputName[0] === chunkOutputName[0]
|
||||
) {
|
||||
baseOutputName.shift();
|
||||
chunkOutputName.shift();
|
||||
}
|
||||
const last = chunkOutputName.join("/");
|
||||
// create final path
|
||||
return getUndoPath(baseOutputName.join("/"), last, true) + last;
|
||||
};
|
||||
|
||||
entrySource.add(
|
||||
hooks.renderStartup.call(
|
||||
startupSource,
|
||||
entries[entries.length - 1][0],
|
||||
{
|
||||
...renderContext,
|
||||
inlined: false
|
||||
}
|
||||
)
|
||||
const entrySource = new ConcatSource();
|
||||
entrySource.add(source);
|
||||
entrySource.add(";\n\n// load runtime\n");
|
||||
entrySource.add(
|
||||
`import ${RuntimeGlobals.require} from ${JSON.stringify(
|
||||
getRelativePath(/** @type {Chunk} */ (runtimeChunk))
|
||||
)};\n`
|
||||
);
|
||||
|
||||
const startupSource = new ConcatSource();
|
||||
startupSource.add(
|
||||
`var __webpack_exec__ = ${runtimeTemplate.returningFunction(
|
||||
`${RuntimeGlobals.require}(${RuntimeGlobals.entryModuleId} = moduleId)`,
|
||||
"moduleId"
|
||||
)}\n`
|
||||
);
|
||||
|
||||
const loadedChunks = new Set();
|
||||
let index = 0;
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const [module, entrypoint] = entries[i];
|
||||
if (!chunkGraph.getModuleSourceTypes(module).has("javascript")) {
|
||||
continue;
|
||||
}
|
||||
const final = i + 1 === entries.length;
|
||||
const moduleId = chunkGraph.getModuleId(module);
|
||||
const chunks = getAllChunks(
|
||||
/** @type {Entrypoint} */ (entrypoint),
|
||||
/** @type {Chunk} */ (runtimeChunk),
|
||||
undefined
|
||||
);
|
||||
for (const chunk of chunks) {
|
||||
if (loadedChunks.has(chunk) || !chunkHasJs(chunk, chunkGraph))
|
||||
continue;
|
||||
loadedChunks.add(chunk);
|
||||
startupSource.add(
|
||||
`import * as __webpack_chunk_${index}__ from ${JSON.stringify(
|
||||
getRelativePath(chunk)
|
||||
)};\n`
|
||||
);
|
||||
startupSource.add(
|
||||
`${RuntimeGlobals.externalInstallChunk}(__webpack_chunk_${index}__);\n`
|
||||
);
|
||||
index++;
|
||||
}
|
||||
startupSource.add(
|
||||
`${
|
||||
final ? `var ${RuntimeGlobals.exports} = ` : ""
|
||||
}__webpack_exec__(${JSON.stringify(moduleId)});\n`
|
||||
);
|
||||
return entrySource;
|
||||
}
|
||||
|
||||
entrySource.add(
|
||||
hooks.renderStartup.call(
|
||||
startupSource,
|
||||
entries[entries.length - 1][0],
|
||||
{
|
||||
...renderContext,
|
||||
inlined: false
|
||||
}
|
||||
)
|
||||
);
|
||||
return entrySource;
|
||||
}
|
||||
return source;
|
||||
});
|
||||
|
|
|
@ -63,6 +63,12 @@ class ModuleChunkLoadingPlugin {
|
|||
compilation.hooks.runtimeRequirementInTree
|
||||
.for(RuntimeGlobals.onChunksLoaded)
|
||||
.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
|
||||
.for(RuntimeGlobals.externalInstallChunk)
|
||||
.tap(PLUGIN_NAME, (chunk, set) => {
|
||||
|
@ -99,6 +105,26 @@ class ModuleChunkLoadingPlugin {
|
|||
|
||||
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(
|
||||
RuntimeGlobals.hmrDownloadUpdateHandlers
|
||||
);
|
||||
const withHmrManifest = this._runtimeRequirements.has(
|
||||
RuntimeGlobals.hmrDownloadManifest
|
||||
);
|
||||
const { linkPreload, linkPrefetch } =
|
||||
ModuleChunkLoadingRuntimeModule.getCompilationHooks(compilation);
|
||||
const isNeutralPlatform = runtimeTemplate.isNeutralPlatform();
|
||||
|
@ -346,7 +349,92 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
"installedChunks[chunkId] === 0",
|
||||
"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;
|
||||
runner._moduleScope.document.head.appendChild(link);
|
||||
} 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 {
|
||||
|
|
|
@ -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")
|
||||
};
|
||||
}
|
||||
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