Skip to content

Commit c2a1d99

Browse files
ScriptedAlchemycursoragent2heal1zhoushaw
authored
Process env build paths (#4482)
Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: 2heal1 <TwoHeal@163.com> Co-authored-by: zhouxiao.shaw <zhouxiao.shaw@bytedance.com>
1 parent 205cedd commit c2a1d99

File tree

16 files changed

+313
-211
lines changed

16 files changed

+313
-211
lines changed

packages/data-prefetch/src/cli/index.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,12 @@ export class PrefetchPlugin implements WebpackPluginInstance {
6161
this.options.runtimePlugins = [];
6262
}
6363

64-
const runtimePluginCandidates =
65-
process.env.IS_ESM_BUILD === 'true'
66-
? ['../plugin.js', '../plugin.cjs']
67-
: ['../plugin.cjs', '../plugin.js'];
68-
const runtimePath = runtimePluginCandidates
69-
.map((candidate) => path.resolve(__dirname, candidate))
70-
.find((candidatePath) => fs.existsSync(candidatePath));
71-
if (!runtimePath) {
64+
const runtimePluginFile =
65+
process.env.IS_ESM_BUILD === 'true' ? '../plugin.js' : '../plugin.cjs';
66+
const runtimePath = path.resolve(__dirname, runtimePluginFile);
67+
if (!fs.existsSync(runtimePath)) {
7268
throw new Error(
73-
[
74-
'[Module Federation Data Prefetch]: Unable to resolve runtime plugin file.',
75-
`Checked paths: ${runtimePluginCandidates
76-
.map((candidate) => path.resolve(__dirname, candidate))
77-
.join(', ')}`,
78-
].join('\n'),
69+
`[Module Federation Data Prefetch]: Unable to resolve runtime plugin file at ${runtimePath}.`,
7970
);
8071
}
8172
if (!this.options.runtimePlugins?.includes(runtimePath)) {

packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts

Lines changed: 11 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -34,88 +34,21 @@ const { mkdirpSync } = require(
3434
normalizeWebpackPath('webpack/lib/util/fs'),
3535
) as typeof import('webpack/lib/util/fs');
3636

37-
function resolveModule(
38-
candidates: string[],
39-
options?: NodeJS.RequireResolveOptions,
40-
): string {
41-
let lastError: unknown;
42-
for (const candidate of candidates) {
43-
try {
44-
return require.resolve(candidate, options);
45-
} catch (error) {
46-
lastError = error;
47-
}
48-
}
49-
50-
throw (
51-
lastError ??
52-
new Error(`Unable to resolve any module from: ${candidates.join(', ')}`)
53-
);
54-
}
55-
56-
function isEsmOutputBuild(compiler: Compiler): boolean {
57-
if (compiler.options.experiments?.outputModule) {
58-
return true;
59-
}
60-
61-
if (compiler.options.output?.module) {
62-
return true;
63-
}
37+
function resolveRuntimePaths(implementation?: string) {
38+
const ext = process.env.IS_ESM_BUILD === 'true' ? '.js' : '.cjs';
39+
const runtimeToolsSpec = `@module-federation/runtime-tools/dist/index${ext}`;
40+
const bundlerRuntimeSpec = `@module-federation/webpack-bundler-runtime/dist/index${ext}`;
41+
const runtimeSpec = `@module-federation/runtime/dist/index${ext}`;
6442

65-
const library = compiler.options.output?.library;
66-
if (
67-
library &&
68-
typeof library === 'object' &&
69-
!Array.isArray(library) &&
70-
'type' in library
71-
) {
72-
return library.type === 'module';
73-
}
74-
75-
return false;
76-
}
77-
78-
function getModuleCandidates(
79-
packageName: string,
80-
preferEsm: boolean,
81-
): string[] {
82-
const preferred = preferEsm
83-
? [`${packageName}/dist/index.js`, `${packageName}/dist/index.cjs`]
84-
: [`${packageName}/dist/index.cjs`, `${packageName}/dist/index.js`];
85-
86-
const legacyFallback = preferEsm
87-
? [`${packageName}/dist/index.esm.js`, `${packageName}/dist/index.cjs.cjs`]
88-
: [`${packageName}/dist/index.cjs.cjs`, `${packageName}/dist/index.esm.js`];
89-
90-
// Keep legacy dist entry names as fallbacks for mixed plugin/runtime versions.
91-
// Resolve package root last to avoid accidentally selecting a CJS export when
92-
// an explicit ESM fallback file exists in legacy runtime layouts.
93-
return [...preferred, ...legacyFallback, packageName];
94-
}
95-
96-
function resolveRuntimePaths(preferEsm: boolean, implementation?: string) {
97-
const runtimeToolsCandidates = getModuleCandidates(
98-
'@module-federation/runtime-tools',
99-
preferEsm,
100-
);
101-
const bundlerRuntimeCandidates = getModuleCandidates(
102-
'@module-federation/webpack-bundler-runtime',
103-
preferEsm,
104-
);
105-
const runtimeCandidates = getModuleCandidates(
106-
'@module-federation/runtime',
107-
preferEsm,
108-
);
109-
110-
const runtimeToolsPath = resolveModule(runtimeToolsCandidates);
43+
const runtimeToolsPath = require.resolve(runtimeToolsSpec);
11144
const modulePaths = implementation ? [implementation] : [runtimeToolsPath];
11245

11346
return {
11447
runtimeToolsPath,
115-
bundlerRuntimePath: resolveModule(bundlerRuntimeCandidates, {
48+
bundlerRuntimePath: require.resolve(bundlerRuntimeSpec, {
11649
paths: modulePaths,
11750
}),
118-
runtimePath: resolveModule(runtimeCandidates, {
51+
runtimePath: require.resolve(runtimeSpec, {
11952
paths: modulePaths,
12053
}),
12154
};
@@ -125,7 +58,7 @@ const {
12558
runtimeToolsPath: RuntimeToolsPath,
12659
bundlerRuntimePath: BundlerRuntimePath,
12760
runtimePath: RuntimePath,
128-
} = resolveRuntimePaths(true);
61+
} = resolveRuntimePaths();
12962
const federationGlobal = getFederationGlobalScope(RuntimeGlobals);
13063

13164
const onceForCompiler = new WeakSet<Compiler>();
@@ -426,10 +359,7 @@ class FederationRuntimePlugin {
426359
const { implementation } = this.options || {};
427360
const alias: any = compiler.options.resolve.alias || {};
428361

429-
const resolvedPaths = resolveRuntimePaths(
430-
isEsmOutputBuild(compiler),
431-
implementation,
432-
);
362+
const resolvedPaths = resolveRuntimePaths(implementation);
433363

434364
this.runtimeToolsPath = resolvedPaths.runtimeToolsPath;
435365
this.bundlerRuntimePath = resolvedPaths.bundlerRuntimePath;
@@ -512,10 +442,7 @@ class FederationRuntimePlugin {
512442
compiler.options.output.uniqueName || `container_${Date.now()}`;
513443
}
514444

515-
const resolvedPaths = resolveRuntimePaths(
516-
isEsmOutputBuild(compiler),
517-
this.options?.implementation,
518-
);
445+
const resolvedPaths = resolveRuntimePaths(this.options?.implementation);
519446
this.bundlerRuntimePath = resolvedPaths.bundlerRuntimePath;
520447
this.runtimePath = resolvedPaths.runtimePath;
521448
this.runtimeToolsPath = resolvedPaths.runtimeToolsPath;

packages/enhanced/test/ConfigTestCases.rstest.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,24 @@ export const describeCases = (config: any) => {
10901090
}
10911091
if (esmMode === 'unlinked') return esm;
10921092
return (async () => {
1093+
if (esm.status === 'evaluated') {
1094+
const ns = (esm as any).namespace;
1095+
return ns.default &&
1096+
ns.default instanceof Promise
1097+
? ns.default
1098+
: ns;
1099+
}
1100+
if (
1101+
esm.status !== 'unlinked' &&
1102+
esm.status !== 'linking'
1103+
) {
1104+
await esm.evaluate();
1105+
const ns = (esm as any).namespace;
1106+
return ns.default &&
1107+
ns.default instanceof Promise
1108+
? ns.default
1109+
: ns;
1110+
}
10931111
await esm.link(
10941112
async (
10951113
specifier: string,
@@ -1116,8 +1134,9 @@ export const describeCases = (config: any) => {
11161134
);
11171135
},
11181136
);
1119-
if ((esm as any).instantiate)
1120-
(esm as any).instantiate();
1137+
// Do not call instantiate(): Node's link() already performs
1138+
// instantiation. Calling instantiate() on a linked module throws
1139+
// "Module status must be unlinked" in Node 20+.
11211140
await esm.evaluate();
11221141
if (esmMode === 'evaluated') return esm as any;
11231142
const ns = (esm as any).namespace;

packages/enhanced/test/helpers/asModule.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ module.exports = async (something, context, unlinked) => {
2424
});
2525
if (unlinked) return m;
2626
await m.link(() => {});
27-
if (m.instantiate) m.instantiate();
27+
// Do not call instantiate(): Node's link() already performs instantiation.
28+
// Calling instantiate() on a linked module throws in Node 20+.
2829
await m.evaluate();
2930
return m;
3031
};

packages/enhanced/test/unit/container/FederationRuntimePlugin.test.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,18 @@ describe('FederationRuntimePlugin runtimePluginCalls', () => {
206206

207207
describe('runtime module resolution compatibility', () => {
208208
const normalizePath = (filePath: string) => filePath.replace(/\\/g, '/');
209+
const originalIsEsmBuild = process.env.IS_ESM_BUILD;
210+
211+
afterEach(() => {
212+
if (originalIsEsmBuild === undefined) {
213+
delete process.env.IS_ESM_BUILD;
214+
} else {
215+
process.env.IS_ESM_BUILD = originalIsEsmBuild;
216+
}
217+
});
209218

210-
it('prefers cjs runtime entry for non-module compiler output', () => {
219+
it('prefers cjs runtime entry when IS_ESM_BUILD is false', () => {
220+
process.env.IS_ESM_BUILD = 'false';
211221
const plugin = new FederationRuntimePlugin({
212222
implementation: '/legacy/runtime-tools',
213223
} as any);
@@ -220,14 +230,15 @@ describe('FederationRuntimePlugin runtimePluginCalls', () => {
220230
);
221231
});
222232

223-
it('prefers esm runtime entry for module compiler output', () => {
233+
it('prefers esm runtime entry when IS_ESM_BUILD is true', () => {
234+
process.env.IS_ESM_BUILD = 'true';
224235
const plugin = new FederationRuntimePlugin({
225236
implementation: '/legacy/runtime-tools',
226237
} as any);
227238
const runtimePath = plugin.getRuntimeAlias({
228239
options: {
229240
resolve: { alias: {} },
230-
output: { module: true },
241+
output: {},
231242
},
232243
} as unknown as Compiler);
233244

@@ -236,7 +247,8 @@ describe('FederationRuntimePlugin runtimePluginCalls', () => {
236247
);
237248
});
238249

239-
it('resolves runtime-tools alias for non-module builds even when runtime alias is preset', () => {
250+
it('resolves runtime-tools alias for CJS mode when runtime alias is preset', () => {
251+
process.env.IS_ESM_BUILD = 'false';
240252
const plugin = new FederationRuntimePlugin({} as any);
241253
const compiler = {
242254
options: {
@@ -258,14 +270,15 @@ describe('FederationRuntimePlugin runtimePluginCalls', () => {
258270
).toMatch(/\/runtime-tools\/dist\/index\.cjs(?:\.cjs)?$/);
259271
});
260272

261-
it('resolves runtime-tools alias for module builds when runtime alias is preset', () => {
273+
it('resolves runtime-tools alias for ESM mode when runtime alias is preset', () => {
274+
process.env.IS_ESM_BUILD = 'true';
262275
const plugin = new FederationRuntimePlugin({} as any);
263276
const compiler = {
264277
options: {
265278
resolve: {
266279
alias: { '@module-federation/runtime$': '/custom/runtime' },
267280
},
268-
output: { module: true },
281+
output: {},
269282
},
270283
} as unknown as Compiler;
271284

packages/modernjs-v3/rslib.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export default defineConfig({
2727
...sharedLibOptions,
2828
format: 'cjs',
2929
syntax: 'es2019',
30+
define: {
31+
'process.env.IS_ESM_BUILD': JSON.stringify('false'),
32+
},
3033
dts: false,
3134
output: {
3235
distPath: {
@@ -38,6 +41,9 @@ export default defineConfig({
3841
...sharedLibOptions,
3942
format: 'esm',
4043
syntax: 'es5',
44+
define: {
45+
'process.env.IS_ESM_BUILD': JSON.stringify('true'),
46+
},
4147
dts: false,
4248
output: {
4349
distPath: {
@@ -49,6 +55,9 @@ export default defineConfig({
4955
...sharedLibOptions,
5056
format: 'esm',
5157
syntax: 'es2019',
58+
define: {
59+
'process.env.IS_ESM_BUILD': JSON.stringify('true'),
60+
},
5261
dts: {
5362
distPath: './dist/types',
5463
},

0 commit comments

Comments
 (0)