@nx/react 23.1.0-beta.4 → 23.1.0-beta.6

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.
@@ -6,8 +6,9 @@ const tslib_1 = require("tslib");
6
6
  const internal_1 = require("@nx/devkit/internal");
7
7
  const assert_supported_react_version_1 = require("../../utils/assert-supported-react-version");
8
8
  const devkit_1 = require("@nx/devkit");
9
+ const internal_2 = require("@nx/devkit/internal");
9
10
  const js_1 = require("@nx/js");
10
- const internal_2 = require("@nx/js/internal");
11
+ const internal_3 = require("@nx/js/internal");
11
12
  const create_ts_config_1 = require("../../utils/create-ts-config");
12
13
  const init_1 = tslib_1.__importDefault(require("../init/init"));
13
14
  const create_application_files_1 = require("./lib/create-application-files");
@@ -34,7 +35,7 @@ async function applicationGenerator(tree, schema) {
34
35
  async function applicationGeneratorInternal(tree, schema) {
35
36
  (0, assert_supported_react_version_1.assertSupportedReactVersion)(tree);
36
37
  const tasks = [];
37
- const addTsPlugin = (0, internal_2.shouldConfigureTsSolutionSetup)(tree, schema.addPlugin, schema.useTsSolution);
38
+ const addTsPlugin = (0, internal_3.shouldConfigureTsSolutionSetup)(tree, schema.addPlugin, schema.useTsSolution);
38
39
  const jsInitTask = await (0, js_1.initGenerator)(tree, {
39
40
  ...schema,
40
41
  tsConfigName: schema.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
@@ -72,18 +73,23 @@ async function applicationGeneratorInternal(tree, schema) {
72
73
  });
73
74
  tasks.push(initTask);
74
75
  if (!options.addPlugin) {
75
- const nxJson = (0, devkit_1.readNxJson)(tree);
76
- nxJson.targetDefaults ??= {};
77
- if (!Object.keys(nxJson.targetDefaults).includes('build')) {
78
- nxJson.targetDefaults.build = {
76
+ const nxJson = (0, devkit_1.readNxJson)(tree) ?? {};
77
+ const existing = findBuildDefault(nxJson.targetDefaults);
78
+ if (!existing) {
79
+ (0, internal_2.upsertTargetDefault)(tree, nxJson, {
80
+ target: 'build',
79
81
  cache: true,
80
82
  dependsOn: ['^build'],
81
- };
83
+ });
84
+ (0, devkit_1.updateNxJson)(tree, nxJson);
82
85
  }
83
- else if (!nxJson.targetDefaults.build.dependsOn) {
84
- nxJson.targetDefaults.build.dependsOn = ['^build'];
86
+ else if (!existing.dependsOn) {
87
+ (0, internal_2.upsertTargetDefault)(tree, nxJson, {
88
+ target: 'build',
89
+ dependsOn: ['^build'],
90
+ });
91
+ (0, devkit_1.updateNxJson)(tree, nxJson);
85
92
  }
86
- (0, devkit_1.updateNxJson)(tree, nxJson);
87
93
  }
88
94
  if (options.bundler === 'webpack') {
89
95
  await (0, add_webpack_1.initWebpack)(tree, options, tasks);
@@ -102,7 +108,7 @@ async function applicationGeneratorInternal(tree, schema) {
102
108
  // If we are using the new TS solution
103
109
  // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project
104
110
  if (options.isUsingTsSolutionConfig) {
105
- await (0, internal_2.addProjectToTsSolutionWorkspace)(tree, options.appProjectRoot);
111
+ await (0, internal_3.addProjectToTsSolutionWorkspace)(tree, options.appProjectRoot);
106
112
  }
107
113
  const lintTask = await (0, add_linting_1.addLinting)(tree, options);
108
114
  tasks.push(lintTask);
@@ -151,14 +157,14 @@ async function applicationGeneratorInternal(tree, schema) {
151
157
  });
152
158
  }
153
159
  // Only for the new TS solution
154
- (0, internal_2.updateTsconfigFiles)(tree, options.appProjectRoot, 'tsconfig.app.json', {
160
+ (0, internal_3.updateTsconfigFiles)(tree, options.appProjectRoot, 'tsconfig.app.json', {
155
161
  jsx: 'react-jsx',
156
162
  module: 'esnext',
157
163
  moduleResolution: 'bundler',
158
164
  }, options.linter === 'eslint'
159
165
  ? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
160
166
  : undefined, options.useReactRouter ? 'app' : 'src');
161
- (0, internal_2.sortPackageJsonFields)(tree, options.appProjectRoot);
167
+ (0, internal_3.sortPackageJsonFields)(tree, options.appProjectRoot);
162
168
  if (!options.skipFormat) {
163
169
  await (0, devkit_1.formatFiles)(tree);
164
170
  }
@@ -167,4 +173,19 @@ async function applicationGeneratorInternal(tree, schema) {
167
173
  });
168
174
  return (0, devkit_1.runTasksInSerial)(...tasks);
169
175
  }
176
+ function findBuildDefault(td) {
177
+ if (!td)
178
+ return undefined;
179
+ const value = td['build'];
180
+ if (value === undefined)
181
+ return undefined;
182
+ if (Array.isArray(value)) {
183
+ const found = value.find((e) => e.filter === undefined);
184
+ if (!found)
185
+ return undefined;
186
+ const { filter: _f, ...rest } = found;
187
+ return rest;
188
+ }
189
+ return value;
190
+ }
170
191
  exports.default = applicationGenerator;
@@ -2,15 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setupSsrGenerator = setupSsrGenerator;
4
4
  const devkit_1 = require("@nx/devkit");
5
+ const internal_1 = require("@nx/devkit/internal");
5
6
  const assert_supported_react_version_1 = require("../../utils/assert-supported-react-version");
6
- const internal_1 = require("@nx/js/internal");
7
+ const internal_2 = require("@nx/js/internal");
7
8
  const path_1 = require("path");
8
9
  const ast_utils_1 = require("../../utils/ast-utils");
9
10
  const versions_1 = require("../../utils/versions");
10
11
  let tsModule;
11
12
  function readEntryFile(host, path) {
12
13
  if (!tsModule) {
13
- tsModule = (0, internal_1.ensureTypescript)();
14
+ tsModule = (0, internal_2.ensureTypescript)();
14
15
  }
15
16
  const content = host.read(path, 'utf-8');
16
17
  return {
@@ -45,7 +46,7 @@ async function setupSsrGenerator(tree, options) {
45
46
  ].map((importPath) => {
46
47
  return {
47
48
  importPath,
48
- filePath: (0, devkit_1.joinPathFragments)((0, internal_1.getProjectSourceRoot)(projectConfig, tree), `${importPath}.tsx`),
49
+ filePath: (0, devkit_1.joinPathFragments)((0, internal_2.getProjectSourceRoot)(projectConfig, tree), `${importPath}.tsx`),
49
50
  };
50
51
  });
51
52
  const appComponentInfo = appImportCandidates.find((candidate) => tree.exists(candidate.filePath));
@@ -161,9 +162,6 @@ async function setupSsrGenerator(tree, options) {
161
162
  'server',
162
163
  ];
163
164
  }
164
- nxJson.targetDefaults ??= {};
165
- nxJson.targetDefaults['server'] ??= {};
166
- nxJson.targetDefaults.server.cache = true;
167
165
  (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectRoot, {
168
166
  tmpl: '',
169
167
  port: Number(options?.serverPort) || 4200,
@@ -184,6 +182,7 @@ async function setupSsrGenerator(tree, options) {
184
182
  const changes = (0, devkit_1.applyChangesToString)(content, (0, ast_utils_1.addStaticRouter)(serverEntry, source));
185
183
  tree.write(serverEntry, changes);
186
184
  }
185
+ (0, internal_1.upsertTargetDefault)(tree, nxJson, { target: 'server', cache: true });
187
186
  (0, devkit_1.updateNxJson)(tree, nxJson);
188
187
  const installTask = (0, devkit_1.addDependenciesToPackageJson)(tree, {
189
188
  express: versions_1.expressVersion,
@@ -1,6 +1,6 @@
1
1
  # React 18 -> 19 Migration Instructions for LLM
2
2
 
3
- Migrate the Nx workspace's React projects from 18 to 19. Run the codemods first, fix the rest by hand, build after each project.
3
+ Migrate the Nx workspace's React projects from 18 to 19. Run the codemods first, fix the rest by hand, typecheck and build after each project.
4
4
 
5
5
  ## Step 1: Codemods
6
6
 
@@ -35,17 +35,55 @@ npx types-react-codemod@latest refobject-defaults ./PROJECT_PATH # RefObje
35
35
 
36
36
  ## Step 4: Types
37
37
 
38
- `@types/react@19`: `useRef` needs an initial arg, implicit `children` removed (declare it on props), `JSX` global moved. The types codemod handles most.
38
+ `@types/react@19` moves the `JSX` namespace out of global scope into the `react` module (`React.JSX`); `useRef` needs an initial arg; implicit `children` is removed (declare it on props). The `scoped-jsx` codemod (part of `preset-19`) rewrites JSX type _usages_: it points `JSX.Element`, `JSX.IntrinsicElements`, and the like at the `react`-scoped namespace.
39
+
40
+ It does NOT rewrite global `JSX` _augmentations_. A custom element or web component typed with a global augmentation:
41
+
42
+ ```ts
43
+ declare global {
44
+ namespace JSX {
45
+ interface IntrinsicElements {
46
+ 'my-element': ...;
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ no longer merges into the JSX that React resolves under the automatic runtime (`jsx: "react-jsx"`), so the element becomes a `TS2339` unknown-property error in every consuming `.tsx`. Re-target the augmentation at the `react` module:
53
+
54
+ ```ts
55
+ import type {} from 'react';
56
+
57
+ declare module 'react' {
58
+ namespace JSX {
59
+ interface IntrinsicElements {
60
+ 'my-element': ...;
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ The `import type {} from 'react'` is required when the file (or its tsconfig `types`) does not otherwise pull in `react`; without `react` in the program the augmentation fails with `TS2664: Invalid module name in augmentation`. Keep any `eslint-disable` comment already on the `namespace` line.
39
67
 
40
68
  ## Validate
41
69
 
70
+ Run build and typecheck across the affected projects, then lint and test:
71
+
42
72
  ```bash
43
73
  nx run PROJECT:build
44
- nx affected -t build,lint,test
74
+ nx affected -t build,typecheck,lint,test
45
75
  ```
46
76
 
77
+ Most React 19 breakages are type-level (the `JSX` namespace move, implicit `children`, ref types) and surface only at `typecheck`, not at `build` or `test`. A type change in a shared library often errors only in its consumers, so re-run until every project that depends on what you changed is green, not just the project you edited.
78
+
47
79
  ## Notes for LLM
48
80
 
49
81
  - Codemods first (API + types), then manual.
50
- - One project at a time, build after each.
82
+ - One project at a time; typecheck and build after each.
83
+ - A custom element or JSX augmentation may live in a non-React library that React code pulls in through a bare side-effect import (`import '@scope/ui';`). Fix it there; `typecheck` on the consumers points you to it.
51
84
  - Confirm third-party libs support React 19 before bumping.
85
+
86
+ ## References
87
+
88
+ - React 19 upgrade guide (the TypeScript section covers the `JSX`, `useRef`, and `ref` changes): https://react.dev/blog/2024/04/25/react-19-upgrade-guide
89
+ - `types-react-codemod` (what each codemod does, including `scoped-jsx`): https://github.com/eps1lon/types-react-codemod
@@ -2,24 +2,28 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.addMfEnvToTargetDefaultInputs = addMfEnvToTargetDefaultInputs;
4
4
  const devkit_1 = require("@nx/devkit");
5
+ const internal_1 = require("@nx/devkit/internal");
5
6
  function addMfEnvToTargetDefaultInputs(tree, bundler) {
6
- const nxJson = (0, devkit_1.readNxJson)(tree);
7
+ const nxJson = (0, devkit_1.readNxJson)(tree) ?? {};
7
8
  const executor = bundler === 'rspack' ? '@nx/rspack:rspack' : '@nx/webpack:webpack';
8
9
  const mfEnvVar = 'NX_MF_DEV_REMOTES';
9
- nxJson.targetDefaults ??= {};
10
- nxJson.targetDefaults[executor] ??= {};
11
- nxJson.targetDefaults[executor].inputs ??= ['production', '^production'];
12
- nxJson.targetDefaults[executor].dependsOn ??= ['^build'];
10
+ const existing = (0, internal_1.findTargetDefault)(nxJson.targetDefaults, { executor });
11
+ const inputs = [...(existing?.inputs ?? ['production', '^production'])];
13
12
  let mfEnvVarExists = false;
14
- for (const input of nxJson.targetDefaults[executor].inputs) {
13
+ for (const input of inputs) {
15
14
  if (typeof input === 'object' && input['env'] === mfEnvVar) {
16
15
  mfEnvVarExists = true;
17
16
  break;
18
17
  }
19
18
  }
20
19
  if (!mfEnvVarExists) {
21
- nxJson.targetDefaults[executor].inputs.push({ env: mfEnvVar });
20
+ inputs.push({ env: mfEnvVar });
22
21
  }
23
- nxJson.targetDefaults[executor].cache = true;
22
+ (0, internal_1.upsertTargetDefault)(tree, nxJson, {
23
+ executor,
24
+ cache: true,
25
+ inputs,
26
+ dependsOn: existing?.dependsOn ?? ['^build'],
27
+ });
24
28
  (0, devkit_1.updateNxJson)(tree, nxJson);
25
29
  }
@@ -32,7 +32,7 @@ async function configureCypressCT(tree, options) {
32
32
  throw new Error(`Cypress Component Testing is not currently supported for this project. Either 'executer' is not defined in '${target} target' of '${project} project.json' or executer present is not valid one. Valid ones are ${JSON.stringify([...options.validExecutorNames])}. Please check https://github.com/nrwl/nx/issues/21546 for more information.`);
33
33
  }
34
34
  }
35
- const { addDefaultCTConfig, getProjectCypressConfigPath, } = require('@nx/cypress/internal');
35
+ const { addDefaultCTConfig, getProjectCypressConfigPath, getInstalledCypressMajorVersion, } = require('@nx/cypress/internal');
36
36
  const ctConfigOptions = {
37
37
  bundler: options.bundler ?? (await getActualBundler(tree, options, found)),
38
38
  };
@@ -49,7 +49,7 @@ async function configureCypressCT(tree, options) {
49
49
  ctConfigOptions.buildTarget = found.target;
50
50
  }
51
51
  const cypressConfigFilePath = getProjectCypressConfigPath(tree, projectConfig.root);
52
- const updatedCyConfig = await addDefaultCTConfig(tree.read(cypressConfigFilePath, 'utf-8'), ctConfigOptions, '@nx/react/plugins/component-testing');
52
+ const updatedCyConfig = await addDefaultCTConfig(tree.read(cypressConfigFilePath, 'utf-8'), ctConfigOptions, '@nx/react/plugins/component-testing', getInstalledCypressMajorVersion(tree));
53
53
  tree.write(cypressConfigFilePath, updatedCyConfig);
54
54
  return found;
55
55
  }
@@ -26,7 +26,7 @@ export declare const reduxjsToolkitVersion = "^2.5.0";
26
26
  export declare const reactReduxVersion = "^9.2.0";
27
27
  export declare const eslintPluginImportVersion = "2.31.0";
28
28
  export declare const eslintPluginJsxA11yVersion = "6.10.1";
29
- export declare const eslintPluginReactVersion = "7.35.0";
29
+ export declare const eslintPluginReactVersion = "^7.35.0";
30
30
  export declare const eslintPluginReactHooksVersion = "5.0.0";
31
31
  export declare const tsLibVersion = "^2.3.0";
32
32
  export declare const postcssVersion = "8.4.38";
@@ -33,7 +33,7 @@ exports.reduxjsToolkitVersion = '^2.5.0';
33
33
  exports.reactReduxVersion = '^9.2.0';
34
34
  exports.eslintPluginImportVersion = '2.31.0';
35
35
  exports.eslintPluginJsxA11yVersion = '6.10.1';
36
- exports.eslintPluginReactVersion = '7.35.0';
36
+ exports.eslintPluginReactVersion = '^7.35.0';
37
37
  exports.eslintPluginReactHooksVersion = '5.0.0';
38
38
  exports.tsLibVersion = '^2.3.0';
39
39
  exports.postcssVersion = '8.4.38';
package/migrations.json CHANGED
@@ -246,6 +246,18 @@
246
246
  "alwaysAddToPackageJson": false
247
247
  }
248
248
  }
249
+ },
250
+ "23.1.0-eslint-plugin-react": {
251
+ "version": "23.1.0-beta.5",
252
+ "requires": {
253
+ "eslint-plugin-react": "<7.35.0"
254
+ },
255
+ "packages": {
256
+ "eslint-plugin-react": {
257
+ "version": "^7.35.0",
258
+ "alwaysAddToPackageJson": false
259
+ }
260
+ }
249
261
  }
250
262
  }
251
263
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/react",
3
- "version": "23.1.0-beta.4",
3
+ "version": "23.1.0-beta.6",
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": {
@@ -108,25 +108,25 @@
108
108
  "express": "^4.21.2",
109
109
  "http-proxy-middleware": "^3.0.5",
110
110
  "semver": "^7.6.3",
111
- "@nx/devkit": "23.1.0-beta.4",
112
- "@nx/js": "23.1.0-beta.4",
113
- "@nx/eslint": "23.1.0-beta.4",
114
- "@nx/web": "23.1.0-beta.4",
115
- "@nx/module-federation": "23.1.0-beta.4",
116
- "@nx/rollup": "23.1.0-beta.4"
111
+ "@nx/devkit": "23.1.0-beta.6",
112
+ "@nx/js": "23.1.0-beta.6",
113
+ "@nx/eslint": "23.1.0-beta.6",
114
+ "@nx/web": "23.1.0-beta.6",
115
+ "@nx/module-federation": "23.1.0-beta.6",
116
+ "@nx/rollup": "23.1.0-beta.6"
117
117
  },
118
118
  "devDependencies": {
119
- "@nx/cypress": "23.1.0-beta.4",
120
- "@nx/playwright": "23.1.0-beta.4",
121
- "@nx/rsbuild": "23.1.0-beta.4",
122
- "@nx/vite": "23.1.0-beta.4",
123
- "@nx/vitest": "23.1.0-beta.4",
124
- "@nx/webpack": "23.1.0-beta.4",
125
- "@nx/storybook": "23.1.0-beta.4",
126
- "nx": "23.1.0-beta.4"
119
+ "@nx/cypress": "23.1.0-beta.6",
120
+ "@nx/playwright": "23.1.0-beta.6",
121
+ "@nx/rsbuild": "23.1.0-beta.6",
122
+ "@nx/vite": "23.1.0-beta.6",
123
+ "@nx/vitest": "23.1.0-beta.6",
124
+ "@nx/webpack": "23.1.0-beta.6",
125
+ "@nx/storybook": "23.1.0-beta.6",
126
+ "nx": "23.1.0-beta.6"
127
127
  },
128
128
  "optionalDependencies": {
129
- "@nx/vite": "23.1.0-beta.4"
129
+ "@nx/vite": "23.1.0-beta.6"
130
130
  },
131
131
  "publishConfig": {
132
132
  "access": "public"