@nx/react 0.0.0-pr-26891-a7e4733 → 0.0.0-pr-27530-db566c6

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 (45) hide show
  1. package/README.md +1 -1
  2. package/migrations.json +18 -0
  3. package/package.json +8 -7
  4. package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.js +12 -12
  5. package/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.d.ts +13 -3
  6. package/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.js +183 -80
  7. package/src/executors/module-federation-ssr-dev-server/schema.json +27 -0
  8. package/src/generators/application/schema.d.ts +2 -2
  9. package/src/generators/component-cypress-spec/schema.json +2 -1
  10. package/src/generators/federate-module/schema.d.ts +3 -1
  11. package/src/generators/host/files/module-federation/webpack.config.js__tmpl__ +6 -1
  12. package/src/generators/host/files/module-federation/webpack.config.prod.js__tmpl__ +6 -1
  13. package/src/generators/host/files/module-federation-ssr/server.ts__tmpl__ +1 -1
  14. package/src/generators/host/files/module-federation-ssr/webpack.server.config.js__tmpl__ +6 -1
  15. package/src/generators/host/files/module-federation-ssr-ts/server.ts__tmpl__ +1 -1
  16. package/src/generators/host/files/module-federation-ssr-ts/webpack.server.config.ts__tmpl__ +6 -1
  17. package/src/generators/host/files/module-federation-ts/webpack.config.prod.ts__tmpl__ +6 -1
  18. package/src/generators/host/files/module-federation-ts/webpack.config.ts__tmpl__ +6 -1
  19. package/src/generators/host/lib/setup-ssr-for-host.js +1 -0
  20. package/src/generators/host/schema.d.ts +2 -2
  21. package/src/generators/library/library.js +5 -1
  22. package/src/generators/library/schema.d.ts +2 -2
  23. package/src/generators/remote/files/module-federation/webpack.config.js__tmpl__ +6 -1
  24. package/src/generators/remote/files/module-federation-ssr/server.ts__tmpl__ +1 -1
  25. package/src/generators/remote/files/module-federation-ssr/webpack.server.config.js__tmpl__ +6 -1
  26. package/src/generators/remote/files/module-federation-ssr-ts/server.ts__tmpl__ +1 -1
  27. package/src/generators/remote/files/module-federation-ssr-ts/webpack.server.config.ts__tmpl__ +6 -1
  28. package/src/generators/remote/files/module-federation-ts/webpack.config.ts__tmpl__ +6 -1
  29. package/src/generators/remote/lib/setup-ssr-for-remote.js +1 -0
  30. package/src/generators/remote/schema.d.ts +2 -2
  31. package/src/generators/setup-ssr/files/server.ts__tmpl__ +2 -2
  32. package/src/generators/setup-ssr/setup-ssr.js +1 -0
  33. package/src/generators/stories/schema.json +4 -2
  34. package/src/generators/storybook-configuration/configuration.d.ts +3 -3
  35. package/src/generators/storybook-configuration/configuration.js +6 -1
  36. package/src/generators/storybook-configuration/schema.d.ts +2 -2
  37. package/src/migrations/update-19-6-0/turn-off-dts-by-default.d.ts +2 -0
  38. package/src/migrations/update-19-6-0/turn-off-dts-by-default.js +56 -0
  39. package/src/migrations/update-19-6-0/update-ssr-server-port.d.ts +2 -0
  40. package/src/migrations/update-19-6-0/update-ssr-server-port.js +74 -0
  41. package/src/migrations/update-19-6-1/ensure-depends-on-for-mf.d.ts +2 -0
  42. package/src/migrations/update-19-6-1/ensure-depends-on-for-mf.js +46 -0
  43. package/src/utils/add-mf-env-to-inputs.js +1 -0
  44. package/src/utils/versions.d.ts +1 -0
  45. package/src/utils/versions.js +2 -1
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # Nx: Smart Monorepos · Fast CI
24
24
 
25
- Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.
25
+ Nx is a build system, optimized for monorepos, with plugins for popular frameworks and tools and advanced CI capabilities including caching and distribution.
26
26
 
27
27
  This package is a [React plugin for Nx](https://nx.dev/nx-api/react).
28
28
 
package/migrations.json CHANGED
@@ -41,6 +41,24 @@
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"
56
+ },
57
+ "update-19-6-1-ensure-module-federation-target-defaults": {
58
+ "cli": "nx",
59
+ "version": "19.6.1-beta.0",
60
+ "description": "Ensure Target Defaults are set correctly for Module Federation.",
61
+ "factory": "./src/migrations/update-19-6-1/ensure-depends-on-for-mf"
44
62
  }
45
63
  },
46
64
  "packageJsonUpdates": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "0.0.0-pr-26891-a7e4733",
3
+ "version": "0.0.0-pr-27530-db566c6",
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": "0.0.0-pr-26891-a7e4733",
43
- "@nx/js": "0.0.0-pr-26891-a7e4733",
44
- "@nx/eslint": "0.0.0-pr-26891-a7e4733",
45
- "@nx/web": "0.0.0-pr-26891-a7e4733",
46
- "@nrwl/react": "0.0.0-pr-26891-a7e4733"
42
+ "@nx/devkit": "0.0.0-pr-27530-db566c6",
43
+ "@nx/js": "0.0.0-pr-27530-db566c6",
44
+ "@nx/eslint": "0.0.0-pr-27530-db566c6",
45
+ "@nx/web": "0.0.0-pr-27530-db566c6",
46
+ "@nrwl/react": "0.0.0-pr-27530-db566c6"
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
  }
@@ -190,15 +187,18 @@ async function* moduleFederationDevServer(options, context) {
190
187
  }, pathToManifestFile);
191
188
  options.staticRemotesPort ??= remotes.staticRemotePort;
192
189
  // Set NX_MF_DEV_REMOTES for the Nx Runtime Library Control Plugin
193
- process.env.NX_MF_DEV_REMOTES = JSON.stringify(remotes.devRemotes.map((r) => (typeof r === 'string' ? r : r.remoteName)));
190
+ process.env.NX_MF_DEV_REMOTES = JSON.stringify([
191
+ ...(remotes.devRemotes.map((r) => typeof r === 'string' ? r : r.remoteName) ?? []),
192
+ p.name,
193
+ ]);
194
194
  const staticRemotesConfig = (0, parse_static_remotes_config_1.parseStaticRemotesConfig)([...remotes.staticRemotes, ...remotes.dynamicRemotes], context);
195
195
  const mappedLocationsOfStaticRemotes = await buildStaticRemotes(staticRemotesConfig, nxBin, context, options);
196
196
  const devRemoteIters = await startRemotes(remotes.devRemotes, context, options, 'serve');
197
197
  const staticRemotesIter = startStaticRemotesFileServer(staticRemotesConfig, context, options);
198
198
  (0, start_remote_proxies_1.startRemoteProxies)(staticRemotesConfig, mappedLocationsOfStaticRemotes, options.ssl
199
199
  ? {
200
- pathToCert: (0, node_path_1.join)(devkit_1.workspaceRoot, options.sslCert),
201
- pathToKey: (0, node_path_1.join)(devkit_1.workspaceRoot, options.sslKey),
200
+ pathToCert: (0, path_1.join)(devkit_1.workspaceRoot, options.sslCert),
201
+ pathToKey: (0, path_1.join)(devkit_1.workspaceRoot, options.sslKey),
202
202
  }
203
203
  : undefined);
204
204
  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,184 @@ 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([
166
+ ...(remotes.devRemotes.map((r) => typeof r === 'string' ? r : r.remoteName) ?? []),
167
+ projectConfig.name,
168
+ ]);
169
+ const staticRemotesConfig = (0, parse_static_remotes_config_1.parseStaticSsrRemotesConfig)([...remotes.staticRemotes, ...remotes.dynamicRemotes], context);
170
+ const mappedLocationsOfStaticRemotes = await buildSsrStaticRemotes(staticRemotesConfig, nxBin, context, options);
171
+ const devRemoteIters = await startRemotes(remotes.devRemotes, context, options);
172
+ const staticRemotesIter = startSsrStaticRemotesFileServer(staticRemotesConfig, context, options);
173
+ (0, start_ssr_remote_proxies_1.startSsrRemoteProxies)(staticRemotesConfig, mappedLocationsOfStaticRemotes, options.ssl
174
+ ? {
175
+ pathToCert: options.sslCert,
176
+ pathToKey: options.sslKey,
106
177
  }
107
- });
178
+ : undefined);
179
+ return yield* (0, async_iterable_1.combineAsyncIterables)(iter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
180
+ if (!options.isInitialHost) {
181
+ done();
182
+ return;
183
+ }
184
+ if (remotes.remotePorts.length === 0) {
185
+ done();
186
+ return;
187
+ }
188
+ try {
189
+ const host = options.host ?? 'localhost';
190
+ const baseUrl = `http${options.ssl ? 's' : ''}://${host}:${options.port}`;
191
+ const portsToWaitFor = staticRemotesIter
192
+ ? [options.staticRemotesPort, ...remotes.remotePorts]
193
+ : [...remotes.remotePorts];
194
+ await Promise.all(portsToWaitFor.map((port) => (0, wait_for_port_open_1.waitForPortOpen)(port, {
195
+ retries: 480,
196
+ retryDelay: 2500,
197
+ host,
198
+ })));
199
+ devkit_1.logger.info(`Nx all ssr remotes have started, server ready at ${baseUrl}`);
200
+ next({ success: true, baseUrl });
201
+ }
202
+ catch (error) {
203
+ throw new Error(`Nx failed to start ssr remotes. Check above for errors.`, {
204
+ cause: error,
205
+ });
206
+ }
207
+ finally {
208
+ done();
209
+ }
210
+ }));
108
211
  }
@@ -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"]
@@ -1,5 +1,5 @@
1
1
  import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
2
- import type { Linter } from '@nx/eslint';
2
+ import type { Linter, LinterType } from '@nx/eslint';
3
3
  import type { SupportedStyles } from '../../../typings/style';
4
4
 
5
5
  export interface Schema {
@@ -12,7 +12,7 @@ export interface Schema {
12
12
  unitTestRunner?: 'jest' | 'vitest' | 'none';
13
13
  inSourceTests?: boolean;
14
14
  e2eTestRunner: 'cypress' | 'playwright' | 'none';
15
- linter: Linter;
15
+ linter: Linter | LinterType;
16
16
  pascalCaseFiles?: boolean;
17
17
  classComponent?: boolean;
18
18
  routing?: boolean;
@@ -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": {
@@ -1,3 +1,5 @@
1
+ import { Linter, LinterType } from '@nx/eslint';
2
+
1
3
  export interface Schema {
2
4
  name: string;
3
5
  path: string;
@@ -6,7 +8,7 @@ export interface Schema {
6
8
  projectNameAndRootFormat?: ProjectNameAndRootFormat;
7
9
  e2eTestRunner?: 'cypress' | 'playwright' | 'none';
8
10
  host?: string;
9
- linter?: Linter;
11
+ linter?: Linter | LinterType;
10
12
  skipFormat?: boolean;
11
13
  style?: SupportedStyles;
12
14
  unitTestRunner?: 'jest' | 'vitest' | 'none';
@@ -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),
@@ -1,5 +1,5 @@
1
1
  import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
2
- import type { Linter } from '@nx/eslint';
2
+ import type { Linter, LinterType } from '@nx/eslint';
3
3
  import type { SupportedStyles } from '../../../typings/style';
4
4
 
5
5
  export interface Schema {
@@ -11,7 +11,7 @@ export interface Schema {
11
11
  e2eTestRunner: 'cypress' | 'playwright' | 'none';
12
12
  globalCss?: boolean;
13
13
  js?: boolean;
14
- linter: Linter;
14
+ linter: Linter | LinterType;
15
15
  name: string;
16
16
  pascalCaseFiles?: boolean;
17
17
  remotes?: string[];
@@ -137,7 +137,11 @@ async function libraryGeneratorInternal(host, schema) {
137
137
  "'react-dom'",
138
138
  "'react/jsx-runtime'",
139
139
  ],
140
- imports: [`import react from '@vitejs/plugin-react'`],
140
+ imports: [
141
+ options.compiler === 'swc'
142
+ ? `import react from '@vitejs/plugin-react-swc'`
143
+ : `import react from '@vitejs/plugin-react'`,
144
+ ],
141
145
  plugins: ['react()'],
142
146
  }, true);
143
147
  }
@@ -1,5 +1,5 @@
1
1
  import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
2
- import type { Linter } from '@nx/eslint';
2
+ import type { Linter, LinterType } from '@nx/eslint';
3
3
  import type { SupportedStyles } from '../../../typings/style';
4
4
 
5
5
  export interface Schema {
@@ -14,7 +14,7 @@ export interface Schema {
14
14
  importPath?: string;
15
15
  inSourceTests?: boolean;
16
16
  js?: boolean;
17
- linter: Linter;
17
+ linter: Linter | LinterType;
18
18
  name: string;
19
19
  pascalCaseFiles?: boolean;
20
20
  publishable?: boolean;
@@ -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,
@@ -1,5 +1,5 @@
1
1
  import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
2
- import type { Linter } from '@nx/eslint';
2
+ import type { Linter, LinterType } from '@nx/eslint';
3
3
  import type { SupportedStyles } from '../../../typings/style';
4
4
  import type { NormalizedSchema as ApplicationNormalizedSchema } from '../application/schema';
5
5
 
@@ -13,7 +13,7 @@ export interface Schema {
13
13
  globalCss?: boolean;
14
14
  host?: string;
15
15
  js?: boolean;
16
- linter: Linter;
16
+ linter: Linter | LinterType;
17
17
  name: string;
18
18
  pascalCaseFiles?: boolean;
19
19
  routing?: boolean;
@@ -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",
@@ -1,6 +1,6 @@
1
1
  import { StorybookConfigureSchema } from './schema';
2
- import { Tree } from '@nx/devkit';
3
- export declare function storybookConfigurationGenerator(host: Tree, schema: StorybookConfigureSchema): Promise<import("@nx/devkit").GeneratorCallback>;
4
- export declare function storybookConfigurationGeneratorInternal(host: Tree, schema: StorybookConfigureSchema): Promise<import("@nx/devkit").GeneratorCallback>;
2
+ import { type GeneratorCallback, Tree } from '@nx/devkit';
3
+ export declare function storybookConfigurationGenerator(host: Tree, schema: StorybookConfigureSchema): Promise<GeneratorCallback>;
4
+ export declare function storybookConfigurationGeneratorInternal(host: Tree, schema: StorybookConfigureSchema): Promise<GeneratorCallback>;
5
5
  export default storybookConfigurationGenerator;
6
6
  export declare function findWebpackConfig(tree: Tree, projectRoot: string): string | undefined;
@@ -29,6 +29,7 @@ function storybookConfigurationGenerator(host, schema) {
29
29
  });
30
30
  }
31
31
  async function storybookConfigurationGeneratorInternal(host, schema) {
32
+ const tasks = [];
32
33
  const nxJson = (0, devkit_1.readNxJson)(host);
33
34
  const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
34
35
  nxJson.useInferencePlugins !== false;
@@ -42,6 +43,9 @@ async function storybookConfigurationGeneratorInternal(host, schema) {
42
43
  projectConfig.targets['build']?.executor === '@nx/expo:build') {
43
44
  uiFramework = '@storybook/react-webpack5';
44
45
  }
46
+ if (uiFramework === '@storybook/react-vite') {
47
+ tasks.push((0, devkit_1.addDependenciesToPackageJson)(host, {}, { '@vitejs/plugin-react': versions_1.reactViteVersion }));
48
+ }
45
49
  const installTask = await configurationGenerator(host, {
46
50
  project: schema.project,
47
51
  configureCypress: schema.configureCypress,
@@ -55,11 +59,12 @@ async function storybookConfigurationGeneratorInternal(host, schema) {
55
59
  skipFormat: true,
56
60
  addPlugin: schema.addPlugin,
57
61
  });
62
+ tasks.push(installTask);
58
63
  if (schema.generateStories) {
59
64
  await generateStories(host, schema);
60
65
  }
61
66
  await (0, devkit_1.formatFiles)(host);
62
- return installTask;
67
+ return (0, devkit_1.runTasksInSerial)(...tasks);
63
68
  }
64
69
  exports.default = storybookConfigurationGenerator;
65
70
  function findWebpackConfig(tree, projectRoot) {
@@ -1,4 +1,4 @@
1
- import { Linter } from '@nx/eslint';
1
+ import { Linter, LinterType } from '@nx/eslint';
2
2
 
3
3
  export interface StorybookConfigureSchema {
4
4
  project: string;
@@ -6,7 +6,7 @@ export interface StorybookConfigureSchema {
6
6
  generateStories?: boolean;
7
7
  js?: boolean;
8
8
  tsConfiguration?: boolean;
9
- linter?: Linter;
9
+ linter?: Linter | LinterType;
10
10
  ignorePaths?: string[];
11
11
  configureStaticServe?: boolean;
12
12
  configureCypress?: 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
+ }
@@ -0,0 +1,2 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function (tree: Tree): Promise<void>;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
6
+ async function default_1(tree) {
7
+ let usesModuleFederation = false;
8
+ (0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nx/webpack:webpack', (options, projectName, targetName) => {
9
+ const webpackConfig = options.webpackConfig;
10
+ if (!webpackConfig) {
11
+ return;
12
+ }
13
+ const webpackContents = tree.read(webpackConfig, 'utf-8');
14
+ if (['withModuleFederation', 'withModuleFederationForSSR'].some((p) => webpackContents.includes(p))) {
15
+ usesModuleFederation = true;
16
+ }
17
+ });
18
+ if (!usesModuleFederation) {
19
+ return;
20
+ }
21
+ const nxJson = (0, devkit_1.readNxJson)(tree);
22
+ const nxMFDevRemotesEnvVar = 'NX_MF_DEV_REMOTES';
23
+ if (!nxJson.targetDefaults ||
24
+ !nxJson.targetDefaults?.['@nx/webpack:webpack']) {
25
+ nxJson.targetDefaults ??= {};
26
+ nxJson.targetDefaults['@nx/webpack:webpack'] = {
27
+ cache: true,
28
+ inputs: ['production', '^production', { env: nxMFDevRemotesEnvVar }],
29
+ dependsOn: ['^build'],
30
+ };
31
+ }
32
+ else {
33
+ nxJson.targetDefaults['@nx/webpack:webpack'].dependsOn ??= [];
34
+ if (!nxJson.targetDefaults['@nx/webpack:webpack'].dependsOn.includes('^build')) {
35
+ nxJson.targetDefaults['@nx/webpack:webpack'].dependsOn.push('^build');
36
+ }
37
+ nxJson.targetDefaults['@nx/webpack:webpack'].inputs ??= [];
38
+ if (!nxJson.targetDefaults['@nx/webpack:webpack'].inputs.find((i) => typeof i === 'string' ? false : i['env'] === nxMFDevRemotesEnvVar)) {
39
+ nxJson.targetDefaults['@nx/webpack:webpack'].inputs.push({
40
+ env: nxMFDevRemotesEnvVar,
41
+ });
42
+ }
43
+ }
44
+ (0, devkit_1.updateNxJson)(tree, nxJson);
45
+ await (0, devkit_1.formatFiles)(tree);
46
+ }
@@ -12,6 +12,7 @@ function addMfEnvToTargetDefaultInputs(tree) {
12
12
  'production',
13
13
  '^production',
14
14
  ];
15
+ nxJson.targetDefaults[webpackExecutor].dependsOn ??= ['^build'];
15
16
  let mfEnvVarExists = false;
16
17
  for (const input of nxJson.targetDefaults[webpackExecutor].inputs) {
17
18
  if (typeof input === 'object' && input['env'] === mfEnvVar) {
@@ -8,6 +8,7 @@ export declare const babelLoaderVersion = "^9.1.2";
8
8
  export declare const typesReactVersion = "18.3.1";
9
9
  export declare const typesReactDomVersion = "18.3.0";
10
10
  export declare const typesReactIsVersion = "18.3.0";
11
+ export declare const reactViteVersion = "^4.2.0";
11
12
  export declare const typesNodeVersion = "18.16.9";
12
13
  export declare const babelPresetReactVersion = "^7.14.5";
13
14
  export declare const babelCoreVersion = "^7.14.5";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.swcPluginStyledComponentsVersion = exports.swcPluginEmotionVersion = exports.swcPluginStyledJsxVersion = exports.svgrRollupVersion = exports.rollupPluginUrlVersion = exports.sassVersion = exports.lessVersion = exports.moduleFederationEnhancedVersion = exports.moduleFederationNodeVersion = exports.typesCorsVersion = exports.corsVersion = exports.isbotVersion = exports.typesExpressVersion = exports.expressVersion = exports.autoprefixerVersion = exports.tailwindcssVersion = exports.postcssVersion = exports.tsLibVersion = exports.babelPluginStyledComponentsVersion = exports.eslintPluginReactHooksVersion = exports.eslintPluginReactVersion = exports.eslintPluginJsxA11yVersion = exports.eslintPluginImportVersion = exports.reactTestRendererVersion = exports.reactReduxVersion = exports.reduxjsToolkitVersion = exports.testingLibraryReactVersion = exports.reactRouterDomVersion = exports.styledJsxVersion = exports.emotionBabelPlugin = exports.emotionReactVersion = exports.emotionStyledVersion = exports.typesStyledComponentsVersion = exports.styledComponentsVersion = exports.babelCoreVersion = exports.babelPresetReactVersion = exports.typesNodeVersion = exports.typesReactIsVersion = exports.typesReactDomVersion = exports.typesReactVersion = exports.babelLoaderVersion = exports.swcLoaderVersion = exports.reactIsVersion = exports.reactDomVersion = exports.reactVersion = exports.nxRspackVersion = exports.nxVersion = void 0;
3
+ exports.swcPluginStyledComponentsVersion = exports.swcPluginEmotionVersion = exports.swcPluginStyledJsxVersion = exports.svgrRollupVersion = exports.rollupPluginUrlVersion = exports.sassVersion = exports.lessVersion = exports.moduleFederationEnhancedVersion = exports.moduleFederationNodeVersion = exports.typesCorsVersion = exports.corsVersion = exports.isbotVersion = exports.typesExpressVersion = exports.expressVersion = exports.autoprefixerVersion = exports.tailwindcssVersion = exports.postcssVersion = exports.tsLibVersion = exports.babelPluginStyledComponentsVersion = exports.eslintPluginReactHooksVersion = exports.eslintPluginReactVersion = exports.eslintPluginJsxA11yVersion = exports.eslintPluginImportVersion = exports.reactTestRendererVersion = exports.reactReduxVersion = exports.reduxjsToolkitVersion = exports.testingLibraryReactVersion = exports.reactRouterDomVersion = exports.styledJsxVersion = exports.emotionBabelPlugin = exports.emotionReactVersion = exports.emotionStyledVersion = exports.typesStyledComponentsVersion = exports.styledComponentsVersion = exports.babelCoreVersion = exports.babelPresetReactVersion = exports.typesNodeVersion = exports.reactViteVersion = exports.typesReactIsVersion = exports.typesReactDomVersion = exports.typesReactVersion = exports.babelLoaderVersion = exports.swcLoaderVersion = exports.reactIsVersion = exports.reactDomVersion = exports.reactVersion = exports.nxRspackVersion = exports.nxVersion = void 0;
4
4
  exports.nxVersion = require('../../package.json').version;
5
5
  // Always pull the latest version until we merge rspack plugin into the repo.
6
6
  exports.nxRspackVersion = '*';
@@ -12,6 +12,7 @@ exports.babelLoaderVersion = '^9.1.2';
12
12
  exports.typesReactVersion = '18.3.1';
13
13
  exports.typesReactDomVersion = '18.3.0';
14
14
  exports.typesReactIsVersion = '18.3.0';
15
+ exports.reactViteVersion = '^4.2.0';
15
16
  exports.typesNodeVersion = '18.16.9';
16
17
  exports.babelPresetReactVersion = '^7.14.5';
17
18
  exports.babelCoreVersion = '^7.14.5';