Skip to content

Commit

Permalink
feat(gatsby): Support React 17's new JSX Transform (#26652)
Browse files Browse the repository at this point in the history
* feature(gatsby): Support React 17s new JSX transform

* move react to peerDep for cli

* simplify code

* change jsx runtime check

* Improve the future proof of using the jsx runtime

* revert react peerDep in gatsby-cli

* fix tests

* Update yarn.lock

* Update index.js.snap

* Update index.js.snap

* Update gatsby-node.js.snap

* Update index.js.snap

* Update query-compiler.js.snap

* Update yarn.lock

* Update babelrc.ts.snap

* review

Co-authored-by: Ward Peeters <ward@coding-tech.com>
  • Loading branch information
blainekasten and wardpeet committed Aug 28, 2020
1 parent 4aa9437 commit 6ba68f8
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 5 deletions.
Expand Up @@ -258,6 +258,7 @@ Object {
Object {
"development": false,
"pragma": "React.createElement",
"runtime": "classic",
"useBuiltIns": true,
},
],
Expand Down Expand Up @@ -527,6 +528,7 @@ Object {
Object {
"development": false,
"pragma": "React.createElement",
"runtime": "classic",
"useBuiltIns": true,
},
],
Expand Down Expand Up @@ -790,6 +792,7 @@ Object {
Object {
"development": false,
"pragma": "React.createElement",
"runtime": "classic",
"useBuiltIns": true,
},
],
Expand Down Expand Up @@ -1053,6 +1056,7 @@ Object {
Object {
"development": true,
"pragma": "React.createElement",
"runtime": "classic",
"useBuiltIns": true,
},
],
Expand Down
14 changes: 14 additions & 0 deletions packages/babel-preset-gatsby/src/__tests__/index.js
Expand Up @@ -26,4 +26,18 @@ describe(`babel-preset-gatsby`, () => {
}),
])
})

it(`Allows to configure react runtime`, () => {
const { presets } = preset(null, {
reactRuntime: `automatic`,
})

expect(presets[1]).toEqual([
expect.stringContaining(path.join(`@babel`, `preset-react`)),
expect.objectContaining({
pragma: undefined,
runtime: `automatic`,
}),
])
})
})
6 changes: 5 additions & 1 deletion packages/babel-preset-gatsby/src/index.js
Expand Up @@ -77,8 +77,12 @@ export default function preset(_, options = {}) {
resolve(`@babel/preset-react`),
{
useBuiltIns: true,
pragma: `React.createElement`,
pragma:
options.reactRuntime === `automatic`
? undefined
: `React.createElement`,
development: stage === `develop`,
runtime: options.reactRuntime || `classic`,
},
],
],
Expand Down
Expand Up @@ -173,6 +173,7 @@ Array [
Array [
"/path/to/module/babel-preset-gatsby",
Object {
"reactRuntime": undefined,
"stage": "test",
},
],
Expand Down
Expand Up @@ -41,6 +41,7 @@ Object {
"cacheDirectory": "<TEMP_DIR>/test/.cache/webpack/babel",
"compact": false,
"configFile": true,
"reactRuntime": "classic",
"stage": "develop",
},
},
Expand Down
3 changes: 2 additions & 1 deletion packages/gatsby/src/utils/babel-loader-helpers.js
Expand Up @@ -24,7 +24,7 @@ const getCustomOptions = stage => {
const prepareOptions = (babel, options = {}, resolve = require.resolve) => {
const pluginBabelConfig = loadCachedConfig()

const { stage } = options
const { stage, reactRuntime } = options

// Required plugins/presets
const requiredPlugins = [
Expand Down Expand Up @@ -67,6 +67,7 @@ const prepareOptions = (babel, options = {}, resolve = require.resolve) => {
resolve(`babel-preset-gatsby`),
{
stage,
reactRuntime,
},
],
{
Expand Down
3 changes: 2 additions & 1 deletion packages/gatsby/src/utils/babel-loader.js
Expand Up @@ -24,10 +24,11 @@ const {
module.exports = babelLoader.custom(babel => {
const toReturn = {
// Passed the loader options.
customOptions({ stage = `test`, ...options }) {
customOptions({ stage = `test`, reactRuntime = `classic`, ...options }) {
return {
custom: {
stage,
reactRuntime,
},
loader: {
sourceType: `unambiguous`,
Expand Down
11 changes: 10 additions & 1 deletion packages/gatsby/src/utils/eslint-config.ts
@@ -1,7 +1,10 @@
import { printSchema, GraphQLSchema } from "graphql"
import { CLIEngine } from "eslint"

export const eslintConfig = (schema: GraphQLSchema): CLIEngine.Options => {
export const eslintConfig = (
schema: GraphQLSchema,
usingJsxRuntime: boolean
): CLIEngine.Options => {
return {
useEslintrc: false,
resolvePluginsRelativeTo: __dirname,
Expand All @@ -14,6 +17,12 @@ export const eslintConfig = (schema: GraphQLSchema): CLIEngine.Options => {
extends: [require.resolve(`eslint-config-react-app`)],
plugins: [`graphql`],
rules: {
// New versions of react use a special jsx runtime that remove the requirement
// for having react in scope for jsx. Once the jsx runtime is backported to all
// versions of react we can make this always be `off`.
// I would also assume that eslint-config-react-app will switch their flag to `off`
// when jsx runtime is stable in all common versions of React.
"react/react-in-jsx-scope": usingJsxRuntime ? `off` : `error`, // Conditionally apply for reactRuntime?
"import/no-webpack-loader-syntax": [0],
"graphql/template-strings": [
`error`,
Expand Down
20 changes: 19 additions & 1 deletion packages/gatsby/src/utils/webpack-utils.ts
Expand Up @@ -137,6 +137,7 @@ export const createWebpackUtils = (

const isSSR = stage.includes(`html`)

const jsxRuntimeExists = reactHasJsxRuntime()
const makeExternalOnly = (original: RuleFactory) => (
options = {}
): RuleSetRule => {
Expand Down Expand Up @@ -273,6 +274,7 @@ export const createWebpackUtils = (
return {
options: {
stage,
reactRuntime: jsxRuntimeExists ? `automatic` : `classic`,
// TODO add proper cache keys
cacheDirectory: path.join(
program.directory,
Expand Down Expand Up @@ -303,7 +305,7 @@ export const createWebpackUtils = (
},

eslint: (schema: GraphQLSchema) => {
const options = eslintConfig(schema)
const options = eslintConfig(schema, jsxRuntimeExists)

return {
options,
Expand Down Expand Up @@ -710,3 +712,19 @@ export const createWebpackUtils = (
plugins,
}
}

function reactHasJsxRuntime(): boolean {
try {
// React is shipping a new jsx runtime that is to be used with
// an option on @babel/preset-react called `runtime: automatic`
// Not every version of React has this jsx-runtime yet. Eventually,
// it will be backported to older versions of react and this check
// will become unnecessary.
return !!require.resolve(`react/jsx-runtime.js`)
} catch (e) {
// If the require.resolve throws, that means this version of React
// does not support the jsx runtime.
}

return false
}

0 comments on commit 6ba68f8

Please sign in to comment.