Skip to content

Commit

Permalink
feat(react): add support for React 19 for new Workspaces (#29286)
Browse files Browse the repository at this point in the history
## Current Behavior
We currently have no support for React 19, generating only React 18
applications.

## Expected Behavior
Add utils to determine what version of React is installed in the
workspace.
If React 18 is the main version of react installed, continue to generate
React 18 projects.
If React 19 is the main version of react installed, generate React 19
projects.
If no React version is installed or can be determined, generate React 19
projects.
  • Loading branch information
Coly010 authored Jan 17, 2025
1 parent bc0566f commit a468d72
Show file tree
Hide file tree
Showing 30 changed files with 512 additions and 155 deletions.
2 changes: 1 addition & 1 deletion docs/generated/packages/react/generators/application.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"bundler": {
"description": "The bundler to use.",
"type": "string",
"enum": ["vite", "webpack", "rspack", "rsbuild"],
"enum": ["vite", "rsbuild", "rspack", "webpack"],
"x-prompt": "Which bundler do you want to use to build the application?",
"default": "vite",
"x-priority": "important"
Expand Down
2 changes: 1 addition & 1 deletion docs/generated/packages/workspace/generators/preset.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"bundler": {
"description": "The bundler to use for building the application.",
"type": "string",
"enum": ["webpack", "vite", "rspack", "esbuild"],
"enum": ["vite", "rspack", "rsbuild", "esbuild", "webpack"],
"default": "vite"
},
"docker": {
Expand Down
4 changes: 2 additions & 2 deletions e2e/next/src/__snapshots__/next.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exports[`Next.js Applications next-env.d.ts should remain the same after a build
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
"
`;

Expand All @@ -14,6 +14,6 @@ exports[`Next.js Applications next-env.d.ts should remain the same after a build
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
"
`;
4 changes: 2 additions & 2 deletions e2e/vite/src/vite-legacy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,14 @@ export default App;
const results = runCLI(`build ${app} --buildLibsFromSource=true`);
expect(results).toContain('Successfully ran target build for project');
// this should be more modules than build from dist
expect(results).toContain('40 modules transformed');
expect(results).toContain('43 modules transformed');
});

it('should build app from libs dist', () => {
const results = runCLI(`build ${app} --buildLibsFromSource=false`);
expect(results).toContain('Successfully ran target build for project');
// this should be less modules than building from source
expect(results).toContain('38 modules transformed');
expect(results).toContain('41 modules transformed');
});

it('should build app from libs without package.json in lib', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"dependencies": {
"@nx/devkit": "file:../devkit",
"@babel/plugin-proposal-decorators": "^7.22.7",
"@svgr/webpack": "^8.0.1",
"@svgr/webpack": "^8.1.0",
"copy-webpack-plugin": "^10.2.4",
"file-loader": "^6.2.0",
"ignore": "^5.0.4",
Expand Down
12 changes: 5 additions & 7 deletions packages/next/src/generators/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import {
} from '@nx/devkit';
import { initGenerator as jsInitGenerator } from '@nx/js';
import { setupTailwindGenerator } from '@nx/react';
import {
testingLibraryReactVersion,
typesReactDomVersion,
typesReactVersion,
} from '@nx/react/src/utils/versions';
import { testingLibraryReactVersion } from '@nx/react/src/utils/versions';
import { getReactDependenciesVersionsToInstall } from '@nx/react/src/utils/version-utils';

import { normalizeOptions } from './lib/normalize-options';
import { Schema } from './schema';
Expand Down Expand Up @@ -104,9 +101,10 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
}

if (!options.skipPackageJson) {
const reactVersions = await getReactDependenciesVersionsToInstall(host);
const devDependencies: Record<string, string> = {
'@types/react': typesReactVersion,
'@types/react-dom': typesReactDomVersion,
'@types/react': reactVersions['@types/react'],
'@types/react-dom': reactVersions['@types/react-dom'],
};

if (options.unitTestRunner && options.unitTestRunner !== 'none') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/<%- appDirType %>/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/<%- appDirType %>/api-reference/config/typescript for more information.
4 changes: 2 additions & 2 deletions packages/next/src/generators/application/lib/add-e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,12 @@ async function getNextE2EWebServerInfo(
{
plugin: '@nx/next/plugin',
serveTargetName: 'devTargetName',
serveStaticTargetName: 'serveStaticTargetName',
serveStaticTargetName: 'startTargetName',
configFilePath,
},
{
defaultServeTargetName: defaultServeTarget,
defaultServeStaticTargetName: 'serve-static',
defaultServeStaticTargetName: 'start',
defaultE2EWebServerAddress: `http://127.0.0.1:${e2ePort}`,
defaultE2ECiBaseUrl: `http://localhost:${e2ePort}`,
defaultE2EPort: e2ePort,
Expand Down
24 changes: 17 additions & 7 deletions packages/next/src/generators/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,33 @@ import {
createProjectGraphAsync,
} from '@nx/devkit';
import { addPlugin } from '@nx/devkit/src/utils/add-plugin';
import { reactDomVersion, reactVersion } from '@nx/react/src/utils/versions';
import {
getReactDependenciesVersionsToInstall,
isReact18,
} from '@nx/react/src/utils/version-utils';
import { addGitIgnoreEntry } from '../../utils/add-gitignore-entry';
import { nextVersion, nxVersion } from '../../utils/versions';
import { nxVersion } from '../../utils/versions';
import { getNextDependenciesVersionsToInstall } from '../../utils/version-utils';
import type { InitSchema } from './schema';

function updateDependencies(host: Tree, schema: InitSchema) {
async function updateDependencies(host: Tree, schema: InitSchema) {
const tasks: GeneratorCallback[] = [];

tasks.push(removeDependenciesFromPackageJson(host, ['@nx/next'], []));

const versions = await getNextDependenciesVersionsToInstall(
host,
await isReact18(host)
);
const reactVersions = await getReactDependenciesVersionsToInstall(host);

tasks.push(
addDependenciesToPackageJson(
host,
{
next: nextVersion,
react: reactVersion,
'react-dom': reactDomVersion,
next: versions.next,
react: reactVersions.react,
'react-dom': reactVersions['react-dom'],
},
{
'@nx/next': nxVersion,
Expand Down Expand Up @@ -86,7 +96,7 @@ export async function nextInitGeneratorInternal(

let installTask: GeneratorCallback = () => {};
if (!schema.skipPackageJson) {
installTask = updateDependencies(host, schema);
installTask = await updateDependencies(host, schema);
}

return installTask;
Expand Down
20 changes: 10 additions & 10 deletions packages/next/src/plugins/__snapshots__/plugin.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ exports[`@nx/next/plugin integrated projects should create nodes 1`] = `
},
},
"my-serve-static": {
"executor": "@nx/web:file-server",
"command": "next start",
"dependsOn": [
"my-build",
],
"options": {
"buildTarget": "my-build",
"port": 3000,
"spa": false,
"staticFilePath": "{projectRoot}/out",
"cwd": "my-app",
},
},
"my-start": {
Expand Down Expand Up @@ -121,12 +121,12 @@ exports[`@nx/next/plugin root projects should create nodes 1`] = `
},
},
"serve-static": {
"executor": "@nx/web:file-server",
"command": "next start",
"dependsOn": [
"build",
],
"options": {
"buildTarget": "build",
"port": 3000,
"spa": false,
"staticFilePath": "{projectRoot}/out",
"cwd": ".",
},
},
"start": {
Expand Down
9 changes: 7 additions & 2 deletions packages/next/src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export interface NextPluginOptions {
buildTargetName?: string;
devTargetName?: string;
startTargetName?: string;
/**
* @deprecated Use `startTargetName` instead.
*/
serveStaticTargetName?: string;
buildDepsTargetName?: string;
watchDepsTargetName?: string;
Expand Down Expand Up @@ -172,9 +175,11 @@ async function buildNextTargets(

targets[options.devTargetName] = getDevTargetConfig(projectRoot);

targets[options.startTargetName] = getStartTargetConfig(options, projectRoot);
const startTarget = getStartTargetConfig(options, projectRoot);

targets[options.serveStaticTargetName] = getStaticServeTargetConfig(options);
targets[options.startTargetName] = startTarget;

targets[options.serveStaticTargetName] = startTarget;

addBuildAndWatchDepsTargets(
context.workspaceRoot,
Expand Down
55 changes: 55 additions & 0 deletions packages/next/src/utils/version-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { type Tree, readJson, createProjectGraphAsync } from '@nx/devkit';
import { clean, coerce, major } from 'semver';
import { nextVersion, next14Version } from './versions';

type NextDependenciesVersions = {
next: string;
};

export async function getNextDependenciesVersionsToInstall(
tree: Tree,
isReact18 = false
): Promise<NextDependenciesVersions> {
if (await isNext14(tree)) {
return {
next: next14Version,
};
} else {
return {
next: nextVersion,
};
}
}

export async function isNext14(tree: Tree) {
let installedNextVersion = await getInstalledNextVersionFromGraph();
if (!installedNextVersion) {
installedNextVersion = getInstalledNextVersion(tree);
}
return major(installedNextVersion) === 14;
}

export function getInstalledNextVersion(tree: Tree): string {
const pkgJson = readJson(tree, 'package.json');
const installedNextVersion =
pkgJson.dependencies && pkgJson.dependencies['next'];

if (
!installedNextVersion ||
installedNextVersion === 'latest' ||
installedNextVersion === 'next'
) {
return clean(nextVersion) ?? coerce(nextVersion).version;
}

return clean(installedNextVersion) ?? coerce(installedNextVersion).version;
}

export async function getInstalledNextVersionFromGraph() {
const graph = await createProjectGraphAsync();
const nextDep = graph.externalNodes?.['npm:next'];
if (!nextDep) {
return undefined;
}
return clean(nextDep.data.version) ?? coerce(nextDep.data.version).version;
}
3 changes: 2 additions & 1 deletion packages/next/src/utils/versions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const nxVersion = require('../../package.json').version;

export const nextVersion = '14.2.16';
export const nextVersion = '~15.1.4';
export const next14Version = '~14.2.16';
export const eslintConfigNextVersion = '14.2.16';
export const sassVersion = '1.62.1';
export const lessLoader = '11.1.0';
Expand Down
9 changes: 9 additions & 0 deletions packages/react/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,15 @@
"alwaysAddToPackageJson": false
}
}
},
"20.3.0": {
"version": "20.3.0-beta.0",
"packages": {
"@testing-library/react": {
"version": "16.1.0",
"alwaysAddToPackageJson": false
}
}
}
}
}
3 changes: 2 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"@nx/web": "file:../web",
"@nx/module-federation": "file:../module-federation",
"express": "^4.19.2",
"http-proxy-middleware": "^3.0.3"
"http-proxy-middleware": "^3.0.3",
"semver": "^7.6.3"
},
"publishConfig": {
"access": "public"
Expand Down
Loading

0 comments on commit a468d72

Please sign in to comment.