@nx/react 0.0.0-pr-27786-e5e2109 → 0.0.0-pr-27404-f7ba497

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.
Files changed (105) hide show
  1. package/executors.json +5 -0
  2. package/migrations.json +13 -0
  3. package/package.json +9 -7
  4. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.d.ts +1 -14
  5. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.js +2 -60
  6. package/src/executors/module-federation-dev-server/schema.d.ts +17 -0
  7. package/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.js +18 -5
  8. package/src/executors/module-federation-static-server/module-federation-static-server.impl.d.ts +12 -0
  9. package/src/executors/module-federation-static-server/module-federation-static-server.impl.js +240 -0
  10. package/src/executors/module-federation-static-server/schema.d.ts +3 -0
  11. package/src/executors/module-federation-static-server/schema.json +14 -0
  12. package/src/generators/application/application.js +14 -2
  13. package/src/generators/application/lib/add-routing.js +4 -1
  14. package/src/generators/application/lib/create-application-files.js +5 -2
  15. package/src/generators/federate-module/federate-module.js +5 -1
  16. package/src/generators/federate-module/schema.d.ts +1 -0
  17. package/src/generators/federate-module/schema.json +6 -0
  18. package/src/generators/host/files/rspack-common/src/app/__fileName__.jsx__tmpl__ +33 -0
  19. package/src/generators/host/files/rspack-common/src/main.jsx__tmpl__ +10 -0
  20. package/src/generators/host/files/rspack-module-federation/rspack.config.js__tmpl__ +16 -0
  21. package/src/generators/host/files/rspack-module-federation/rspack.config.prod.js__tmpl__ +36 -0
  22. package/src/generators/host/files/rspack-module-federation-ssr/module-federation.server.config.js__tmpl__ +16 -0
  23. package/src/generators/host/files/rspack-module-federation-ssr/rspack.server.config.js__tmpl__ +16 -0
  24. package/src/generators/host/files/rspack-module-federation-ssr-ts/module-federation.server.config.ts__tmpl__ +13 -0
  25. package/src/generators/host/files/rspack-module-federation-ssr-ts/rspack.server.config.ts__tmpl__ +16 -0
  26. package/src/generators/host/files/rspack-module-federation-ts/module-federation.config.ts__tmpl__ +25 -0
  27. package/src/generators/host/files/rspack-module-federation-ts/rspack.config.prod.ts__tmpl__ +36 -0
  28. package/src/generators/host/files/rspack-module-federation-ts/rspack.config.ts__tmpl__ +16 -0
  29. package/src/generators/host/files/webpack-module-federation/module-federation.config.js__tmpl__ +21 -0
  30. package/src/generators/host/files/webpack-module-federation-ssr/server.ts__tmpl__ +28 -0
  31. package/src/generators/host/files/webpack-module-federation-ssr/tsconfig.server.json__tmpl__ +17 -0
  32. package/src/generators/host/files/webpack-module-federation-ssr-ts/server.ts__tmpl__ +28 -0
  33. package/src/generators/host/files/webpack-module-federation-ssr-ts/tsconfig.server.json__tmpl__ +17 -0
  34. package/src/generators/host/host.js +10 -3
  35. package/src/generators/host/lib/add-module-federation-files.js +23 -11
  36. package/src/generators/host/lib/setup-ssr-for-host.js +6 -3
  37. package/src/generators/host/schema.d.ts +1 -0
  38. package/src/generators/host/schema.json +8 -0
  39. package/src/generators/library/lib/add-linting.d.ts +2 -1
  40. package/src/generators/library/lib/add-linting.js +17 -2
  41. package/src/generators/remote/files/rspack-common/src/main.jsx__tmpl__ +1 -0
  42. package/src/generators/remote/files/rspack-common/src/remote-entry.js__tmpl__ +1 -0
  43. package/src/generators/remote/files/rspack-module-federation/rspack.config.js__tmpl__ +16 -0
  44. package/src/generators/remote/files/rspack-module-federation/rspack.config.prod.js__tmpl__ +1 -0
  45. package/src/generators/remote/files/rspack-module-federation-ssr/module-federation.server.config.js__tmpl__ +6 -0
  46. package/src/generators/remote/files/rspack-module-federation-ssr/rspack.server.config.js__tmpl__ +16 -0
  47. package/src/generators/remote/files/rspack-module-federation-ssr-ts/module-federation.server.config.ts__tmpl__ +10 -0
  48. package/src/generators/remote/files/rspack-module-federation-ssr-ts/rspack.server.config.ts__tmpl__ +16 -0
  49. package/src/generators/remote/files/rspack-module-federation-ssr-ts/tsconfig.lint.json__tmpl__ +19 -0
  50. package/src/generators/remote/files/rspack-module-federation-ts/module-federation.config.ts__tmpl__ +13 -0
  51. package/src/generators/remote/files/rspack-module-federation-ts/rspack.config.prod.ts__tmpl__ +1 -0
  52. package/src/generators/remote/files/rspack-module-federation-ts/rspack.config.ts__tmpl__ +16 -0
  53. package/src/generators/remote/files/rspack-module-federation-ts/tsconfig.lint.json__tmpl__ +19 -0
  54. package/src/generators/remote/files/webpack-module-federation/module-federation.config.js__tmpl__ +9 -0
  55. package/src/generators/remote/files/webpack-module-federation-ssr/server.ts__tmpl__ +45 -0
  56. package/src/generators/remote/files/webpack-module-federation-ssr-ts/server.ts__tmpl__ +45 -0
  57. package/src/generators/remote/files/webpack-module-federation-ts/tsconfig.lint.json__tmpl__ +19 -0
  58. package/src/generators/remote/lib/setup-ssr-for-remote.js +2 -2
  59. package/src/generators/remote/lib/update-host-with-remote.js +4 -0
  60. package/src/generators/remote/remote.js +24 -11
  61. package/src/generators/remote/schema.d.ts +1 -0
  62. package/src/generators/remote/schema.json +8 -0
  63. package/src/generators/setup-ssr/schema.d.ts +1 -0
  64. package/src/generators/setup-ssr/schema.json +6 -0
  65. package/src/generators/setup-ssr/setup-ssr.js +11 -3
  66. package/src/module-federation/utils.js +1 -1
  67. package/src/module-federation/with-module-federation-ssr.js +7 -7
  68. package/src/module-federation/with-module-federation.js +3 -11
  69. package/src/rules/update-module-federation-project.d.ts +3 -2
  70. package/src/rules/update-module-federation-project.js +54 -23
  71. package/src/utils/add-mf-env-to-inputs.js +2 -1
  72. package/src/utils/build-static.remotes.d.ts +4 -0
  73. package/src/utils/build-static.remotes.js +65 -0
  74. package/src/utils/maybe-js.d.ts +1 -0
  75. package/src/utils/maybe-js.js +1 -1
  76. package/src/utils/versions.d.ts +4 -5
  77. package/src/utils/versions.js +5 -6
  78. /package/src/generators/{remote/files/module-federation-ssr-ts → host/files/rspack-common}/tsconfig.lint.json__tmpl__ +0 -0
  79. /package/src/generators/host/files/{module-federation → rspack-module-federation}/module-federation.config.js__tmpl__ +0 -0
  80. /package/src/generators/host/files/{module-federation-ssr → rspack-module-federation-ssr}/server.ts__tmpl__ +0 -0
  81. /package/src/generators/host/files/{module-federation-ssr → rspack-module-federation-ssr}/tsconfig.server.json__tmpl__ +0 -0
  82. /package/src/generators/host/files/{module-federation-ssr-ts → rspack-module-federation-ssr-ts}/server.ts__tmpl__ +0 -0
  83. /package/src/generators/host/files/{module-federation-ssr-ts → rspack-module-federation-ssr-ts}/tsconfig.server.json__tmpl__ +0 -0
  84. /package/src/generators/host/files/{module-federation → webpack-module-federation}/webpack.config.js__tmpl__ +0 -0
  85. /package/src/generators/host/files/{module-federation → webpack-module-federation}/webpack.config.prod.js__tmpl__ +0 -0
  86. /package/src/generators/host/files/{module-federation-ssr → webpack-module-federation-ssr}/module-federation.server.config.js__tmpl__ +0 -0
  87. /package/src/generators/host/files/{module-federation-ssr → webpack-module-federation-ssr}/webpack.server.config.js__tmpl__ +0 -0
  88. /package/src/generators/host/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/module-federation.server.config.ts__tmpl__ +0 -0
  89. /package/src/generators/host/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/webpack.server.config.ts__tmpl__ +0 -0
  90. /package/src/generators/host/files/{module-federation-ts → webpack-module-federation-ts}/module-federation.config.ts__tmpl__ +0 -0
  91. /package/src/generators/host/files/{module-federation-ts → webpack-module-federation-ts}/webpack.config.prod.ts__tmpl__ +0 -0
  92. /package/src/generators/host/files/{module-federation-ts → webpack-module-federation-ts}/webpack.config.ts__tmpl__ +0 -0
  93. /package/src/generators/remote/files/{module-federation → rspack-module-federation}/module-federation.config.js__tmpl__ +0 -0
  94. /package/src/generators/remote/files/{module-federation-ssr → rspack-module-federation-ssr}/server.ts__tmpl__ +0 -0
  95. /package/src/generators/remote/files/{module-federation-ssr-ts → rspack-module-federation-ssr-ts}/server.ts__tmpl__ +0 -0
  96. /package/src/generators/remote/files/{module-federation → webpack-module-federation}/webpack.config.js__tmpl__ +0 -0
  97. /package/src/generators/remote/files/{module-federation → webpack-module-federation}/webpack.config.prod.js__tmpl__ +0 -0
  98. /package/src/generators/remote/files/{module-federation-ssr → webpack-module-federation-ssr}/module-federation.server.config.js__tmpl__ +0 -0
  99. /package/src/generators/remote/files/{module-federation-ssr → webpack-module-federation-ssr}/webpack.server.config.js__tmpl__ +0 -0
  100. /package/src/generators/remote/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/module-federation.server.config.ts__tmpl__ +0 -0
  101. /package/src/generators/remote/files/{module-federation-ts → webpack-module-federation-ssr-ts}/tsconfig.lint.json__tmpl__ +0 -0
  102. /package/src/generators/remote/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/webpack.server.config.ts__tmpl__ +0 -0
  103. /package/src/generators/remote/files/{module-federation-ts → webpack-module-federation-ts}/module-federation.config.ts__tmpl__ +0 -0
  104. /package/src/generators/remote/files/{module-federation-ts → webpack-module-federation-ts}/webpack.config.prod.ts__tmpl__ +0 -0
  105. /package/src/generators/remote/files/{module-federation-ts → webpack-module-federation-ts}/webpack.config.ts__tmpl__ +0 -0
package/executors.json CHANGED
@@ -9,6 +9,11 @@
9
9
  "implementation": "./src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl",
10
10
  "schema": "./src/executors/module-federation-ssr-dev-server/schema.json",
11
11
  "description": "Serve a host application along with it's known remotes."
12
+ },
13
+ "module-federation-static-server": {
14
+ "implementation": "./src/executors/module-federation-static-server/module-federation-static-server.impl",
15
+ "schema": "./src/executors/module-federation-static-server/schema.json",
16
+ "description": "Serve a host and its remotes statically."
12
17
  }
13
18
  }
14
19
  }
package/migrations.json CHANGED
@@ -229,6 +229,19 @@
229
229
  "alwaysAddToPackageJson": false
230
230
  }
231
231
  }
232
+ },
233
+ "19.7.0": {
234
+ "version": "19.7.0-beta.0",
235
+ "packages": {
236
+ "@module-federation/enhanced": {
237
+ "version": "~0.6.0",
238
+ "alwaysAddToPackageJson": false
239
+ },
240
+ "@module-federation/node": {
241
+ "version": "~2.5.0",
242
+ "alwaysAddToPackageJson": false
243
+ }
244
+ }
232
245
  }
233
246
  }
234
247
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "0.0.0-pr-27786-e5e2109",
3
+ "version": "0.0.0-pr-27404-f7ba497",
4
4
  "private": false,
5
5
  "description": "The React plugin for Nx contains executors and generators for managing React applications and libraries within an Nx workspace. It provides:\n\n\n- Integration with libraries such as Jest, Vitest, Playwright, Cypress, and Storybook.\n\n- Generators for applications, libraries, components, hooks, and more.\n\n- Library build support for publishing packages to npm or other registries.\n\n- Utilities for automatic workspace refactoring.",
6
6
  "repository": {
@@ -38,12 +38,14 @@
38
38
  "file-loader": "^6.2.0",
39
39
  "minimatch": "9.0.3",
40
40
  "tslib": "^2.3.0",
41
- "@module-federation/enhanced": "~0.2.3",
42
- "@nx/devkit": "0.0.0-pr-27786-e5e2109",
43
- "@nx/js": "0.0.0-pr-27786-e5e2109",
44
- "@nx/eslint": "0.0.0-pr-27786-e5e2109",
45
- "@nx/web": "0.0.0-pr-27786-e5e2109",
46
- "@nrwl/react": "0.0.0-pr-27786-e5e2109"
41
+ "@module-federation/enhanced": "~0.6.0",
42
+ "@nx/devkit": "0.0.0-pr-27404-f7ba497",
43
+ "@nx/js": "0.0.0-pr-27404-f7ba497",
44
+ "@nx/eslint": "0.0.0-pr-27404-f7ba497",
45
+ "@nx/web": "0.0.0-pr-27404-f7ba497",
46
+ "express": "^4.19.2",
47
+ "http-proxy-middleware": "^3.0.0",
48
+ "@nrwl/react": "0.0.0-pr-27404-f7ba497"
47
49
  },
48
50
  "publishConfig": {
49
51
  "access": "public"
@@ -1,19 +1,6 @@
1
1
  import { ExecutorContext } from '@nx/devkit';
2
- import { WebDevServerOptions } from '@nx/webpack/src/executors/dev-server/schema';
3
- type ModuleFederationDevServerOptions = WebDevServerOptions & {
4
- devRemotes?: (string | {
5
- remoteName: string;
6
- configuration: string;
7
- })[];
8
- skipRemotes?: string[];
9
- static?: boolean;
10
- isInitialHost?: boolean;
11
- parallel?: number;
12
- staticRemotesPort?: number;
13
- pathToManifestFile?: string;
14
- };
2
+ import { ModuleFederationDevServerOptions } from './schema';
15
3
  export default function moduleFederationDevServer(options: ModuleFederationDevServerOptions, context: ExecutorContext): AsyncIterableIterator<{
16
4
  success: boolean;
17
5
  baseUrl?: string;
18
6
  }>;
19
- export {};
@@ -7,12 +7,11 @@ const file_server_impl_1 = require("@nx/web/src/executors/file-server/file-serve
7
7
  const module_federation_1 = require("@nx/webpack/src/utils/module-federation");
8
8
  const async_iterable_1 = require("@nx/devkit/src/utils/async-iterable");
9
9
  const wait_for_port_open_1 = require("@nx/web/src/utils/wait-for-port-open");
10
- const cache_directory_1 = require("nx/src/utils/cache-directory");
11
- const node_child_process_1 = require("node:child_process");
12
10
  const fs_1 = require("fs");
13
11
  const path_1 = require("path");
14
12
  const start_remote_proxies_1 = require("@nx/webpack/src/utils/module-federation/start-remote-proxies");
15
13
  const parse_static_remotes_config_1 = require("@nx/webpack/src/utils/module-federation/parse-static-remotes-config");
14
+ const build_static_remotes_1 = require("../../utils/build-static.remotes");
16
15
  function getBuildOptions(buildTarget, context) {
17
16
  const target = (0, devkit_1.parseTargetString)(buildTarget, context);
18
17
  const buildOptions = (0, devkit_1.readTargetOptions)(target, context);
@@ -92,63 +91,6 @@ async function startRemotes(remotes, context, options, target = 'serve') {
92
91
  }
93
92
  return remoteIters;
94
93
  }
95
- async function buildStaticRemotes(staticRemotesConfig, nxBin, context, options) {
96
- if (!staticRemotesConfig.remotes.length) {
97
- return;
98
- }
99
- devkit_1.logger.info(`NX Building ${staticRemotesConfig.remotes.length} static remotes...`);
100
- const mappedLocationOfRemotes = {};
101
- for (const app of staticRemotesConfig.remotes) {
102
- mappedLocationOfRemotes[app] = `http${options.ssl ? 's' : ''}://${options.host}:${options.staticRemotesPort}/${staticRemotesConfig.config[app].urlSegment}`;
103
- }
104
- await new Promise((res, rej) => {
105
- const staticProcess = (0, node_child_process_1.fork)(nxBin, [
106
- 'run-many',
107
- `--target=build`,
108
- `--projects=${staticRemotesConfig.remotes.join(',')}`,
109
- ...(context.configurationName
110
- ? [`--configuration=${context.configurationName}`]
111
- : []),
112
- ...(options.parallel ? [`--parallel=${options.parallel}`] : []),
113
- ], {
114
- cwd: context.root,
115
- stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
116
- });
117
- // File to debug build failures e.g. 2024-01-01T00_00_0_0Z-build.log'
118
- const remoteBuildLogFile = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, `${new Date().toISOString().replace(/[:\.]/g, '_')}-build.log`);
119
- const stdoutStream = (0, fs_1.createWriteStream)(remoteBuildLogFile);
120
- staticProcess.stdout.on('data', (data) => {
121
- const ANSII_CODE_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
122
- const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
123
- stdoutStream.write(stdoutString);
124
- // in addition to writing into the stdout stream, also show error directly in console
125
- // so the error is easily discoverable. 'ERROR in' is the key word to search in webpack output.
126
- if (stdoutString.includes('ERROR in')) {
127
- devkit_1.logger.log(stdoutString);
128
- }
129
- if (stdoutString.includes('Successfully ran target build')) {
130
- staticProcess.stdout.removeAllListeners('data');
131
- devkit_1.logger.info(`NX Built ${staticRemotesConfig.remotes.length} static remotes`);
132
- res();
133
- }
134
- });
135
- staticProcess.stderr.on('data', (data) => devkit_1.logger.info(data.toString()));
136
- staticProcess.once('exit', (code) => {
137
- stdoutStream.end();
138
- staticProcess.stdout.removeAllListeners('data');
139
- staticProcess.stderr.removeAllListeners('data');
140
- if (code !== 0) {
141
- rej(`Remote failed to start. A complete log can be found in: ${remoteBuildLogFile}`);
142
- }
143
- else {
144
- res();
145
- }
146
- });
147
- process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
148
- process.on('exit', () => staticProcess.kill('SIGTERM'));
149
- });
150
- return mappedLocationOfRemotes;
151
- }
152
94
  async function* moduleFederationDevServer(options, context) {
153
95
  // Force Node to resolve to look for the nx binary that is inside node_modules
154
96
  const nxBin = require.resolve('nx/bin/nx');
@@ -192,7 +134,7 @@ async function* moduleFederationDevServer(options, context) {
192
134
  p.name,
193
135
  ]);
194
136
  const staticRemotesConfig = (0, parse_static_remotes_config_1.parseStaticRemotesConfig)([...remotes.staticRemotes, ...remotes.dynamicRemotes], context);
195
- const mappedLocationsOfStaticRemotes = await buildStaticRemotes(staticRemotesConfig, nxBin, context, options);
137
+ const mappedLocationsOfStaticRemotes = await (0, build_static_remotes_1.buildStaticRemotes)(staticRemotesConfig, nxBin, context, options);
196
138
  const devRemoteIters = await startRemotes(remotes.devRemotes, context, options, 'serve');
197
139
  const staticRemotesIter = startStaticRemotesFileServer(staticRemotesConfig, context, options);
198
140
  (0, start_remote_proxies_1.startRemoteProxies)(staticRemotesConfig, mappedLocationsOfStaticRemotes, options.ssl
@@ -0,0 +1,17 @@
1
+ import { WebDevServerOptions } from '@nx/webpack';
2
+
3
+ export type ModuleFederationDevServerOptions = WebDevServerOptions & {
4
+ devRemotes?: (
5
+ | string
6
+ | {
7
+ remoteName: string;
8
+ configuration: string;
9
+ }
10
+ )[];
11
+ skipRemotes?: string[];
12
+ static?: boolean;
13
+ isInitialHost?: boolean;
14
+ parallel?: number;
15
+ staticRemotesPort?: number;
16
+ pathToManifestFile?: string;
17
+ };
@@ -28,8 +28,9 @@ function getBuildOptions(buildTarget, context) {
28
28
  ...buildOptions,
29
29
  };
30
30
  }
31
- function startSsrStaticRemotesFileServer(ssrStaticRemotesConfig, context, options) {
31
+ async function* startSsrStaticRemotesFileServer(ssrStaticRemotesConfig, context, options) {
32
32
  if (ssrStaticRemotesConfig.remotes.length === 0) {
33
+ yield { success: true };
33
34
  return;
34
35
  }
35
36
  // The directories are usually generated with /browser and /server suffixes so we need to copy them to a common directory
@@ -55,7 +56,7 @@ function startSsrStaticRemotesFileServer(ssrStaticRemotesConfig, context, option
55
56
  sslKey: options.sslKey,
56
57
  cacheSeconds: -1,
57
58
  }, context);
58
- return staticRemotesIter;
59
+ yield* staticRemotesIter;
59
60
  }
60
61
  async function startRemotes(remotes, context, options) {
61
62
  const remoteIters = [];
@@ -176,18 +177,30 @@ async function* moduleFederationSsrDevServer(ssrDevServerOptions, context) {
176
177
  pathToKey: options.sslKey,
177
178
  }
178
179
  : undefined);
179
- return yield* (0, async_iterable_1.combineAsyncIterables)(iter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
180
+ const combined = (0, async_iterable_1.combineAsyncIterables)(staticRemotesIter, ...devRemoteIters);
181
+ let refs = 1 + (devRemoteIters?.length ?? 0);
182
+ for await (const result of combined) {
183
+ if (result.success === false)
184
+ throw new Error('Remotes failed to start');
185
+ if (result.success)
186
+ refs--;
187
+ if (refs === 0)
188
+ break;
189
+ }
190
+ return yield* (0, async_iterable_1.combineAsyncIterables)(iter, (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
191
+ const host = options.host ?? 'localhost';
192
+ const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${options.port}`;
180
193
  if (!options.isInitialHost) {
194
+ next({ success: true, baseUrl });
181
195
  done();
182
196
  return;
183
197
  }
184
198
  if (remotes.remotePorts.length === 0) {
199
+ next({ success: true, baseUrl });
185
200
  done();
186
201
  return;
187
202
  }
188
203
  try {
189
- const host = options.host ?? 'localhost';
190
- const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${options.port}`;
191
204
  const portsToWaitFor = staticRemotesIter
192
205
  ? [options.staticRemotesPort, ...remotes.remotePorts]
193
206
  : [...remotes.remotePorts];
@@ -0,0 +1,12 @@
1
+ import { ModuleFederationStaticServerSchema } from './schema';
2
+ import { ModuleFederationDevServerOptions } from '../module-federation-dev-server/schema';
3
+ import { ExecutorContext } from 'nx/src/config/misc-interfaces';
4
+ import { StaticRemotesConfig } from '@nx/webpack/src/utils/module-federation/parse-static-remotes-config';
5
+ export declare function startProxies(staticRemotesConfig: StaticRemotesConfig, hostServeOptions: ModuleFederationDevServerOptions, mappedLocationOfHost: string, mappedLocationsOfRemotes: Record<string, string>, sslOptions?: {
6
+ pathToCert: string;
7
+ pathToKey: string;
8
+ }): void;
9
+ export default function moduleFederationStaticServer(schema: ModuleFederationStaticServerSchema, context: ExecutorContext): AsyncGenerator<{
10
+ success: boolean;
11
+ baseUrl: string;
12
+ }, any, unknown>;
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startProxies = startProxies;
4
+ exports.default = moduleFederationStaticServer;
5
+ const path_1 = require("path");
6
+ const devkit_1 = require("@nx/devkit");
7
+ const fs_1 = require("fs");
8
+ const module_federation_1 = require("@nx/webpack/src/utils/module-federation");
9
+ const parse_static_remotes_config_1 = require("@nx/webpack/src/utils/module-federation/parse-static-remotes-config");
10
+ const build_static_remotes_1 = require("../../utils/build-static.remotes");
11
+ const child_process_1 = require("child_process");
12
+ const process = require("node:process");
13
+ const file_server_impl_1 = require("@nx/web/src/executors/file-server/file-server.impl");
14
+ const async_iterable_1 = require("@nx/devkit/src/utils/async-iterable");
15
+ const wait_for_port_open_1 = require("@nx/web/src/utils/wait-for-port-open");
16
+ function getBuildAndServeOptionsFromServeTarget(serveTarget, context) {
17
+ const target = (0, devkit_1.parseTargetString)(serveTarget, context);
18
+ const serveOptions = (0, devkit_1.readTargetOptions)(target, context);
19
+ const buildTarget = (0, devkit_1.parseTargetString)(serveOptions.buildTarget, context);
20
+ const buildOptions = (0, devkit_1.readTargetOptions)(buildTarget, context);
21
+ let pathToManifestFile = (0, path_1.join)(context.root, context.projectGraph.nodes[context.projectName].data.sourceRoot, 'assets/module-federation.manifest.json');
22
+ if (serveOptions.pathToManifestFile) {
23
+ const userPathToManifestFile = (0, path_1.join)(context.root, serveOptions.pathToManifestFile);
24
+ if (!(0, fs_1.existsSync)(userPathToManifestFile)) {
25
+ throw new Error(`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`);
26
+ }
27
+ else if ((0, path_1.extname)(serveOptions.pathToManifestFile) !== '.json') {
28
+ throw new Error(`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`);
29
+ }
30
+ pathToManifestFile = userPathToManifestFile;
31
+ }
32
+ return {
33
+ buildTarget,
34
+ buildOptions,
35
+ serveOptions,
36
+ pathToManifestFile,
37
+ };
38
+ }
39
+ async function buildHost(nxBin, buildTarget, context) {
40
+ await new Promise((res, rej) => {
41
+ const staticProcess = (0, child_process_1.fork)(nxBin, [
42
+ `run`,
43
+ `${buildTarget.project}:${buildTarget.target}${buildTarget.configuration
44
+ ? `:${buildTarget.configuration}`
45
+ : context.configurationName
46
+ ? `:${context.configurationName}`
47
+ : ''}`,
48
+ ], {
49
+ cwd: context.root,
50
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
51
+ });
52
+ staticProcess.stdout.on('data', (data) => {
53
+ const ANSII_CODE_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
54
+ const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
55
+ // in addition to writing into the stdout stream, also show error directly in console
56
+ // so the error is easily discoverable. 'ERROR in' is the key word to search in webpack output.
57
+ if (stdoutString.includes('ERROR in')) {
58
+ devkit_1.logger.log(stdoutString);
59
+ }
60
+ if (stdoutString.includes('Successfully ran target build')) {
61
+ staticProcess.stdout.removeAllListeners('data');
62
+ devkit_1.logger.info(`NX Built host`);
63
+ res();
64
+ }
65
+ });
66
+ staticProcess.stderr.on('data', (data) => devkit_1.logger.info(data.toString()));
67
+ staticProcess.once('exit', (code) => {
68
+ staticProcess.stdout.removeAllListeners('data');
69
+ staticProcess.stderr.removeAllListeners('data');
70
+ if (code !== 0) {
71
+ rej(`Host failed to build. See above for details.`);
72
+ }
73
+ else {
74
+ res();
75
+ }
76
+ });
77
+ process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
78
+ process.on('exit', () => staticProcess.kill('SIGTERM'));
79
+ });
80
+ }
81
+ function moveToTmpDirectory(staticRemotesConfig, hostOutputPath, hostUrlSegment) {
82
+ const commonOutputDirectory = (0, path_1.join)(devkit_1.workspaceRoot, 'tmp/static-module-federation');
83
+ for (const app of staticRemotesConfig.remotes) {
84
+ const remoteConfig = staticRemotesConfig.config[app];
85
+ (0, fs_1.cpSync)(remoteConfig.outputPath, (0, path_1.join)(commonOutputDirectory, remoteConfig.urlSegment), {
86
+ force: true,
87
+ recursive: true,
88
+ });
89
+ }
90
+ (0, fs_1.cpSync)(hostOutputPath, (0, path_1.join)(commonOutputDirectory, hostUrlSegment), {
91
+ force: true,
92
+ recursive: true,
93
+ });
94
+ const cleanup = () => {
95
+ (0, fs_1.rmSync)(commonOutputDirectory, { force: true, recursive: true });
96
+ };
97
+ process.on('SIGTERM', () => {
98
+ cleanup();
99
+ });
100
+ process.on('exit', () => {
101
+ cleanup();
102
+ });
103
+ return commonOutputDirectory;
104
+ }
105
+ function startProxies(staticRemotesConfig, hostServeOptions, mappedLocationOfHost, mappedLocationsOfRemotes, sslOptions) {
106
+ const { createProxyMiddleware } = require('http-proxy-middleware');
107
+ const express = require('express');
108
+ let sslCert;
109
+ let sslKey;
110
+ if (sslOptions && sslOptions.pathToCert && sslOptions.pathToKey) {
111
+ if ((0, fs_1.existsSync)(sslOptions.pathToCert) && (0, fs_1.existsSync)(sslOptions.pathToKey)) {
112
+ sslCert = (0, fs_1.readFileSync)(sslOptions.pathToCert);
113
+ sslKey = (0, fs_1.readFileSync)(sslOptions.pathToKey);
114
+ }
115
+ else {
116
+ devkit_1.logger.warn(`Encountered SSL options in project.json, however, the certificate files do not exist in the filesystem. Using http.`);
117
+ devkit_1.logger.warn(`Attempted to find '${sslOptions.pathToCert}' and '${sslOptions.pathToKey}'.`);
118
+ }
119
+ }
120
+ const http = require('http');
121
+ const https = require('https');
122
+ devkit_1.logger.info(`NX Starting static remotes proxies...`);
123
+ for (const app of staticRemotesConfig.remotes) {
124
+ const expressProxy = express();
125
+ expressProxy.use(createProxyMiddleware({
126
+ target: mappedLocationsOfRemotes[app],
127
+ changeOrigin: true,
128
+ secure: sslCert ? false : undefined,
129
+ }));
130
+ const proxyServer = (sslCert ? https : http)
131
+ .createServer({ cert: sslCert, key: sslKey }, expressProxy)
132
+ .listen(staticRemotesConfig.config[app].port);
133
+ process.on('SIGTERM', () => proxyServer.close());
134
+ process.on('exit', () => proxyServer.close());
135
+ }
136
+ devkit_1.logger.info(`NX Static remotes proxies started successfully`);
137
+ devkit_1.logger.info(`NX Starting static host proxy...`);
138
+ const expressProxy = express();
139
+ expressProxy.use(createProxyMiddleware({
140
+ target: mappedLocationOfHost,
141
+ changeOrigin: true,
142
+ secure: sslCert ? false : undefined,
143
+ pathRewrite: (path) => {
144
+ let pathRewrite = path;
145
+ for (const app of staticRemotesConfig.remotes) {
146
+ if (path.endsWith(app)) {
147
+ pathRewrite = '/';
148
+ break;
149
+ }
150
+ }
151
+ return pathRewrite;
152
+ },
153
+ }));
154
+ const proxyServer = (sslCert ? https : http)
155
+ .createServer({ cert: sslCert, key: sslKey }, expressProxy)
156
+ .listen(hostServeOptions.port);
157
+ process.on('SIGTERM', () => proxyServer.close());
158
+ process.on('exit', () => proxyServer.close());
159
+ devkit_1.logger.info('NX Static host proxy started successfully');
160
+ }
161
+ async function* moduleFederationStaticServer(schema, context) {
162
+ // Force Node to resolve to look for the nx binary that is inside node_modules
163
+ const nxBin = require.resolve('nx/bin/nx');
164
+ // Get the remotes from the module federation config
165
+ const p = context.projectsConfigurations.projects[context.projectName];
166
+ const options = getBuildAndServeOptionsFromServeTarget(schema.serveTarget, context);
167
+ const moduleFederationConfig = (0, module_federation_1.getModuleFederationConfig)(options.buildOptions.tsConfig, context.root, p.root, 'react');
168
+ const remotes = (0, module_federation_1.getRemotes)([], options.serveOptions.skipRemotes, moduleFederationConfig, {
169
+ projectName: context.projectName,
170
+ projectGraph: context.projectGraph,
171
+ root: context.root,
172
+ }, options.pathToManifestFile);
173
+ const staticRemotesConfig = (0, parse_static_remotes_config_1.parseStaticRemotesConfig)([...remotes.staticRemotes, ...remotes.dynamicRemotes], context);
174
+ options.serveOptions.staticRemotesPort ??= remotes.staticRemotePort;
175
+ const mappedLocationsOfStaticRemotes = await (0, build_static_remotes_1.buildStaticRemotes)(staticRemotesConfig, nxBin, context, options.serveOptions);
176
+ // Build the host
177
+ const hostUrlSegment = (0, path_1.basename)(options.buildOptions.outputPath);
178
+ const mappedLocationOfHost = `http${options.serveOptions.ssl ? 's' : ''}://${options.serveOptions.host}:${options.serveOptions.staticRemotesPort}/${hostUrlSegment}`;
179
+ await buildHost(nxBin, options.buildTarget, context);
180
+ // Move to a temporary directory
181
+ const commonOutputDirectory = moveToTmpDirectory(staticRemotesConfig, options.buildOptions.outputPath, hostUrlSegment);
182
+ // File Serve the temporary directory
183
+ const staticFileServerIter = (0, file_server_impl_1.default)({
184
+ cors: true,
185
+ watch: false,
186
+ staticFilePath: commonOutputDirectory,
187
+ parallel: false,
188
+ spa: false,
189
+ withDeps: false,
190
+ host: options.serveOptions.host,
191
+ port: options.serveOptions.staticRemotesPort,
192
+ ssl: options.serveOptions.ssl,
193
+ sslCert: options.serveOptions.sslCert,
194
+ sslKey: options.serveOptions.sslKey,
195
+ cacheSeconds: -1,
196
+ }, context);
197
+ // express proxy all of it
198
+ startProxies(staticRemotesConfig, options.serveOptions, mappedLocationOfHost, mappedLocationsOfStaticRemotes, options.serveOptions.ssl
199
+ ? {
200
+ pathToCert: (0, path_1.join)(devkit_1.workspaceRoot, options.serveOptions.sslCert),
201
+ pathToKey: (0, path_1.join)(devkit_1.workspaceRoot, options.serveOptions.sslKey),
202
+ }
203
+ : undefined);
204
+ return yield* (0, async_iterable_1.combineAsyncIterables)(staticFileServerIter, (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
205
+ const host = options.serveOptions.host ?? 'localhost';
206
+ const baseUrl = `http${options.serveOptions.ssl ? 's' : ''}://${host}:${options.serveOptions.port}`;
207
+ if (remotes.remotePorts.length === 0) {
208
+ const portsToWaitFor = [options.serveOptions.staticRemotesPort];
209
+ await Promise.all(portsToWaitFor.map((port) => (0, wait_for_port_open_1.waitForPortOpen)(port, {
210
+ retries: 480,
211
+ retryDelay: 2500,
212
+ host: host,
213
+ })));
214
+ devkit_1.logger.info(`NX Server ready at ${baseUrl}`);
215
+ next({ success: true, baseUrl: baseUrl });
216
+ done();
217
+ return;
218
+ }
219
+ try {
220
+ const portsToWaitFor = staticFileServerIter
221
+ ? [options.serveOptions.staticRemotesPort, ...remotes.remotePorts]
222
+ : [...remotes.remotePorts];
223
+ await Promise.all(portsToWaitFor.map((port) => (0, wait_for_port_open_1.waitForPortOpen)(port, {
224
+ retries: 480,
225
+ retryDelay: 2500,
226
+ host: host,
227
+ })));
228
+ devkit_1.logger.info(`NX Server ready at ${baseUrl}`);
229
+ next({ success: true, baseUrl: baseUrl });
230
+ }
231
+ catch (err) {
232
+ throw new Error(`Failed to start. Check above for any errors.`, {
233
+ cause: err,
234
+ });
235
+ }
236
+ finally {
237
+ done();
238
+ }
239
+ }));
240
+ }
@@ -0,0 +1,3 @@
1
+ export interface ModuleFederationStaticServerSchema {
2
+ serveTarget: string;
3
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 2,
3
+ "outputCapture": "direct-nodejs",
4
+ "title": "Module Federation Static Dev Server",
5
+ "description": "Serve a host application statically along with it's remotes.",
6
+ "cli": "nx",
7
+ "type": "object",
8
+ "properties": {
9
+ "serveTarget": {
10
+ "type": "string"
11
+ }
12
+ },
13
+ "required": ["serveTarget"]
14
+ }
@@ -26,6 +26,7 @@ const eslint_file_1 = require("@nx/eslint/src/generators/utils/eslint-file");
26
26
  const js_1 = require("@nx/js");
27
27
  const log_show_project_command_1 = require("@nx/devkit/src/utils/log-show-project-command");
28
28
  const setup_tailwind_1 = require("../setup-tailwind/setup-tailwind");
29
+ const flat_config_1 = require("@nx/eslint/src/utils/flat-config");
29
30
  async function addLinting(host, options) {
30
31
  const tasks = [];
31
32
  if (options.linter === eslint_1.Linter.EsLint) {
@@ -43,7 +44,15 @@ async function addLinting(host, options) {
43
44
  });
44
45
  tasks.push(lintTask);
45
46
  if ((0, eslint_file_1.isEslintConfigSupported)(host)) {
46
- (0, eslint_file_1.addExtendsToLintConfig)(host, options.appProjectRoot, 'plugin:@nx/react');
47
+ if ((0, flat_config_1.useFlatConfig)(host)) {
48
+ (0, eslint_file_1.addPredefinedConfigToFlatLintConfig)(host, options.appProjectRoot, 'flat/react');
49
+ // Add an empty rules object to users know how to add/override rules
50
+ (0, eslint_file_1.addOverrideToLintConfig)(host, options.appProjectRoot, { rules: {} });
51
+ }
52
+ else {
53
+ const addExtendsTask = (0, eslint_file_1.addExtendsToLintConfig)(host, options.appProjectRoot, { name: 'plugin:@nx/react', needCompatFixup: true });
54
+ tasks.push(addExtendsTask);
55
+ }
47
56
  }
48
57
  if (!options.skipPackageJson) {
49
58
  const installTask = (0, devkit_1.addDependenciesToPackageJson)(host, lint_1.extraEslintDependencies.dependencies, lint_1.extraEslintDependencies.devDependencies);
@@ -153,7 +162,10 @@ async function applicationGeneratorInternal(host, schema) {
153
162
  const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/rspack', versions_1.nxRspackVersion);
154
163
  const rspackTask = await configurationGenerator(host, {
155
164
  project: options.projectName,
156
- main: (0, devkit_1.joinPathFragments)(options.appProjectRoot, (0, maybe_js_1.maybeJs)(options, `src/main.tsx`)),
165
+ main: (0, devkit_1.joinPathFragments)(options.appProjectRoot, (0, maybe_js_1.maybeJs)({
166
+ js: options.js,
167
+ useJsx: true,
168
+ }, `src/main.tsx`)),
157
169
  tsConfig: (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'tsconfig.app.json'),
158
170
  target: 'web',
159
171
  newProject: true,
@@ -14,7 +14,10 @@ function addRouting(host, options) {
14
14
  if (!tsModule) {
15
15
  tsModule = (0, ensure_typescript_1.ensureTypescript)();
16
16
  }
17
- const appPath = (0, devkit_1.joinPathFragments)(options.appProjectRoot, (0, maybe_js_1.maybeJs)(options, `src/app/${options.fileName}.tsx`));
17
+ const appPath = (0, devkit_1.joinPathFragments)(options.appProjectRoot, (0, maybe_js_1.maybeJs)({
18
+ js: options.js,
19
+ useJsx: options.bundler === 'vite' || options.bundler === 'rspack',
20
+ }, `src/app/${options.fileName}.tsx`));
18
21
  const appFileContent = host.read(appPath, 'utf-8');
19
22
  const appSource = tsModule.createSourceFile(appPath, appFileContent, tsModule.ScriptTarget.Latest, true);
20
23
  const changes = (0, devkit_1.applyChangesToString)(appFileContent, (0, ast_utils_1.addInitialRoutes)(appPath, appSource));
@@ -126,7 +126,7 @@ async function createApplicationFiles(host, options) {
126
126
  (0, devkit_1.generateFiles)(host, (0, path_1.join)(__dirname, styleSolutionSpecificAppFiles), options.appProjectRoot, templateVariables);
127
127
  if (options.js) {
128
128
  (0, devkit_1.toJS)(host, {
129
- useJsx: options.bundler === 'vite',
129
+ useJsx: options.bundler === 'vite' || options.bundler === 'rspack',
130
130
  });
131
131
  }
132
132
  (0, create_ts_config_1.createTsConfig)(host, options.appProjectRoot, 'app', options, relativePathToRootTsConfig);
@@ -140,7 +140,10 @@ function createNxWebpackPluginOptions(options) {
140
140
  : options.projectName),
141
141
  index: './src/index.html',
142
142
  baseHref: '/',
143
- main: (0, maybe_js_1.maybeJs)(options, `./src/main.tsx`),
143
+ main: (0, maybe_js_1.maybeJs)({
144
+ js: options.js,
145
+ useJsx: options.bundler === 'vite' || options.bundler === 'rspack',
146
+ }, `./src/main.tsx`),
144
147
  tsConfig: './tsconfig.app.json',
145
148
  assets: ['./src/favicon.ico', './src/assets'],
146
149
  styles: options.styledModule || !options.hasStyles
@@ -29,6 +29,7 @@ async function federateModuleGenerator(tree, schema) {
29
29
  unitTestRunner: schema.unitTestRunner,
30
30
  host: schema.host,
31
31
  projectNameAndRootFormat: schema.projectNameAndRootFormat ?? 'derived',
32
+ bundler: schema.bundler ?? 'rspack',
32
33
  });
33
34
  tasks.push(remoteGenerator);
34
35
  const { projectName, projectRoot: remoteRoot } = await (0, project_name_and_root_utils_1.determineProjectNameAndRootOptions)(tree, {
@@ -46,7 +47,10 @@ async function federateModuleGenerator(tree, schema) {
46
47
  remoteName = remote.name;
47
48
  }
48
49
  // add path to exposes property
49
- (0, utils_1.addPathToExposes)(tree, projectRoot, schema.name, schema.path);
50
+ const normalizedModulePath = schema.bundler === 'rspack'
51
+ ? (0, devkit_1.joinPathFragments)((0, devkit_1.offsetFromRoot)(projectRoot), schema.path)
52
+ : schema.path;
53
+ (0, utils_1.addPathToExposes)(tree, projectRoot, schema.name, normalizedModulePath);
50
54
  // Add new path to tsconfig
51
55
  const rootJSON = (0, devkit_1.readJson)(tree, (0, js_1.getRootTsConfigPathInTree)(tree));
52
56
  if (!rootJSON?.compilerOptions?.paths[`${remoteName}/${schema.name}`]) {
@@ -12,4 +12,5 @@ export interface Schema {
12
12
  skipFormat?: boolean;
13
13
  style?: SupportedStyles;
14
14
  unitTestRunner?: 'jest' | 'vitest' | 'none';
15
+ bundler?: 'rspack' | 'webpack';
15
16
  }
@@ -76,6 +76,12 @@
76
76
  "host": {
77
77
  "type": "string",
78
78
  "description": "The host / shell application for this remote."
79
+ },
80
+ "bundler": {
81
+ "description": "The bundler to use.",
82
+ "type": "string",
83
+ "enum": ["rspack", "webpack"],
84
+ "default": "rspack"
79
85
  }
80
86
  },
81
87
  "required": ["name", "path", "remote"],