@nx/react 17.0.0-beta.8 → 17.0.0-rc.1

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/babel.d.ts CHANGED
@@ -7,6 +7,9 @@ interface NxReactBabelOptions {
7
7
  decoratorsBeforeExport?: boolean;
8
8
  legacy?: boolean;
9
9
  };
10
+ loose?: boolean;
11
+ /** @deprecated Use `loose` option instead of `classProperties.loose`
12
+ */
10
13
  classProperties?: {
11
14
  loose?: boolean;
12
15
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "17.0.0-beta.8",
3
+ "version": "17.0.0-rc.1",
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": {
@@ -37,11 +37,11 @@
37
37
  "file-loader": "^6.2.0",
38
38
  "minimatch": "3.0.5",
39
39
  "tslib": "^2.3.0",
40
- "@nx/devkit": "17.0.0-beta.8",
41
- "@nx/js": "17.0.0-beta.8",
42
- "@nx/eslint": "17.0.0-beta.8",
43
- "@nx/web": "17.0.0-beta.8",
44
- "@nrwl/react": "17.0.0-beta.8"
40
+ "@nx/devkit": "17.0.0-rc.1",
41
+ "@nx/js": "17.0.0-rc.1",
42
+ "@nx/eslint": "17.0.0-rc.1",
43
+ "@nx/web": "17.0.0-rc.1",
44
+ "@nrwl/react": "17.0.0-rc.1"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public"
@@ -1,8 +1,10 @@
1
1
  import { ExecutorContext } from '@nx/devkit';
2
2
  import { WebDevServerOptions } from '@nx/webpack/src/executors/dev-server/schema';
3
3
  type ModuleFederationDevServerOptions = WebDevServerOptions & {
4
- devRemotes?: string | string[];
4
+ devRemotes?: string[];
5
5
  skipRemotes?: string[];
6
+ static?: boolean;
7
+ isInitialHost?: boolean;
6
8
  };
7
9
  export default function moduleFederationDevServer(options: ModuleFederationDevServerOptions, context: ExecutorContext): AsyncIterableIterator<{
8
10
  success: boolean;
@@ -2,14 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const devkit_1 = require("@nx/devkit");
4
4
  const dev_server_impl_1 = require("@nx/webpack/src/executors/dev-server/dev-server.impl");
5
- const path_1 = require("path");
5
+ const file_server_impl_1 = require("@nx/web/src/executors/file-server/file-server.impl");
6
+ const module_federation_1 = require("@nx/webpack/src/utils/module-federation");
6
7
  const async_iterable_1 = require("@nx/devkit/src/utils/async-iterable");
7
- const chalk = require("chalk");
8
8
  const wait_for_port_open_1 = require("@nx/web/src/utils/wait-for-port-open");
9
- const find_matching_projects_1 = require("nx/src/utils/find-matching-projects");
10
9
  const child_process_1 = require("child_process");
11
- const fs_1 = require("fs");
12
- const internal_1 = require("@nx/js/src/internal");
13
10
  function getBuildOptions(buildTarget, context) {
14
11
  const target = (0, devkit_1.parseTargetString)(buildTarget, context);
15
12
  const buildOptions = (0, devkit_1.readTargetOptions)(target, context);
@@ -17,111 +14,88 @@ function getBuildOptions(buildTarget, context) {
17
14
  ...buildOptions,
18
15
  };
19
16
  }
20
- function getModuleFederationConfig(tsconfigPath, workspaceRoot, projectRoot) {
21
- const moduleFederationConfigPathJS = (0, path_1.join)(workspaceRoot, projectRoot, 'module-federation.config.js');
22
- const moduleFederationConfigPathTS = (0, path_1.join)(workspaceRoot, projectRoot, 'module-federation.config.ts');
23
- let moduleFederationConfigPath = moduleFederationConfigPathJS;
24
- // create a no-op so this can be called with issue
25
- const fullTSconfigPath = tsconfigPath.startsWith(workspaceRoot)
26
- ? tsconfigPath
27
- : (0, path_1.join)(workspaceRoot, tsconfigPath);
28
- let cleanupTranspiler = () => { };
29
- if ((0, fs_1.existsSync)(moduleFederationConfigPathTS)) {
30
- cleanupTranspiler = (0, internal_1.registerTsProject)(fullTSconfigPath);
31
- moduleFederationConfigPath = moduleFederationConfigPathTS;
32
- }
33
- try {
34
- const config = require(moduleFederationConfigPath);
35
- cleanupTranspiler();
36
- return config.default || config;
37
- }
38
- catch {
39
- 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`);
40
- }
41
- }
42
17
  async function* moduleFederationDevServer(options, context) {
43
18
  const nxBin = require.resolve('nx');
44
- const currIter = (0, dev_server_impl_1.default)(options, context);
19
+ const currIter = options.static
20
+ ? (0, file_server_impl_1.default)({
21
+ ...options,
22
+ parallel: false,
23
+ withDeps: false,
24
+ spa: false,
25
+ cors: true,
26
+ }, context)
27
+ : (0, dev_server_impl_1.default)(options, context);
45
28
  const p = context.projectsConfigurations.projects[context.projectName];
46
29
  const buildOptions = getBuildOptions(options.buildTarget, context);
47
- const moduleFederationConfig = getModuleFederationConfig(buildOptions.tsConfig, context.root, p.root);
48
- const remotesToSkip = new Set((0, find_matching_projects_1.findMatchingProjects)(options.skipRemotes, context.projectGraph.nodes) ?? []);
49
- if (remotesToSkip.size > 0) {
50
- devkit_1.logger.info(`Remotes not served automatically: ${[...remotesToSkip.values()].join(', ')}`);
30
+ if (!options.isInitialHost) {
31
+ return yield* currIter;
51
32
  }
52
- const remotesNotInWorkspace = [];
53
- const knownRemotes = (moduleFederationConfig.remotes ?? []).filter((r) => {
54
- const validRemote = Array.isArray(r) ? r[0] : r;
55
- if (remotesToSkip.has(validRemote)) {
56
- return false;
57
- }
58
- else if (!context.projectGraph.nodes[validRemote]) {
59
- remotesNotInWorkspace.push(validRemote);
60
- return false;
61
- }
62
- else {
63
- return true;
64
- }
33
+ const moduleFederationConfig = (0, module_federation_1.getModuleFederationConfig)(buildOptions.tsConfig, context.root, p.root, 'react');
34
+ const remotes = (0, module_federation_1.getRemotes)(options.devRemotes, options.skipRemotes, moduleFederationConfig, {
35
+ projectName: context.projectName,
36
+ projectGraph: context.projectGraph,
37
+ root: context.root,
65
38
  });
66
- if (remotesNotInWorkspace.length > 0) {
67
- devkit_1.logger.warn(`Skipping serving ${remotesNotInWorkspace.join(', ')} as they could not be found in the workspace. Ensure they are served correctly.`);
68
- }
69
- const remotePorts = knownRemotes.map((r) => context.projectGraph.nodes[r].data.targets['serve'].options.port);
70
- const devServeApps = !options.devRemotes
71
- ? []
72
- : Array.isArray(options.devRemotes)
73
- ? (0, find_matching_projects_1.findMatchingProjects)(options.devRemotes, context.projectGraph.nodes)
74
- : (0, find_matching_projects_1.findMatchingProjects)([options.devRemotes], context.projectGraph.nodes);
75
- devkit_1.logger.info(`NX Starting module federation dev-server for ${chalk.bold(context.projectName)} with ${knownRemotes.length} remotes`);
76
- const devRemoteIters = [];
77
39
  let isCollectingStaticRemoteOutput = true;
78
- for (const app of knownRemotes) {
79
- const appName = Array.isArray(app) ? app[0] : app;
80
- if (devServeApps.includes(appName)) {
81
- devRemoteIters.push(await (0, devkit_1.runExecutor)({
82
- project: appName,
83
- target: 'serve',
84
- configuration: context.configurationName,
85
- }, {
86
- watch: true,
87
- }, context));
88
- }
89
- else {
90
- let outWithErr = [];
91
- const staticProcess = (0, child_process_1.fork)(nxBin, [
92
- 'run',
93
- `${appName}:serve-static${context.configurationName ? `:${context.configurationName}` : ''}`,
94
- ], {
95
- cwd: context.root,
96
- stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
97
- });
98
- staticProcess.stdout.on('data', (data) => {
99
- if (isCollectingStaticRemoteOutput) {
100
- outWithErr.push(data.toString());
101
- }
102
- else {
103
- outWithErr = null;
104
- staticProcess.stdout.removeAllListeners('data');
105
- }
106
- });
107
- staticProcess.stderr.on('data', (data) => devkit_1.logger.info(data.toString()));
108
- staticProcess.on('exit', (code) => {
109
- if (code !== 0) {
110
- devkit_1.logger.info(outWithErr.join(''));
111
- throw new Error(`Remote failed to start. See above for errors.`);
112
- }
113
- });
114
- process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
115
- process.on('exit', () => staticProcess.kill('SIGTERM'));
116
- }
40
+ const devRemoteIters = [];
41
+ for (const app of remotes.devRemotes) {
42
+ const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets['serve'];
43
+ const isUsingModuleFederationDevServerExecutor = remoteProjectServeTarget.executor.includes('module-federation-dev-server');
44
+ devRemoteIters.push(await (0, devkit_1.runExecutor)({
45
+ project: app,
46
+ target: 'serve',
47
+ configuration: context.configurationName,
48
+ }, {
49
+ watch: true,
50
+ ...(isUsingModuleFederationDevServerExecutor
51
+ ? { isInitialHost: false }
52
+ : {}),
53
+ }, context));
54
+ }
55
+ for (const app of remotes.staticRemotes) {
56
+ const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets['serve-static'];
57
+ const isUsingModuleFederationDevServerExecutor = remoteProjectServeTarget.executor.includes('module-federation-dev-server');
58
+ let outWithErr = [];
59
+ const staticProcess = (0, child_process_1.fork)(nxBin, [
60
+ 'run',
61
+ `${app}:serve-static${context.configurationName ? `:${context.configurationName}` : ''}`,
62
+ ...(isUsingModuleFederationDevServerExecutor
63
+ ? [`--isInitialHost=false`]
64
+ : []),
65
+ ], {
66
+ cwd: context.root,
67
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
68
+ });
69
+ staticProcess.stdout.on('data', (data) => {
70
+ if (isCollectingStaticRemoteOutput) {
71
+ outWithErr.push(data.toString());
72
+ }
73
+ else {
74
+ outWithErr = null;
75
+ staticProcess.stdout.removeAllListeners('data');
76
+ }
77
+ });
78
+ staticProcess.stderr.on('data', (data) => devkit_1.logger.info(data.toString()));
79
+ staticProcess.on('exit', (code) => {
80
+ if (code !== 0) {
81
+ devkit_1.logger.info(outWithErr.join(''));
82
+ throw new Error(`Remote failed to start. See above for errors.`);
83
+ }
84
+ });
85
+ process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
86
+ process.on('exit', () => staticProcess.kill('SIGTERM'));
117
87
  }
118
88
  return yield* (0, async_iterable_1.combineAsyncIterables)(currIter, ...devRemoteIters, (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
119
- if (remotePorts.length === 0) {
89
+ if (!options.isInitialHost) {
90
+ done();
91
+ return;
92
+ }
93
+ if (remotes.remotePorts.length === 0) {
120
94
  done();
121
95
  return;
122
96
  }
123
97
  try {
124
- await Promise.all(remotePorts.map((port) =>
98
+ await Promise.all(remotes.remotePorts.map((port) =>
125
99
  // Allow 20 minutes for each remote to start, which is plenty of time but we can tweak it later if needed.
126
100
  // Most remotes should start in under 1 minute.
127
101
  (0, wait_for_port_open_1.waitForPortOpen)(port, {
@@ -91,6 +91,16 @@
91
91
  "baseHref": {
92
92
  "type": "string",
93
93
  "description": "Base url for the application being built."
94
+ },
95
+ "static": {
96
+ "type": "boolean",
97
+ "description": "Whether to use a static file server instead of the webpack-dev-server. This should be used for remote applications that are also host applications."
98
+ },
99
+ "isInitialHost": {
100
+ "type": "boolean",
101
+ "description": "Whether the host that is running this executor is the first in the project tree to do so.",
102
+ "default": true,
103
+ "x-priority": "internal"
94
104
  }
95
105
  }
96
106
  }
@@ -38,7 +38,7 @@ async function normalizeOptions(host, options, callingGenerator = '@nx/react:app
38
38
  ? null
39
39
  : options.style;
40
40
  (0, assertion_1.assertValidStyle)(options.style);
41
- if (options.bundler === 'vite') {
41
+ if (options.bundler === 'vite' && options.unitTestRunner !== 'none') {
42
42
  options.unitTestRunner = 'vitest';
43
43
  }
44
44
  const normalized = {
@@ -58,9 +58,7 @@ async function normalizeOptions(host, options, callingGenerator = '@nx/react:app
58
58
  normalized.classComponent = normalized.classComponent ?? false;
59
59
  normalized.compiler = normalized.compiler ?? 'babel';
60
60
  normalized.bundler = normalized.bundler ?? 'webpack';
61
- normalized.unitTestRunner =
62
- normalized.unitTestRunner ??
63
- (normalized.bundler === 'vite' ? 'vitest' : 'jest');
61
+ normalized.unitTestRunner = normalized.unitTestRunner ?? 'jest';
64
62
  normalized.e2eTestRunner = normalized.e2eTestRunner ?? 'cypress';
65
63
  normalized.inSourceTests = normalized.minimal || normalized.inSourceTests;
66
64
  normalized.devServerPort ??= (0, find_free_port_1.findFreePort)(host);
@@ -73,7 +73,7 @@ function addExportsToBarrel(host, options) {
73
73
  tsModule = (0, ensure_typescript_1.ensureTypescript)();
74
74
  }
75
75
  const workspace = (0, devkit_1.getProjects)(host);
76
- const isApp = workspace.get(options.project).projectType === 'application';
76
+ const isApp = workspace.get(options.projectName).projectType === 'application';
77
77
  if (options.export && !isApp) {
78
78
  const indexFilePath = (0, devkit_1.joinPathFragments)(options.projectSourceRoot, options.js ? 'index.js' : 'index.ts');
79
79
  const indexSource = host.read(indexFilePath, 'utf-8');
@@ -35,7 +35,7 @@ async function normalizeOptions(tree, options) {
35
35
  options.inSourceTests = options.inSourceTests ?? false;
36
36
  return {
37
37
  ...options,
38
- project: projectName,
38
+ projectName,
39
39
  directory,
40
40
  styledModule,
41
41
  hasStyles: options.style !== 'none',
@@ -5,7 +5,7 @@ export interface Schema {
5
5
  /**
6
6
  * @deprecated Provide the `directory` option instead and use the `as-provided` format. The project will be determined from the directory provided. It will be removed in Nx v18.
7
7
  */
8
- project: string;
8
+ project?: string;
9
9
  style: SupportedStyles;
10
10
  skipTests?: boolean;
11
11
  directory?: string;
@@ -37,6 +37,7 @@ export interface Schema {
37
37
 
38
38
  export interface NormalizedSchema extends Schema {
39
39
  projectSourceRoot: string;
40
+ projectName: string;
40
41
  fileName: string;
41
42
  filePath: string;
42
43
  className: string;
@@ -7,6 +7,12 @@ const utils_1 = require("./lib/utils");
7
7
  const project_name_and_root_utils_1 = require("@nx/devkit/src/generators/project-name-and-root-utils");
8
8
  const js_1 = require("@nx/js");
9
9
  async function federateModuleGenerator(tree, schema) {
10
+ // Check if the file exists
11
+ if (!tree.exists(schema.path)) {
12
+ throw new Error((0, devkit_1.stripIndents) `The "path" provided does not exist. Please verify the path is correct and pointing to a file that exists in the workspace.
13
+
14
+ Path: ${schema.path}`);
15
+ }
10
16
  const tasks = [];
11
17
  // Check remote exists
12
18
  const remote = (0, utils_1.checkRemoteExists)(tree, schema.remote);
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "path": {
27
27
  "type": "string",
28
- "description": "The path to locate the federated module.",
28
+ "description": "The path to locate the federated module. This path should be relative to the workspace root and the file should exist.",
29
29
  "x-prompt": "What is the path to the module to be federated?"
30
30
  },
31
31
  "remote": {
@@ -45,7 +45,7 @@ function addExportsToBarrel(host, options) {
45
45
  tsModule = (0, ensure_typescript_1.ensureTypescript)();
46
46
  }
47
47
  const workspace = (0, devkit_1.getProjects)(host);
48
- const isApp = workspace.get(options.project).projectType === 'application';
48
+ const isApp = workspace.get(options.projectName).projectType === 'application';
49
49
  if (options.export && !isApp) {
50
50
  const indexFilePath = (0, devkit_1.joinPathFragments)(options.projectSourceRoot, options.js ? 'index.js' : 'index.ts');
51
51
  const indexSource = host.read(indexFilePath, 'utf-8');
@@ -88,7 +88,7 @@ async function normalizeOptions(host, options) {
88
88
  : 'use-'.concat(fileName);
89
89
  const hookName = 'use'.concat(className);
90
90
  const hookTypeName = 'Use'.concat(className);
91
- const project = (0, devkit_1.getProjects)(host).get(options.project);
91
+ const project = (0, devkit_1.getProjects)(host).get(projectName);
92
92
  const { sourceRoot: projectSourceRoot, projectType } = project;
93
93
  if (options.export && projectType === 'application') {
94
94
  devkit_1.logger.warn(`The "--export" option should not be used with applications and will do nothing.`);
@@ -112,6 +112,7 @@ async function normalizeOptions(host, options) {
112
112
  hookTypeName,
113
113
  fileName: hookFilename,
114
114
  projectSourceRoot,
115
+ projectName,
115
116
  };
116
117
  }
117
118
  function assertValidOptions(options) {
@@ -115,7 +115,7 @@ async function normalizeOptions(host, options) {
115
115
  // for it without needing to specify --appProject.
116
116
  options.appProject =
117
117
  options.appProject ||
118
- (projectType === 'application' ? options.project : undefined);
118
+ (projectType === 'application' ? projectName : undefined);
119
119
  if (options.appProject) {
120
120
  const appConfig = projects.get(options.appProject);
121
121
  if (appConfig.projectType !== 'application') {
@@ -1,6 +1,9 @@
1
1
  export interface Schema {
2
2
  name: string;
3
- project: string;
3
+ /**
4
+ * @deprecated Provide the `directory` option instead and use the `as-provided` format. The project will be determined from the directory provided. It will be removed in Nx v18.
5
+ */
6
+ project?: string;
4
7
  directory?: string;
5
8
  appProject?: string;
6
9
  js?: string;
@@ -44,6 +44,9 @@
44
44
  "type": "string",
45
45
  "description": "Path to ignore."
46
46
  },
47
+ "default": [
48
+ "*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"
49
+ ],
47
50
  "examples": [
48
51
  "apps/my-app/src/not-stories/**",
49
52
  "**/**/src/**/not-stories/**",
@@ -78,6 +78,9 @@
78
78
  "type": "string",
79
79
  "description": "Path to ignore."
80
80
  },
81
+ "default": [
82
+ "*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"
83
+ ],
81
84
  "examples": [
82
85
  "apps/my-app/src/not-stories/**",
83
86
  "**/**/src/**/not-stories/**",