@module-federation/rsbuild-plugin 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,6 +34,53 @@ export default defineConfig({
34
34
  });
35
35
  ```
36
36
 
37
+ ### Rsbuild App SSR (Node target with custom environment)
38
+
39
+ Use `target: 'node'` with an explicit `environment` to apply federation to a
40
+ specific Rsbuild app environment (for example `ssr`).
41
+
42
+ ```ts
43
+ import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
44
+ import { defineConfig } from '@rsbuild/core';
45
+
46
+ export default defineConfig({
47
+ environments: {
48
+ client: {},
49
+ ssr: {},
50
+ },
51
+ plugins: [
52
+ pluginModuleFederation(
53
+ {
54
+ name: 'host',
55
+ remotes: {
56
+ remote: 'remote@http://localhost:3001/mf-manifest.json',
57
+ },
58
+ },
59
+ {
60
+ target: 'node',
61
+ environment: 'ssr',
62
+ },
63
+ ),
64
+ ],
65
+ });
66
+ ```
67
+
68
+ `target: 'dual'` support remains scoped to Rslib/Rspress workflows.
69
+
70
+ ### Default environment detection
71
+
72
+ If `environment` is omitted, the plugin will choose a default per tool:
73
+
74
+ - **Rslib**: `mf`
75
+ - **Rsbuild app**:
76
+ - `target: 'web'` → `web`
77
+ - `target: 'node'` → `node`
78
+ - **Rspress**:
79
+ - `target: 'web'` → `web`
80
+ - `target: 'node'` → `node`
81
+
82
+ You can still override with `environment` when your project uses custom names.
83
+
37
84
  ### Rslib Module
38
85
 
39
86
  ```js
package/dist/cli/index.js CHANGED
@@ -97,6 +97,23 @@ const LIB_FORMAT = [
97
97
  'modern-module'
98
98
  ];
99
99
  const DEFAULT_MF_ENVIRONMENT_NAME = 'mf';
100
+ const DEFAULT_WEB_ENVIRONMENT_NAME = 'web';
101
+ const DEFAULT_NODE_ENVIRONMENT_NAME = 'node';
102
+ const resolveDefaultEnvironmentName = ({ callerName, target })=>{
103
+ if (callerName === external_constant_js_namespaceObject.CALL_NAME_MAP.RSLIB) {
104
+ return DEFAULT_MF_ENVIRONMENT_NAME;
105
+ }
106
+ if (callerName === external_constant_js_namespaceObject.CALL_NAME_MAP.RSPRESS) {
107
+ if (target === 'node') {
108
+ return DEFAULT_NODE_ENVIRONMENT_NAME;
109
+ }
110
+ return DEFAULT_WEB_ENVIRONMENT_NAME;
111
+ }
112
+ if (target === 'node') {
113
+ return DEFAULT_NODE_ENVIRONMENT_NAME;
114
+ }
115
+ return DEFAULT_WEB_ENVIRONMENT_NAME;
116
+ };
100
117
  function isStoryBook(rsbuildConfig) {
101
118
  var _rsbuildConfig_plugins;
102
119
  if ((_rsbuildConfig_plugins = rsbuildConfig.plugins) === null || _rsbuildConfig_plugins === void 0 ? void 0 : _rsbuildConfig_plugins.find((p)=>p && 'name' in p && p.name === 'module-federation-storybook-plugin')) {
@@ -123,7 +140,10 @@ const isRspressSSGConfig = (bundlerConfigName)=>{
123
140
  const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
124
141
  name: RSBUILD_PLUGIN_MODULE_FEDERATION_NAME,
125
142
  setup: (api)=>{
126
- const { target = 'web', ssr = undefined, ssrDir = external_utils_js_namespaceObject.SSR_DIR, environment = DEFAULT_MF_ENVIRONMENT_NAME } = rsbuildOptions || {};
143
+ if (!(moduleFederationOptions === null || moduleFederationOptions === void 0 ? void 0 : moduleFederationOptions.name)) {
144
+ throw new Error('The module federation option "name" is required in @module-federation/rsbuild-plugin.');
145
+ }
146
+ const { target = 'web', ssr = undefined, ssrDir = external_utils_js_namespaceObject.SSR_DIR, environment: configuredEnvironment } = rsbuildOptions || {};
127
147
  if (ssr) {
128
148
  throw new Error("The `ssr` option is deprecated. If you want to enable SSR, please use `target: 'dual'` instead.");
129
149
  }
@@ -135,6 +155,10 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
135
155
  const isRslib = callerName === external_constant_js_namespaceObject.CALL_NAME_MAP.RSLIB;
136
156
  const isRspress = callerName === external_constant_js_namespaceObject.CALL_NAME_MAP.RSPRESS;
137
157
  const isSSR = target === 'dual';
158
+ const environment = configuredEnvironment ?? resolveDefaultEnvironmentName({
159
+ callerName,
160
+ target
161
+ });
138
162
  if (isSSR && !isStoryBook(originalRsbuildConfig)) {
139
163
  var _rsbuildConfig_environments;
140
164
  if (!isRslib && !isRspress) {
@@ -198,7 +222,11 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
198
222
  if ((_config_environments = config.environments) === null || _config_environments === void 0 ? void 0 : _config_environments[external_utils_js_namespaceObject.SSR_ENV_NAME]) {
199
223
  throw new Error(`'${external_utils_js_namespaceObject.SSR_ENV_NAME}' environment is already defined.Please use another name.`);
200
224
  }
201
- config.environments[external_utils_js_namespaceObject.SSR_ENV_NAME] = (0,external_utils_js_namespaceObject.createSSRREnvConfig)((_config_environments1 = config.environments) === null || _config_environments1 === void 0 ? void 0 : _config_environments1[environment], moduleFederationOptions, ssrDir, config, callerName);
225
+ const currentEnvironment = (_config_environments1 = config.environments) === null || _config_environments1 === void 0 ? void 0 : _config_environments1[environment];
226
+ if (!currentEnvironment) {
227
+ throw new Error(`Can not find environment '${environment}' when enabling SSR.`);
228
+ }
229
+ config.environments[external_utils_js_namespaceObject.SSR_ENV_NAME] = (0,external_utils_js_namespaceObject.createSSRREnvConfig)(currentEnvironment, moduleFederationOptions, ssrDir, config, callerName);
202
230
  const ssgMDEnv = config.environments[external_constant_js_namespaceObject.RSPRESS_SSG_MD_ENV_NAME];
203
231
  if (isRspress && ssgMDEnv) {
204
232
  (0,ssr_js_namespaceObject.patchToolsTspack)(ssgMDEnv, (config, { environment })=>{
@@ -206,8 +234,14 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
206
234
  });
207
235
  }
208
236
  } else if (target === 'node') {
209
- const mfEnv = config.environments[ssr_js_namespaceObject.ENV_NAME];
210
- (0,ssr_js_namespaceObject.patchToolsTspack)(mfEnv, (config, { environment })=>{
237
+ var _config_environments2;
238
+ const nodeTargetEnv = (_config_environments2 = config.environments) === null || _config_environments2 === void 0 ? void 0 : _config_environments2[environment];
239
+ if (!nodeTargetEnv) {
240
+ const availableEnvironments = Object.keys(config.environments || {});
241
+ const availableEnvironmentsLabel = availableEnvironments.length > 0 ? availableEnvironments.join(', ') : '(none)';
242
+ throw new Error(`Can not find environment '${environment}' when using target: 'node'. Available environments: ${availableEnvironmentsLabel}.`);
243
+ }
244
+ (0,ssr_js_namespaceObject.patchToolsTspack)(nodeTargetEnv, (config, { environment })=>{
211
245
  config.target = 'async-node';
212
246
  });
213
247
  }
@@ -284,7 +318,22 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
284
318
  throw new Error('Can not get bundlerConfigs!');
285
319
  }
286
320
  bundlerConfigs.forEach((bundlerConfig)=>{
287
- if (!isMFFormat(bundlerConfig) && !isRspress) {
321
+ const bundlerConfigName = bundlerConfig.name || '';
322
+ const isConfiguredEnvironmentConfig = bundlerConfigName === environment;
323
+ const isNodeTargetEnvironmentConfig = target === 'node' && bundlerConfigName === environment;
324
+ const isRspressSSGEnvironmentConfig = isRspressSSGConfig(bundlerConfig.name);
325
+ const isActiveRspressSSGEnvironmentConfig = isRspress && isRspressSSGEnvironmentConfig;
326
+ const shouldUseSSRPluginConfig = isSSRConfig(bundlerConfig.name) || isNodeTargetEnvironmentConfig;
327
+ if (target === 'node' && !isNodeTargetEnvironmentConfig && !isActiveRspressSSGEnvironmentConfig) {
328
+ return;
329
+ }
330
+ // For non-node targets, scope each plugin instance to its configured
331
+ // environment plus explicit SSR/SSG environments. This prevents a
332
+ // browser-targeted instance from mutating SSR configs.
333
+ if (target !== 'node' && !isConfiguredEnvironmentConfig && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
334
+ return;
335
+ }
336
+ if (!isMFFormat(bundlerConfig) && !isRspress && !isNodeTargetEnvironmentConfig) {
288
337
  return;
289
338
  } else if (isStoryBook(originalRsbuildConfig)) {
290
339
  bundlerConfig.output.uniqueName = `${moduleFederationOptions.name} -storybook - host`;
@@ -292,7 +341,8 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
292
341
  var _bundlerConfig_optimization, _bundlerConfig_optimization1, _bundlerConfig_output, _bundlerConfig_output1, _bundlerConfig_output2;
293
342
  // mf
294
343
  (0,external_utils_js_namespaceObject.autoDeleteSplitChunkCacheGroups)(moduleFederationOptions, bundlerConfig === null || bundlerConfig === void 0 ? void 0 : (_bundlerConfig_optimization = bundlerConfig.optimization) === null || _bundlerConfig_optimization === void 0 ? void 0 : _bundlerConfig_optimization.splitChunks);
295
- (0,external_utils_js_namespaceObject.addDataFetchExposes)(moduleFederationOptions.exposes, isSSRConfig(bundlerConfig.name));
344
+ (0,external_utils_js_namespaceObject.addDataFetchExposes)(moduleFederationOptions.exposes, shouldUseSSRPluginConfig);
345
+ const ssrModuleFederationOptions = shouldUseSSRPluginConfig ? (0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions) : undefined;
296
346
  (_bundlerConfig_optimization1 = bundlerConfig.optimization) === null || _bundlerConfig_optimization1 === void 0 ? true : delete _bundlerConfig_optimization1.runtimeChunk;
297
347
  const externals = bundlerConfig.externals;
298
348
  if (Array.isArray(externals)) {
@@ -334,13 +384,12 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
334
384
  }
335
385
  }
336
386
  }
337
- if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !isSSRConfig(bundlerConfig.name) && !isRspressSSGConfig(bundlerConfig.name) && target !== 'node') {
387
+ if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig && target !== 'node') {
338
388
  bundlerConfig.output.chunkLoading = 'jsonp';
339
389
  bundlerConfig.output.chunkLoadingGlobal = `chunk_${moduleFederationOptions.name} `;
340
390
  }
341
- if (target === 'node' && isMFFormat(bundlerConfig)) {
342
- (0,ssr_js_namespaceObject.patchNodeConfig)(bundlerConfig, moduleFederationOptions);
343
- (0,ssr_js_namespaceObject.patchNodeMFConfig)(moduleFederationOptions);
391
+ if (isNodeTargetEnvironmentConfig) {
392
+ (0,ssr_js_namespaceObject.patchNodeConfig)(bundlerConfig, ssrModuleFederationOptions ?? moduleFederationOptions);
344
393
  }
345
394
  // `uniqueName` is required for react refresh to work
346
395
  if (!((_bundlerConfig_output1 = bundlerConfig.output) === null || _bundlerConfig_output1 === void 0 ? void 0 : _bundlerConfig_output1.uniqueName)) {
@@ -348,17 +397,18 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
348
397
  }
349
398
  // Set default publicPath to 'auto' if not explicitly configured
350
399
  // This allows remote chunks to load from the same origin as the remote application's manifest
351
- if (((_bundlerConfig_output2 = bundlerConfig.output) === null || _bundlerConfig_output2 === void 0 ? void 0 : _bundlerConfig_output2.publicPath) === undefined && !isSSRConfig(bundlerConfig.name) && !isRspressSSGConfig(bundlerConfig.name)) {
400
+ if (((_bundlerConfig_output2 = bundlerConfig.output) === null || _bundlerConfig_output2 === void 0 ? void 0 : _bundlerConfig_output2.publicPath) === undefined && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
352
401
  bundlerConfig.output.publicPath = 'auto';
353
402
  }
354
403
  if (!bundlerConfig.plugins.find((p)=>p && p.name === rspack_namespaceObject.PLUGIN_NAME)) {
355
404
  var _bundlerConfig_output3;
356
- if (isSSRConfig(bundlerConfig.name)) {
357
- generateMergedStatsAndManifestOptions.options.nodePlugin = new rspack_namespaceObject.ModuleFederationPlugin((0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions));
405
+ if (shouldUseSSRPluginConfig) {
406
+ const ssrMFConfig = ssrModuleFederationOptions ?? (0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions);
407
+ generateMergedStatsAndManifestOptions.options.nodePlugin = new rspack_namespaceObject.ModuleFederationPlugin(ssrMFConfig);
358
408
  generateMergedStatsAndManifestOptions.options.nodeEnvironmentName = bundlerConfig.name || external_utils_js_namespaceObject.SSR_ENV_NAME;
359
409
  bundlerConfig.plugins.push(generateMergedStatsAndManifestOptions.options.nodePlugin);
360
410
  return;
361
- } else if (isRspressSSGConfig(bundlerConfig.name)) {
411
+ } else if (isActiveRspressSSGEnvironmentConfig) {
362
412
  const mfConfig = {
363
413
  ...(0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions),
364
414
  // expose in mf-ssg env
@@ -5,7 +5,7 @@ import package_0 from "../../package.json";
5
5
  import logger from "../logger.mjs";
6
6
  import { SSR_DIR, SSR_ENV_NAME, addDataFetchExposes, autoDeleteSplitChunkCacheGroups, createSSRMFConfig, createSSRREnvConfig, isRegExp, patchSSRRspackConfig, setSSREnv, updateStatsAndManifest } from "../utils.mjs";
7
7
  import { CALL_NAME_MAP, RSPRESS_BUNDLER_CONFIG_NAME, RSPRESS_SSG_MD_ENV_NAME, RSPRESS_SSR_DIR } from "../constant.mjs";
8
- import { ENV_NAME, patchNodeConfig, patchNodeMFConfig, patchToolsTspack } from "../utils/ssr.mjs";
8
+ import { patchNodeConfig, patchToolsTspack } from "../utils/ssr.mjs";
9
9
 
10
10
  ;// CONCATENATED MODULE: external "@module-federation/enhanced"
11
11
 
@@ -40,6 +40,23 @@ const LIB_FORMAT = [
40
40
  'modern-module'
41
41
  ];
42
42
  const DEFAULT_MF_ENVIRONMENT_NAME = 'mf';
43
+ const DEFAULT_WEB_ENVIRONMENT_NAME = 'web';
44
+ const DEFAULT_NODE_ENVIRONMENT_NAME = 'node';
45
+ const resolveDefaultEnvironmentName = ({ callerName, target })=>{
46
+ if (callerName === CALL_NAME_MAP.RSLIB) {
47
+ return DEFAULT_MF_ENVIRONMENT_NAME;
48
+ }
49
+ if (callerName === CALL_NAME_MAP.RSPRESS) {
50
+ if (target === 'node') {
51
+ return DEFAULT_NODE_ENVIRONMENT_NAME;
52
+ }
53
+ return DEFAULT_WEB_ENVIRONMENT_NAME;
54
+ }
55
+ if (target === 'node') {
56
+ return DEFAULT_NODE_ENVIRONMENT_NAME;
57
+ }
58
+ return DEFAULT_WEB_ENVIRONMENT_NAME;
59
+ };
43
60
  function isStoryBook(rsbuildConfig) {
44
61
  var _rsbuildConfig_plugins;
45
62
  if ((_rsbuildConfig_plugins = rsbuildConfig.plugins) === null || _rsbuildConfig_plugins === void 0 ? void 0 : _rsbuildConfig_plugins.find((p)=>p && 'name' in p && p.name === 'module-federation-storybook-plugin')) {
@@ -66,7 +83,10 @@ const isRspressSSGConfig = (bundlerConfigName)=>{
66
83
  const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
67
84
  name: RSBUILD_PLUGIN_MODULE_FEDERATION_NAME,
68
85
  setup: (api)=>{
69
- const { target = 'web', ssr = undefined, ssrDir = SSR_DIR, environment = DEFAULT_MF_ENVIRONMENT_NAME } = rsbuildOptions || {};
86
+ if (!(moduleFederationOptions === null || moduleFederationOptions === void 0 ? void 0 : moduleFederationOptions.name)) {
87
+ throw new Error('The module federation option "name" is required in @module-federation/rsbuild-plugin.');
88
+ }
89
+ const { target = 'web', ssr = undefined, ssrDir = SSR_DIR, environment: configuredEnvironment } = rsbuildOptions || {};
70
90
  if (ssr) {
71
91
  throw new Error("The `ssr` option is deprecated. If you want to enable SSR, please use `target: 'dual'` instead.");
72
92
  }
@@ -78,6 +98,10 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
78
98
  const isRslib = callerName === CALL_NAME_MAP.RSLIB;
79
99
  const isRspress = callerName === CALL_NAME_MAP.RSPRESS;
80
100
  const isSSR = target === 'dual';
101
+ const environment = configuredEnvironment ?? resolveDefaultEnvironmentName({
102
+ callerName,
103
+ target
104
+ });
81
105
  if (isSSR && !isStoryBook(originalRsbuildConfig)) {
82
106
  var _rsbuildConfig_environments;
83
107
  if (!isRslib && !isRspress) {
@@ -141,7 +165,11 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
141
165
  if ((_config_environments = config.environments) === null || _config_environments === void 0 ? void 0 : _config_environments[SSR_ENV_NAME]) {
142
166
  throw new Error(`'${SSR_ENV_NAME}' environment is already defined.Please use another name.`);
143
167
  }
144
- config.environments[SSR_ENV_NAME] = createSSRREnvConfig((_config_environments1 = config.environments) === null || _config_environments1 === void 0 ? void 0 : _config_environments1[environment], moduleFederationOptions, ssrDir, config, callerName);
168
+ const currentEnvironment = (_config_environments1 = config.environments) === null || _config_environments1 === void 0 ? void 0 : _config_environments1[environment];
169
+ if (!currentEnvironment) {
170
+ throw new Error(`Can not find environment '${environment}' when enabling SSR.`);
171
+ }
172
+ config.environments[SSR_ENV_NAME] = createSSRREnvConfig(currentEnvironment, moduleFederationOptions, ssrDir, config, callerName);
145
173
  const ssgMDEnv = config.environments[RSPRESS_SSG_MD_ENV_NAME];
146
174
  if (isRspress && ssgMDEnv) {
147
175
  patchToolsTspack(ssgMDEnv, (config, { environment })=>{
@@ -149,8 +177,14 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
149
177
  });
150
178
  }
151
179
  } else if (target === 'node') {
152
- const mfEnv = config.environments[ENV_NAME];
153
- patchToolsTspack(mfEnv, (config, { environment })=>{
180
+ var _config_environments2;
181
+ const nodeTargetEnv = (_config_environments2 = config.environments) === null || _config_environments2 === void 0 ? void 0 : _config_environments2[environment];
182
+ if (!nodeTargetEnv) {
183
+ const availableEnvironments = Object.keys(config.environments || {});
184
+ const availableEnvironmentsLabel = availableEnvironments.length > 0 ? availableEnvironments.join(', ') : '(none)';
185
+ throw new Error(`Can not find environment '${environment}' when using target: 'node'. Available environments: ${availableEnvironmentsLabel}.`);
186
+ }
187
+ patchToolsTspack(nodeTargetEnv, (config, { environment })=>{
154
188
  config.target = 'async-node';
155
189
  });
156
190
  }
@@ -227,7 +261,22 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
227
261
  throw new Error('Can not get bundlerConfigs!');
228
262
  }
229
263
  bundlerConfigs.forEach((bundlerConfig)=>{
230
- if (!isMFFormat(bundlerConfig) && !isRspress) {
264
+ const bundlerConfigName = bundlerConfig.name || '';
265
+ const isConfiguredEnvironmentConfig = bundlerConfigName === environment;
266
+ const isNodeTargetEnvironmentConfig = target === 'node' && bundlerConfigName === environment;
267
+ const isRspressSSGEnvironmentConfig = isRspressSSGConfig(bundlerConfig.name);
268
+ const isActiveRspressSSGEnvironmentConfig = isRspress && isRspressSSGEnvironmentConfig;
269
+ const shouldUseSSRPluginConfig = isSSRConfig(bundlerConfig.name) || isNodeTargetEnvironmentConfig;
270
+ if (target === 'node' && !isNodeTargetEnvironmentConfig && !isActiveRspressSSGEnvironmentConfig) {
271
+ return;
272
+ }
273
+ // For non-node targets, scope each plugin instance to its configured
274
+ // environment plus explicit SSR/SSG environments. This prevents a
275
+ // browser-targeted instance from mutating SSR configs.
276
+ if (target !== 'node' && !isConfiguredEnvironmentConfig && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
277
+ return;
278
+ }
279
+ if (!isMFFormat(bundlerConfig) && !isRspress && !isNodeTargetEnvironmentConfig) {
231
280
  return;
232
281
  } else if (isStoryBook(originalRsbuildConfig)) {
233
282
  bundlerConfig.output.uniqueName = `${moduleFederationOptions.name} -storybook - host`;
@@ -235,7 +284,8 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
235
284
  var _bundlerConfig_optimization, _bundlerConfig_optimization1, _bundlerConfig_output, _bundlerConfig_output1, _bundlerConfig_output2;
236
285
  // mf
237
286
  autoDeleteSplitChunkCacheGroups(moduleFederationOptions, bundlerConfig === null || bundlerConfig === void 0 ? void 0 : (_bundlerConfig_optimization = bundlerConfig.optimization) === null || _bundlerConfig_optimization === void 0 ? void 0 : _bundlerConfig_optimization.splitChunks);
238
- addDataFetchExposes(moduleFederationOptions.exposes, isSSRConfig(bundlerConfig.name));
287
+ addDataFetchExposes(moduleFederationOptions.exposes, shouldUseSSRPluginConfig);
288
+ const ssrModuleFederationOptions = shouldUseSSRPluginConfig ? createSSRMFConfig(moduleFederationOptions) : undefined;
239
289
  (_bundlerConfig_optimization1 = bundlerConfig.optimization) === null || _bundlerConfig_optimization1 === void 0 ? true : delete _bundlerConfig_optimization1.runtimeChunk;
240
290
  const externals = bundlerConfig.externals;
241
291
  if (Array.isArray(externals)) {
@@ -277,13 +327,12 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
277
327
  }
278
328
  }
279
329
  }
280
- if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !isSSRConfig(bundlerConfig.name) && !isRspressSSGConfig(bundlerConfig.name) && target !== 'node') {
330
+ if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig && target !== 'node') {
281
331
  bundlerConfig.output.chunkLoading = 'jsonp';
282
332
  bundlerConfig.output.chunkLoadingGlobal = `chunk_${moduleFederationOptions.name} `;
283
333
  }
284
- if (target === 'node' && isMFFormat(bundlerConfig)) {
285
- patchNodeConfig(bundlerConfig, moduleFederationOptions);
286
- patchNodeMFConfig(moduleFederationOptions);
334
+ if (isNodeTargetEnvironmentConfig) {
335
+ patchNodeConfig(bundlerConfig, ssrModuleFederationOptions ?? moduleFederationOptions);
287
336
  }
288
337
  // `uniqueName` is required for react refresh to work
289
338
  if (!((_bundlerConfig_output1 = bundlerConfig.output) === null || _bundlerConfig_output1 === void 0 ? void 0 : _bundlerConfig_output1.uniqueName)) {
@@ -291,17 +340,18 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
291
340
  }
292
341
  // Set default publicPath to 'auto' if not explicitly configured
293
342
  // This allows remote chunks to load from the same origin as the remote application's manifest
294
- if (((_bundlerConfig_output2 = bundlerConfig.output) === null || _bundlerConfig_output2 === void 0 ? void 0 : _bundlerConfig_output2.publicPath) === undefined && !isSSRConfig(bundlerConfig.name) && !isRspressSSGConfig(bundlerConfig.name)) {
343
+ if (((_bundlerConfig_output2 = bundlerConfig.output) === null || _bundlerConfig_output2 === void 0 ? void 0 : _bundlerConfig_output2.publicPath) === undefined && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
295
344
  bundlerConfig.output.publicPath = 'auto';
296
345
  }
297
346
  if (!bundlerConfig.plugins.find((p)=>p && p.name === PLUGIN_NAME)) {
298
347
  var _bundlerConfig_output3;
299
- if (isSSRConfig(bundlerConfig.name)) {
300
- generateMergedStatsAndManifestOptions.options.nodePlugin = new ModuleFederationPlugin(createSSRMFConfig(moduleFederationOptions));
348
+ if (shouldUseSSRPluginConfig) {
349
+ const ssrMFConfig = ssrModuleFederationOptions ?? createSSRMFConfig(moduleFederationOptions);
350
+ generateMergedStatsAndManifestOptions.options.nodePlugin = new ModuleFederationPlugin(ssrMFConfig);
301
351
  generateMergedStatsAndManifestOptions.options.nodeEnvironmentName = bundlerConfig.name || SSR_ENV_NAME;
302
352
  bundlerConfig.plugins.push(generateMergedStatsAndManifestOptions.options.nodePlugin);
303
353
  return;
304
- } else if (isRspressSSGConfig(bundlerConfig.name)) {
354
+ } else if (isActiveRspressSSGEnvironmentConfig) {
305
355
  const mfConfig = {
306
356
  ...createSSRMFConfig(moduleFederationOptions),
307
357
  // expose in mf-ssg env
@@ -28,7 +28,6 @@ export declare function createSSRMFConfig(mfConfig: moduleFederationPlugin.Modul
28
28
  manifest?: boolean | moduleFederationPlugin.PluginManifestOptions;
29
29
  dev?: boolean | moduleFederationPlugin.PluginDevOptions;
30
30
  dts?: boolean | moduleFederationPlugin.PluginDtsOptions;
31
- dataPrefetch?: moduleFederationPlugin.DataPrefetch;
32
31
  virtualRuntimeEntry?: boolean;
33
32
  experiments?: {
34
33
  externalRuntime?: boolean;
package/dist/utils/ssr.js CHANGED
@@ -81,6 +81,20 @@ const external_constant_js_namespaceObject = require("../constant.js");
81
81
 
82
82
  const ssr_require = (0,external_node_module_namespaceObject.createRequire)(__rslib_import_meta_url__);
83
83
  const resolve = ssr_require.resolve;
84
+ function resolveOrRequest(request) {
85
+ try {
86
+ return resolve(request);
87
+ } catch {
88
+ return request;
89
+ }
90
+ }
91
+ function safeRequire(request) {
92
+ try {
93
+ return ssr_require(request);
94
+ } catch {
95
+ return undefined;
96
+ }
97
+ }
84
98
  const SSR_DIR = 'ssr';
85
99
  const SSR_ENV_NAME = 'mf-ssr';
86
100
  const ENV_NAME = 'mf';
@@ -93,16 +107,27 @@ const isDev = ()=>{
93
107
  function patchNodeConfig(config, mfConfig) {
94
108
  var _config_output;
95
109
  config.output ||= {};
110
+ if (config.output.publicPath === 'auto') {
111
+ config.output.publicPath = '';
112
+ }
96
113
  config.target = 'async-node';
97
- // @module-federation/node/universe-entry-chunk-tracker-plugin only export cjs
98
- const UniverseEntryChunkTrackerPlugin = ssr_require('@module-federation/node/universe-entry-chunk-tracker-plugin').default;
114
+ // Force node federation output to CJS + async chunk loading.
115
+ // This prevents browser jsonp runtime handlers from leaking into SSR remotes.
116
+ config.output.module = false;
117
+ config.output.chunkFormat = 'commonjs';
118
+ config.output.chunkLoading = 'async-node';
119
+ delete config.output.chunkLoadingGlobal;
120
+ const UniverseEntryChunkTrackerPluginModule = safeRequire('@module-federation/node/universe-entry-chunk-tracker-plugin');
121
+ const UniverseEntryChunkTrackerPlugin = UniverseEntryChunkTrackerPluginModule === null || UniverseEntryChunkTrackerPluginModule === void 0 ? void 0 : UniverseEntryChunkTrackerPluginModule.default;
99
122
  config.plugins ||= [];
100
- isDev() && config.plugins.push(new UniverseEntryChunkTrackerPlugin());
123
+ if (isDev() && UniverseEntryChunkTrackerPlugin) {
124
+ config.plugins.push(new UniverseEntryChunkTrackerPlugin());
125
+ }
101
126
  const uniqueName = mfConfig.name || ((_config_output = config.output) === null || _config_output === void 0 ? void 0 : _config_output.uniqueName);
102
127
  const chunkFileName = config.output.chunkFilename;
103
128
  if (typeof chunkFileName === 'string' && uniqueName && !chunkFileName.includes(uniqueName)) {
104
- const suffix = `${(0,sdk_namespaceObject.encodeName)(uniqueName)}-[contenthash].js`;
105
- config.output.chunkFilename = chunkFileName.replace('.js', suffix);
129
+ const encodedName = (0,sdk_namespaceObject.encodeName)(uniqueName);
130
+ config.output.chunkFilename = chunkFileName.endsWith('.js') ? chunkFileName.replace(/\.js$/, `${encodedName}.js`) : `${chunkFileName}${encodedName}`;
106
131
  }
107
132
  }
108
133
  function patchSSRRspackConfig(config, mfConfig, ssrDir, callerName, resetEntry = true, modifyPublicPath = true) {
@@ -113,11 +138,10 @@ function patchSSRRspackConfig(config, mfConfig, ssrDir, callerName, resetEntry =
113
138
  throw new Error('publicPath must be string!');
114
139
  }
115
140
  const publicPath = config.output.publicPath;
116
- if (publicPath === 'auto') {
117
- throw new Error('publicPath can not be "auto"!');
141
+ if (publicPath !== 'auto') {
142
+ const publicPathWithSSRDir = `${publicPath}${ssrDir}/`;
143
+ config.output.publicPath = publicPathWithSSRDir;
118
144
  }
119
- const publicPathWithSSRDir = `${publicPath}${ssrDir}/`;
120
- config.output.publicPath = publicPathWithSSRDir;
121
145
  }
122
146
  if (callerName === external_constant_js_namespaceObject.CALL_NAME_MAP.RSPRESS && resetEntry) {
123
147
  // set virtue entry, only need mf entry
@@ -196,10 +220,9 @@ function patchNodeMFConfig(mfConfig) {
196
220
  mfConfig.runtimePlugins = [
197
221
  ...mfConfig.runtimePlugins || []
198
222
  ];
199
- mfConfig.runtimePlugins.push(resolve('@module-federation/node/runtimePlugin'));
223
+ mfConfig.runtimePlugins.push(resolveOrRequest('@module-federation/node/runtimePlugin'));
200
224
  if (isDev()) {
201
- mfConfig.runtimePlugins.push(// @ts-ignore
202
- resolve('@module-federation/node/record-dynamic-remote-entry-hash-plugin'));
225
+ mfConfig.runtimePlugins.push(resolveOrRequest('@module-federation/node/record-dynamic-remote-entry-hash-plugin'));
203
226
  }
204
227
  }
205
228
  function createSSRMFConfig(mfConfig) {
@@ -18,6 +18,20 @@ import { CALL_NAME_MAP } from "../constant.mjs";
18
18
 
19
19
  const ssr_require = createRequire(import.meta.url);
20
20
  const resolve = ssr_require.resolve;
21
+ function resolveOrRequest(request) {
22
+ try {
23
+ return resolve(request);
24
+ } catch {
25
+ return request;
26
+ }
27
+ }
28
+ function safeRequire(request) {
29
+ try {
30
+ return ssr_require(request);
31
+ } catch {
32
+ return undefined;
33
+ }
34
+ }
21
35
  const SSR_DIR = 'ssr';
22
36
  const SSR_ENV_NAME = 'mf-ssr';
23
37
  const ENV_NAME = 'mf';
@@ -30,16 +44,27 @@ const isDev = ()=>{
30
44
  function patchNodeConfig(config, mfConfig) {
31
45
  var _config_output;
32
46
  config.output ||= {};
47
+ if (config.output.publicPath === 'auto') {
48
+ config.output.publicPath = '';
49
+ }
33
50
  config.target = 'async-node';
34
- // @module-federation/node/universe-entry-chunk-tracker-plugin only export cjs
35
- const UniverseEntryChunkTrackerPlugin = ssr_require('@module-federation/node/universe-entry-chunk-tracker-plugin').default;
51
+ // Force node federation output to CJS + async chunk loading.
52
+ // This prevents browser jsonp runtime handlers from leaking into SSR remotes.
53
+ config.output.module = false;
54
+ config.output.chunkFormat = 'commonjs';
55
+ config.output.chunkLoading = 'async-node';
56
+ delete config.output.chunkLoadingGlobal;
57
+ const UniverseEntryChunkTrackerPluginModule = safeRequire('@module-federation/node/universe-entry-chunk-tracker-plugin');
58
+ const UniverseEntryChunkTrackerPlugin = UniverseEntryChunkTrackerPluginModule === null || UniverseEntryChunkTrackerPluginModule === void 0 ? void 0 : UniverseEntryChunkTrackerPluginModule.default;
36
59
  config.plugins ||= [];
37
- isDev() && config.plugins.push(new UniverseEntryChunkTrackerPlugin());
60
+ if (isDev() && UniverseEntryChunkTrackerPlugin) {
61
+ config.plugins.push(new UniverseEntryChunkTrackerPlugin());
62
+ }
38
63
  const uniqueName = mfConfig.name || ((_config_output = config.output) === null || _config_output === void 0 ? void 0 : _config_output.uniqueName);
39
64
  const chunkFileName = config.output.chunkFilename;
40
65
  if (typeof chunkFileName === 'string' && uniqueName && !chunkFileName.includes(uniqueName)) {
41
- const suffix = `${encodeName(uniqueName)}-[contenthash].js`;
42
- config.output.chunkFilename = chunkFileName.replace('.js', suffix);
66
+ const encodedName = encodeName(uniqueName);
67
+ config.output.chunkFilename = chunkFileName.endsWith('.js') ? chunkFileName.replace(/\.js$/, `${encodedName}.js`) : `${chunkFileName}${encodedName}`;
43
68
  }
44
69
  }
45
70
  function patchSSRRspackConfig(config, mfConfig, ssrDir, callerName, resetEntry = true, modifyPublicPath = true) {
@@ -50,11 +75,10 @@ function patchSSRRspackConfig(config, mfConfig, ssrDir, callerName, resetEntry =
50
75
  throw new Error('publicPath must be string!');
51
76
  }
52
77
  const publicPath = config.output.publicPath;
53
- if (publicPath === 'auto') {
54
- throw new Error('publicPath can not be "auto"!');
78
+ if (publicPath !== 'auto') {
79
+ const publicPathWithSSRDir = `${publicPath}${ssrDir}/`;
80
+ config.output.publicPath = publicPathWithSSRDir;
55
81
  }
56
- const publicPathWithSSRDir = `${publicPath}${ssrDir}/`;
57
- config.output.publicPath = publicPathWithSSRDir;
58
82
  }
59
83
  if (callerName === CALL_NAME_MAP.RSPRESS && resetEntry) {
60
84
  // set virtue entry, only need mf entry
@@ -133,10 +157,9 @@ function patchNodeMFConfig(mfConfig) {
133
157
  mfConfig.runtimePlugins = [
134
158
  ...mfConfig.runtimePlugins || []
135
159
  ];
136
- mfConfig.runtimePlugins.push(resolve('@module-federation/node/runtimePlugin'));
160
+ mfConfig.runtimePlugins.push(resolveOrRequest('@module-federation/node/runtimePlugin'));
137
161
  if (isDev()) {
138
- mfConfig.runtimePlugins.push(// @ts-ignore
139
- resolve('@module-federation/node/record-dynamic-remote-entry-hash-plugin'));
162
+ mfConfig.runtimePlugins.push(resolveOrRequest('@module-federation/node/record-dynamic-remote-entry-hash-plugin'));
140
163
  }
141
164
  }
142
165
  function createSSRMFConfig(mfConfig) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/rsbuild-plugin",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "Module Federation plugin for Rsbuild",
5
5
  "homepage": "https://module-federation.io",
6
6
  "bugs": {
@@ -50,16 +50,16 @@
50
50
  ],
51
51
  "dependencies": {
52
52
  "fs-extra": "11.3.0",
53
- "@module-federation/sdk": "2.0.1",
54
- "@module-federation/enhanced": "2.0.1",
55
- "@module-federation/node": "2.7.32"
53
+ "@module-federation/enhanced": "2.2.0",
54
+ "@module-federation/node": "2.7.34",
55
+ "@module-federation/sdk": "2.2.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@rslib/core": "^0.12.4",
59
59
  "@rsbuild/core": "2.0.0-beta.2"
60
60
  },
61
61
  "peerDependencies": {
62
- "@rsbuild/core": "2.0.0-beta.2"
62
+ "@rsbuild/core": "^1.3.21 || ^2.0.0-0"
63
63
  },
64
64
  "peerDependenciesMeta": {
65
65
  "@rsbuild/core": {
@@ -72,5 +72,11 @@
72
72
  "publishConfig": {
73
73
  "access": "public",
74
74
  "registry": "https://registry.npmjs.org/"
75
+ },
76
+ "scripts": {
77
+ "build": "rslib build",
78
+ "lint": "ESLINT_USE_FLAT_CONFIG=false pnpm exec eslint --ignore-pattern node_modules \"**/*.ts\" \"package.json\"",
79
+ "test": "pnpm exec vitest run --passWithNoTests --config vite.config.mts",
80
+ "pre-release": "pnpm run test && pnpm run build"
75
81
  }
76
82
  }