@nx/react 19.7.0-canary.20240906-a3c2db8 → 19.7.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.
Files changed (97) hide show
  1. package/executors.json +5 -0
  2. package/package.json +8 -6
  3. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.d.ts +1 -14
  4. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.js +2 -60
  5. package/src/executors/module-federation-dev-server/schema.d.ts +17 -0
  6. package/src/executors/module-federation-static-server/module-federation-static-server.impl.d.ts +12 -0
  7. package/src/executors/module-federation-static-server/module-federation-static-server.impl.js +240 -0
  8. package/src/executors/module-federation-static-server/schema.d.ts +3 -0
  9. package/src/executors/module-federation-static-server/schema.json +14 -0
  10. package/src/generators/application/application.js +4 -1
  11. package/src/generators/application/lib/add-routing.js +4 -1
  12. package/src/generators/application/lib/create-application-files.js +5 -2
  13. package/src/generators/federate-module/federate-module.js +5 -1
  14. package/src/generators/federate-module/schema.d.ts +1 -0
  15. package/src/generators/federate-module/schema.json +6 -0
  16. package/src/generators/host/files/rspack-common/src/app/__fileName__.jsx__tmpl__ +33 -0
  17. package/src/generators/host/files/rspack-common/src/main.jsx__tmpl__ +10 -0
  18. package/src/generators/host/files/rspack-module-federation/rspack.config.js__tmpl__ +16 -0
  19. package/src/generators/host/files/rspack-module-federation/rspack.config.prod.js__tmpl__ +36 -0
  20. package/src/generators/host/files/rspack-module-federation-ssr/module-federation.server.config.js__tmpl__ +16 -0
  21. package/src/generators/host/files/rspack-module-federation-ssr/rspack.server.config.js__tmpl__ +16 -0
  22. package/src/generators/host/files/rspack-module-federation-ssr-ts/module-federation.server.config.ts__tmpl__ +13 -0
  23. package/src/generators/host/files/rspack-module-federation-ssr-ts/rspack.server.config.ts__tmpl__ +16 -0
  24. package/src/generators/host/files/rspack-module-federation-ts/module-federation.config.ts__tmpl__ +25 -0
  25. package/src/generators/host/files/rspack-module-federation-ts/rspack.config.prod.ts__tmpl__ +36 -0
  26. package/src/generators/host/files/rspack-module-federation-ts/rspack.config.ts__tmpl__ +16 -0
  27. package/src/generators/host/files/webpack-module-federation/module-federation.config.js__tmpl__ +21 -0
  28. package/src/generators/host/files/webpack-module-federation-ssr/server.ts__tmpl__ +28 -0
  29. package/src/generators/host/files/webpack-module-federation-ssr/tsconfig.server.json__tmpl__ +17 -0
  30. package/src/generators/host/files/webpack-module-federation-ssr-ts/server.ts__tmpl__ +28 -0
  31. package/src/generators/host/files/webpack-module-federation-ssr-ts/tsconfig.server.json__tmpl__ +17 -0
  32. package/src/generators/host/host.js +10 -3
  33. package/src/generators/host/lib/add-module-federation-files.js +23 -11
  34. package/src/generators/host/lib/setup-ssr-for-host.js +6 -3
  35. package/src/generators/host/schema.d.ts +1 -0
  36. package/src/generators/host/schema.json +8 -0
  37. package/src/generators/remote/files/rspack-common/src/main.jsx__tmpl__ +1 -0
  38. package/src/generators/remote/files/rspack-common/src/remote-entry.js__tmpl__ +1 -0
  39. package/src/generators/remote/files/rspack-module-federation/rspack.config.js__tmpl__ +16 -0
  40. package/src/generators/remote/files/rspack-module-federation/rspack.config.prod.js__tmpl__ +1 -0
  41. package/src/generators/remote/files/rspack-module-federation-ssr/module-federation.server.config.js__tmpl__ +6 -0
  42. package/src/generators/remote/files/rspack-module-federation-ssr/rspack.server.config.js__tmpl__ +16 -0
  43. package/src/generators/remote/files/rspack-module-federation-ssr-ts/module-federation.server.config.ts__tmpl__ +10 -0
  44. package/src/generators/remote/files/rspack-module-federation-ssr-ts/rspack.server.config.ts__tmpl__ +16 -0
  45. package/src/generators/remote/files/rspack-module-federation-ssr-ts/tsconfig.lint.json__tmpl__ +19 -0
  46. package/src/generators/remote/files/rspack-module-federation-ts/module-federation.config.ts__tmpl__ +13 -0
  47. package/src/generators/remote/files/rspack-module-federation-ts/rspack.config.prod.ts__tmpl__ +1 -0
  48. package/src/generators/remote/files/rspack-module-federation-ts/rspack.config.ts__tmpl__ +16 -0
  49. package/src/generators/remote/files/rspack-module-federation-ts/tsconfig.lint.json__tmpl__ +19 -0
  50. package/src/generators/remote/files/webpack-module-federation/module-federation.config.js__tmpl__ +9 -0
  51. package/src/generators/remote/files/webpack-module-federation-ssr/server.ts__tmpl__ +45 -0
  52. package/src/generators/remote/files/webpack-module-federation-ssr-ts/server.ts__tmpl__ +45 -0
  53. package/src/generators/remote/files/webpack-module-federation-ts/tsconfig.lint.json__tmpl__ +19 -0
  54. package/src/generators/remote/lib/setup-ssr-for-remote.js +2 -2
  55. package/src/generators/remote/lib/update-host-with-remote.js +4 -0
  56. package/src/generators/remote/remote.js +24 -11
  57. package/src/generators/remote/schema.d.ts +1 -0
  58. package/src/generators/remote/schema.json +8 -0
  59. package/src/generators/setup-ssr/schema.d.ts +1 -0
  60. package/src/generators/setup-ssr/schema.json +6 -0
  61. package/src/generators/setup-ssr/setup-ssr.js +11 -3
  62. package/src/module-federation/utils.js +1 -1
  63. package/src/module-federation/with-module-federation.js +2 -11
  64. package/src/rules/update-module-federation-project.d.ts +3 -2
  65. package/src/rules/update-module-federation-project.js +54 -23
  66. package/src/utils/build-static.remotes.d.ts +4 -0
  67. package/src/utils/build-static.remotes.js +65 -0
  68. package/src/utils/maybe-js.d.ts +1 -0
  69. package/src/utils/maybe-js.js +1 -1
  70. /package/src/generators/{remote/files/module-federation-ssr-ts → host/files/rspack-common}/tsconfig.lint.json__tmpl__ +0 -0
  71. /package/src/generators/host/files/{module-federation → rspack-module-federation}/module-federation.config.js__tmpl__ +0 -0
  72. /package/src/generators/host/files/{module-federation-ssr → rspack-module-federation-ssr}/server.ts__tmpl__ +0 -0
  73. /package/src/generators/host/files/{module-federation-ssr → rspack-module-federation-ssr}/tsconfig.server.json__tmpl__ +0 -0
  74. /package/src/generators/host/files/{module-federation-ssr-ts → rspack-module-federation-ssr-ts}/server.ts__tmpl__ +0 -0
  75. /package/src/generators/host/files/{module-federation-ssr-ts → rspack-module-federation-ssr-ts}/tsconfig.server.json__tmpl__ +0 -0
  76. /package/src/generators/host/files/{module-federation → webpack-module-federation}/webpack.config.js__tmpl__ +0 -0
  77. /package/src/generators/host/files/{module-federation → webpack-module-federation}/webpack.config.prod.js__tmpl__ +0 -0
  78. /package/src/generators/host/files/{module-federation-ssr → webpack-module-federation-ssr}/module-federation.server.config.js__tmpl__ +0 -0
  79. /package/src/generators/host/files/{module-federation-ssr → webpack-module-federation-ssr}/webpack.server.config.js__tmpl__ +0 -0
  80. /package/src/generators/host/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/module-federation.server.config.ts__tmpl__ +0 -0
  81. /package/src/generators/host/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/webpack.server.config.ts__tmpl__ +0 -0
  82. /package/src/generators/host/files/{module-federation-ts → webpack-module-federation-ts}/module-federation.config.ts__tmpl__ +0 -0
  83. /package/src/generators/host/files/{module-federation-ts → webpack-module-federation-ts}/webpack.config.prod.ts__tmpl__ +0 -0
  84. /package/src/generators/host/files/{module-federation-ts → webpack-module-federation-ts}/webpack.config.ts__tmpl__ +0 -0
  85. /package/src/generators/remote/files/{module-federation → rspack-module-federation}/module-federation.config.js__tmpl__ +0 -0
  86. /package/src/generators/remote/files/{module-federation-ssr → rspack-module-federation-ssr}/server.ts__tmpl__ +0 -0
  87. /package/src/generators/remote/files/{module-federation-ssr-ts → rspack-module-federation-ssr-ts}/server.ts__tmpl__ +0 -0
  88. /package/src/generators/remote/files/{module-federation → webpack-module-federation}/webpack.config.js__tmpl__ +0 -0
  89. /package/src/generators/remote/files/{module-federation → webpack-module-federation}/webpack.config.prod.js__tmpl__ +0 -0
  90. /package/src/generators/remote/files/{module-federation-ssr → webpack-module-federation-ssr}/module-federation.server.config.js__tmpl__ +0 -0
  91. /package/src/generators/remote/files/{module-federation-ssr → webpack-module-federation-ssr}/webpack.server.config.js__tmpl__ +0 -0
  92. /package/src/generators/remote/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/module-federation.server.config.ts__tmpl__ +0 -0
  93. /package/src/generators/remote/files/{module-federation-ts → webpack-module-federation-ssr-ts}/tsconfig.lint.json__tmpl__ +0 -0
  94. /package/src/generators/remote/files/{module-federation-ssr-ts → webpack-module-federation-ssr-ts}/webpack.server.config.ts__tmpl__ +0 -0
  95. /package/src/generators/remote/files/{module-federation-ts → webpack-module-federation-ts}/module-federation.config.ts__tmpl__ +0 -0
  96. /package/src/generators/remote/files/{module-federation-ts → webpack-module-federation-ts}/webpack.config.prod.ts__tmpl__ +0 -0
  97. /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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "19.7.0-canary.20240906-a3c2db8",
3
+ "version": "19.7.0",
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": {
@@ -39,11 +39,13 @@
39
39
  "minimatch": "9.0.3",
40
40
  "tslib": "^2.3.0",
41
41
  "@module-federation/enhanced": "~0.6.0",
42
- "@nx/devkit": "19.7.0-canary.20240906-a3c2db8",
43
- "@nx/js": "19.7.0-canary.20240906-a3c2db8",
44
- "@nx/eslint": "19.7.0-canary.20240906-a3c2db8",
45
- "@nx/web": "19.7.0-canary.20240906-a3c2db8",
46
- "@nrwl/react": "19.7.0-canary.20240906-a3c2db8"
42
+ "@nx/devkit": "19.7.0",
43
+ "@nx/js": "19.7.0",
44
+ "@nx/eslint": "19.7.0",
45
+ "@nx/web": "19.7.0",
46
+ "express": "^4.19.2",
47
+ "http-proxy-middleware": "^3.0.0",
48
+ "@nrwl/react": "19.7.0"
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
+ };
@@ -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
+ }
@@ -153,7 +153,10 @@ async function applicationGeneratorInternal(host, schema) {
153
153
  const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/rspack', versions_1.nxRspackVersion);
154
154
  const rspackTask = await configurationGenerator(host, {
155
155
  project: options.projectName,
156
- main: (0, devkit_1.joinPathFragments)(options.appProjectRoot, (0, maybe_js_1.maybeJs)(options, `src/main.tsx`)),
156
+ main: (0, devkit_1.joinPathFragments)(options.appProjectRoot, (0, maybe_js_1.maybeJs)({
157
+ js: options.js,
158
+ useJsx: true,
159
+ }, `src/main.tsx`)),
157
160
  tsConfig: (0, devkit_1.joinPathFragments)(options.appProjectRoot, 'tsconfig.app.json'),
158
161
  target: 'web',
159
162
  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"],
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ <% if (!minimal) { %>
3
+ import NxWelcome from "./nx-welcome";
4
+ <% } %>
5
+ import { Link, Route, Routes } from 'react-router-dom';
6
+
7
+ <% if (remotes.length > 0) { %>
8
+ <% remotes.forEach(function(r) { %>
9
+ const <%= r.className %> = React.lazy(() => import('<%= r.fileName %>/Module'));
10
+ <% }); %>
11
+ <% } %>
12
+ export function App() {
13
+ return (
14
+ <React.Suspense fallback={null}>
15
+ <ul>
16
+ <li><Link to="/">Home</Link></li>
17
+ <% remotes.forEach(function(r) { %>
18
+ <li><Link to="/<%=r.fileName%>"><%=r.className%></Link></li>
19
+ <% }); %>
20
+ </ul>
21
+ <Routes>
22
+ <% if (!minimal) { %>
23
+ <Route path="/" element={<NxWelcome title="<%= projectName %>"/>} />
24
+ <% } %>
25
+ <% remotes.forEach(function(r) { %>
26
+ <Route path="/<%=r.fileName%>" element={<<%= r.className %>/>} />
27
+ <% }); %>
28
+ </Routes>
29
+ </React.Suspense>
30
+ );
31
+ }
32
+
33
+ export default App;
@@ -0,0 +1,10 @@
1
+ <% if (dynamic) { %>
2
+ import { setRemoteDefinitions } from '@nx/react/mf';
3
+
4
+ fetch('/assets/module-federation.manifest.json')
5
+ .then((res) => res.json())
6
+ .then(definitions => setRemoteDefinitions(definitions))
7
+ .then(() => import('./bootstrap').catch(err => console.error(err)));
8
+ <% } else { %>
9
+ import('./bootstrap').catch(err => console.error(err));
10
+ <% } %>
@@ -0,0 +1,16 @@
1
+ const { composePlugins, withNx, withReact } = require('@nx/rspack');
2
+ const { withModuleFederation } = require('@nx/rspack/module-federation');
3
+
4
+ const baseConfig = require('./module-federation.config');
5
+
6
+ const config = {
7
+ ...baseConfig,
8
+ };
9
+
10
+ // Nx plugins for rspack to build config object from Nx options and context.
11
+ /**
12
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
13
+ * The DTS Plugin can be enabled by setting dts: true
14
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
15
+ */
16
+ module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
@@ -0,0 +1,36 @@
1
+ const { composePlugins, withNx, withReact } = require('@nx/rspack');
2
+ const { withModuleFederation } = require('@nx/rspack/module-federation');
3
+
4
+ const baseConfig = require('./module-federation.config');
5
+
6
+ const prodConfig = {
7
+ ...baseConfig,
8
+ /*
9
+ * Remote overrides for production.
10
+ * Each entry is a pair of a unique name and the URL where it is deployed.
11
+ *
12
+ * e.g.
13
+ * remotes: [
14
+ * ['app1', 'http://app1.example.com'],
15
+ * ['app2', 'http://app2.example.com'],
16
+ * ]
17
+ *
18
+ * You can also use a full path to the remoteEntry.js file if desired.
19
+ *
20
+ * remotes: [
21
+ * ['app1', 'http://example.com/path/to/app1/remoteEntry.js'],
22
+ * ['app2', 'http://example.com/path/to/app2/remoteEntry.js'],
23
+ * ]
24
+ */
25
+ remotes: [
26
+ <% remotes.forEach(function(r) {%>['<%= r.fileName %>', 'http://localhost:<%= r.port %>/'],<% }); %>
27
+ ],
28
+ };
29
+
30
+ // Nx plugins for rspack to build config object from Nx options and context.
31
+ /**
32
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
33
+ * The DTS Plugin can be enabled by setting dts: true
34
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
35
+ */
36
+ module.exports = composePlugins(withNx(), withReact(), withModuleFederation(prodConfig, { dts: false }));
@@ -0,0 +1,16 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @type {import('@nx/rspack/module-federation').ModuleFederationConfig}
5
+ **/
6
+ const moduleFederationConfig = {
7
+ name: '<%= projectName %>',
8
+ remotes: [
9
+ <% if (static) {
10
+ remotes.forEach(function(r) { %> "<%= r.fileName %>", <% });
11
+ }
12
+ %>
13
+ ],
14
+ };
15
+
16
+ module.exports = moduleFederationConfig;