diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index 6d95facbebd42..f7f2bdf58aadd 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -161,21 +161,12 @@ describe('Next.js Applications', () => { updateFile( `apps/${appName}/pages/index.tsx`, ` - import React, { useEffect, useState } from 'react'; - - export const Index = () => { - const [greeting, setGreeting] = useState(''); - - useEffect(() => { - fetch('/external-api/hello') - .then(r => r.text()) - .then(setGreeting); - }, []); - - return <> -

{greeting}

-

{process.env.NX_CUSTOM_VAR}

- ; + import React from 'react'; + + export const Index = ({ greeting }: any) => { + return ( +

{process.env.NX_CUSTOM_VAR}

+ ); }; export default Index; ` @@ -185,7 +176,7 @@ describe('Next.js Applications', () => { `apps/${appName}/pages/api/hello.js`, ` export default (_req, res) => { - res.status(200).send('Welcome to ${appName}'); + res.status(200).send('Welcome'); }; ` ); @@ -198,9 +189,10 @@ describe('Next.js Applications', () => { } ); - const data = await getData(port); - expect(data).toContain(`Welcome to ${appName}`); - expect(data).toContain(`test value from a file`); + const apiData = await getData(port, '/external-api/hello'); + const pageData = await getData(port, '/'); + expect(apiData).toContain(`Welcome`); + expect(pageData).toContain(`test value from a file`); try { await promisifiedTreeKill(p.pid, 'SIGKILL'); @@ -356,7 +348,7 @@ describe('Next.js Applications', () => { ); const data = await getData(port); - expect(data).toContain(`Welcome to ${appName}`); + expect(data).toContain(`Welcome`); try { await promisifiedTreeKill(p.pid, 'SIGKILL'); @@ -419,9 +411,9 @@ describe('Next.js Applications', () => { }, 300000); }); -function getData(port: number): Promise { +function getData(port: number, path = ''): Promise { return new Promise((resolve) => { - http.get(`http://localhost:${port}`, (res) => { + http.get(`http://localhost:${port}${path}`, (res) => { expect(res.statusCode).toEqual(200); let data = ''; res.on('data', (chunk) => { diff --git a/packages/next/migrations.json b/packages/next/migrations.json index dfeaea3b84de2..51beb49861e94 100644 --- a/packages/next/migrations.json +++ b/packages/next/migrations.json @@ -220,6 +220,23 @@ "alwaysAddToPackageJson": false } } + }, + "13.10.0": { + "version": "13.10.0-beta.1", + "packages": { + "next": { + "version": "12.1.2", + "alwaysAddToPackageJson": false + }, + "eslint-config-next": { + "version": "12.1.2", + "alwaysAddToPackageJson": false + }, + "sass": { + "version": "1.49.9", + "alwaysAddToPackageJson": false + } + } } } } diff --git a/packages/next/src/utils/versions.ts b/packages/next/src/utils/versions.ts index 17ce54ebe6f3d..ddc0126ad4e34 100644 --- a/packages/next/src/utils/versions.ts +++ b/packages/next/src/utils/versions.ts @@ -1,7 +1,7 @@ export const nxVersion = '*'; -export const nextVersion = '12.1.0'; -export const eslintConfigNextVersion = '12.1.1-canary.3'; +export const nextVersion = '12.1.2'; +export const eslintConfigNextVersion = '12.1.2'; export const sassVersion = '1.43.2'; export const lessLoader = '10.2.0'; export const stylusLoader = '6.2.0'; diff --git a/packages/react-native/migrations.json b/packages/react-native/migrations.json index 053e63657d7e9..e765a3b0b2b77 100644 --- a/packages/react-native/migrations.json +++ b/packages/react-native/migrations.json @@ -440,6 +440,35 @@ "alwaysAddToPackageJson": false } } + }, + "13.10.0": { + "version": "13.10.0-beta.1", + "packages": { + "react-native": { + "version": "0.67.4", + "alwaysAddToPackageJson": false + }, + "metro": { + "version": "0.70.0", + "alwaysAddToPackageJson": false + }, + "@types/react": { + "version": "17.0.43", + "alwaysAddToPackageJson": false + }, + "metro-react-native-babel-preset": { + "version": "0.70.0", + "alwaysAddToPackageJson": false + }, + "@testing-library/react-native": { + "version": "9.1.0", + "alwaysAddToPackageJson": false + }, + "@babel/runtime": { + "version": "7.17.8", + "alwaysAddToPackageJson": false + } + } } } } diff --git a/packages/react-native/src/generators/init/init.spec.ts b/packages/react-native/src/generators/init/init.spec.ts index 1bdbc6e63b1cd..e8917d8fa6369 100644 --- a/packages/react-native/src/generators/init/init.spec.ts +++ b/packages/react-native/src/generators/init/init.spec.ts @@ -1,3 +1,4 @@ +import { logger } from '@nrwl/devkit'; import { Tree, readJson, updateJson } from '@nrwl/devkit'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { reactNativeInitGenerator } from './init'; @@ -34,6 +35,31 @@ describe('init', () => { expect(content).toMatch(/# Nested node_modules/); }); + it.each` + version + ${'18.0.0'} + ${'~18.0.0'} + ${'^18.0.0'} + `( + 'should warn if React v18 is already installed in workspace', + async ({ version }) => { + const spy = jest.spyOn(logger, 'warn'); + spy.mockImplementation(() => {}); + updateJson(tree, 'package.json', (json) => { + json.dependencies = { + react: version, + }; + return json; + }); + + await reactNativeInitGenerator(tree, { e2eTestRunner: 'none' }); + + expect(spy).toHaveBeenCalledWith(expect.stringContaining('incompatible')); + + spy.mockRestore(); + } + ); + describe('defaultCollection', () => { it('should be set if none was set before', async () => { await reactNativeInitGenerator(tree, { e2eTestRunner: 'none' }); diff --git a/packages/react-native/src/generators/init/init.ts b/packages/react-native/src/generators/init/init.ts index f3764cc8493b7..b45ab12534c25 100644 --- a/packages/react-native/src/generators/init/init.ts +++ b/packages/react-native/src/generators/init/init.ts @@ -4,6 +4,8 @@ import { convertNxGenerator, detectPackageManager, formatFiles, + logger, + readJson, removeDependenciesFromPackageJson, Tree, } from '@nrwl/devkit'; @@ -11,10 +13,7 @@ import { Schema } from './schema'; import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; import { jestInitGenerator } from '@nrwl/jest'; import { detoxInitGenerator } from '@nrwl/detox'; -import { - reactVersion, - typesReactVersion, -} from '@nrwl/react/src/utils/versions'; +import { typesReactVersion } from '@nrwl/react/src/utils/versions'; import { babelRuntimeVersion, @@ -31,6 +30,7 @@ import { reactNativeSvgVersion, reactNativeVersion, reactTestRendererVersion, + reactVersion, testingLibraryJestNativeVersion, testingLibraryReactNativeVersion, typesReactNativeVersion, @@ -65,6 +65,13 @@ export async function reactNativeInitGenerator(host: Tree, schema: Schema) { export function updateDependencies(host: Tree) { const isPnpm = detectPackageManager(host.root) === 'pnpm'; + const { dependencies = {} } = readJson(host, 'package.json'); + // TODO(jack): Remove this once React Native 0.68.0 is out. + if (dependencies['react']?.match(/[\^~]?18/)) { + logger.warn( + `React version ${dependencies['react']} is incompatible with React Native version ${reactNativeVersion}. Nx will downgrade the version to ${reactVersion}.` + ); + } return addDependenciesToPackageJson( host, { diff --git a/packages/react-native/src/utils/versions.ts b/packages/react-native/src/utils/versions.ts index 8f2b4b913635b..d9cc73d4a9bb2 100644 --- a/packages/react-native/src/utils/versions.ts +++ b/packages/react-native/src/utils/versions.ts @@ -1,9 +1,14 @@ export const nxVersion = '*'; -export const reactNativeVersion = '0.67.3'; +export const reactNativeVersion = '0.67.4'; export const typesReactNativeVersion = '0.67.3'; -export const metroVersion = '0.69.1'; +export const metroVersion = '0.70.0'; + +// TODO(jack): Remove this once react-native 0.68.0 is released. +// v18 is only supported on the New Architecture +// See: https://reactnative.dev/blog/2022/03/15/an-update-on-the-new-architecture-rollout +export const reactVersion = '17.0.2'; export const reactNativeCommunityCli = '7.0.1'; export const reactNativeCommunityCliIos = '7.0.1'; @@ -12,9 +17,9 @@ export const reactNativeCommunityCliAndroid = '7.0.1'; export const reactNativeConfigVersion = '1.4.5'; export const reactNativeAsyncStorageAsyncStorageVersion = '1.16.3'; -export const metroReactNativeBabelPresetVersion = '0.69.1'; +export const metroReactNativeBabelPresetVersion = '0.70.0'; -export const testingLibraryReactNativeVersion = '9.0.0'; +export const testingLibraryReactNativeVersion = '9.1.0'; export const testingLibraryJestNativeVersion = '4.0.4'; export const jestReactNativeVersion = '18.0.0'; @@ -24,4 +29,4 @@ export const reactTestRendererVersion = '17.0.2'; export const reactNativeSvgTransformerVersion = '1.0.0'; export const reactNativeSvgVersion = '12.3.0'; -export const babelRuntimeVersion = '7.17.7'; +export const babelRuntimeVersion = '7.17.8'; diff --git a/packages/react/migrations.json b/packages/react/migrations.json index fd325e02154eb..3378935fc04d2 100644 --- a/packages/react/migrations.json +++ b/packages/react/migrations.json @@ -29,6 +29,12 @@ "version": "13.0.0-beta.0", "description": "Migrate Storybook to use webpack 5", "factory": "./src/migrations/update-13-0-0/migrate-storybook-to-webpack-5" + }, + "update-react-18-13.10.0": { + "cli": "nx", + "version": "13.10.0-beta.0", + "description": "Update to React 18", + "factory": "./src/migrations/update-13-10-0/update-13-10-0" } }, "packageJsonUpdates": { @@ -268,12 +274,16 @@ "version": "5.3.5", "alwaysAddToPackageJson": false }, + "eslint-plugin-react": { + "version": "7.29.4", + "alwaysAddToPackageJson": false + }, "styled-jsx": { - "version": "5.0.1", + "version": "5.0.2", "alwaysAddToPackageJson": false }, - "eslint-plugin-react": { - "version": "7.29.4", + "eslint-plugin-react-hooks": { + "version": "4.4.0", "alwaysAddToPackageJson": false } } diff --git a/packages/react/src/migrations/update-13-10-0/update-13-10-0.spec.ts b/packages/react/src/migrations/update-13-10-0/update-13-10-0.spec.ts new file mode 100644 index 0000000000000..39862a215b18e --- /dev/null +++ b/packages/react/src/migrations/update-13-10-0/update-13-10-0.spec.ts @@ -0,0 +1,50 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { writeJson, readJson, Tree } from '@nrwl/devkit'; +import migrate from './update-13-10-0'; + +describe('Update tsconfig for React apps', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it('should update to React 18 if React Native is not installed', async () => { + writeJson(tree, 'package.json', { + dependencies: { + react: '17.0.2', + }, + }); + + const installTask = await migrate(tree); + expect(installTask).toBeDefined(); + }); + + it('should update to React 18 if React Native 0.68.0-rc.4 is installed', async () => { + writeJson(tree, 'package.json', { + dependencies: { + react: '17.0.2', + 'react-native': '0.68.0-rc.4', + }, + }); + + await migrate(tree); + + const installTask = await migrate(tree); + expect(installTask).toBeDefined(); + }); + + it('should skip update to React 18 if React Native 0.67.0 is installed', async () => { + writeJson(tree, 'package.json', { + dependencies: { + react: '17.0.2', + 'react-native': '0.67.0', + }, + }); + + await migrate(tree); + + const installTask = await migrate(tree); + expect(installTask).not.toBeDefined(); + }); +}); diff --git a/packages/react/src/migrations/update-13-10-0/update-13-10-0.ts b/packages/react/src/migrations/update-13-10-0/update-13-10-0.ts new file mode 100644 index 0000000000000..8da7193550ac0 --- /dev/null +++ b/packages/react/src/migrations/update-13-10-0/update-13-10-0.ts @@ -0,0 +1,34 @@ +import { + addDependenciesToPackageJson, + readJson, + logger, + Tree, +} from '@nrwl/devkit'; + +// Putting this here because React Native 0.67 is incompatible with React 18. +// Waiting for 0.68 to come out with support for React 18. +// TODO(jack): For Nx 14 let's add another migration for React 18 in migrations.json because by then React Native 0.68.0 should be released. +export async function updateToReact18(host: Tree) { + const { dependencies } = readJson(host, 'package.json'); + if ( + dependencies['react-native'] && + !dependencies['react-native'].match(/[\^~]?0.68/) + ) { + logger.info( + `React Native ${dependencies['react-native']} is incompatible with React 18. Skipping update until React Native 0.68.0 is released.` + ); + } else { + return addDependenciesToPackageJson( + host, + { + react: '18.0.0', + 'react-dom': '18.0.0', + 'react-is': '18.0.0', + 'react-test-renderer': '18.0.0', + }, + {} + ); + } +} + +export default updateToReact18; diff --git a/packages/react/src/utils/versions.ts b/packages/react/src/utils/versions.ts index c7538d59aece6..099a99e8b54ed 100755 --- a/packages/react/src/utils/versions.ts +++ b/packages/react/src/utils/versions.ts @@ -1,8 +1,8 @@ export const nxVersion = '*'; -export const reactVersion = '17.0.2'; -export const reactDomVersion = '17.0.2'; -export const reactIsVersion = '17.0.2'; +export const reactVersion = '18.0.0'; +export const reactDomVersion = '18.0.0'; +export const reactIsVersion = '18.0.0'; export const typesReactVersion = '17.0.43'; export const typesReactDomVersion = '17.0.14'; export const typesReactIsVersion = '17.0.3'; @@ -14,7 +14,7 @@ export const emotionStyledVersion = '11.8.1'; export const emotionReactVersion = '11.8.2'; export const emotionBabelPlugin = '11.7.2'; -export const styledJsxVersion = '5.0.1'; +export const styledJsxVersion = '5.0.2'; export const reactRouterDomVersion = '5.3.0'; export const typesReactRouterDomVersion = '5.3.3'; @@ -24,11 +24,11 @@ export const testingLibraryReactHooksVersion = '7.0.2'; export const reduxjsToolkitVersion = '1.8.0'; export const reactReduxVersion = '7.2.6'; -export const reactTestRendererVersion = '17.0.2'; +export const reactTestRendererVersion = '18.0.0'; export const eslintPluginImportVersion = '2.25.4'; export const eslintPluginJsxA11yVersion = '6.5.1'; export const eslintPluginReactVersion = '7.29.4'; -export const eslintPluginReactHooksVersion = '4.3.0'; +export const eslintPluginReactHooksVersion = '4.4.0'; export const babelPluginStyledComponentsVersion = '1.10.7';