@nx/react 18.2.2 → 18.2.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "18.2.2",
3
+ "version": "18.2.3",
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, 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": {
@@ -34,13 +34,14 @@
34
34
  "@phenomnomnominal/tsquery": "~5.0.1",
35
35
  "@svgr/webpack": "^8.0.1",
36
36
  "chalk": "^4.1.0",
37
+ "file-loader": "^6.2.0",
37
38
  "minimatch": "9.0.3",
38
39
  "tslib": "^2.3.0",
39
- "@nx/devkit": "18.2.2",
40
- "@nx/js": "18.2.2",
41
- "@nx/eslint": "18.2.2",
42
- "@nx/web": "18.2.2",
43
- "@nrwl/react": "18.2.2"
40
+ "@nx/devkit": "18.2.3",
41
+ "@nx/js": "18.2.3",
42
+ "@nx/eslint": "18.2.3",
43
+ "@nx/web": "18.2.3",
44
+ "@nrwl/react": "18.2.3"
44
45
  },
45
46
  "publishConfig": {
46
47
  "access": "public"
@@ -7,6 +7,26 @@ function applyReactConfig(options, config = {}) {
7
7
  addHotReload(config);
8
8
  if (options.svgr !== false) {
9
9
  removeSvgLoaderIfPresent(config);
10
+ // TODO(v20): Remove file-loader and use `?react` querystring to differentiate between asset and SVGR.
11
+ // It should be:
12
+ // use: [{
13
+ // test: /\.svg$/i,
14
+ // type: 'asset',
15
+ // resourceQuery: /react/, // *.svg?react
16
+ // },
17
+ // {
18
+ // test: /\.svg$/i,
19
+ // issuer: /\.[jt]sx?$/,
20
+ // resourceQuery: { not: [/react/] }, // exclude react component if *.svg?react
21
+ // use: ['@svgr/webpack'],
22
+ // }],
23
+ // See:
24
+ // - SVGR: https://react-svgr.com/docs/webpack/#use-svgr-and-asset-svg-in-the-same-project
25
+ // - Vite: https://www.npmjs.com/package/vite-plugin-svgr
26
+ // - Rsbuild: https://github.com/web-infra-dev/rsbuild/pull/1783
27
+ // Note: We also need a migration for any projects that are using SVGR to convert
28
+ // `import { ReactComponent as X } from './x.svg` to
29
+ // `import X from './x.svg?react';
10
30
  config.module.rules.push({
11
31
  test: /\.svg$/,
12
32
  issuer: /\.(js|ts|md)x?$/,
@@ -19,6 +39,12 @@ function applyReactConfig(options, config = {}) {
19
39
  ref: true,
20
40
  },
21
41
  },
42
+ {
43
+ loader: require.resolve('file-loader'),
44
+ options: {
45
+ name: '[name].[hash].[ext]',
46
+ },
47
+ },
22
48
  ],
23
49
  });
24
50
  }
@@ -7,6 +7,7 @@ type ModuleFederationDevServerOptions = WebDevServerOptions & {
7
7
  isInitialHost?: boolean;
8
8
  parallel?: number;
9
9
  staticRemotesPort?: number;
10
+ pathToManifestFile?: string;
10
11
  };
11
12
  type StaticRemoteConfig = {
12
13
  basePath: string;
@@ -11,6 +11,8 @@ const cache_directory_1 = require("nx/src/utils/cache-directory");
11
11
  const node_child_process_1 = require("node:child_process");
12
12
  const node_path_1 = require("node:path");
13
13
  const node_fs_1 = require("node:fs");
14
+ const fs_1 = require("fs");
15
+ const path_1 = require("path");
14
16
  function getBuildOptions(buildTarget, context) {
15
17
  const target = (0, devkit_1.parseTargetString)(buildTarget, context);
16
18
  const buildOptions = (0, devkit_1.readTargetOptions)(target, context);
@@ -56,27 +58,30 @@ function startStaticRemotesFileServer(staticRemotesConfig, context, options) {
56
58
  }, context);
57
59
  return staticRemotesIter;
58
60
  }
59
- async function startDevRemotes(remotes, context, options) {
60
- const devRemoteIters = [];
61
- for (const app of remotes.devRemotes) {
62
- const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets['serve'];
61
+ async function startRemotes(remotes, context, options, target = 'serve') {
62
+ const remoteIters = [];
63
+ for (const app of remotes) {
64
+ const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets[target];
63
65
  const isUsingModuleFederationDevServerExecutor = remoteProjectServeTarget.executor.includes('module-federation-dev-server');
64
- devRemoteIters.push(await (0, devkit_1.runExecutor)({
66
+ const overrides = target === 'serve'
67
+ ? {
68
+ watch: true,
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
+ ...(isUsingModuleFederationDevServerExecutor
74
+ ? { isInitialHost: false }
75
+ : {}),
76
+ }
77
+ : {};
78
+ remoteIters.push(await (0, devkit_1.runExecutor)({
65
79
  project: app,
66
- target: 'serve',
80
+ target,
67
81
  configuration: context.configurationName,
68
- }, {
69
- watch: true,
70
- ...(options.host ? { host: options.host } : {}),
71
- ...(options.ssl ? { ssl: options.ssl } : {}),
72
- ...(options.sslCert ? { sslCert: options.sslCert } : {}),
73
- ...(options.sslKey ? { sslKey: options.sslKey } : {}),
74
- ...(isUsingModuleFederationDevServerExecutor
75
- ? { isInitialHost: false }
76
- : {}),
77
- }, context));
82
+ }, overrides, context));
78
83
  }
79
- return devRemoteIters;
84
+ return remoteIters;
80
85
  }
81
86
  async function buildStaticRemotes(staticRemotesConfig, nxBin, context, options) {
82
87
  if (!staticRemotesConfig.remotes.length) {
@@ -155,6 +160,17 @@ async function* moduleFederationDevServer(options, context) {
155
160
  : (0, dev_server_impl_1.default)(options, context);
156
161
  const p = context.projectsConfigurations.projects[context.projectName];
157
162
  const buildOptions = getBuildOptions(options.buildTarget, context);
163
+ let pathToManifestFile = (0, node_path_1.join)(context.root, p.sourceRoot, 'assets/module-federation.manifest.json');
164
+ if (options.pathToManifestFile) {
165
+ const userPathToManifestFile = (0, node_path_1.join)(context.root, options.pathToManifestFile);
166
+ if (!(0, fs_1.existsSync)(userPathToManifestFile)) {
167
+ throw new Error(`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`);
168
+ }
169
+ else if ((0, path_1.extname)(options.pathToManifestFile) !== '.json') {
170
+ throw new Error(`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`);
171
+ }
172
+ pathToManifestFile = userPathToManifestFile;
173
+ }
158
174
  if (!options.isInitialHost) {
159
175
  return yield* currIter;
160
176
  }
@@ -163,7 +179,7 @@ async function* moduleFederationDevServer(options, context) {
163
179
  projectName: context.projectName,
164
180
  projectGraph: context.projectGraph,
165
181
  root: context.root,
166
- });
182
+ }, pathToManifestFile);
167
183
  if (remotes.devRemotes.length > 0 && !initialStaticRemotesPorts) {
168
184
  options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => {
169
185
  const remotePort = context.projectGraph.nodes[r].data.targets['serve'].options.port;
@@ -177,11 +193,12 @@ async function* moduleFederationDevServer(options, context) {
177
193
  }
178
194
  const staticRemotesConfig = parseStaticRemotesConfig(remotes.staticRemotes, context);
179
195
  await buildStaticRemotes(staticRemotesConfig, nxBin, context, options);
180
- const devRemoteIters = await startDevRemotes(remotes, context, options);
196
+ const devRemoteIters = await startRemotes(remotes.devRemotes, context, options, 'serve');
197
+ const dynamicRemotesIters = await startRemotes(remotes.dynamicRemotes, context, options, 'serve-static');
181
198
  const staticRemotesIter = remotes.staticRemotes.length > 0
182
199
  ? startStaticRemotesFileServer(staticRemotesConfig, context, options)
183
200
  : undefined;
184
- return yield* (0, async_iterable_1.combineAsyncIterables)(currIter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
201
+ return yield* (0, async_iterable_1.combineAsyncIterables)(currIter, ...devRemoteIters, ...dynamicRemotesIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
185
202
  if (!options.isInitialHost) {
186
203
  done();
187
204
  return;
@@ -109,6 +109,10 @@
109
109
  "staticRemotesPort": {
110
110
  "type": "number",
111
111
  "description": "The port at which to serve the file-server for the static remotes."
112
+ },
113
+ "pathToManifestFile": {
114
+ "type": "string",
115
+ "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."
112
116
  }
113
117
  }
114
118
  }
@@ -34,13 +34,13 @@ async function addE2e(tree, options) {
34
34
  // the name and root are already normalized, instruct the generator to use them as is
35
35
  bundler: options.bundler === 'rspack' ? 'webpack' : options.bundler,
36
36
  skipFormat: true,
37
- devServerTarget: `${options.projectName}:serve`,
38
- baseUrl: `http://localhost:${options.devServerPort ?? 4200}`,
37
+ devServerTarget: `${options.projectName}:${options.e2eWebServerTarget}`,
38
+ baseUrl: options.e2eWebServerAddress,
39
39
  jsx: true,
40
40
  rootProject: options.rootProject,
41
41
  webServerCommands: hasNxBuildPlugin
42
42
  ? {
43
- default: `nx run ${options.projectName}:serve`,
43
+ default: `nx run ${options.projectName}:${options.e2eWebServerTarget}`,
44
44
  production: `nx run ${options.projectName}:preview`,
45
45
  }
46
46
  : undefined,
@@ -66,8 +66,8 @@ async function addE2e(tree, options) {
66
66
  js: false,
67
67
  linter: options.linter,
68
68
  setParserOptionsProject: options.setParserOptionsProject,
69
- webServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx serve ${options.name}`,
70
- webServerAddress: `http://localhost:${options.devServerPort ?? 4200}`,
69
+ webServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx ${options.e2eWebServerTarget} ${options.name}`,
70
+ webServerAddress: options.e2eWebServerAddress,
71
71
  rootProject: options.rootProject,
72
72
  addPlugin: options.addPlugin,
73
73
  });
@@ -32,8 +32,35 @@ async function normalizeOptions(host, options, callingGenerator = '@nx/react:app
32
32
  options.addPlugin ??= addPlugin;
33
33
  options.rootProject = appProjectRoot === '.';
34
34
  options.projectNameAndRootFormat = projectNameAndRootFormat;
35
+ let e2eWebServerTarget = 'serve';
36
+ if (options.addPlugin) {
37
+ if (nxJson.plugins) {
38
+ for (const plugin of nxJson.plugins) {
39
+ if (options.bundler === 'vite' &&
40
+ typeof plugin === 'object' &&
41
+ plugin.plugin === '@nx/vite/plugin' &&
42
+ plugin.options.serveTargetName) {
43
+ e2eWebServerTarget = plugin.options
44
+ .serveTargetName;
45
+ }
46
+ else if (options.bundler === 'webpack' &&
47
+ typeof plugin === 'object' &&
48
+ plugin.plugin === '@nx/webpack/plugin' &&
49
+ plugin.options.serveTargetName) {
50
+ e2eWebServerTarget = plugin.options
51
+ .serveTargetName;
52
+ }
53
+ }
54
+ }
55
+ }
56
+ let e2ePort = options.devServerPort ?? 4200;
57
+ if (nxJson.targetDefaults?.[e2eWebServerTarget] &&
58
+ nxJson.targetDefaults?.[e2eWebServerTarget].options?.port) {
59
+ e2ePort = nxJson.targetDefaults?.[e2eWebServerTarget].options?.port;
60
+ }
35
61
  const e2eProjectName = options.rootProject ? 'e2e' : `${appProjectName}-e2e`;
36
62
  const e2eProjectRoot = options.rootProject ? 'e2e' : `${appProjectRoot}-e2e`;
63
+ const e2eWebServerAddress = `http://localhost:${e2ePort}`;
37
64
  const parsedTags = options.tags
38
65
  ? options.tags.split(',').map((s) => s.trim())
39
66
  : [];
@@ -52,6 +79,9 @@ async function normalizeOptions(host, options, callingGenerator = '@nx/react:app
52
79
  appProjectRoot,
53
80
  e2eProjectName,
54
81
  e2eProjectRoot,
82
+ e2eWebServerAddress,
83
+ e2eWebServerTarget,
84
+ e2ePort,
55
85
  parsedTags,
56
86
  fileName,
57
87
  styledModule,
@@ -36,6 +36,9 @@ export interface NormalizedSchema<T extends Schema = Schema> extends T {
36
36
  appProjectRoot: string;
37
37
  e2eProjectName: string;
38
38
  e2eProjectRoot: string;
39
+ e2eWebServerAddress: string;
40
+ e2eWebServerTarget: string;
41
+ e2ePort: number;
39
42
  parsedTags: string[];
40
43
  fileName: string;
41
44
  styledModule: null | SupportedStyles;