@nx/react 19.6.0-canary.20240814-6d83ae2 → 19.6.0-rc.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 (30) hide show
  1. package/migrations.json +12 -0
  2. package/package.json +8 -7
  3. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.js +8 -11
  4. package/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.d.ts +13 -3
  5. package/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.js +180 -80
  6. package/src/executors/module-federation-ssr-dev-server/schema.json +27 -0
  7. package/src/generators/component-cypress-spec/schema.json +2 -1
  8. package/src/generators/host/files/module-federation/webpack.config.js__tmpl__ +6 -1
  9. package/src/generators/host/files/module-federation/webpack.config.prod.js__tmpl__ +6 -1
  10. package/src/generators/host/files/module-federation-ssr/server.ts__tmpl__ +1 -1
  11. package/src/generators/host/files/module-federation-ssr/webpack.server.config.js__tmpl__ +6 -1
  12. package/src/generators/host/files/module-federation-ssr-ts/server.ts__tmpl__ +1 -1
  13. package/src/generators/host/files/module-federation-ssr-ts/webpack.server.config.ts__tmpl__ +6 -1
  14. package/src/generators/host/files/module-federation-ts/webpack.config.prod.ts__tmpl__ +6 -1
  15. package/src/generators/host/files/module-federation-ts/webpack.config.ts__tmpl__ +6 -1
  16. package/src/generators/host/lib/setup-ssr-for-host.js +1 -0
  17. package/src/generators/remote/files/module-federation/webpack.config.js__tmpl__ +6 -1
  18. package/src/generators/remote/files/module-federation-ssr/server.ts__tmpl__ +1 -1
  19. package/src/generators/remote/files/module-federation-ssr/webpack.server.config.js__tmpl__ +6 -1
  20. package/src/generators/remote/files/module-federation-ssr-ts/server.ts__tmpl__ +1 -1
  21. package/src/generators/remote/files/module-federation-ssr-ts/webpack.server.config.ts__tmpl__ +6 -1
  22. package/src/generators/remote/files/module-federation-ts/webpack.config.ts__tmpl__ +6 -1
  23. package/src/generators/remote/lib/setup-ssr-for-remote.js +1 -0
  24. package/src/generators/setup-ssr/files/server.ts__tmpl__ +2 -2
  25. package/src/generators/setup-ssr/setup-ssr.js +1 -0
  26. package/src/generators/stories/schema.json +4 -2
  27. package/src/migrations/update-19-6-0/turn-off-dts-by-default.d.ts +2 -0
  28. package/src/migrations/update-19-6-0/turn-off-dts-by-default.js +56 -0
  29. package/src/migrations/update-19-6-0/update-ssr-server-port.d.ts +2 -0
  30. package/src/migrations/update-19-6-0/update-ssr-server-port.js +74 -0
package/migrations.json CHANGED
@@ -41,6 +41,18 @@
41
41
  "version": "18.1.1-beta.0",
42
42
  "description": "Ensure targetDefaults inputs for task hashing when '@nx/webpack:webpack' is used are correct for Module Federation.",
43
43
  "factory": "./src/migrations/update-18-1-1/fix-target-defaults-inputs"
44
+ },
45
+ "update-19-6-0-turn-module-federation-dts-off": {
46
+ "cli": "nx",
47
+ "version": "19.6.0-beta.4",
48
+ "description": "Ensure Module Federation DTS is turned off by default.",
49
+ "factory": "./src/migrations/update-19-6-0/turn-off-dts-by-default"
50
+ },
51
+ "update-module-federation-ssr-server-file": {
52
+ "cli": "nx",
53
+ "version": "19.6.0-beta.4",
54
+ "description": "Update the server file for Module Federation SSR port value to be the same as the 'serve' target port value.",
55
+ "factory": "./src/migrations/update-19-6-0/update-ssr-server-port"
44
56
  }
45
57
  },
46
58
  "packageJsonUpdates": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "19.6.0-canary.20240814-6d83ae2",
3
+ "version": "19.6.0-rc.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,14 +39,15 @@
39
39
  "minimatch": "9.0.3",
40
40
  "tslib": "^2.3.0",
41
41
  "@module-federation/enhanced": "~0.2.3",
42
- "@nx/devkit": "19.6.0-canary.20240814-6d83ae2",
43
- "@nx/js": "19.6.0-canary.20240814-6d83ae2",
44
- "@nx/eslint": "19.6.0-canary.20240814-6d83ae2",
45
- "@nx/web": "19.6.0-canary.20240814-6d83ae2",
46
- "@nrwl/react": "19.6.0-canary.20240814-6d83ae2"
42
+ "@nx/devkit": "19.6.0-rc.0",
43
+ "@nx/js": "19.6.0-rc.0",
44
+ "@nx/eslint": "19.6.0-rc.0",
45
+ "@nx/web": "19.6.0-rc.0",
46
+ "@nrwl/react": "19.6.0-rc.0"
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"
50
50
  },
51
- "type": "commonjs"
51
+ "type": "commonjs",
52
+ "types": "./index.d.ts"
52
53
  }
@@ -9,8 +9,6 @@ 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
10
  const cache_directory_1 = require("nx/src/utils/cache-directory");
11
11
  const node_child_process_1 = require("node:child_process");
12
- const node_path_1 = require("node:path");
13
- const node_fs_1 = require("node:fs");
14
12
  const fs_1 = require("fs");
15
13
  const path_1 = require("path");
16
14
  const start_remote_proxies_1 = require("@nx/webpack/src/utils/module-federation/start-remote-proxies");
@@ -40,10 +38,10 @@ function startStaticRemotesFileServer(staticRemotesConfig, context, options) {
40
38
  }
41
39
  }
42
40
  if (shouldMoveToCommonLocation) {
43
- commonOutputDirectory = (0, node_path_1.join)(devkit_1.workspaceRoot, 'tmp/static-remotes');
41
+ commonOutputDirectory = (0, path_1.join)(devkit_1.workspaceRoot, 'tmp/static-remotes');
44
42
  for (const app of staticRemotesConfig.remotes) {
45
43
  const remoteConfig = staticRemotesConfig.config[app];
46
- (0, node_fs_1.cpSync)(remoteConfig.outputPath, (0, node_path_1.join)(commonOutputDirectory, remoteConfig.urlSegment), {
44
+ (0, fs_1.cpSync)(remoteConfig.outputPath, (0, path_1.join)(commonOutputDirectory, remoteConfig.urlSegment), {
47
45
  force: true,
48
46
  recursive: true,
49
47
  });
@@ -103,7 +101,6 @@ async function buildStaticRemotes(staticRemotesConfig, nxBin, context, options)
103
101
  for (const app of staticRemotesConfig.remotes) {
104
102
  mappedLocationOfRemotes[app] = `http${options.ssl ? 's' : ''}://${options.host}:${options.staticRemotesPort}/${staticRemotesConfig.config[app].urlSegment}`;
105
103
  }
106
- process.env.NX_MF_DEV_SERVER_STATIC_REMOTES = JSON.stringify(mappedLocationOfRemotes);
107
104
  await new Promise((res, rej) => {
108
105
  const staticProcess = (0, node_child_process_1.fork)(nxBin, [
109
106
  'run-many',
@@ -118,8 +115,8 @@ async function buildStaticRemotes(staticRemotesConfig, nxBin, context, options)
118
115
  stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
119
116
  });
120
117
  // File to debug build failures e.g. 2024-01-01T00_00_0_0Z-build.log'
121
- const remoteBuildLogFile = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `${new Date().toISOString().replace(/[:\.]/g, '_')}-build.log`);
122
- const stdoutStream = (0, node_fs_1.createWriteStream)(remoteBuildLogFile);
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);
123
120
  staticProcess.stdout.on('data', (data) => {
124
121
  const ANSII_CODE_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
125
122
  const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
@@ -167,9 +164,9 @@ async function* moduleFederationDevServer(options, context) {
167
164
  : (0, dev_server_impl_1.default)(options, context);
168
165
  const p = context.projectsConfigurations.projects[context.projectName];
169
166
  const buildOptions = getBuildOptions(options.buildTarget, context);
170
- let pathToManifestFile = (0, node_path_1.join)(context.root, p.sourceRoot, 'assets/module-federation.manifest.json');
167
+ let pathToManifestFile = (0, path_1.join)(context.root, p.sourceRoot, 'assets/module-federation.manifest.json');
171
168
  if (options.pathToManifestFile) {
172
- const userPathToManifestFile = (0, node_path_1.join)(context.root, options.pathToManifestFile);
169
+ const userPathToManifestFile = (0, path_1.join)(context.root, options.pathToManifestFile);
173
170
  if (!(0, fs_1.existsSync)(userPathToManifestFile)) {
174
171
  throw new Error(`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`);
175
172
  }
@@ -197,8 +194,8 @@ async function* moduleFederationDevServer(options, context) {
197
194
  const staticRemotesIter = startStaticRemotesFileServer(staticRemotesConfig, context, options);
198
195
  (0, start_remote_proxies_1.startRemoteProxies)(staticRemotesConfig, mappedLocationsOfStaticRemotes, options.ssl
199
196
  ? {
200
- pathToCert: (0, node_path_1.join)(devkit_1.workspaceRoot, options.sslCert),
201
- pathToKey: (0, node_path_1.join)(devkit_1.workspaceRoot, options.sslKey),
197
+ pathToCert: (0, path_1.join)(devkit_1.workspaceRoot, options.sslCert),
198
+ pathToKey: (0, path_1.join)(devkit_1.workspaceRoot, options.sslKey),
202
199
  }
203
200
  : undefined);
204
201
  return yield* (0, async_iterable_1.combineAsyncIterables)(currIter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
@@ -1,9 +1,19 @@
1
1
  import { ExecutorContext } from '@nx/devkit';
2
2
  import { WebSsrDevServerOptions } from '@nx/webpack/src/executors/ssr-dev-server/schema';
3
- type ModuleFederationDevServerOptions = WebSsrDevServerOptions & {
4
- devRemotes?: string | string[];
3
+ type ModuleFederationSsrDevServerOptions = WebSsrDevServerOptions & {
4
+ devRemotes?: (string | {
5
+ remoteName: string;
6
+ configuration: string;
7
+ })[];
5
8
  skipRemotes?: string[];
6
9
  host: string;
10
+ pathToManifestFile?: string;
11
+ staticRemotesPort?: number;
12
+ parallel?: number;
13
+ ssl?: boolean;
14
+ sslKey?: string;
15
+ sslCert?: string;
16
+ isInitialHost?: boolean;
7
17
  };
8
- export default function moduleFederationSsrDevServer(options: ModuleFederationDevServerOptions, context: ExecutorContext): AsyncGenerator<any, any, undefined>;
18
+ export default function moduleFederationSsrDevServer(ssrDevServerOptions: ModuleFederationSsrDevServerOptions, context: ExecutorContext): AsyncGenerator<any, any, any>;
9
19
  export {};
@@ -4,11 +4,23 @@ exports.default = moduleFederationSsrDevServer;
4
4
  const devkit_1 = require("@nx/devkit");
5
5
  const ssr_dev_server_impl_1 = require("@nx/webpack/src/executors/ssr-dev-server/ssr-dev-server.impl");
6
6
  const path_1 = require("path");
7
- const chalk = require("chalk");
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 child_process_1 = require("child_process");
10
10
  const fs_1 = require("fs");
11
- const internal_1 = require("@nx/js/src/internal");
11
+ const parse_static_remotes_config_1 = require("@nx/webpack/src/utils/module-federation/parse-static-remotes-config");
12
+ const file_server_impl_1 = require("@nx/web/src/executors/file-server/file-server.impl");
13
+ const cache_directory_1 = require("nx/src/utils/cache-directory");
14
+ const start_ssr_remote_proxies_1 = require("@nx/webpack/src/utils/module-federation/start-ssr-remote-proxies");
15
+ const wait_for_port_open_1 = require("@nx/web/src/utils/wait-for-port-open");
16
+ function normalizeOptions(options) {
17
+ return {
18
+ ...options,
19
+ ssl: options.ssl ?? false,
20
+ sslCert: options.sslCert ? (0, path_1.join)(devkit_1.workspaceRoot, options.sslCert) : undefined,
21
+ sslKey: options.sslKey ? (0, path_1.join)(devkit_1.workspaceRoot, options.sslKey) : undefined,
22
+ };
23
+ }
12
24
  function getBuildOptions(buildTarget, context) {
13
25
  const target = (0, devkit_1.parseTargetString)(buildTarget, context);
14
26
  const buildOptions = (0, devkit_1.readTargetOptions)(target, context);
@@ -16,93 +28,181 @@ function getBuildOptions(buildTarget, context) {
16
28
  ...buildOptions,
17
29
  };
18
30
  }
19
- function getModuleFederationConfig(tsconfigPath, workspaceRoot, projectRoot) {
20
- const moduleFederationConfigPathJS = (0, path_1.join)(workspaceRoot, projectRoot, 'module-federation.config.js');
21
- const moduleFederationConfigPathTS = (0, path_1.join)(workspaceRoot, projectRoot, 'module-federation.config.ts');
22
- let moduleFederationConfigPath = moduleFederationConfigPathJS;
23
- const fullTSconfigPath = tsconfigPath.startsWith(workspaceRoot)
24
- ? tsconfigPath
25
- : (0, path_1.join)(workspaceRoot, tsconfigPath);
26
- // create a no-op so this can be called with issue
27
- let cleanupTranspiler = () => { };
28
- if ((0, fs_1.existsSync)(moduleFederationConfigPathTS)) {
29
- cleanupTranspiler = (0, internal_1.registerTsProject)(fullTSconfigPath);
30
- moduleFederationConfigPath = moduleFederationConfigPathTS;
31
+ function startSsrStaticRemotesFileServer(ssrStaticRemotesConfig, context, options) {
32
+ if (ssrStaticRemotesConfig.remotes.length === 0) {
33
+ return;
31
34
  }
32
- try {
33
- const config = require(moduleFederationConfigPath);
34
- cleanupTranspiler();
35
- return config.default || config;
35
+ // The directories are usually generated with /browser and /server suffixes so we need to copy them to a common directory
36
+ const commonOutputDirectory = (0, path_1.join)(devkit_1.workspaceRoot, 'tmp/static-remotes');
37
+ for (const app of ssrStaticRemotesConfig.remotes) {
38
+ const remoteConfig = ssrStaticRemotesConfig.config[app];
39
+ (0, fs_1.cpSync)(remoteConfig.outputPath, (0, path_1.join)(commonOutputDirectory, remoteConfig.urlSegment), {
40
+ force: true,
41
+ recursive: true,
42
+ });
36
43
  }
37
- catch {
38
- throw new Error(`Could not load ${moduleFederationConfigPath}. Was this project generated with "@nx/react:host"?\nSee: https://nx.dev/concepts/more-concepts/faster-builds-with-module-federation`);
44
+ const staticRemotesIter = (0, file_server_impl_1.default)({
45
+ cors: true,
46
+ watch: false,
47
+ staticFilePath: commonOutputDirectory,
48
+ parallel: false,
49
+ spa: false,
50
+ withDeps: false,
51
+ host: options.host,
52
+ port: options.staticRemotesPort,
53
+ ssl: options.ssl,
54
+ sslCert: options.sslCert,
55
+ sslKey: options.sslKey,
56
+ cacheSeconds: -1,
57
+ }, context);
58
+ return staticRemotesIter;
59
+ }
60
+ async function startRemotes(remotes, context, options) {
61
+ const remoteIters = [];
62
+ const target = 'serve';
63
+ for (const app of remotes) {
64
+ const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets[target];
65
+ const isUsingModuleFederationSsrDevServerExecutor = remoteProjectServeTarget.executor.includes('module-federation-ssr-dev-server');
66
+ const configurationOverride = options.devRemotes?.find((remote) => typeof remote !== 'string' && remote.remoteName === app)?.configuration;
67
+ {
68
+ const defaultOverrides = {
69
+ ...(options.host ? { host: options.host } : {}),
70
+ ...(options.ssl ? { ssl: options.ssl } : {}),
71
+ ...(options.sslCert ? { sslCert: options.sslCert } : {}),
72
+ ...(options.sslKey ? { sslKey: options.sslKey } : {}),
73
+ };
74
+ const overrides = {
75
+ watch: true,
76
+ ...defaultOverrides,
77
+ ...(isUsingModuleFederationSsrDevServerExecutor
78
+ ? { isInitialHost: false }
79
+ : {}),
80
+ };
81
+ remoteIters.push(await (0, devkit_1.runExecutor)({
82
+ project: app,
83
+ target,
84
+ configuration: configurationOverride ?? context.configurationName,
85
+ }, overrides, context));
86
+ }
39
87
  }
88
+ return remoteIters;
89
+ }
90
+ async function buildSsrStaticRemotes(staticRemotesConfig, nxBin, context, options) {
91
+ if (!staticRemotesConfig.remotes.length) {
92
+ return;
93
+ }
94
+ devkit_1.logger.info(`Nx is building ${staticRemotesConfig.remotes.length} static remotes...`);
95
+ const mapLocationOfRemotes = {};
96
+ for (const remoteApp of staticRemotesConfig.remotes) {
97
+ mapLocationOfRemotes[remoteApp] = `http${options.ssl ? 's' : ''}://${options.host}:${options.staticRemotesPort}/${staticRemotesConfig.config[remoteApp].urlSegment}`;
98
+ }
99
+ await new Promise((resolve) => {
100
+ const childProcess = (0, child_process_1.fork)(nxBin, [
101
+ 'run-many',
102
+ '--target=server',
103
+ '--projects',
104
+ staticRemotesConfig.remotes.join(','),
105
+ ...(context.configurationName
106
+ ? [`--configuration=${context.configurationName}`]
107
+ : []),
108
+ ...(options.parallel ? [`--parallel=${options.parallel}`] : []),
109
+ ], {
110
+ cwd: context.root,
111
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
112
+ });
113
+ // Add a listener to the child process to capture the build log
114
+ const remoteBuildLogFile = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, `${new Date().toISOString().replace(/[:\.]/g, '_')}-build.log`);
115
+ const remoteBuildLogStream = (0, fs_1.createWriteStream)(remoteBuildLogFile);
116
+ childProcess.stdout.on('data', (data) => {
117
+ const ANSII_CODE_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
118
+ const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
119
+ remoteBuildLogStream.write(stdoutString);
120
+ // in addition to writing into the stdout stream, also show error directly in console
121
+ // so the error is easily discoverable. 'ERROR in' is the key word to search in webpack output.
122
+ if (stdoutString.includes('ERROR in')) {
123
+ devkit_1.logger.log(stdoutString);
124
+ }
125
+ if (stdoutString.includes('Successfully ran target server')) {
126
+ childProcess.stdout.removeAllListeners('data');
127
+ devkit_1.logger.info(`Nx Built ${staticRemotesConfig.remotes.length} static remotes.`);
128
+ resolve();
129
+ }
130
+ });
131
+ process.on('SIGTERM', () => childProcess.kill('SIGTERM'));
132
+ process.on('exit', () => childProcess.kill('SIGTERM'));
133
+ });
134
+ return mapLocationOfRemotes;
40
135
  }
41
- async function* moduleFederationSsrDevServer(options, context) {
136
+ async function* moduleFederationSsrDevServer(ssrDevServerOptions, context) {
137
+ const options = normalizeOptions(ssrDevServerOptions);
138
+ // Force Node to resolve to look for the nx binary that is inside node_modules
139
+ const nxBin = require.resolve('nx/bin/nx');
42
140
  let iter = (0, ssr_dev_server_impl_1.default)(options, context);
43
- const p = context.projectsConfigurations.projects[context.projectName];
141
+ const projectConfig = context.projectsConfigurations.projects[context.projectName];
44
142
  const buildOptions = getBuildOptions(options.browserTarget, context);
45
- const moduleFederationConfig = getModuleFederationConfig(buildOptions.tsConfig, context.root, p.root);
46
- const remotesToSkip = new Set(options.skipRemotes ?? []);
47
- const remotesNotInWorkspace = [];
48
- const knownRemotes = (moduleFederationConfig.remotes ?? []).filter((r) => {
49
- const validRemote = Array.isArray(r) ? r[0] : r;
50
- if (remotesToSkip.has(validRemote)) {
51
- return false;
143
+ let pathToManifestFile = (0, path_1.join)(context.root, projectConfig.sourceRoot, 'assets/module-federation.manifest.json');
144
+ if (options.pathToManifestFile) {
145
+ const userPathToManifestFile = (0, path_1.join)(context.root, options.pathToManifestFile);
146
+ if (!(0, fs_1.existsSync)(userPathToManifestFile)) {
147
+ throw new Error(`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`);
52
148
  }
53
- else if (!context.projectGraph.nodes[validRemote]) {
54
- remotesNotInWorkspace.push(validRemote);
55
- return false;
149
+ else if ((0, path_1.extname)(userPathToManifestFile) !== '.json') {
150
+ throw new Error(`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`);
56
151
  }
57
- else {
58
- return true;
59
- }
60
- });
61
- if (remotesNotInWorkspace.length > 0) {
62
- devkit_1.logger.warn(`Skipping serving ${remotesNotInWorkspace.join(', ')} as they could not be found in the workspace. Ensure they are served correctly.`);
152
+ pathToManifestFile = userPathToManifestFile;
63
153
  }
64
- const devServeApps = !options.devRemotes
65
- ? []
66
- : Array.isArray(options.devRemotes)
67
- ? options.devRemotes
68
- : [options.devRemotes];
69
- // Set NX_MF_DEV_REMOTES for the Nx Runtime Library Control Plugin
70
- process.env.NX_MF_DEV_REMOTES = JSON.stringify(devServeApps);
71
- for (const app of knownRemotes) {
72
- const [appName] = Array.isArray(app) ? app : [app];
73
- const isDev = devServeApps.includes(appName);
74
- const remoteServeIter = isDev
75
- ? await (0, devkit_1.runExecutor)({
76
- project: appName,
77
- target: 'serve',
78
- configuration: context.configurationName,
79
- }, {
80
- watch: isDev,
81
- }, context)
82
- : (0, async_iterable_1.mapAsyncIterable)((0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
83
- const remoteProject = context.projectsConfigurations.projects[appName];
84
- const remoteServerOutput = (0, path_1.join)(devkit_1.workspaceRoot, remoteProject.targets.server.options.outputPath, remoteProject.targets.server.options.outputFileName);
85
- const pm = (0, devkit_1.getPackageManagerCommand)();
86
- (0, child_process_1.execSync)(`${pm.exec} nx run ${appName}:server${context.configurationName ? `:${context.configurationName}` : ''}`, { stdio: 'inherit' });
87
- const child = (0, child_process_1.fork)(remoteServerOutput, {
88
- env: {
89
- PORT: remoteProject.targets['serve-browser'].options.port,
90
- },
91
- });
92
- child.on('message', (msg) => {
93
- if (msg === 'nx.server.ready') {
94
- next(true);
95
- done();
96
- }
97
- });
98
- }), (x) => x);
99
- iter = (0, async_iterable_1.combineAsyncIterables)(iter, remoteServeIter);
154
+ if (!options.isInitialHost) {
155
+ return yield* iter;
100
156
  }
101
- let numAwaiting = knownRemotes.length + 1; // remotes + host
102
- return yield* (0, async_iterable_1.tapAsyncIterable)(iter, (x) => {
103
- numAwaiting--;
104
- if (numAwaiting === 0) {
105
- devkit_1.logger.info(`[ ${chalk.green('ready')} ] http://${options.host ?? 'localhost'}:${options.port ?? 4200}`);
157
+ const moduleFederationConfig = (0, module_federation_1.getModuleFederationConfig)(buildOptions.tsConfig, context.root, projectConfig.root, 'react');
158
+ const remoteNames = options.devRemotes?.map((remote) => typeof remote === 'string' ? remote : remote.remoteName);
159
+ const remotes = (0, module_federation_1.getRemotes)(remoteNames, options.skipRemotes, moduleFederationConfig, {
160
+ projectName: context.projectName,
161
+ projectGraph: context.projectGraph,
162
+ root: context.root,
163
+ }, pathToManifestFile);
164
+ options.staticRemotesPort ??= remotes.staticRemotePort;
165
+ process.env.NX_MF_DEV_REMOTES = JSON.stringify(remotes.devRemotes.map((r) => (typeof r === 'string' ? r : r.remoteName)));
166
+ const staticRemotesConfig = (0, parse_static_remotes_config_1.parseStaticSsrRemotesConfig)([...remotes.staticRemotes, ...remotes.dynamicRemotes], context);
167
+ const mappedLocationsOfStaticRemotes = await buildSsrStaticRemotes(staticRemotesConfig, nxBin, context, options);
168
+ const devRemoteIters = await startRemotes(remotes.devRemotes, context, options);
169
+ const staticRemotesIter = startSsrStaticRemotesFileServer(staticRemotesConfig, context, options);
170
+ (0, start_ssr_remote_proxies_1.startSsrRemoteProxies)(staticRemotesConfig, mappedLocationsOfStaticRemotes, options.ssl
171
+ ? {
172
+ pathToCert: options.sslCert,
173
+ pathToKey: options.sslKey,
106
174
  }
107
- });
175
+ : undefined);
176
+ return yield* (0, async_iterable_1.combineAsyncIterables)(iter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
177
+ if (!options.isInitialHost) {
178
+ done();
179
+ return;
180
+ }
181
+ if (remotes.remotePorts.length === 0) {
182
+ done();
183
+ return;
184
+ }
185
+ try {
186
+ const host = options.host ?? 'localhost';
187
+ const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${options.port}`;
188
+ const portsToWaitFor = staticRemotesIter
189
+ ? [options.staticRemotesPort, ...remotes.remotePorts]
190
+ : [...remotes.remotePorts];
191
+ await Promise.all(portsToWaitFor.map((port) => (0, wait_for_port_open_1.waitForPortOpen)(port, {
192
+ retries: 480,
193
+ retryDelay: 2500,
194
+ host,
195
+ })));
196
+ devkit_1.logger.info(`Nx all ssr remotes have started, server ready at ${baseUrl}`);
197
+ next({ success: true, baseUrl });
198
+ }
199
+ catch (error) {
200
+ throw new Error(`Nx failed to start ssr remotes. Check above for errors.`, {
201
+ cause: error,
202
+ });
203
+ }
204
+ finally {
205
+ done();
206
+ }
207
+ }));
108
208
  }
@@ -42,6 +42,33 @@
42
42
  "type": "string",
43
43
  "description": "Host to listen on.",
44
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
+ "isInitialHost": {
68
+ "type": "boolean",
69
+ "description": "Whether the host that is running this executor is the first in the project tree to do so.",
70
+ "default": true,
71
+ "x-priority": "internal"
45
72
  }
46
73
  },
47
74
  "required": ["browserTarget", "serverTarget"]
@@ -3,7 +3,8 @@
3
3
  "cli": "nx",
4
4
  "$id": "NxReactComponentCypressSpec",
5
5
  "title": "Create component Cypress spec",
6
- "description": "Create a Cypress spec for a UI component that has a story.",
6
+ "description": "Create a Storybook Cypress spec for a UI component that has a story.",
7
+ "x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
7
8
  "type": "object",
8
9
  "properties": {
9
10
  "project": {
@@ -9,4 +9,9 @@ const config = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
@@ -29,4 +29,9 @@ const prodConfig = {
29
29
  };
30
30
 
31
31
  // Nx plugins for webpack to build config object from Nx options and context.
32
- module.exports = composePlugins(withNx(), withReact(), withModuleFederation(prodConfig));
32
+ /**
33
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
34
+ * The DTS Plugin can be enabled by setting dts: true
35
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
36
+ */
37
+ module.exports = composePlugins(withNx(), withReact(), withModuleFederation(prodConfig, { dts: false }));
@@ -4,7 +4,7 @@ import cors from 'cors';
4
4
 
5
5
  import { handleRequest } from './src/main.server';
6
6
 
7
- const port = process.env['PORT'] || 4200;
7
+ const port = process.env['PORT'] || <%= port %>;
8
8
  const app = express();
9
9
 
10
10
  const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
@@ -9,4 +9,9 @@ const defaultConfig = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
@@ -4,7 +4,7 @@ import cors from 'cors';
4
4
 
5
5
  import { handleRequest } from './src/main.server';
6
6
 
7
- const port = process.env['PORT'] || 4200;
7
+ const port = process.env['PORT'] || <%= port %>;
8
8
  const app = express();
9
9
 
10
10
  const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
@@ -9,4 +9,9 @@ const defaultConfig = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
@@ -30,4 +30,9 @@ const prodConfig: ModuleFederationConfig = {
30
30
  };
31
31
 
32
32
  // Nx plugins for webpack to build config object from Nx options and context.
33
- export default composePlugins(withNx(), withReact(), withModuleFederation(prodConfig));
33
+ /**
34
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
35
+ * The DTS Plugin can be enabled by setting dts: true
36
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
37
+ */
38
+ export default composePlugins(withNx(), withReact(), withModuleFederation(prodConfig, { dts: false }));
@@ -9,4 +9,9 @@ const config: ModuleFederationConfig = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- export default composePlugins(withNx(), withReact(), withModuleFederation(config));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ export default composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
@@ -14,6 +14,7 @@ async function setupSsrForHost(tree, options, appName, defaultRemoteManifest) {
14
14
  (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, `../files/${pathToModuleFederationSsrFiles}`), project.root, {
15
15
  ...options,
16
16
  static: !options?.dynamic,
17
+ port: Number(options?.devServerPort) || 4200,
17
18
  remotes: defaultRemoteManifest.map(({ name, port }) => {
18
19
  return {
19
20
  ...(0, devkit_1.names)(name),
@@ -9,4 +9,9 @@ const config = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
@@ -4,7 +4,7 @@ import cors from 'cors';
4
4
 
5
5
  import { handleRequest } from './src/main.server';
6
6
 
7
- const port = process.env['PORT'] || 4200;
7
+ const port = process.env['PORT'] || <%= port %>;
8
8
  const app = express();
9
9
 
10
10
  const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
@@ -9,4 +9,9 @@ const defaultConfig = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
@@ -4,7 +4,7 @@ import cors from 'cors';
4
4
 
5
5
  import { handleRequest } from './src/main.server';
6
6
 
7
- const port = process.env['PORT'] || 4200;
7
+ const port = process.env['PORT'] || <%= port %>;
8
8
  const app = express();
9
9
 
10
10
  const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
@@ -9,4 +9,9 @@ const defaultConfig = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
@@ -9,4 +9,9 @@ const config = {
9
9
  };
10
10
 
11
11
  // Nx plugins for webpack to build config object from Nx options and context.
12
- export default composePlugins(withNx(), withReact(), withModuleFederation(config));
12
+ /**
13
+ * DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
14
+ * The DTS Plugin can be enabled by setting dts: true
15
+ * Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
16
+ */
17
+ export default composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
@@ -11,6 +11,7 @@ async function setupSsrForRemote(tree, options, appName) {
11
11
  : 'module-federation-ssr';
12
12
  (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, `../files/${pathToModuleFederationSsrFiles}`), project.root, {
13
13
  ...options,
14
+ port: Number(options?.devServerPort) || 4200,
14
15
  appName,
15
16
  tmpl: '',
16
17
  browserBuildOutputPath: project.targets.build.options.outputPath,
@@ -4,11 +4,11 @@ import cors from 'cors';
4
4
 
5
5
  import { handleRequest } from './src/main.server';
6
6
 
7
- const port = process.env['PORT'] || 4200;
7
+ const port = process.env['PORT'] || <%= port %>;
8
8
  const app = express();
9
9
 
10
10
  const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
11
- const indexPath =path.join(browserDist, 'index.html');
11
+ const indexPath = path.join(browserDist, 'index.html');
12
12
 
13
13
  app.use(cors());
14
14
 
@@ -150,6 +150,7 @@ async function setupSsrGenerator(tree, options) {
150
150
  nxJson.targetDefaults.server.cache = true;
151
151
  (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectRoot, {
152
152
  tmpl: '',
153
+ port: Number(options?.serverPort) || 4200,
153
154
  extraInclude: options.extraInclude?.length > 0
154
155
  ? `"${options.extraInclude.join('", "')}",`
155
156
  : '',
@@ -19,11 +19,13 @@
19
19
  },
20
20
  "generateCypressSpecs": {
21
21
  "type": "boolean",
22
- "description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator."
22
+ "description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
23
+ "x-deprecated": "Use interactionTests instead. This option will be removed in v20."
23
24
  },
24
25
  "cypressProject": {
25
26
  "type": "string",
26
- "description": "The Cypress project to generate the stories under. This is inferred from `project` by default."
27
+ "description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
28
+ "x-deprecated": "Use interactionTests instead. This option will be removed in v20."
27
29
  },
28
30
  "interactionTests": {
29
31
  "type": "boolean",
@@ -0,0 +1,2 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function (tree: Tree): Promise<void>;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const minimatch_1 = require("minimatch");
6
+ const tsquery_1 = require("@phenomnomnominal/tsquery");
7
+ async function default_1(tree) {
8
+ (0, devkit_1.visitNotIgnoredFiles)(tree, '', (path) => {
9
+ const webpackConfigGlob = '**/webpack*.config*.{js,ts,mjs,cjs}';
10
+ const result = (0, minimatch_1.minimatch)(path, webpackConfigGlob);
11
+ if (!result) {
12
+ return;
13
+ }
14
+ let webpackConfigContents = tree.read(path, 'utf-8');
15
+ if (!/withModuleFederationSSR|withModuleFederation/.test(webpackConfigContents)) {
16
+ return;
17
+ }
18
+ const WITH_MODULE_FEDERATION_SELECTOR = 'CallExpression:has(Identifier[name=composePlugins]) > CallExpression:has(Identifier[name=withModuleFederation]),CallExpression:has(Identifier[name=composePlugins]) > CallExpression:has(Identifier[name=withModuleFederationForSSR])';
19
+ const EXISTING_MF_OVERRIDES_SELECTOR = 'ObjectLiteralExpression';
20
+ const ast = tsquery_1.tsquery.ast(webpackConfigContents);
21
+ const withModuleFederationNodes = (0, tsquery_1.tsquery)(ast, WITH_MODULE_FEDERATION_SELECTOR, { visitAllChildren: true });
22
+ if (!withModuleFederationNodes.length) {
23
+ return;
24
+ }
25
+ const withModuleFederationNode = withModuleFederationNodes[0];
26
+ const existingOverridesNodes = (0, tsquery_1.tsquery)(withModuleFederationNode, EXISTING_MF_OVERRIDES_SELECTOR, { visitAllChildren: true });
27
+ if (!existingOverridesNodes.length) {
28
+ // doesn't exist, add it
29
+ webpackConfigContents = `${webpackConfigContents.slice(0, withModuleFederationNode.getEnd() - 1)},${JSON.stringify({ dts: false })}${webpackConfigContents.slice(withModuleFederationNode.getEnd() - 1)}`;
30
+ }
31
+ else {
32
+ let existingOverrideNode;
33
+ for (const node of existingOverridesNodes) {
34
+ if (!existingOverrideNode) {
35
+ existingOverrideNode = node;
36
+ }
37
+ if (existingOverrideNode.getText().includes(node.getText())) {
38
+ continue;
39
+ }
40
+ existingOverrideNode = node;
41
+ }
42
+ const DTS_PROPERTY_SELECTOR = 'PropertyAssignment > Identifier[name=dts]';
43
+ const dtsPropertyNode = (0, tsquery_1.tsquery)(existingOverrideNode, DTS_PROPERTY_SELECTOR);
44
+ if (dtsPropertyNode.length) {
45
+ // dts already exists, do nothing
46
+ return;
47
+ }
48
+ const newOverrides = `{ dts: false, ${existingOverrideNode
49
+ .getText()
50
+ .slice(1)}`;
51
+ webpackConfigContents = `${webpackConfigContents.slice(0, existingOverrideNode.getStart())}${newOverrides}${webpackConfigContents.slice(existingOverrideNode.getEnd())}`;
52
+ }
53
+ tree.write(path, webpackConfigContents);
54
+ });
55
+ await (0, devkit_1.formatFiles)(tree);
56
+ }
@@ -0,0 +1,2 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function update(tree: Tree): void;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = update;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const tsquery_1 = require("@phenomnomnominal/tsquery");
6
+ const ts = require("typescript");
7
+ const minimatch_1 = require("minimatch");
8
+ const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
9
+ function update(tree) {
10
+ const projects = (0, devkit_1.getProjects)(tree);
11
+ const executors = [
12
+ '@nx/webpack:ssr-dev-server',
13
+ '@nx/react:module-federation-ssr-dev-server',
14
+ ];
15
+ executors.forEach((executor) => {
16
+ (0, executor_options_utils_1.forEachExecutorOptions)(tree, executor, (options, projectName) => {
17
+ const project = projects.get(projectName);
18
+ if (isModuleFederationSSRProject(tree, project)) {
19
+ const port = options.port;
20
+ if (tree.exists((0, devkit_1.joinPathFragments)(project.root, 'server.ts'))) {
21
+ const serverContent = tree.read((0, devkit_1.joinPathFragments)(project.root, 'server.ts'), 'utf-8');
22
+ if (serverContent && port) {
23
+ const updatedServerContent = updateServerPort(serverContent, port);
24
+ if (updatedServerContent) {
25
+ tree.write((0, devkit_1.joinPathFragments)(project.root, 'server.ts'), updatedServerContent);
26
+ }
27
+ }
28
+ }
29
+ }
30
+ });
31
+ });
32
+ }
33
+ function updateServerPort(serverContent, port) {
34
+ const sourceFile = tsquery_1.tsquery.ast(serverContent);
35
+ const serverPortNode = (0, tsquery_1.tsquery)(sourceFile, `VariableDeclaration:has(Identifier[name="port"])`)[0];
36
+ if (serverPortNode) {
37
+ const binaryExpression = (0, tsquery_1.tsquery)(serverPortNode, 'BinaryExpression')[0];
38
+ if (binaryExpression) {
39
+ const leftExpression = (0, tsquery_1.tsquery)(binaryExpression, 'PropertyAccessExpression:has(Identifier[name="env"])')[0];
40
+ const rightExpression = (0, tsquery_1.tsquery)(binaryExpression, 'NumericLiteral[text="4200"]')[0];
41
+ if (leftExpression && rightExpression) {
42
+ const serverPortDeclaration = serverPortNode;
43
+ const newInitializer = ts.factory.createBinaryExpression(
44
+ // process.env.PORT
45
+ ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('process'), ts.factory.createIdentifier('env')), 'PORT'),
46
+ // ||
47
+ ts.SyntaxKind.BarBarToken,
48
+ // port value
49
+ ts.factory.createNumericLiteral(port.toString()));
50
+ const updatePortDeclaration = ts.factory.updateVariableDeclaration(serverPortDeclaration, serverPortDeclaration.name, serverPortDeclaration.exclamationToken, serverPortDeclaration.type, newInitializer);
51
+ const updatedStatements = sourceFile.statements.map((statement) => {
52
+ if (ts.isVariableStatement(statement)) {
53
+ const updatedDeclarationList = statement.declarationList.declarations.map((decl) => decl === serverPortDeclaration ? updatePortDeclaration : decl);
54
+ const updatedDeclList = ts.factory.updateVariableDeclarationList(statement.declarationList, updatedDeclarationList);
55
+ return ts.factory.updateVariableStatement(statement, statement.modifiers, updatedDeclList);
56
+ }
57
+ return statement;
58
+ });
59
+ const updatedSourceFile = ts.factory.updateSourceFile(sourceFile, updatedStatements);
60
+ const printer = ts.createPrinter();
61
+ return printer.printNode(ts.EmitHint.Unspecified, updatedSourceFile, sourceFile);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ function isModuleFederationSSRProject(tree, project) {
67
+ let hasMfeServerConfig = false;
68
+ (0, devkit_1.visitNotIgnoredFiles)(tree, project.root, (filePath) => {
69
+ if ((0, minimatch_1.minimatch)(filePath, '**/module-federation*.server.config.*')) {
70
+ hasMfeServerConfig = true;
71
+ }
72
+ });
73
+ return hasMfeServerConfig;
74
+ }