Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto enable React's new JSX transform on 17.x #16603

Merged
merged 21 commits into from Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/build_test_deploy.yml
Expand Up @@ -96,8 +96,8 @@ jobs:
steps:
- run: exit 0

testWebpack5:
name: webpack 5 (Basic, Production, Acceptance)
testFutureDependencies:
name: React 17 + webpack 5 (Basic, Production, Acceptance)
runs-on: ubuntu-latest
env:
NEXT_TELEMETRY_DISABLED: 1
Expand All @@ -108,6 +108,8 @@ jobs:
- uses: actions/checkout@v2
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- run: cat package.json | jq '.resolutions.webpack = "^5.0.0-beta.22"' > package.json.tmp && mv package.json.tmp package.json
- run: cat package.json | jq '.resolutions.react = "^17.0.0-rc.1"' > package.json.tmp && mv package.json.tmp package.json
- run: cat package.json | jq '.resolutions."react-dom" = "^17.0.0-rc.1"' > package.json.tmp && mv package.json.tmp package.json
- run: yarn install --check-files
- run: node run-tests.js test/integration/production/test/index.test.js
- run: node run-tests.js test/integration/basic/test/index.test.js
Expand Down
8 changes: 4 additions & 4 deletions package.json
Expand Up @@ -37,9 +37,9 @@
},
"pre-commit": "lint-staged",
"devDependencies": {
"@babel/plugin-proposal-object-rest-spread": "7.9.6",
"@babel/preset-flow": "7.9.0",
"@babel/preset-react": "7.9.4",
"@babel/plugin-proposal-object-rest-spread": "7.11.0",
"@babel/preset-flow": "7.10.4",
"@babel/preset-react": "7.10.4",
"@fullhuman/postcss-purgecss": "1.3.0",
"@mdx-js/loader": "0.18.0",
"@types/cheerio": "0.22.16",
Expand All @@ -53,7 +53,7 @@
"@zeit/next-sass": "1.0.2-canary.2",
"@zeit/next-typescript": "1.1.2-canary.0",
"abort-controller": "3.0.0",
"amphtml-validator": "1.0.30",
"amphtml-validator": "1.0.33",
"async-sema": "3.0.1",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.3",
Expand Down
17 changes: 8 additions & 9 deletions packages/next/build/babel/preset.ts
Expand Up @@ -65,6 +65,9 @@ module.exports = (
const supportsESM = api.caller(supportsStaticESM)
const isServer = api.caller((caller: any) => !!caller && caller.isServer)
const isModern = api.caller((caller: any) => !!caller && caller.isModern)
const hasJsxRuntime = Boolean(
api.caller((caller: any) => !!caller && caller.hasJsxRuntime)
)

const isLaxModern =
isModern ||
Expand Down Expand Up @@ -113,7 +116,7 @@ module.exports = (
// This adds @babel/plugin-transform-react-jsx-source and
// @babel/plugin-transform-react-jsx-self automatically in development
development: isDevelopment || isTest,
pragma: '__jsx',
...(hasJsxRuntime ? { runtime: 'automatic' } : { pragma: '__jsx' }),
...options['preset-react'],
},
],
Expand All @@ -123,7 +126,7 @@ module.exports = (
],
],
plugins: [
[
!hasJsxRuntime && [
require('./plugins/jsx-pragma'),
{
// This produces the following injected import for modules containing JSX:
Expand Down Expand Up @@ -181,14 +184,10 @@ module.exports = (
require('@babel/plugin-proposal-optional-chaining'),
require('@babel/plugin-proposal-nullish-coalescing-operator'),
isServer && require('@babel/plugin-syntax-bigint'),
[require('@babel/plugin-proposal-numeric-separator').default, false],
// Always compile numeric separator because the resulting number is
// smaller.
require('@babel/plugin-proposal-numeric-separator'),
require('@babel/plugin-proposal-export-namespace-from'),
].filter(Boolean),
overrides: [
{
test: /\.tsx?$/,
plugins: [require('@babel/plugin-proposal-numeric-separator').default],
},
],
}
}
14 changes: 12 additions & 2 deletions packages/next/build/webpack-config.ts
Expand Up @@ -3,6 +3,7 @@ import ReactRefreshWebpackPlugin from '@next/react-refresh-utils/ReactRefreshWeb
import crypto from 'crypto'
import { readFileSync } from 'fs'
import chalk from 'next/dist/compiled/chalk'
import semver from 'next/dist/compiled/semver'
import TerserPlugin from 'next/dist/compiled/terser-webpack-plugin'
import path from 'path'
import webpack from 'webpack'
Expand All @@ -14,6 +15,8 @@ import {
PAGES_DIR_ALIAS,
} from '../lib/constants'
import { fileExists } from '../lib/file-exists'
import { getPackageVersion } from '../lib/get-package-version'
import { Rewrite } from '../lib/load-custom-routes'
import { resolveRequest } from '../lib/resolve-request'
import { getTypeScriptConfiguration } from '../lib/typescript/getTypeScriptConfiguration'
import {
Expand Down Expand Up @@ -54,7 +57,7 @@ import WebpackConformancePlugin, {
ReactSyncScriptsConformanceCheck,
} from './webpack/plugins/webpack-conformance-plugin'
import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin'
import { Rewrite } from '../lib/load-custom-routes'

type ExcludesFalse = <T>(x: T | false) => x is T

const isWebpack5 = parseInt(webpack.version!) === 5
Expand Down Expand Up @@ -226,7 +229,13 @@ export default async function getBaseWebpackConfig(
}
}

const hasReactRefresh = dev && !isServer
const reactVersion = await getPackageVersion({ cwd: dir, name: 'react' })
const hasReactRefresh: boolean = dev && !isServer
const hasJsxRuntime: boolean =
Boolean(reactVersion) &&
// 17.0.0-rc.0 had a breaking change not compatible with Next.js, but was
// fixed in rc.1.
semver.gte(reactVersion!, '17.0.0-rc.1')

const distDir = path.join(dir, config.distDir)
const defaultLoaders = {
Expand All @@ -243,6 +252,7 @@ export default async function getBaseWebpackConfig(
hasModern: !!config.experimental.modern,
development: dev,
hasReactRefresh,
hasJsxRuntime,
},
},
// Backwards compat
Expand Down
9 changes: 7 additions & 2 deletions packages/next/build/webpack/loaders/next-babel-loader.js
Expand Up @@ -3,9 +3,9 @@ import hash from 'next/dist/compiled/string-hash'
import { basename, join } from 'path'
import * as Log from '../../output/log'

// increment 'm' to invalidate cache
// increment 'n' to invalidate cache
// eslint-disable-next-line no-useless-concat
const cacheKey = 'babel-cache-' + 'm' + '-'
const cacheKey = 'babel-cache-' + 'n' + '-'
const nextBabelPreset = require('../../babel/preset')

const getModernOptions = (babelOptions = {}) => {
Expand Down Expand Up @@ -61,6 +61,7 @@ module.exports = babelLoader.custom((babel) => {
babelPresetPlugins: opts.babelPresetPlugins,
development: opts.development,
hasReactRefresh: opts.hasReactRefresh,
hasJsxRuntime: opts.hasJsxRuntime,
}
const filename = join(opts.cwd, 'noop.js')
const loader = Object.assign(
Expand All @@ -76,6 +77,7 @@ module.exports = babelLoader.custom((babel) => {
'-new-polyfills' +
(opts.development ? '-development' : '-production') +
(opts.hasReactRefresh ? '-react-refresh' : '') +
(opts.hasJsxRuntime ? '-jsx-runtime' : '') +
JSON.stringify(
babel.loadPartialConfig({
filename,
Expand All @@ -99,6 +101,7 @@ module.exports = babelLoader.custom((babel) => {
delete loader.babelPresetPlugins
delete loader.development
delete loader.hasReactRefresh
delete loader.hasJsxRuntime
return { loader, custom }
},
config(
Expand All @@ -113,6 +116,7 @@ module.exports = babelLoader.custom((babel) => {
babelPresetPlugins,
development,
hasReactRefresh,
hasJsxRuntime,
},
}
) {
Expand All @@ -136,6 +140,7 @@ module.exports = babelLoader.custom((babel) => {
options.caller.isServer = isServer
options.caller.isModern = isModern
options.caller.isDev = development
options.caller.hasJsxRuntime = hasJsxRuntime

const emitWarning = this.emitWarning.bind(this)
Object.defineProperty(options.caller, 'onWarning', {
Expand Down
2 changes: 1 addition & 1 deletion packages/next/compiled/amphtml-validator/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/next/compiled/comment-json/index.js

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions packages/next/package.json
Expand Up @@ -59,24 +59,24 @@
},
"dependencies": {
"@ampproject/toolbox-optimizer": "2.6.0",
"@babel/code-frame": "7.8.3",
"@babel/code-frame": "7.10.4",
"@babel/core": "7.7.7",
"@babel/plugin-proposal-class-properties": "7.8.3",
"@babel/plugin-proposal-class-properties": "7.10.4",
"@babel/plugin-proposal-export-namespace-from": "7.10.4",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3",
"@babel/plugin-proposal-numeric-separator": "7.8.3",
"@babel/plugin-proposal-object-rest-spread": "7.9.6",
"@babel/plugin-proposal-optional-chaining": "7.9.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.10.4",
"@babel/plugin-proposal-numeric-separator": "7.10.4",
"@babel/plugin-proposal-object-rest-spread": "7.11.0",
"@babel/plugin-proposal-optional-chaining": "7.11.0",
"@babel/plugin-syntax-bigint": "7.8.3",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/plugin-transform-modules-commonjs": "7.9.6",
"@babel/plugin-transform-runtime": "7.9.6",
"@babel/preset-env": "7.9.6",
"@babel/preset-modules": "0.1.3",
"@babel/preset-react": "7.9.4",
"@babel/preset-typescript": "7.9.0",
"@babel/runtime": "7.9.6",
"@babel/types": "7.9.6",
"@babel/plugin-transform-modules-commonjs": "7.10.4",
"@babel/plugin-transform-runtime": "7.11.5",
"@babel/preset-env": "7.11.5",
"@babel/preset-modules": "0.1.4",
"@babel/preset-react": "7.10.4",
"@babel/preset-typescript": "7.10.4",
"@babel/runtime": "7.11.2",
"@babel/types": "7.11.5",
"@next/react-dev-overlay": "9.5.3-canary.25",
"@next/react-refresh-utils": "9.5.3-canary.25",
"ast-types": "0.13.2",
Expand Down Expand Up @@ -160,7 +160,7 @@
"@types/text-table": "0.2.1",
"@types/webpack-sources": "0.1.5",
"@zeit/ncc": "0.22.0",
"amphtml-validator": "1.0.31",
"amphtml-validator": "1.0.33",
"arg": "4.1.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",
Expand All @@ -169,7 +169,7 @@
"cache-loader": "4.1.0",
"chalk": "2.4.2",
"ci-info": "watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540",
"comment-json": "3.0.2",
"comment-json": "3.0.3",
"compression": "1.7.4",
"conf": "5.0.0",
"content-type": "1.0.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-dev-overlay/package.json
Expand Up @@ -16,7 +16,7 @@
"build": "tsc -d -w -p tsconfig.json"
},
"dependencies": {
"@babel/code-frame": "7.8.3",
"@babel/code-frame": "7.10.4",
"ally.js": "1.4.1",
"anser": "1.4.9",
"chalk": "4.0.0",
Expand Down
2 changes: 2 additions & 0 deletions test/acceptance/ReactRefreshLogBox.test.js
Expand Up @@ -238,6 +238,7 @@ test('render error not shown right after syntax error', async () => {
await session.patch(
'index.js',
`
import * as React from 'react';
class ClassDefault extends React.Component {
render() {
return <h1>Default Export</h1>;
Expand Down Expand Up @@ -576,6 +577,7 @@ test('boundaries', async () => {
'index.js',
`
import FunctionDefault from './FunctionDefault.js'
import * as React from 'react'
class ErrorBoundary extends React.Component {
constructor() {
super()
Expand Down
3 changes: 3 additions & 0 deletions test/acceptance/helpers.js
Expand Up @@ -38,6 +38,9 @@ export async function sandbox(
})
const browser = await webdriver(appPort, '/')

// Slow down tests a bit to ensure application is ready:
await new Promise((resolve) => setTimeout(resolve, 750))

return [
{
sandboxDirectory,
Expand Down
8 changes: 4 additions & 4 deletions test/integration/basic/test/dynamic.js
Expand Up @@ -13,17 +13,17 @@ export default (context, render) => {
it('should render dynamic import components', async () => {
const $ = await get$('/dynamic/ssr')
// Make sure the client side knows it has to wait for the bundle
expect($('body').html()).toContain(
'"dynamicIds":["./components/hello1.js"]'
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'./components/hello1.js'
)
expect($('body').text()).toMatch(/Hello World 1/)
})

it('should render dynamic import components using a function as first parameter', async () => {
const $ = await get$('/dynamic/function')
// Make sure the client side knows it has to wait for the bundle
expect($('body').html()).toContain(
'"dynamicIds":["./components/hello1.js"]'
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'./components/hello1.js'
)
expect($('body').text()).toMatch(/Hello World 1/)
})
Expand Down
19 changes: 3 additions & 16 deletions test/integration/basic/test/error-recovery.js
Expand Up @@ -253,22 +253,9 @@ export default (context, renderViaHTTP) => {
'__WEBPACK_DEFAULT_EXPORT__',
'Unknown'
)
).toMatchInlineSnapshot(`
" 1 of 1 unhandled error
Server Error

Error: Objects are not valid as a React child (found: /search/). If you meant to render a collection of children, use an array instead.
in Unknown
in App
in Unknown
in Context.Provider
in Context.Provider
in Context.Provider
in Context.Provider
in AppContainer

This error happened while generating the page. Any console logs will be displayed in the terminal window."
`)
).toMatch(
'Objects are not valid as a React child (found: /search/). If you meant to render a collection of children, use an array instead.'
)

aboutPage.restore()

Expand Down
22 changes: 22 additions & 0 deletions test/integration/numeric-sep/test/index.test.js
@@ -0,0 +1,22 @@
/* eslint-env jest */

import { nextBuild } from 'next-test-utils'
import { join } from 'path'

jest.setTimeout(1000 * 60 * 2)

const appDir = join(__dirname, '../')

describe('Numeric Separator Support', () => {
it('should successfully build for a JavaScript file', async () => {
const { code, stdout, stderr } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})

expect(code).toBe(0)

expect(stdout).toContain('Compiled successfully')
expect(stderr).not.toContain('Failed to compile')
})
})

This file was deleted.