@module-federation/rsbuild-plugin 2.0.1 → 2.1.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) {
@@ -206,8 +230,14 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
206
230
  });
207
231
  }
208
232
  } else if (target === 'node') {
209
- const mfEnv = config.environments[ssr_js_namespaceObject.ENV_NAME];
210
- (0,ssr_js_namespaceObject.patchToolsTspack)(mfEnv, (config, { environment })=>{
233
+ var _config_environments2;
234
+ const nodeTargetEnv = (_config_environments2 = config.environments) === null || _config_environments2 === void 0 ? void 0 : _config_environments2[environment];
235
+ if (!nodeTargetEnv) {
236
+ const availableEnvironments = Object.keys(config.environments || {});
237
+ const availableEnvironmentsLabel = availableEnvironments.length > 0 ? availableEnvironments.join(', ') : '(none)';
238
+ throw new Error(`Can not find environment '${environment}' when using target: 'node'. Available environments: ${availableEnvironmentsLabel}.`);
239
+ }
240
+ (0,ssr_js_namespaceObject.patchToolsTspack)(nodeTargetEnv, (config, { environment })=>{
211
241
  config.target = 'async-node';
212
242
  });
213
243
  }
@@ -284,7 +314,22 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
284
314
  throw new Error('Can not get bundlerConfigs!');
285
315
  }
286
316
  bundlerConfigs.forEach((bundlerConfig)=>{
287
- if (!isMFFormat(bundlerConfig) && !isRspress) {
317
+ const bundlerConfigName = bundlerConfig.name || '';
318
+ const isConfiguredEnvironmentConfig = bundlerConfigName === environment;
319
+ const isNodeTargetEnvironmentConfig = target === 'node' && bundlerConfigName === environment;
320
+ const isRspressSSGEnvironmentConfig = isRspressSSGConfig(bundlerConfig.name);
321
+ const isActiveRspressSSGEnvironmentConfig = isRspress && isRspressSSGEnvironmentConfig;
322
+ const shouldUseSSRPluginConfig = isSSRConfig(bundlerConfig.name) || isNodeTargetEnvironmentConfig;
323
+ if (target === 'node' && !isNodeTargetEnvironmentConfig && !isActiveRspressSSGEnvironmentConfig) {
324
+ return;
325
+ }
326
+ // For non-node targets, scope each plugin instance to its configured
327
+ // environment plus explicit SSR/SSG environments. This prevents a
328
+ // browser-targeted instance from mutating SSR configs.
329
+ if (target !== 'node' && !isConfiguredEnvironmentConfig && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
330
+ return;
331
+ }
332
+ if (!isMFFormat(bundlerConfig) && !isRspress && !isNodeTargetEnvironmentConfig) {
288
333
  return;
289
334
  } else if (isStoryBook(originalRsbuildConfig)) {
290
335
  bundlerConfig.output.uniqueName = `${moduleFederationOptions.name} -storybook - host`;
@@ -292,7 +337,8 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
292
337
  var _bundlerConfig_optimization, _bundlerConfig_optimization1, _bundlerConfig_output, _bundlerConfig_output1, _bundlerConfig_output2;
293
338
  // mf
294
339
  (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));
340
+ (0,external_utils_js_namespaceObject.addDataFetchExposes)(moduleFederationOptions.exposes, shouldUseSSRPluginConfig);
341
+ const ssrModuleFederationOptions = shouldUseSSRPluginConfig ? (0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions) : undefined;
296
342
  (_bundlerConfig_optimization1 = bundlerConfig.optimization) === null || _bundlerConfig_optimization1 === void 0 ? true : delete _bundlerConfig_optimization1.runtimeChunk;
297
343
  const externals = bundlerConfig.externals;
298
344
  if (Array.isArray(externals)) {
@@ -334,13 +380,12 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
334
380
  }
335
381
  }
336
382
  }
337
- if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !isSSRConfig(bundlerConfig.name) && !isRspressSSGConfig(bundlerConfig.name) && target !== 'node') {
383
+ if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig && target !== 'node') {
338
384
  bundlerConfig.output.chunkLoading = 'jsonp';
339
385
  bundlerConfig.output.chunkLoadingGlobal = `chunk_${moduleFederationOptions.name} `;
340
386
  }
341
- if (target === 'node' && isMFFormat(bundlerConfig)) {
342
- (0,ssr_js_namespaceObject.patchNodeConfig)(bundlerConfig, moduleFederationOptions);
343
- (0,ssr_js_namespaceObject.patchNodeMFConfig)(moduleFederationOptions);
387
+ if (isNodeTargetEnvironmentConfig) {
388
+ (0,ssr_js_namespaceObject.patchNodeConfig)(bundlerConfig, ssrModuleFederationOptions ?? moduleFederationOptions);
344
389
  }
345
390
  // `uniqueName` is required for react refresh to work
346
391
  if (!((_bundlerConfig_output1 = bundlerConfig.output) === null || _bundlerConfig_output1 === void 0 ? void 0 : _bundlerConfig_output1.uniqueName)) {
@@ -348,17 +393,18 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
348
393
  }
349
394
  // Set default publicPath to 'auto' if not explicitly configured
350
395
  // 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)) {
396
+ if (((_bundlerConfig_output2 = bundlerConfig.output) === null || _bundlerConfig_output2 === void 0 ? void 0 : _bundlerConfig_output2.publicPath) === undefined && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
352
397
  bundlerConfig.output.publicPath = 'auto';
353
398
  }
354
399
  if (!bundlerConfig.plugins.find((p)=>p && p.name === rspack_namespaceObject.PLUGIN_NAME)) {
355
400
  var _bundlerConfig_output3;
356
- if (isSSRConfig(bundlerConfig.name)) {
357
- generateMergedStatsAndManifestOptions.options.nodePlugin = new rspack_namespaceObject.ModuleFederationPlugin((0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions));
401
+ if (shouldUseSSRPluginConfig) {
402
+ const ssrMFConfig = ssrModuleFederationOptions ?? (0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions);
403
+ generateMergedStatsAndManifestOptions.options.nodePlugin = new rspack_namespaceObject.ModuleFederationPlugin(ssrMFConfig);
358
404
  generateMergedStatsAndManifestOptions.options.nodeEnvironmentName = bundlerConfig.name || external_utils_js_namespaceObject.SSR_ENV_NAME;
359
405
  bundlerConfig.plugins.push(generateMergedStatsAndManifestOptions.options.nodePlugin);
360
406
  return;
361
- } else if (isRspressSSGConfig(bundlerConfig.name)) {
407
+ } else if (isActiveRspressSSGEnvironmentConfig) {
362
408
  const mfConfig = {
363
409
  ...(0,external_utils_js_namespaceObject.createSSRMFConfig)(moduleFederationOptions),
364
410
  // 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) {
@@ -149,8 +173,14 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
149
173
  });
150
174
  }
151
175
  } else if (target === 'node') {
152
- const mfEnv = config.environments[ENV_NAME];
153
- patchToolsTspack(mfEnv, (config, { environment })=>{
176
+ var _config_environments2;
177
+ const nodeTargetEnv = (_config_environments2 = config.environments) === null || _config_environments2 === void 0 ? void 0 : _config_environments2[environment];
178
+ if (!nodeTargetEnv) {
179
+ const availableEnvironments = Object.keys(config.environments || {});
180
+ const availableEnvironmentsLabel = availableEnvironments.length > 0 ? availableEnvironments.join(', ') : '(none)';
181
+ throw new Error(`Can not find environment '${environment}' when using target: 'node'. Available environments: ${availableEnvironmentsLabel}.`);
182
+ }
183
+ patchToolsTspack(nodeTargetEnv, (config, { environment })=>{
154
184
  config.target = 'async-node';
155
185
  });
156
186
  }
@@ -227,7 +257,22 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
227
257
  throw new Error('Can not get bundlerConfigs!');
228
258
  }
229
259
  bundlerConfigs.forEach((bundlerConfig)=>{
230
- if (!isMFFormat(bundlerConfig) && !isRspress) {
260
+ const bundlerConfigName = bundlerConfig.name || '';
261
+ const isConfiguredEnvironmentConfig = bundlerConfigName === environment;
262
+ const isNodeTargetEnvironmentConfig = target === 'node' && bundlerConfigName === environment;
263
+ const isRspressSSGEnvironmentConfig = isRspressSSGConfig(bundlerConfig.name);
264
+ const isActiveRspressSSGEnvironmentConfig = isRspress && isRspressSSGEnvironmentConfig;
265
+ const shouldUseSSRPluginConfig = isSSRConfig(bundlerConfig.name) || isNodeTargetEnvironmentConfig;
266
+ if (target === 'node' && !isNodeTargetEnvironmentConfig && !isActiveRspressSSGEnvironmentConfig) {
267
+ return;
268
+ }
269
+ // For non-node targets, scope each plugin instance to its configured
270
+ // environment plus explicit SSR/SSG environments. This prevents a
271
+ // browser-targeted instance from mutating SSR configs.
272
+ if (target !== 'node' && !isConfiguredEnvironmentConfig && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
273
+ return;
274
+ }
275
+ if (!isMFFormat(bundlerConfig) && !isRspress && !isNodeTargetEnvironmentConfig) {
231
276
  return;
232
277
  } else if (isStoryBook(originalRsbuildConfig)) {
233
278
  bundlerConfig.output.uniqueName = `${moduleFederationOptions.name} -storybook - host`;
@@ -235,7 +280,8 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
235
280
  var _bundlerConfig_optimization, _bundlerConfig_optimization1, _bundlerConfig_output, _bundlerConfig_output1, _bundlerConfig_output2;
236
281
  // mf
237
282
  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));
283
+ addDataFetchExposes(moduleFederationOptions.exposes, shouldUseSSRPluginConfig);
284
+ const ssrModuleFederationOptions = shouldUseSSRPluginConfig ? createSSRMFConfig(moduleFederationOptions) : undefined;
239
285
  (_bundlerConfig_optimization1 = bundlerConfig.optimization) === null || _bundlerConfig_optimization1 === void 0 ? true : delete _bundlerConfig_optimization1.runtimeChunk;
240
286
  const externals = bundlerConfig.externals;
241
287
  if (Array.isArray(externals)) {
@@ -277,13 +323,12 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
277
323
  }
278
324
  }
279
325
  }
280
- if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !isSSRConfig(bundlerConfig.name) && !isRspressSSGConfig(bundlerConfig.name) && target !== 'node') {
326
+ if (!((_bundlerConfig_output = bundlerConfig.output) === null || _bundlerConfig_output === void 0 ? void 0 : _bundlerConfig_output.chunkLoadingGlobal) && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig && target !== 'node') {
281
327
  bundlerConfig.output.chunkLoading = 'jsonp';
282
328
  bundlerConfig.output.chunkLoadingGlobal = `chunk_${moduleFederationOptions.name} `;
283
329
  }
284
- if (target === 'node' && isMFFormat(bundlerConfig)) {
285
- patchNodeConfig(bundlerConfig, moduleFederationOptions);
286
- patchNodeMFConfig(moduleFederationOptions);
330
+ if (isNodeTargetEnvironmentConfig) {
331
+ patchNodeConfig(bundlerConfig, ssrModuleFederationOptions ?? moduleFederationOptions);
287
332
  }
288
333
  // `uniqueName` is required for react refresh to work
289
334
  if (!((_bundlerConfig_output1 = bundlerConfig.output) === null || _bundlerConfig_output1 === void 0 ? void 0 : _bundlerConfig_output1.uniqueName)) {
@@ -291,17 +336,18 @@ const pluginModuleFederation = (moduleFederationOptions, rsbuildOptions)=>({
291
336
  }
292
337
  // Set default publicPath to 'auto' if not explicitly configured
293
338
  // 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)) {
339
+ if (((_bundlerConfig_output2 = bundlerConfig.output) === null || _bundlerConfig_output2 === void 0 ? void 0 : _bundlerConfig_output2.publicPath) === undefined && !shouldUseSSRPluginConfig && !isActiveRspressSSGEnvironmentConfig) {
295
340
  bundlerConfig.output.publicPath = 'auto';
296
341
  }
297
342
  if (!bundlerConfig.plugins.find((p)=>p && p.name === PLUGIN_NAME)) {
298
343
  var _bundlerConfig_output3;
299
- if (isSSRConfig(bundlerConfig.name)) {
300
- generateMergedStatsAndManifestOptions.options.nodePlugin = new ModuleFederationPlugin(createSSRMFConfig(moduleFederationOptions));
344
+ if (shouldUseSSRPluginConfig) {
345
+ const ssrMFConfig = ssrModuleFederationOptions ?? createSSRMFConfig(moduleFederationOptions);
346
+ generateMergedStatsAndManifestOptions.options.nodePlugin = new ModuleFederationPlugin(ssrMFConfig);
301
347
  generateMergedStatsAndManifestOptions.options.nodeEnvironmentName = bundlerConfig.name || SSR_ENV_NAME;
302
348
  bundlerConfig.plugins.push(generateMergedStatsAndManifestOptions.options.nodePlugin);
303
349
  return;
304
- } else if (isRspressSSGConfig(bundlerConfig.name)) {
350
+ } else if (isActiveRspressSSGEnvironmentConfig) {
305
351
  const mfConfig = {
306
352
  ...createSSRMFConfig(moduleFederationOptions),
307
353
  // expose in mf-ssg env
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.1.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/sdk": "2.1.0",
54
+ "@module-federation/enhanced": "2.1.0",
55
+ "@module-federation/node": "2.7.33"
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": {