mirror of https://github.com/webpack/webpack.git
Merge b8c489f690
into 9f98d803c0
This commit is contained in:
commit
e270331275
|
@ -6,6 +6,7 @@
|
|||
"use strict";
|
||||
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const { getLibraryType } = require("../util/LibraryHelpers");
|
||||
const ExportWebpackRequireRuntimeModule = require("./ExportWebpackRequireRuntimeModule");
|
||||
const ModuleChunkLoadingRuntimeModule = require("./ModuleChunkLoadingRuntimeModule");
|
||||
|
||||
|
@ -104,7 +105,15 @@ class ModuleChunkLoadingPlugin {
|
|||
set.add(RuntimeGlobals.publicPath);
|
||||
}
|
||||
|
||||
set.add(RuntimeGlobals.getChunkScriptFilename);
|
||||
// Avoid generating dynamic filename helper for ESM libraries with outputModule
|
||||
const outputModule =
|
||||
compilation.options &&
|
||||
compilation.options.experiments &&
|
||||
compilation.options.experiments.outputModule;
|
||||
const isESMLibrary = getLibraryType(chunk, compilation) === "module";
|
||||
if (!(outputModule && isESMLibrary)) {
|
||||
set.add(RuntimeGlobals.getChunkScriptFilename);
|
||||
}
|
||||
});
|
||||
|
||||
compilation.hooks.runtimeRequirementInTree
|
||||
|
|
|
@ -17,6 +17,7 @@ const {
|
|||
getChunkFilenameTemplate
|
||||
} = require("../javascript/JavascriptModulesPlugin");
|
||||
const { getInitialChunkIds } = require("../javascript/StartupHelpers");
|
||||
const { getLibraryType } = require("../util/LibraryHelpers");
|
||||
const compileBooleanMatcher = require("../util/compileBooleanMatcher");
|
||||
const { getUndoPath } = require("../util/identifier");
|
||||
|
||||
|
@ -95,6 +96,13 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
runtimeTemplate,
|
||||
outputOptions: { importFunctionName, crossOriginLoading, charset }
|
||||
} = compilation;
|
||||
const outputModule =
|
||||
compilation.options &&
|
||||
compilation.options.experiments &&
|
||||
compilation.options.experiments.outputModule;
|
||||
|
||||
const libraryType = getLibraryType(chunk, compilation);
|
||||
const isESMLibrary = libraryType === "module";
|
||||
const fn = RuntimeGlobals.ensureChunkHandlers;
|
||||
const withBaseURI = this._runtimeRequirements.has(RuntimeGlobals.baseURI);
|
||||
const withExternalInstallChunk = this._runtimeRequirements.has(
|
||||
|
@ -221,19 +229,83 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
: `if(${hasJsMatcher("chunkId")}) {`,
|
||||
Template.indent([
|
||||
"// setup Promise in chunk cache",
|
||||
`var promise = ${importFunctionName}(${
|
||||
compilation.outputOptions.publicPath === "auto"
|
||||
? JSON.stringify(rootOutputDir)
|
||||
: RuntimeGlobals.publicPath
|
||||
} + ${
|
||||
RuntimeGlobals.getChunkScriptFilename
|
||||
}(chunkId)).then(installChunk, ${runtimeTemplate.basicFunction(
|
||||
"e",
|
||||
[
|
||||
"if(installedChunks[chunkId] !== 0) installedChunks[chunkId] = undefined;",
|
||||
"throw e;"
|
||||
]
|
||||
)});`,
|
||||
outputModule && isESMLibrary
|
||||
? // For ESM library output generate statically analyzable imports per chunk
|
||||
(() => {
|
||||
// Build a switch over known async JS chunks with literal URLs
|
||||
const meta =
|
||||
compilation.outputOptions.importMetaName ||
|
||||
"import.meta";
|
||||
const relevantChunks = new Set();
|
||||
for (const c of chunk.getAllAsyncChunks()) {
|
||||
relevantChunks.add(c);
|
||||
}
|
||||
const includeEntries = chunkGraph
|
||||
.getTreeRuntimeRequirements(chunk)
|
||||
.has(
|
||||
RuntimeGlobals.ensureChunkIncludeEntries
|
||||
);
|
||||
if (includeEntries) {
|
||||
for (const c of chunkGraph.getRuntimeChunkDependentChunksIterable(
|
||||
chunk
|
||||
)) {
|
||||
relevantChunks.add(c);
|
||||
}
|
||||
}
|
||||
for (const ep of chunk.getAllReferencedAsyncEntrypoints()) {
|
||||
relevantChunks.add(
|
||||
ep.chunks[ep.chunks.length - 1]
|
||||
);
|
||||
}
|
||||
const cases = [];
|
||||
for (const c of relevantChunks) {
|
||||
if (!chunkHasJs(c, chunkGraph)) continue;
|
||||
const filename = compilation.getPath(
|
||||
getChunkFilenameTemplate(
|
||||
c,
|
||||
compilation.outputOptions
|
||||
),
|
||||
{ chunk: c, contentHashType: "javascript" }
|
||||
);
|
||||
const spec = JSON.stringify(
|
||||
rootOutputDir + filename
|
||||
);
|
||||
const cid = JSON.stringify(
|
||||
/** @type {string|number} */ (c.id)
|
||||
);
|
||||
cases.push(
|
||||
`case ${cid}: promise = ${importFunctionName}(new URL(${spec}, ${meta}.url).href); break;`
|
||||
);
|
||||
}
|
||||
return Template.asString([
|
||||
"var promise;",
|
||||
"switch(chunkId) {",
|
||||
Template.indent(cases),
|
||||
"default: promise = Promise.reject(new Error('Missing chunk mapping for ' + chunkId));",
|
||||
"}",
|
||||
`promise = promise.then(installChunk, ${runtimeTemplate.basicFunction(
|
||||
"e",
|
||||
[
|
||||
"if(installedChunks[chunkId] !== 0) installedChunks[chunkId] = undefined;",
|
||||
"throw e;"
|
||||
]
|
||||
)});`
|
||||
]);
|
||||
})()
|
||||
: // Traditional string concatenation for non-ESM output
|
||||
`var promise = ${importFunctionName}(${
|
||||
compilation.outputOptions.publicPath === "auto"
|
||||
? JSON.stringify(rootOutputDir)
|
||||
: RuntimeGlobals.publicPath
|
||||
} + ${
|
||||
RuntimeGlobals.getChunkScriptFilename
|
||||
}(chunkId)).then(installChunk, ${runtimeTemplate.basicFunction(
|
||||
"e",
|
||||
[
|
||||
"if(installedChunks[chunkId] !== 0) installedChunks[chunkId] = undefined;",
|
||||
"throw e;"
|
||||
]
|
||||
)});`,
|
||||
`var promise = Promise.race([promise, new Promise(${runtimeTemplate.expressionFunction(
|
||||
"installedChunkData = installedChunks[chunkId] = [resolve]",
|
||||
"resolve"
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
|
||||
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
|
||||
/** @typedef {import("../Chunk")} Chunk */
|
||||
/** @typedef {import("../Compilation")} Compilation */
|
||||
|
||||
/**
|
||||
* Determine library type from chunk entry options or compilation output options
|
||||
* @param {Chunk} chunk The chunk to get library type for
|
||||
* @param {Compilation} compilation The compilation
|
||||
* @returns {LibraryType | undefined} The library type or undefined
|
||||
*/
|
||||
module.exports.getLibraryType = (chunk, compilation) => {
|
||||
const entryOptions = chunk.getEntryOptions();
|
||||
const libraryType =
|
||||
entryOptions && entryOptions.library !== undefined
|
||||
? entryOptions.library.type
|
||||
: compilation.outputOptions.library &&
|
||||
typeof compilation.outputOptions.library === "object" &&
|
||||
!Array.isArray(compilation.outputOptions.library)
|
||||
? compilation.outputOptions.library.type
|
||||
: undefined;
|
||||
return libraryType;
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
export default function () {
|
||||
return 2;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export default async function getNumber() {
|
||||
const num = (await import("./chunk.js")).default;
|
||||
return 1 + num();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Test for issue #15947 - ESM library with dynamic imports
|
||||
it("should generate statically analyzable dynamic imports for ESM library output", () => {
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const outputPath = path.join(__dirname, "lib.js");
|
||||
const content = fs.readFileSync(outputPath, "utf-8");
|
||||
|
||||
// Should use new URL with import.meta.url and literal path
|
||||
expect(content).toMatch(/import\(\s*new\s+URL\(\s*"[^"]+"\s*,\s*import\.meta\.url\s*\)\.href\s*\)/);
|
||||
// Should not use dynamic __webpack_require__.u() or publicPath string concatenation
|
||||
expect(content).not.toMatch(/__webpack_require__\.u\(/);
|
||||
expect(content).not.toMatch(/\+\s*__webpack_require__\.p\s*\+/);
|
||||
|
||||
// Verify that the chunk file was created
|
||||
const chunkFiles = fs
|
||||
.readdirSync(__dirname)
|
||||
.filter(f => f.startsWith("chunk.") && f.endsWith(".js"));
|
||||
expect(chunkFiles.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify the ESM export is present
|
||||
expect(content).toMatch(/export\s*\{/);
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
findBundle() {
|
||||
return ["./index.js"];
|
||||
}
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
"use strict";
|
||||
|
||||
/** @type {import("../../../../types").Configuration} */
|
||||
module.exports = [
|
||||
{
|
||||
entry: "./entry.js",
|
||||
output: {
|
||||
filename: "lib.js",
|
||||
chunkFilename: "chunk.[chunkhash:8].js",
|
||||
library: {
|
||||
type: "module"
|
||||
}
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true
|
||||
}
|
||||
},
|
||||
{
|
||||
entry: "./index.js",
|
||||
output: {
|
||||
filename: "index.js"
|
||||
}
|
||||
}
|
||||
];
|
Loading…
Reference in New Issue