diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index e3afd99d82de979..ac8e09f65b608d7 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -154,7 +154,7 @@ jobs: - run: exit 0 testFutureDependencies: - name: React 17 + webpack 5 (Basic, Production, Acceptance) + name: Webpack 5 (Basic, Production, Acceptance) runs-on: ubuntu-latest env: NEXT_TELEMETRY_DISABLED: 1 @@ -171,16 +171,42 @@ jobs: - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} - - run: cat package.json | jq '.resolutions.react = "^17.0.1"' > package.json.tmp && mv package.json.tmp package.json + - run: yarn install --check-files + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} + + - run: yarn list webpack + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} + + - run: xvfb-run node run-tests.js test/integration/{link-ref,production,basic,async-modules,ssr-ctx}/test/index.test.js test/acceptance/*.test.js + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} + + testLegacyReact: + name: React 16 + Webpack 4 (Basic, Production, Acceptance) + runs-on: ubuntu-latest + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + HEADLESS: true + + steps: + - uses: actions/checkout@v2 + + - run: echo ::set-output name=DOCS_CHANGE::$(node skip-docs-change.js echo 'docs-only') + id: docs-change + + - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} + + - run: cat package.json | jq '.resolutions.react = "^16.14.0"' > package.json.tmp && mv package.json.tmp package.json if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} - - run: cat package.json | jq '.resolutions."react-dom" = "^17.0.1"' > package.json.tmp && mv package.json.tmp package.json + - run: cat package.json | jq '.resolutions."react-dom" = "^16.14.0"' > package.json.tmp && mv package.json.tmp package.json if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} - run: yarn install --check-files if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} - - run: yarn list webpack react react-dom + - run: yarn list react react-dom if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs-only' }} - run: xvfb-run node run-tests.js test/integration/{link-ref,production,basic,async-modules,ssr-ctx,worker-loader}/test/index.test.js test/acceptance/*.test.js diff --git a/errors/react-version.md b/errors/react-version.md index cf80364d199908d..cfa4412e8846129 100644 --- a/errors/react-version.md +++ b/errors/react-version.md @@ -5,7 +5,7 @@ Your project is using an old version of `react` or `react-dom` that does not meet the suggested minimum version requirement. -Next.js suggests using, at a minimum, `react@16.10.0` and `react-dom@16.10.0`. +Next.js suggests using, at a minimum, `react@17.0.1` and `react-dom@17.0.1`. Older versions of `react` and `react-dom` do work with Next.js, however, they do not enable all of Next.js' features. @@ -39,8 +39,8 @@ yarn add react@latest react-dom@latest ```json { "dependencies": { - "react": "^16.10.0", - "react-dom": "^16.10.0" + "react": "^17.0.1", + "react-dom": "^17.0.1" } } ``` diff --git a/package.json b/package.json index 9b3340f9237d4b2..bbac3861f33a207 100644 --- a/package.json +++ b/package.json @@ -115,8 +115,8 @@ "prettier": "2.0.5", "pretty-bytes": "5.3.0", "pretty-ms": "7.0.0", - "react": "16.12.0", - "react-dom": "16.12.0", + "react": "17.0.1", + "react-dom": "17.0.1", "react-ssr-prepass": "1.0.8", "release": "6.3.0", "request-promise-core": "1.1.2", diff --git a/packages/next/build/webpack/plugins/font-stylesheet-gathering-plugin.ts b/packages/next/build/webpack/plugins/font-stylesheet-gathering-plugin.ts index 215f5f5c00aa151..ba003408ed629fa 100644 --- a/packages/next/build/webpack/plugins/font-stylesheet-gathering-plugin.ts +++ b/packages/next/build/webpack/plugins/font-stylesheet-gathering-plugin.ts @@ -122,6 +122,10 @@ export class FontStylesheetGatheringPlugin { parser.hooks.call .for('__jsx') .tap(this.constructor.name, jsxNodeHandler) + // New React JSX transform: + parser.hooks.call + .for('imported var') + .tap(this.constructor.name, jsxNodeHandler) }) } } diff --git a/packages/next/cli/next-build.ts b/packages/next/cli/next-build.ts index 6712d13546eb6b2..a3e08062512a813 100755 --- a/packages/next/cli/next-build.ts +++ b/packages/next/cli/next-build.ts @@ -55,11 +55,50 @@ const nextBuild: cliCommand = (argv) => { printAndExit(`> No such directory exists as the project root: ${dir}`) } - return build(dir, null, args['--profile'], args['--debug']).catch((err) => { - console.error('') - console.error('> Build error occurred') - printAndExit(err) - }) + async function preflight() { + const { getPackageVersion } = await import('../lib/get-package-version') + const semver = await import('next/dist/compiled/semver').then( + (res) => res.default + ) + + const reactVersion: string | null = await getPackageVersion({ + cwd: dir, + name: 'react', + }) + if ( + reactVersion && + semver.lt(reactVersion, '17.0.1') && + semver.coerce(reactVersion)?.version !== '0.0.0' + ) { + Log.warn( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + + ' Read more: https://err.sh/next.js/react-version' + ) + } else { + const reactDomVersion: string | null = await getPackageVersion({ + cwd: dir, + name: 'react-dom', + }) + if ( + reactDomVersion && + semver.lt(reactDomVersion, '17.0.1') && + semver.coerce(reactDomVersion)?.version !== '0.0.0' + ) { + Log.warn( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + + ' Read more: https://err.sh/next.js/react-version' + ) + } + } + } + + return preflight() + .then(() => build(dir, null, args['--profile'], args['--debug'])) + .catch((err) => { + console.error('') + console.error('> Build error occurred') + printAndExit(err) + }) } export { nextBuild } diff --git a/packages/next/cli/next-dev.ts b/packages/next/cli/next-dev.ts index 53fa86d444949e1..1190a89f9fa9873 100755 --- a/packages/next/cli/next-dev.ts +++ b/packages/next/cli/next-dev.ts @@ -68,11 +68,11 @@ const nextDev: cliCommand = (argv) => { }) if ( reactVersion && - semver.lt(reactVersion, '16.10.0') && + semver.lt(reactVersion, '17.0.1') && semver.coerce(reactVersion)?.version !== '0.0.0' ) { Log.warn( - 'Fast Refresh is disabled in your application due to an outdated `react` version. Please upgrade 16.10 or newer!' + + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ' Read more: https://err.sh/next.js/react-version' ) } else { @@ -82,11 +82,11 @@ const nextDev: cliCommand = (argv) => { }) if ( reactDomVersion && - semver.lt(reactDomVersion, '16.10.0') && + semver.lt(reactDomVersion, '17.0.1') && semver.coerce(reactDomVersion)?.version !== '0.0.0' ) { Log.warn( - 'Fast Refresh is disabled in your application due to an outdated `react-dom` version. Please upgrade 16.10 or newer!' + + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ' Read more: https://err.sh/next.js/react-version' ) } diff --git a/test/integration/build-output/test/index.test.js b/test/integration/build-output/test/index.test.js index 501c6c75a07d3a3..cf8eca1c83ff25a 100644 --- a/test/integration/build-output/test/index.test.js +++ b/test/integration/build-output/test/index.test.js @@ -94,17 +94,17 @@ describe('Build Output', () => { expect(parseFloat(indexSize) - 266).toBeLessThanOrEqual(0) expect(indexSize.endsWith('B')).toBe(true) - // should be no bigger than 62.2 kb - expect(parseFloat(indexFirstLoad)).toBeCloseTo(62.4, 1) + // should be no bigger than 63.8 kb + expect(parseFloat(indexFirstLoad)).toBeCloseTo(63.8, 1) expect(indexFirstLoad.endsWith('kB')).toBe(true) expect(parseFloat(err404Size) - 3.7).toBeLessThanOrEqual(0) expect(err404Size.endsWith('kB')).toBe(true) - expect(parseFloat(err404FirstLoad)).toBeCloseTo(65.6, 1) + expect(parseFloat(err404FirstLoad)).toBeCloseTo(67, 1) expect(err404FirstLoad.endsWith('kB')).toBe(true) - expect(parseFloat(sharedByAll)).toBeCloseTo(62.1, 1) + expect(parseFloat(sharedByAll)).toBeCloseTo(63.5, 1) expect(sharedByAll.endsWith('kB')).toBe(true) if (_appSize.endsWith('kB')) { @@ -121,7 +121,7 @@ describe('Build Output', () => { expect(parseFloat(mainSize) - 7.3).toBeLessThanOrEqual(0) expect(mainSize.endsWith('kB')).toBe(true) - expect(parseFloat(frameworkSize) - 42).toBeLessThanOrEqual(0) + expect(parseFloat(frameworkSize) - 42.1).toBeLessThanOrEqual(0) expect(frameworkSize.endsWith('kB')).toBe(true) }) @@ -168,7 +168,7 @@ describe('Build Output', () => { expect(parseFloat(indexSize)).toBeGreaterThanOrEqual(2) expect(indexSize.endsWith('kB')).toBe(true) - expect(parseFloat(indexFirstLoad)).toBeLessThanOrEqual(65.2) + expect(parseFloat(indexFirstLoad)).toBeLessThanOrEqual(66.6) expect(parseFloat(indexFirstLoad)).toBeGreaterThanOrEqual(60) expect(indexFirstLoad.endsWith('kB')).toBe(true) }) diff --git a/test/integration/cli/test/index.test.js b/test/integration/cli/test/index.test.js index d6d36778dd0b3f7..a916e79f16c7cfe 100644 --- a/test/integration/cli/test/index.test.js +++ b/test/integration/cli/test/index.test.js @@ -115,6 +115,69 @@ describe('CLI Usage', () => { expect(code).toBe(expectedExitCode) expect(signal).toBe(expectedExitSignal) }) + + test('too old of react version', async () => { + const { stderr } = await runNextCommand(['build', dirOldReact], { + stderr: true, + }) + + expect(stderr).toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + }) + + test('too old of react-dom version', async () => { + const { stderr } = await runNextCommand(['build', dirOldReactDom], { + stderr: true, + }) + + expect(stderr).toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + }) + + test('experimental react version', async () => { + const { stderr } = await runNextCommand(['build', dirExperimentalReact], { + stderr: true, + }) + + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + }) + + test('experimental react-dom version', async () => { + const { stderr } = await runNextCommand( + ['build', dirExperimentalReactDom], + { + stderr: true, + } + ) + + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + }) + + test('recommended react version', async () => { + const { stderr } = await runNextCommand(['build'], { + stderr: true, + }) + + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + }) + + test('recommended react-dom version', async () => { + const { stderr } = await runNextCommand(['build'], { + stderr: true, + }) + + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + }) }) describe('dev', () => { @@ -296,9 +359,8 @@ describe('CLI Usage', () => { }) expect(stderr).toMatch( - 'Fast Refresh is disabled in your application due to an outdated `react` version' + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' ) - expect(stderr).not.toMatch(`react-dom`) await killApp(instance) }) @@ -315,9 +377,8 @@ describe('CLI Usage', () => { }) expect(stderr).toMatch( - 'Fast Refresh is disabled in your application due to an outdated `react-dom` version' + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' ) - expect(stderr).not.toMatch('`react`') await killApp(instance) }) @@ -333,9 +394,9 @@ describe('CLI Usage', () => { }, }) - expect(stderr).not.toMatch('disabled') - expect(stderr).not.toMatch('outdated') - expect(stderr).not.toMatch(`react-dom`) + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) await killApp(instance) }) @@ -351,9 +412,45 @@ describe('CLI Usage', () => { }, }) - expect(stderr).not.toMatch('disabled') - expect(stderr).not.toMatch('outdated') - expect(stderr).not.toMatch('`react`') + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + + await killApp(instance) + }) + + test('recommended react version', async () => { + const port = await findPort() + + let stderr = '' + let instance = await launchApp(dir, port, { + stderr: true, + onStderr(msg) { + stderr += msg + }, + }) + + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) + + await killApp(instance) + }) + + test('recommended react-dom version', async () => { + const port = await findPort() + + let stderr = '' + let instance = await launchApp(dir, port, { + stderr: true, + onStderr(msg) { + stderr += msg + }, + }) + + expect(stderr).not.toMatch( + 'React 17.0.1 or newer will be required to leverage all of the upcoming features in Next.js 11.' + ) await killApp(instance) }) diff --git a/test/integration/size-limit/test/index.test.js b/test/integration/size-limit/test/index.test.js index fcf4002ff8bc6a9..a30cdafa6644443 100644 --- a/test/integration/size-limit/test/index.test.js +++ b/test/integration/size-limit/test/index.test.js @@ -81,6 +81,6 @@ describe('Production response size', () => { const delta = responseSizesBytes / 1024 // Expected difference: < 0.5 - expect(delta).toBeCloseTo(282.6, 0) + expect(delta).toBeCloseTo(284.1, 0) }) }) diff --git a/yarn.lock b/yarn.lock index ee294c2bf16ff07..88aa37b229935ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12970,7 +12970,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@15.7.2, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.7.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" dependencies: @@ -13185,14 +13185,14 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@16.12.0: - version "16.12.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11" +react-dom@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6" + integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.18.0" + scheduler "^0.20.1" react-is@16.13.1, react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4: version "16.13.1" @@ -13209,13 +13209,13 @@ react-ssr-prepass@1.0.8: dependencies: object-is "^1.0.1" -react@16.12.0: - version "16.12.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" +react@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" + integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.2" read-cmd-shim@^1.0.1: version "1.0.5" @@ -14051,9 +14051,10 @@ saxes@^5.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4" +scheduler@^0.20.1: + version "0.20.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c" + integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1"