Skip to content

Commit

Permalink
feat(react): update to react 18 (#9613)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaysoo committed Mar 30, 2022
1 parent 1bc2298 commit b02beb5
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 42 deletions.
36 changes: 14 additions & 22 deletions e2e/next/src/next.test.ts
Expand Up @@ -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 <>
<h1>{greeting}</h1>
<h2>{process.env.NX_CUSTOM_VAR}</h2>
</>;
import React from 'react';
export const Index = ({ greeting }: any) => {
return (
<p>{process.env.NX_CUSTOM_VAR}</p>
);
};
export default Index;
`
Expand All @@ -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');
};
`
);
Expand All @@ -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');
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -419,9 +411,9 @@ describe('Next.js Applications', () => {
}, 300000);
});

function getData(port: number): Promise<any> {
function getData(port: number, path = ''): Promise<any> {
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) => {
Expand Down
17 changes: 17 additions & 0 deletions packages/next/migrations.json
Expand Up @@ -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
}
}
}
}
}
4 changes: 2 additions & 2 deletions 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';
Expand Down
29 changes: 29 additions & 0 deletions packages/react-native/migrations.json
Expand Up @@ -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
}
}
}
}
}
26 changes: 26 additions & 0 deletions 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';
Expand Down Expand Up @@ -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' });
Expand Down
15 changes: 11 additions & 4 deletions packages/react-native/src/generators/init/init.ts
Expand Up @@ -4,17 +4,16 @@ import {
convertNxGenerator,
detectPackageManager,
formatFiles,
logger,
readJson,
removeDependenciesFromPackageJson,
Tree,
} from '@nrwl/devkit';
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,
Expand All @@ -31,6 +30,7 @@ import {
reactNativeSvgVersion,
reactNativeVersion,
reactTestRendererVersion,
reactVersion,
testingLibraryJestNativeVersion,
testingLibraryReactNativeVersion,
typesReactNativeVersion,
Expand Down Expand Up @@ -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,
{
Expand Down
15 changes: 10 additions & 5 deletions 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';
Expand All @@ -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';
Expand All @@ -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';
16 changes: 13 additions & 3 deletions packages/react/migrations.json
Expand Up @@ -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": {
Expand Down Expand Up @@ -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
}
}
Expand Down
@@ -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();
});
});
34 changes: 34 additions & 0 deletions 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;

0 comments on commit b02beb5

Please sign in to comment.