Skip to content

Commit 6ba68f8

Browse files
feat(gatsby): Support React 17's new JSX Transform (#26652)
* 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>
1 parent 4aa9437 commit 6ba68f8

File tree

9 files changed

+58
-5
lines changed

9 files changed

+58
-5
lines changed

packages/babel-preset-gatsby/src/__tests__/__snapshots__/index.js.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ Object {
258258
Object {
259259
"development": false,
260260
"pragma": "React.createElement",
261+
"runtime": "classic",
261262
"useBuiltIns": true,
262263
},
263264
],
@@ -527,6 +528,7 @@ Object {
527528
Object {
528529
"development": false,
529530
"pragma": "React.createElement",
531+
"runtime": "classic",
530532
"useBuiltIns": true,
531533
},
532534
],
@@ -790,6 +792,7 @@ Object {
790792
Object {
791793
"development": false,
792794
"pragma": "React.createElement",
795+
"runtime": "classic",
793796
"useBuiltIns": true,
794797
},
795798
],
@@ -1053,6 +1056,7 @@ Object {
10531056
Object {
10541057
"development": true,
10551058
"pragma": "React.createElement",
1059+
"runtime": "classic",
10561060
"useBuiltIns": true,
10571061
},
10581062
],

packages/babel-preset-gatsby/src/__tests__/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,18 @@ describe(`babel-preset-gatsby`, () => {
2626
}),
2727
])
2828
})
29+
30+
it(`Allows to configure react runtime`, () => {
31+
const { presets } = preset(null, {
32+
reactRuntime: `automatic`,
33+
})
34+
35+
expect(presets[1]).toEqual([
36+
expect.stringContaining(path.join(`@babel`, `preset-react`)),
37+
expect.objectContaining({
38+
pragma: undefined,
39+
runtime: `automatic`,
40+
}),
41+
])
42+
})
2943
})

packages/babel-preset-gatsby/src/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,12 @@ export default function preset(_, options = {}) {
7777
resolve(`@babel/preset-react`),
7878
{
7979
useBuiltIns: true,
80-
pragma: `React.createElement`,
80+
pragma:
81+
options.reactRuntime === `automatic`
82+
? undefined
83+
: `React.createElement`,
8184
development: stage === `develop`,
85+
runtime: options.reactRuntime || `classic`,
8286
},
8387
],
8488
],

packages/gatsby/src/redux/__tests__/__snapshots__/babelrc.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ Array [
173173
Array [
174174
"/path/to/module/babel-preset-gatsby",
175175
Object {
176+
"reactRuntime": undefined,
176177
"stage": "test",
177178
},
178179
],

packages/gatsby/src/utils/__tests__/__snapshots__/webpack-utils.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Object {
4141
"cacheDirectory": "<TEMP_DIR>/test/.cache/webpack/babel",
4242
"compact": false,
4343
"configFile": true,
44+
"reactRuntime": "classic",
4445
"stage": "develop",
4546
},
4647
},

packages/gatsby/src/utils/babel-loader-helpers.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const getCustomOptions = stage => {
2424
const prepareOptions = (babel, options = {}, resolve = require.resolve) => {
2525
const pluginBabelConfig = loadCachedConfig()
2626

27-
const { stage } = options
27+
const { stage, reactRuntime } = options
2828

2929
// Required plugins/presets
3030
const requiredPlugins = [
@@ -67,6 +67,7 @@ const prepareOptions = (babel, options = {}, resolve = require.resolve) => {
6767
resolve(`babel-preset-gatsby`),
6868
{
6969
stage,
70+
reactRuntime,
7071
},
7172
],
7273
{

packages/gatsby/src/utils/babel-loader.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ const {
2424
module.exports = babelLoader.custom(babel => {
2525
const toReturn = {
2626
// Passed the loader options.
27-
customOptions({ stage = `test`, ...options }) {
27+
customOptions({ stage = `test`, reactRuntime = `classic`, ...options }) {
2828
return {
2929
custom: {
3030
stage,
31+
reactRuntime,
3132
},
3233
loader: {
3334
sourceType: `unambiguous`,

packages/gatsby/src/utils/eslint-config.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { printSchema, GraphQLSchema } from "graphql"
22
import { CLIEngine } from "eslint"
33

4-
export const eslintConfig = (schema: GraphQLSchema): CLIEngine.Options => {
4+
export const eslintConfig = (
5+
schema: GraphQLSchema,
6+
usingJsxRuntime: boolean
7+
): CLIEngine.Options => {
58
return {
69
useEslintrc: false,
710
resolvePluginsRelativeTo: __dirname,
@@ -14,6 +17,12 @@ export const eslintConfig = (schema: GraphQLSchema): CLIEngine.Options => {
1417
extends: [require.resolve(`eslint-config-react-app`)],
1518
plugins: [`graphql`],
1619
rules: {
20+
// New versions of react use a special jsx runtime that remove the requirement
21+
// for having react in scope for jsx. Once the jsx runtime is backported to all
22+
// versions of react we can make this always be `off`.
23+
// I would also assume that eslint-config-react-app will switch their flag to `off`
24+
// when jsx runtime is stable in all common versions of React.
25+
"react/react-in-jsx-scope": usingJsxRuntime ? `off` : `error`, // Conditionally apply for reactRuntime?
1726
"import/no-webpack-loader-syntax": [0],
1827
"graphql/template-strings": [
1928
`error`,

packages/gatsby/src/utils/webpack-utils.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export const createWebpackUtils = (
137137

138138
const isSSR = stage.includes(`html`)
139139

140+
const jsxRuntimeExists = reactHasJsxRuntime()
140141
const makeExternalOnly = (original: RuleFactory) => (
141142
options = {}
142143
): RuleSetRule => {
@@ -273,6 +274,7 @@ export const createWebpackUtils = (
273274
return {
274275
options: {
275276
stage,
277+
reactRuntime: jsxRuntimeExists ? `automatic` : `classic`,
276278
// TODO add proper cache keys
277279
cacheDirectory: path.join(
278280
program.directory,
@@ -303,7 +305,7 @@ export const createWebpackUtils = (
303305
},
304306

305307
eslint: (schema: GraphQLSchema) => {
306-
const options = eslintConfig(schema)
308+
const options = eslintConfig(schema, jsxRuntimeExists)
307309

308310
return {
309311
options,
@@ -710,3 +712,19 @@ export const createWebpackUtils = (
710712
plugins,
711713
}
712714
}
715+
716+
function reactHasJsxRuntime(): boolean {
717+
try {
718+
// React is shipping a new jsx runtime that is to be used with
719+
// an option on @babel/preset-react called `runtime: automatic`
720+
// Not every version of React has this jsx-runtime yet. Eventually,
721+
// it will be backported to older versions of react and this check
722+
// will become unnecessary.
723+
return !!require.resolve(`react/jsx-runtime.js`)
724+
} catch (e) {
725+
// If the require.resolve throws, that means this version of React
726+
// does not support the jsx runtime.
727+
}
728+
729+
return false
730+
}

0 commit comments

Comments
 (0)