Skip to content

Commit 7dd5e42

Browse files
authored
fix: module library when exports are provided unknownly (#20293)
=
1 parent c11d456 commit 7dd5e42

File tree

10 files changed

+174
-6
lines changed

10 files changed

+174
-6
lines changed

.changeset/tender-pillows-build.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": patch
3+
---
4+
5+
Preserve star exports for dependencies in ESMA module output.

lib/library/ModuleLibraryPlugin.js

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
const { ConcatSource } = require("webpack-sources");
99
const { UsageState } = require("../ExportsInfo");
10+
const ExternalModule = require("../ExternalModule");
1011
const RuntimeGlobals = require("../RuntimeGlobals");
1112
const Template = require("../Template");
13+
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
1214
const ConcatenatedModule = require("../optimize/ConcatenatedModule");
1315
const propertyAccess = require("../util/propertyAccess");
1416
const { getEntryRuntime } = require("../util/runtime");
@@ -20,11 +22,14 @@ const AbstractLibraryPlugin = require("./AbstractLibraryPlugin");
2022
/** @typedef {import("../../declarations/WebpackOptions").LibraryExport} LibraryExport */
2123
/** @typedef {import("../Chunk")} Chunk */
2224
/** @typedef {import("../Compiler")} Compiler */
25+
/** @typedef {import("../ModuleGraph")} ModuleGraph */
2326
/** @typedef {import("../Module")} Module */
2427
/** @typedef {import("../Module").BuildMeta} BuildMeta */
2528
/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
2629
/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
2730
/** @typedef {import("../javascript/JavascriptModulesPlugin").ModuleRenderContext} ModuleRenderContext */
31+
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
32+
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
2833

2934
/**
3035
* @template T
@@ -116,8 +121,13 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
116121
exportsInfo.canMangleUse = false;
117122
} else {
118123
const exportsInfo = moduleGraph.getExportsInfo(module);
119-
// If the entry module is commonjs, its exports cannot be mangled
120-
if (module.buildMeta && module.buildMeta.treatAsCommonJs) {
124+
125+
if (
126+
// If the entry module is commonjs, its exports cannot be mangled
127+
(module.buildMeta && module.buildMeta.treatAsCommonJs) ||
128+
// The entry module provides unknown exports
129+
exportsInfo._otherExportsInfo.provided === null
130+
) {
121131
exportsInfo.setUsedInUnknownWay(runtime);
122132
} else {
123133
exportsInfo.setAllKnownExportsUsed(runtime);
@@ -144,6 +154,94 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
144154
};
145155
}
146156

157+
/**
158+
* @param {Source} source source
159+
* @param {Module} module module
160+
* @param {ModuleGraph} moduleGraph moduleGraph
161+
* @param {RuntimeSpec} runtime chunk runtime
162+
* @param {[string, string][]} exports exports
163+
* @param {Set<string>} alreadyRenderedExports already rendered exports
164+
* @returns {ConcatSource} source with null provided exports
165+
*/
166+
_analyzeUnknownProvidedExports(
167+
source,
168+
module,
169+
moduleGraph,
170+
runtime,
171+
exports,
172+
alreadyRenderedExports
173+
) {
174+
const result = new ConcatSource(source);
175+
/** @type {Set<string>} */
176+
const moduleRequests = new Set();
177+
/** @type {Map<string, string>} */
178+
const unknownProvidedExports = new Map();
179+
180+
/**
181+
* @param {Module} module the module
182+
* @param {boolean} isDynamicReexport if module is dynamic reexported
183+
*/
184+
const resolveDynamicStarReexport = (module, isDynamicReexport) => {
185+
for (const connection of moduleGraph.getOutgoingConnections(module)) {
186+
const dep = connection.dependency;
187+
188+
// Only handle star-reexport statement
189+
if (
190+
dep instanceof HarmonyExportImportedSpecifierDependency &&
191+
dep.name === null
192+
) {
193+
const importedModule = connection.resolvedModule;
194+
const importedModuleExportsInfo =
195+
moduleGraph.getExportsInfo(importedModule);
196+
197+
// The imported module provides unknown exports
198+
// So keep the reexports rendered in the bundle
199+
if (
200+
dep.getMode(moduleGraph, runtime).type === "dynamic-reexport" &&
201+
importedModuleExportsInfo._otherExportsInfo.provided === null
202+
) {
203+
// Handle export * from 'external'
204+
if (importedModule instanceof ExternalModule) {
205+
moduleRequests.add(importedModule.userRequest);
206+
} else {
207+
resolveDynamicStarReexport(importedModule, true);
208+
}
209+
}
210+
// If importer modules existing `dynamic-reexport` dependency
211+
// We should keep export statement rendered in the bundle
212+
else if (isDynamicReexport) {
213+
for (const exportInfo of importedModuleExportsInfo.orderedExports) {
214+
if (!exportInfo.provided || exportInfo.name === "default") {
215+
continue;
216+
}
217+
const originalName = exportInfo.name;
218+
const usedName = exportInfo.getUsedName(originalName, runtime);
219+
220+
if (!alreadyRenderedExports.has(originalName) && usedName) {
221+
unknownProvidedExports.set(originalName, usedName);
222+
}
223+
}
224+
}
225+
}
226+
}
227+
};
228+
229+
resolveDynamicStarReexport(module, false);
230+
231+
for (const request of moduleRequests) {
232+
result.add(`export * from "${request}";\n`);
233+
}
234+
235+
for (const [origin, used] of unknownProvidedExports) {
236+
exports.push([
237+
origin,
238+
`${RuntimeGlobals.exports}${propertyAccess([used])}`
239+
]);
240+
}
241+
242+
return result;
243+
}
244+
147245
/**
148246
* @param {Source} source source
149247
* @param {Module} module module
@@ -164,9 +262,8 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
164262
},
165263
{ options, compilation }
166264
) {
167-
const result = new ConcatSource(source);
168-
169-
const exportsInfo = options.export
265+
let result = new ConcatSource(source);
266+
const exportInfos = options.export
170267
? [
171268
moduleGraph.getExportInfo(
172269
module,
@@ -188,6 +285,9 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
188285
const shortHandedExports = [];
189286
/** @type {[string, string][]} */
190287
const exports = [];
288+
/** @type {Set<string>} */
289+
const alreadyRenderedExports = new Set();
290+
191291
const isAsync = moduleGraph.isAsync(module);
192292

193293
const treatAsCommonJs =
@@ -200,7 +300,7 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
200300
);
201301
}
202302

203-
outer: for (const exportInfo of exportsInfo) {
303+
outer: for (const exportInfo of exportInfos) {
204304
if (!exportInfo.provided) continue;
205305

206306
const originalName = exportInfo.name;
@@ -273,6 +373,8 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
273373
: `${finalName} as ${originalName}`
274374
);
275375
}
376+
377+
alreadyRenderedExports.add(originalName);
276378
}
277379

278380
if (treatAsCommonJs) {
@@ -283,6 +385,15 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
283385
result.add(`export { ${shortHandedExports.join(", ")} };\n`);
284386
}
285387

388+
result = this._analyzeUnknownProvidedExports(
389+
result,
390+
module,
391+
moduleGraph,
392+
chunk.runtime,
393+
exports,
394+
alreadyRenderedExports
395+
);
396+
286397
for (const [exportName, final] of exports) {
287398
result.add(
288399
`export ${runtimeTemplate.renderConst()} ${exportName} = ${final};\n`
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const foo = 3;
2+
export const bar = 3;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const foo = 2;
2+
export const bar = 2;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export * from "fs";
2+
export * from "./reexport-foo";
3+
export * from "./reexport-bar";
4+
5+
export const foo = 1;
6+
export const bar = 1;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "path";
2+
export * from "./bar";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./foo";

test/configCases/library/0-create-library/webpack.config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,5 +934,20 @@ module.exports = (env, { testPath }) => [
934934
type: "system"
935935
}
936936
}
937+
},
938+
{
939+
entry: "./esm-star-reexport-and-external",
940+
target: "node14",
941+
output: {
942+
uniqueName: "esm-star-reexport-and-external",
943+
filename: "esm-star-reexport-and-external.mjs",
944+
module: true,
945+
library: {
946+
type: "module"
947+
}
948+
},
949+
experiments: {
950+
outputModule: true
951+
}
937952
}
938953
];
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { readFile, foo, bar, resolve } from "library";
2+
import fs from "fs"
3+
import path from "path"
4+
5+
it("should handle external module correctly with star reexports", () => {
6+
// `foo` and `bar` are reexported by `reexport-foo` module and `reexport-bar` module,
7+
// but the entry module's exports take precedence.
8+
expect(foo).toBe(1);
9+
expect(bar).toBe(1);
10+
11+
expect(readFile).toBe(fs.readFile);
12+
expect(resolve).toBe(path.resolve);
13+
});

test/configCases/library/1-use-library/webpack.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,5 +734,16 @@ module.exports = (env, { testPath }) => [
734734
NAME: JSON.stringify("commonjs-bundle-to-esm")
735735
})
736736
]
737+
},
738+
{
739+
entry: "./esm-star-reexport-and-external",
740+
resolve: {
741+
alias: {
742+
library: path.resolve(
743+
testPath,
744+
"../0-create-library/esm-star-reexport-and-external.mjs"
745+
)
746+
}
747+
}
737748
}
738749
];

0 commit comments

Comments
 (0)