@nx/rspack 0.0.0-pr-28218-5048fd1

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 (133) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +117 -0
  3. package/executors.json +35 -0
  4. package/generators.json +31 -0
  5. package/migrations.json +98 -0
  6. package/module-federation.d.ts +1 -0
  7. package/module-federation.js +4 -0
  8. package/package.json +49 -0
  9. package/plugin.d.ts +2 -0
  10. package/plugin.js +6 -0
  11. package/src/executors/dev-server/dev-server.impl.d.ts +6 -0
  12. package/src/executors/dev-server/dev-server.impl.js +50 -0
  13. package/src/executors/dev-server/lib/get-dev-server-config.d.ts +4 -0
  14. package/src/executors/dev-server/lib/get-dev-server-config.js +69 -0
  15. package/src/executors/dev-server/lib/serve-path.d.ts +3 -0
  16. package/src/executors/dev-server/lib/serve-path.js +44 -0
  17. package/src/executors/dev-server/schema.d.ts +12 -0
  18. package/src/executors/dev-server/schema.json +45 -0
  19. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.d.ts +6 -0
  20. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.js +177 -0
  21. package/src/executors/module-federation-dev-server/schema.d.ts +18 -0
  22. package/src/executors/module-federation-dev-server/schema.json +98 -0
  23. package/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.d.ts +21 -0
  24. package/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.js +214 -0
  25. package/src/executors/module-federation-ssr-dev-server/schema.json +79 -0
  26. package/src/executors/module-federation-static-server/module-federation-static-server.impl.d.ts +12 -0
  27. package/src/executors/module-federation-static-server/module-federation-static-server.impl.js +244 -0
  28. package/src/executors/module-federation-static-server/schema.d.ts +3 -0
  29. package/src/executors/module-federation-static-server/schema.json +14 -0
  30. package/src/executors/rspack/rspack.impl.d.ts +6 -0
  31. package/src/executors/rspack/rspack.impl.js +118 -0
  32. package/src/executors/rspack/schema.d.ts +34 -0
  33. package/src/executors/rspack/schema.json +177 -0
  34. package/src/executors/ssr-dev-server/lib/wait-until-server-is-listening.d.ts +1 -0
  35. package/src/executors/ssr-dev-server/lib/wait-until-server-is-listening.js +39 -0
  36. package/src/executors/ssr-dev-server/schema.d.ts +11 -0
  37. package/src/executors/ssr-dev-server/schema.json +36 -0
  38. package/src/executors/ssr-dev-server/ssr-dev-server.impl.d.ts +8 -0
  39. package/src/executors/ssr-dev-server/ssr-dev-server.impl.js +38 -0
  40. package/src/generators/application/application.d.ts +3 -0
  41. package/src/generators/application/application.js +90 -0
  42. package/src/generators/application/lib/create-ts-config.d.ts +3 -0
  43. package/src/generators/application/lib/create-ts-config.js +49 -0
  44. package/src/generators/application/lib/normalize-options.d.ts +5 -0
  45. package/src/generators/application/lib/normalize-options.js +42 -0
  46. package/src/generators/application/schema.d.ts +16 -0
  47. package/src/generators/application/schema.json +98 -0
  48. package/src/generators/configuration/configuration.d.ts +4 -0
  49. package/src/generators/configuration/configuration.js +79 -0
  50. package/src/generators/configuration/schema.d.ts +12 -0
  51. package/src/generators/configuration/schema.json +73 -0
  52. package/src/generators/init/init.d.ts +5 -0
  53. package/src/generators/init/init.js +71 -0
  54. package/src/generators/init/schema.d.ts +11 -0
  55. package/src/generators/init/schema.json +31 -0
  56. package/src/generators/preset/preset.d.ts +3 -0
  57. package/src/generators/preset/preset.js +34 -0
  58. package/src/generators/preset/schema.d.ts +18 -0
  59. package/src/generators/preset/schema.json +71 -0
  60. package/src/index.d.ts +6 -0
  61. package/src/index.js +9 -0
  62. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.d.ts +2 -0
  63. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +9 -0
  64. package/src/plugins/generate-package-json-plugin.d.ts +12 -0
  65. package/src/plugins/generate-package-json-plugin.js +46 -0
  66. package/src/plugins/plugin.d.ts +9 -0
  67. package/src/plugins/plugin.js +149 -0
  68. package/src/utils/config.d.ts +17 -0
  69. package/src/utils/config.js +22 -0
  70. package/src/utils/create-compiler.d.ts +7 -0
  71. package/src/utils/create-compiler.js +33 -0
  72. package/src/utils/generator-utils.d.ts +34 -0
  73. package/src/utils/generator-utils.js +444 -0
  74. package/src/utils/get-copy-patterns.d.ts +9 -0
  75. package/src/utils/get-copy-patterns.js +22 -0
  76. package/src/utils/jest-utils.d.ts +1 -0
  77. package/src/utils/jest-utils.js +8 -0
  78. package/src/utils/mode-utils.d.ts +2 -0
  79. package/src/utils/mode-utils.js +6 -0
  80. package/src/utils/model.d.ts +6 -0
  81. package/src/utils/model.js +2 -0
  82. package/src/utils/module-federation/build-static.remotes.d.ts +4 -0
  83. package/src/utils/module-federation/build-static.remotes.js +69 -0
  84. package/src/utils/module-federation/dependencies.d.ts +6 -0
  85. package/src/utils/module-federation/dependencies.js +56 -0
  86. package/src/utils/module-federation/get-remotes-for-host.d.ts +16 -0
  87. package/src/utils/module-federation/get-remotes-for-host.js +99 -0
  88. package/src/utils/module-federation/index.d.ts +6 -0
  89. package/src/utils/module-federation/index.js +9 -0
  90. package/src/utils/module-federation/models/index.d.ts +47 -0
  91. package/src/utils/module-federation/models/index.js +2 -0
  92. package/src/utils/module-federation/package-json.d.ts +8 -0
  93. package/src/utils/module-federation/package-json.js +12 -0
  94. package/src/utils/module-federation/parse-static-remotes-config.d.ts +13 -0
  95. package/src/utils/module-federation/parse-static-remotes-config.js +34 -0
  96. package/src/utils/module-federation/plugins/runtime-library-control.plugin.d.ts +3 -0
  97. package/src/utils/module-federation/plugins/runtime-library-control.plugin.js +54 -0
  98. package/src/utils/module-federation/public-api.d.ts +8 -0
  99. package/src/utils/module-federation/public-api.js +20 -0
  100. package/src/utils/module-federation/remotes.d.ts +19 -0
  101. package/src/utils/module-federation/remotes.js +77 -0
  102. package/src/utils/module-federation/secondary-entry-points.d.ts +12 -0
  103. package/src/utils/module-federation/secondary-entry-points.js +104 -0
  104. package/src/utils/module-federation/share.d.ts +48 -0
  105. package/src/utils/module-federation/share.js +235 -0
  106. package/src/utils/module-federation/start-remote-proxies.d.ts +5 -0
  107. package/src/utils/module-federation/start-remote-proxies.js +45 -0
  108. package/src/utils/module-federation/start-ssr-remote-proxies.d.ts +5 -0
  109. package/src/utils/module-federation/start-ssr-remote-proxies.js +59 -0
  110. package/src/utils/module-federation/typescript.d.ts +4 -0
  111. package/src/utils/module-federation/typescript.js +53 -0
  112. package/src/utils/module-federation/with-module-federation/package-json.d.ts +8 -0
  113. package/src/utils/module-federation/with-module-federation/package-json.js +12 -0
  114. package/src/utils/module-federation/with-module-federation/utils.d.ts +12 -0
  115. package/src/utils/module-federation/with-module-federation/utils.js +76 -0
  116. package/src/utils/module-federation/with-module-federation/with-module-federation-ssr.d.ts +3 -0
  117. package/src/utils/module-federation/with-module-federation/with-module-federation-ssr.js +55 -0
  118. package/src/utils/module-federation/with-module-federation/with-module-federation.d.ts +8 -0
  119. package/src/utils/module-federation/with-module-federation/with-module-federation.js +70 -0
  120. package/src/utils/normalize-assets.d.ts +1 -0
  121. package/src/utils/normalize-assets.js +42 -0
  122. package/src/utils/read-rspack-options.d.ts +10 -0
  123. package/src/utils/read-rspack-options.js +37 -0
  124. package/src/utils/resolve-user-defined-rspack-config.d.ts +3 -0
  125. package/src/utils/resolve-user-defined-rspack-config.js +38 -0
  126. package/src/utils/versions.d.ts +21 -0
  127. package/src/utils/versions.js +24 -0
  128. package/src/utils/with-nx.d.ts +3 -0
  129. package/src/utils/with-nx.js +187 -0
  130. package/src/utils/with-react.d.ts +3 -0
  131. package/src/utils/with-react.js +69 -0
  132. package/src/utils/with-web.d.ts +9 -0
  133. package/src/utils/with-web.js +106 -0
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = moduleFederationDevServer;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const async_iterable_1 = require("@nx/devkit/src/utils/async-iterable");
7
+ const file_server_impl_1 = tslib_1.__importDefault(require("@nx/web/src/executors/file-server/file-server.impl"));
8
+ const wait_for_port_open_1 = require("@nx/web/src/utils/wait-for-port-open");
9
+ const fs_1 = require("fs");
10
+ const path_1 = require("path");
11
+ const module_federation_1 = require("../../utils/module-federation");
12
+ const build_static_remotes_1 = require("../../utils/module-federation/build-static.remotes");
13
+ const parse_static_remotes_config_1 = require("../../utils/module-federation/parse-static-remotes-config");
14
+ const start_remote_proxies_1 = require("../../utils/module-federation/start-remote-proxies");
15
+ const dev_server_impl_1 = tslib_1.__importDefault(require("../dev-server/dev-server.impl"));
16
+ function getBuildOptions(buildTarget, context) {
17
+ const target = (0, devkit_1.parseTargetString)(buildTarget, context);
18
+ const buildOptions = (0, devkit_1.readTargetOptions)(target, context);
19
+ return {
20
+ ...buildOptions,
21
+ };
22
+ }
23
+ function startStaticRemotesFileServer(staticRemotesConfig, context, options) {
24
+ if (!staticRemotesConfig.remotes ||
25
+ staticRemotesConfig.remotes.length === 0) {
26
+ return;
27
+ }
28
+ let shouldMoveToCommonLocation = false;
29
+ let commonOutputDirectory;
30
+ for (const app of staticRemotesConfig.remotes) {
31
+ const remoteBasePath = staticRemotesConfig.config[app].basePath;
32
+ if (!commonOutputDirectory) {
33
+ commonOutputDirectory = remoteBasePath;
34
+ }
35
+ else if (commonOutputDirectory !== remoteBasePath) {
36
+ shouldMoveToCommonLocation = true;
37
+ break;
38
+ }
39
+ }
40
+ if (shouldMoveToCommonLocation) {
41
+ commonOutputDirectory = (0, path_1.join)(devkit_1.workspaceRoot, 'tmp/static-remotes');
42
+ for (const app of staticRemotesConfig.remotes) {
43
+ const remoteConfig = staticRemotesConfig.config[app];
44
+ (0, fs_1.cpSync)(remoteConfig.outputPath, (0, path_1.join)(commonOutputDirectory, remoteConfig.urlSegment), {
45
+ force: true,
46
+ recursive: true,
47
+ });
48
+ }
49
+ }
50
+ const staticRemotesIter = (0, file_server_impl_1.default)({
51
+ cors: true,
52
+ watch: false,
53
+ staticFilePath: commonOutputDirectory,
54
+ parallel: false,
55
+ spa: false,
56
+ withDeps: false,
57
+ host: options.host,
58
+ port: options.staticRemotesPort,
59
+ ssl: options.ssl,
60
+ sslCert: options.sslCert,
61
+ sslKey: options.sslKey,
62
+ cacheSeconds: -1,
63
+ }, context);
64
+ return staticRemotesIter;
65
+ }
66
+ async function startRemotes(remotes, context, options, target = 'serve') {
67
+ const remoteIters = [];
68
+ for (const app of remotes) {
69
+ const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets[target];
70
+ const isUsingModuleFederationDevServerExecutor = remoteProjectServeTarget.executor.includes('module-federation-dev-server');
71
+ const configurationOverride = options.devRemotes?.find((r) => typeof r !== 'string' && r.remoteName === app)?.configuration;
72
+ const defaultOverrides = {
73
+ ...(options.host ? { host: options.host } : {}),
74
+ ...(options.ssl ? { ssl: options.ssl } : {}),
75
+ ...(options.sslCert ? { sslCert: options.sslCert } : {}),
76
+ ...(options.sslKey ? { sslKey: options.sslKey } : {}),
77
+ };
78
+ const overrides = target === 'serve'
79
+ ? {
80
+ watch: true,
81
+ ...(isUsingModuleFederationDevServerExecutor
82
+ ? { isInitialHost: false }
83
+ : {}),
84
+ ...defaultOverrides,
85
+ }
86
+ : { ...defaultOverrides };
87
+ remoteIters.push(await (0, devkit_1.runExecutor)({
88
+ project: app,
89
+ target,
90
+ configuration: configurationOverride ?? context.configurationName,
91
+ }, overrides, context));
92
+ }
93
+ return remoteIters;
94
+ }
95
+ async function* moduleFederationDevServer(options, context) {
96
+ // Force Node to resolve to look for the nx binary that is inside node_modules
97
+ const nxBin = require.resolve('nx/bin/nx');
98
+ const currIter = options.static
99
+ ? (0, file_server_impl_1.default)({
100
+ ...options,
101
+ parallel: false,
102
+ withDeps: false,
103
+ spa: false,
104
+ cors: true,
105
+ cacheSeconds: -1,
106
+ }, context)
107
+ : (0, dev_server_impl_1.default)(options, context);
108
+ const p = context.projectsConfigurations.projects[context.projectName];
109
+ const buildOptions = getBuildOptions(options.buildTarget, context);
110
+ let pathToManifestFile = (0, path_1.join)(context.root, p.sourceRoot, 'assets/module-federation.manifest.json');
111
+ if (options.pathToManifestFile) {
112
+ const userPathToManifestFile = (0, path_1.join)(context.root, options.pathToManifestFile);
113
+ if (!(0, fs_1.existsSync)(userPathToManifestFile)) {
114
+ throw new Error(`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`);
115
+ }
116
+ else if ((0, path_1.extname)(options.pathToManifestFile) !== '.json') {
117
+ throw new Error(`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`);
118
+ }
119
+ pathToManifestFile = userPathToManifestFile;
120
+ }
121
+ if (!options.isInitialHost) {
122
+ return yield* currIter;
123
+ }
124
+ const moduleFederationConfig = (0, module_federation_1.getModuleFederationConfig)(buildOptions.tsConfig, context.root, p.root, 'react');
125
+ const remoteNames = options.devRemotes?.map((r) => typeof r === 'string' ? r : r.remoteName);
126
+ const remotes = (0, module_federation_1.getRemotes)(remoteNames, options.skipRemotes, moduleFederationConfig, {
127
+ projectName: context.projectName,
128
+ projectGraph: context.projectGraph,
129
+ root: context.root,
130
+ }, pathToManifestFile);
131
+ options.staticRemotesPort ??= remotes.staticRemotePort;
132
+ // Set NX_MF_DEV_REMOTES for the Nx Runtime Library Control Plugin
133
+ process.env.NX_MF_DEV_REMOTES = JSON.stringify([
134
+ ...(remotes.devRemotes.map((r) => typeof r === 'string' ? r : r.remoteName) ?? []),
135
+ p.name,
136
+ ]);
137
+ const staticRemotesConfig = (0, parse_static_remotes_config_1.parseStaticRemotesConfig)([...remotes.staticRemotes, ...remotes.dynamicRemotes], context);
138
+ const mappedLocationsOfStaticRemotes = await (0, build_static_remotes_1.buildStaticRemotes)(staticRemotesConfig, nxBin, context, options);
139
+ const devRemoteIters = await startRemotes(remotes.devRemotes, context, options, 'serve');
140
+ const staticRemotesIter = startStaticRemotesFileServer(staticRemotesConfig, context, options);
141
+ (0, start_remote_proxies_1.startRemoteProxies)(staticRemotesConfig, mappedLocationsOfStaticRemotes, options.ssl
142
+ ? {
143
+ pathToCert: (0, path_1.join)(devkit_1.workspaceRoot, options.sslCert),
144
+ pathToKey: (0, path_1.join)(devkit_1.workspaceRoot, options.sslKey),
145
+ }
146
+ : undefined);
147
+ return yield* (0, async_iterable_1.combineAsyncIterables)(currIter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
148
+ if (!options.isInitialHost) {
149
+ done();
150
+ return;
151
+ }
152
+ if (remotes.remotePorts.length === 0) {
153
+ done();
154
+ return;
155
+ }
156
+ try {
157
+ const host = options.host ?? 'localhost';
158
+ const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${options.port}`;
159
+ const portsToWaitFor = staticRemotesIter
160
+ ? [options.staticRemotesPort, ...remotes.remotePorts]
161
+ : [...remotes.remotePorts];
162
+ await Promise.all(portsToWaitFor.map((port) => (0, wait_for_port_open_1.waitForPortOpen)(port, {
163
+ retries: 480,
164
+ retryDelay: 2500,
165
+ host: host,
166
+ })));
167
+ devkit_1.logger.info(`NX All remotes started, server ready at ${baseUrl}`);
168
+ next({ success: true, baseUrl: baseUrl });
169
+ }
170
+ catch (err) {
171
+ throw new Error(`Failed to start remotes. Check above for any errors.`);
172
+ }
173
+ finally {
174
+ done();
175
+ }
176
+ }));
177
+ }
@@ -0,0 +1,18 @@
1
+ import { DevServerExecutorSchema } from '../dev-server/schema';
2
+
3
+ export type ModuleFederationDevServerOptions = DevServerExecutorSchema & {
4
+ // Module Federation Specific Options
5
+ devRemotes?: (
6
+ | string
7
+ | {
8
+ remoteName: string;
9
+ configuration: string;
10
+ }
11
+ )[];
12
+ skipRemotes?: string[];
13
+ static?: boolean;
14
+ isInitialHost?: boolean;
15
+ parallel?: number;
16
+ staticRemotesPort?: number;
17
+ pathToManifestFile?: string;
18
+ };
@@ -0,0 +1,98 @@
1
+ {
2
+ "version": 2,
3
+ "outputCapture": "direct-nodejs",
4
+ "title": "Rspack Module Federation Dev Server",
5
+ "description": "Serve a module federation application.",
6
+ "cli": "nx",
7
+ "type": "object",
8
+ "properties": {
9
+ "devRemotes": {
10
+ "type": "array",
11
+ "items": {
12
+ "oneOf": [
13
+ {
14
+ "type": "string"
15
+ },
16
+ {
17
+ "type": "object",
18
+ "properties": {
19
+ "remoteName": {
20
+ "type": "string"
21
+ },
22
+ "configuration": {
23
+ "type": "string"
24
+ }
25
+ },
26
+ "required": ["remoteName"],
27
+ "additionalProperties": false
28
+ }
29
+ ]
30
+ },
31
+ "description": "List of remote applications to run in development mode (i.e. using serve target).",
32
+ "x-priority": "important"
33
+ },
34
+ "skipRemotes": {
35
+ "type": "array",
36
+ "items": {
37
+ "type": "string"
38
+ },
39
+ "description": "List of remote applications to not automatically serve, either statically or in development mode. This will not remove the remotes from the `module-federation.config` file, and therefore the application may still try to fetch these remotes.\nThis option is useful if you have other means for serving the `remote` application(s).\n**NOTE:** Remotes that are not in the workspace will be skipped automatically.",
40
+ "x-priority": "important"
41
+ },
42
+ "buildTarget": {
43
+ "type": "string",
44
+ "description": "Target which builds the application.",
45
+ "x-priority": "important"
46
+ },
47
+ "port": {
48
+ "type": "number",
49
+ "description": "Port to listen on.",
50
+ "default": 4200,
51
+ "x-priority": "important"
52
+ },
53
+ "host": {
54
+ "type": "string",
55
+ "description": "Host to listen on.",
56
+ "default": "localhost"
57
+ },
58
+ "ssl": {
59
+ "type": "boolean",
60
+ "description": "Serve using `HTTPS`.",
61
+ "default": false
62
+ },
63
+ "sslKey": {
64
+ "type": "string",
65
+ "description": "SSL key to use for serving `HTTPS`."
66
+ },
67
+ "sslCert": {
68
+ "type": "string",
69
+ "description": "SSL certificate to use for serving `HTTPS`."
70
+ },
71
+ "publicHost": {
72
+ "type": "string",
73
+ "description": "Public URL where the application will be served."
74
+ },
75
+ "static": {
76
+ "type": "boolean",
77
+ "description": "Whether to use a static file server instead of the rspack-dev-server. This should be used for remote applications that are also host applications."
78
+ },
79
+ "isInitialHost": {
80
+ "type": "boolean",
81
+ "description": "Whether the host that is running this executor is the first in the project tree to do so.",
82
+ "default": true,
83
+ "x-priority": "internal"
84
+ },
85
+ "parallel": {
86
+ "type": "number",
87
+ "description": "Max number of parallel processes for building static remotes"
88
+ },
89
+ "staticRemotesPort": {
90
+ "type": "number",
91
+ "description": "The port at which to serve the file-server for the static remotes."
92
+ },
93
+ "pathToManifestFile": {
94
+ "type": "string",
95
+ "description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,21 @@
1
+ import { ExecutorContext } from '@nx/devkit';
2
+ import { RspackSsrDevServerOptions } from '../ssr-dev-server/schema';
3
+ type ModuleFederationSsrDevServerOptions = RspackSsrDevServerOptions & {
4
+ devRemotes?: (string | {
5
+ remoteName: string;
6
+ configuration: string;
7
+ })[];
8
+ skipRemotes?: string[];
9
+ host: string;
10
+ pathToManifestFile?: string;
11
+ staticRemotesPort?: number;
12
+ parallel?: number;
13
+ ssl?: boolean;
14
+ sslKey?: string;
15
+ sslCert?: string;
16
+ isInitialHost?: boolean;
17
+ };
18
+ export default function moduleFederationSsrDevServer(ssrDevServerOptions: ModuleFederationSsrDevServerOptions, context: ExecutorContext): AsyncGenerator<{
19
+ success: boolean;
20
+ }, any, unknown>;
21
+ export {};
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = moduleFederationSsrDevServer;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const path_1 = require("path");
7
+ const module_federation_1 = require("../../utils/module-federation");
8
+ const ssr_dev_server_impl_1 = tslib_1.__importDefault(require("../ssr-dev-server/ssr-dev-server.impl"));
9
+ const async_iterable_1 = require("@nx/devkit/src/utils/async-iterable");
10
+ const child_process_1 = require("child_process");
11
+ const fs_1 = require("fs");
12
+ const parse_static_remotes_config_1 = require("../../utils/module-federation/parse-static-remotes-config");
13
+ const file_server_impl_1 = tslib_1.__importDefault(require("@nx/web/src/executors/file-server/file-server.impl"));
14
+ const wait_for_port_open_1 = require("@nx/web/src/utils/wait-for-port-open");
15
+ const cache_directory_1 = require("nx/src/utils/cache-directory");
16
+ const start_ssr_remote_proxies_1 = require("../../utils/module-federation/start-ssr-remote-proxies");
17
+ function normalizeOptions(options) {
18
+ return {
19
+ ...options,
20
+ ssl: options.ssl ?? false,
21
+ sslCert: options.sslCert ? (0, path_1.join)(devkit_1.workspaceRoot, options.sslCert) : undefined,
22
+ sslKey: options.sslKey ? (0, path_1.join)(devkit_1.workspaceRoot, options.sslKey) : undefined,
23
+ };
24
+ }
25
+ function getBuildOptions(buildTarget, context) {
26
+ const target = (0, devkit_1.parseTargetString)(buildTarget, context);
27
+ const buildOptions = (0, devkit_1.readTargetOptions)(target, context);
28
+ return {
29
+ ...buildOptions,
30
+ };
31
+ }
32
+ function startSsrStaticRemotesFileServer(ssrStaticRemotesConfig, context, options) {
33
+ if (ssrStaticRemotesConfig.remotes.length === 0) {
34
+ return;
35
+ }
36
+ // The directories are usually generated with /browser and /server suffixes so we need to copy them to a common directory
37
+ const commonOutputDirectory = (0, path_1.join)(devkit_1.workspaceRoot, 'tmp/static-remotes');
38
+ for (const app of ssrStaticRemotesConfig.remotes) {
39
+ const remoteConfig = ssrStaticRemotesConfig.config[app];
40
+ (0, fs_1.cpSync)(remoteConfig.outputPath, (0, path_1.join)(commonOutputDirectory, remoteConfig.urlSegment), {
41
+ force: true,
42
+ recursive: true,
43
+ });
44
+ }
45
+ const staticRemotesIter = (0, file_server_impl_1.default)({
46
+ cors: true,
47
+ watch: false,
48
+ staticFilePath: commonOutputDirectory,
49
+ parallel: false,
50
+ spa: false,
51
+ withDeps: false,
52
+ host: options.host,
53
+ port: options.staticRemotesPort,
54
+ ssl: options.ssl,
55
+ sslCert: options.sslCert,
56
+ sslKey: options.sslKey,
57
+ cacheSeconds: -1,
58
+ }, context);
59
+ return staticRemotesIter;
60
+ }
61
+ async function startRemotes(remotes, context, options) {
62
+ const remoteIters = [];
63
+ const target = 'serve';
64
+ for (const app of remotes) {
65
+ const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets[target];
66
+ const isUsingModuleFederationSsrDevServerExecutor = remoteProjectServeTarget.executor.includes('module-federation-ssr-dev-server');
67
+ const configurationOverride = options.devRemotes?.find((remote) => typeof remote !== 'string' && remote.remoteName === app)?.configuration;
68
+ {
69
+ const defaultOverrides = {
70
+ ...(options.host ? { host: options.host } : {}),
71
+ ...(options.ssl ? { ssl: options.ssl } : {}),
72
+ ...(options.sslCert ? { sslCert: options.sslCert } : {}),
73
+ ...(options.sslKey ? { sslKey: options.sslKey } : {}),
74
+ };
75
+ const overrides = {
76
+ watch: true,
77
+ ...defaultOverrides,
78
+ ...(isUsingModuleFederationSsrDevServerExecutor
79
+ ? { isInitialHost: false }
80
+ : {}),
81
+ };
82
+ remoteIters.push(await (0, devkit_1.runExecutor)({
83
+ project: app,
84
+ target,
85
+ configuration: configurationOverride ?? context.configurationName,
86
+ }, overrides, context));
87
+ }
88
+ }
89
+ return remoteIters;
90
+ }
91
+ async function buildSsrStaticRemotes(staticRemotesConfig, nxBin, context, options) {
92
+ if (!staticRemotesConfig.remotes.length) {
93
+ return;
94
+ }
95
+ devkit_1.logger.info(`Nx is building ${staticRemotesConfig.remotes.length} static remotes...`);
96
+ const mapLocationOfRemotes = {};
97
+ for (const remoteApp of staticRemotesConfig.remotes) {
98
+ mapLocationOfRemotes[remoteApp] = `http${options.ssl ? 's' : ''}://${options.host}:${options.staticRemotesPort}/${staticRemotesConfig.config[remoteApp].urlSegment}`;
99
+ }
100
+ await new Promise((resolve) => {
101
+ const childProcess = (0, child_process_1.fork)(nxBin, [
102
+ 'run-many',
103
+ '--target=server',
104
+ '--projects',
105
+ staticRemotesConfig.remotes.join(','),
106
+ ...(context.configurationName
107
+ ? [`--configuration=${context.configurationName}`]
108
+ : []),
109
+ ...(options.parallel ? [`--parallel=${options.parallel}`] : []),
110
+ ], {
111
+ cwd: context.root,
112
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
113
+ });
114
+ // Add a listener to the child process to capture the build log
115
+ const remoteBuildLogFile = (0, path_1.join)(cache_directory_1.workspaceDataDirectory,
116
+ // eslint-disable-next-line
117
+ `${new Date().toISOString().replace(/[:\.]/g, '_')}-build.log`);
118
+ const remoteBuildLogStream = (0, fs_1.createWriteStream)(remoteBuildLogFile);
119
+ childProcess.stdout.on('data', (data) => {
120
+ const ANSII_CODE_REGEX =
121
+ // eslint-disable-next-line no-control-regex
122
+ /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
123
+ const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
124
+ remoteBuildLogStream.write(stdoutString);
125
+ // in addition to writing into the stdout stream, also show error directly in console
126
+ // so the error is easily discoverable. 'ERROR in' is the key word to search in webpack output.
127
+ if (stdoutString.includes('ERROR in')) {
128
+ devkit_1.logger.log(stdoutString);
129
+ }
130
+ if (stdoutString.includes('Successfully ran target server')) {
131
+ childProcess.stdout.removeAllListeners('data');
132
+ devkit_1.logger.info(`Nx Built ${staticRemotesConfig.remotes.length} static remotes.`);
133
+ resolve();
134
+ }
135
+ });
136
+ process.on('SIGTERM', () => childProcess.kill('SIGTERM'));
137
+ process.on('exit', () => childProcess.kill('SIGTERM'));
138
+ });
139
+ return mapLocationOfRemotes;
140
+ }
141
+ async function* moduleFederationSsrDevServer(ssrDevServerOptions, context) {
142
+ const options = normalizeOptions(ssrDevServerOptions);
143
+ // Force Node to resolve to look for the nx binary that is inside node_modules
144
+ const nxBin = require.resolve('nx/bin/nx');
145
+ const iter = (0, ssr_dev_server_impl_1.default)(options, context);
146
+ const projectConfig = context.projectsConfigurations.projects[context.projectName];
147
+ const buildOptions = getBuildOptions(options.browserTarget, context);
148
+ let pathToManifestFile = (0, path_1.join)(context.root, projectConfig.sourceRoot, 'assets/module-federation.manifest.json');
149
+ if (options.pathToManifestFile) {
150
+ const userPathToManifestFile = (0, path_1.join)(context.root, options.pathToManifestFile);
151
+ if (!(0, fs_1.existsSync)(userPathToManifestFile)) {
152
+ throw new Error(`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`);
153
+ }
154
+ else if ((0, path_1.extname)(userPathToManifestFile) !== '.json') {
155
+ throw new Error(`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`);
156
+ }
157
+ pathToManifestFile = userPathToManifestFile;
158
+ }
159
+ if (!options.isInitialHost) {
160
+ return yield* iter;
161
+ }
162
+ const moduleFederationConfig = (0, module_federation_1.getModuleFederationConfig)(buildOptions.tsConfig, context.root, projectConfig.root, 'react');
163
+ const remoteNames = options.devRemotes?.map((remote) => typeof remote === 'string' ? remote : remote.remoteName);
164
+ const remotes = (0, module_federation_1.getRemotes)(remoteNames, options.skipRemotes, moduleFederationConfig, {
165
+ projectName: context.projectName,
166
+ projectGraph: context.projectGraph,
167
+ root: context.root,
168
+ }, pathToManifestFile);
169
+ options.staticRemotesPort ??= remotes.staticRemotePort;
170
+ process.env.NX_MF_DEV_REMOTES = JSON.stringify([
171
+ ...(remotes.devRemotes.map((r) => typeof r === 'string' ? r : r.remoteName) ?? []),
172
+ projectConfig.name,
173
+ ]);
174
+ const staticRemotesConfig = (0, parse_static_remotes_config_1.parseStaticSsrRemotesConfig)([...remotes.staticRemotes, ...remotes.dynamicRemotes], context);
175
+ const mappedLocationsOfStaticRemotes = await buildSsrStaticRemotes(staticRemotesConfig, nxBin, context, options);
176
+ const devRemoteIters = await startRemotes(remotes.devRemotes, context, options);
177
+ const staticRemotesIter = startSsrStaticRemotesFileServer(staticRemotesConfig, context, options);
178
+ (0, start_ssr_remote_proxies_1.startSsrRemoteProxies)(staticRemotesConfig, mappedLocationsOfStaticRemotes, options.ssl
179
+ ? {
180
+ pathToCert: options.sslCert,
181
+ pathToKey: options.sslKey,
182
+ }
183
+ : undefined);
184
+ return yield* (0, async_iterable_1.combineAsyncIterables)(iter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
185
+ if (!options.isInitialHost) {
186
+ done();
187
+ return;
188
+ }
189
+ if (remotes.remotePorts.length === 0) {
190
+ done();
191
+ return;
192
+ }
193
+ try {
194
+ const host = options.host ?? 'localhost';
195
+ const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${options.port}`;
196
+ const portsToWaitFor = staticRemotesIter
197
+ ? [options.staticRemotesPort, ...remotes.remotePorts]
198
+ : [...remotes.remotePorts];
199
+ await Promise.all(portsToWaitFor.map((port) => (0, wait_for_port_open_1.waitForPortOpen)(port, {
200
+ retries: 480,
201
+ retryDelay: 2500,
202
+ host,
203
+ })));
204
+ devkit_1.logger.info(`Nx all ssr remotes have started, server ready at ${baseUrl}`);
205
+ next({ success: true, baseUrl });
206
+ }
207
+ catch (error) {
208
+ throw new Error(`Nx failed to start ssr remotes. Check above for errors.`);
209
+ }
210
+ finally {
211
+ done();
212
+ }
213
+ }));
214
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "version": 2,
3
+ "outputCapture": "direct-nodejs",
4
+ "title": "Module Federation SSR Dev Server",
5
+ "description": "Serve a SSR host application along with its known remotes.",
6
+ "cli": "nx",
7
+ "type": "object",
8
+ "properties": {
9
+ "browserTarget": {
10
+ "type": "string",
11
+ "description": "Target which builds the browser application.",
12
+ "x-priority": "important"
13
+ },
14
+ "serverTarget": {
15
+ "type": "string",
16
+ "description": "Target which builds the server application.",
17
+ "x-priority": "important"
18
+ },
19
+ "port": {
20
+ "type": "number",
21
+ "description": "The port to be set on `process.env.PORT` for use in the server.",
22
+ "default": 4200,
23
+ "x-priority": "important"
24
+ },
25
+ "devRemotes": {
26
+ "type": "array",
27
+ "items": {
28
+ "type": "string"
29
+ },
30
+ "description": "List of remote applications to run in development mode (i.e. using serve target).",
31
+ "x-priority": "important"
32
+ },
33
+ "skipRemotes": {
34
+ "type": "array",
35
+ "items": {
36
+ "type": "string"
37
+ },
38
+ "description": "List of remote applications to not automatically serve, either statically or in development mode.",
39
+ "x-priority": "important"
40
+ },
41
+ "host": {
42
+ "type": "string",
43
+ "description": "Host to listen on.",
44
+ "default": "localhost"
45
+ },
46
+ "staticRemotesPort": {
47
+ "type": "number",
48
+ "description": "The port at which to serve the file-server for the static remotes."
49
+ },
50
+ "pathToManifestFile": {
51
+ "type": "string",
52
+ "description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
53
+ },
54
+ "ssl": {
55
+ "type": "boolean",
56
+ "description": "Serve using HTTPS.",
57
+ "default": false
58
+ },
59
+ "sslKey": {
60
+ "type": "string",
61
+ "description": "SSL key to use for serving HTTPS."
62
+ },
63
+ "sslCert": {
64
+ "type": "string",
65
+ "description": "SSL certificate to use for serving HTTPS."
66
+ },
67
+ "publicHost": {
68
+ "type": "string",
69
+ "description": "Public URL where the application will be served."
70
+ },
71
+ "isInitialHost": {
72
+ "type": "boolean",
73
+ "description": "Whether the host that is running this executor is the first in the project tree to do so.",
74
+ "default": true,
75
+ "x-priority": "internal"
76
+ }
77
+ },
78
+ "required": ["browserTarget", "serverTarget"]
79
+ }
@@ -0,0 +1,12 @@
1
+ import { ExecutorContext } from 'nx/src/config/misc-interfaces';
2
+ import { StaticRemotesConfig } from '../../utils/module-federation/parse-static-remotes-config';
3
+ import { ModuleFederationDevServerOptions } from '../module-federation-dev-server/schema';
4
+ import { ModuleFederationStaticServerSchema } from './schema';
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>;