diff --git a/babel.config.js b/babel.config.js
index d4d1e3213c574..f8a28b20cc87d 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -3,7 +3,6 @@
module.exports = {
plugins: [
'@babel/plugin-syntax-jsx',
- '@babel/plugin-transform-react-jsx',
'@babel/plugin-transform-flow-strip-types',
['@babel/plugin-proposal-class-properties', {loose: true}],
'syntax-trailing-function-commas',
diff --git a/package.json b/package.json
index 1f60dfc00ce59..9a4fdf3cc6625 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"@babel/plugin-proposal-object-rest-spread": "^7.11.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-import-meta": "^7.10.4",
- "@babel/plugin-syntax-jsx": "^7.10.4",
+ "@babel/plugin-syntax-jsx": "^7.23.3",
"@babel/plugin-syntax-typescript": "^7.14.5",
"@babel/plugin-transform-arrow-functions": "^7.10.4",
"@babel/plugin-transform-block-scoped-functions": "^7.10.4",
@@ -27,12 +27,13 @@
"@babel/plugin-transform-modules-commonjs": "^7.10.4",
"@babel/plugin-transform-object-super": "^7.10.4",
"@babel/plugin-transform-parameters": "^7.10.5",
- "@babel/plugin-transform-react-jsx-source": "^7.10.5",
+ "@babel/plugin-transform-react-jsx": "^7.23.4",
+ "@babel/plugin-transform-react-jsx-development": "^7.22.5",
"@babel/plugin-transform-shorthand-properties": "^7.10.4",
"@babel/plugin-transform-spread": "^7.11.0",
"@babel/plugin-transform-template-literals": "^7.10.5",
"@babel/preset-flow": "^7.10.4",
- "@babel/preset-react": "^7.10.4",
+ "@babel/preset-react": "^7.23.3",
"@babel/traverse": "^7.11.0",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^24.0.1",
diff --git a/packages/react-dom/src/__tests__/ReactComponent-test.js b/packages/react-dom/src/__tests__/ReactComponent-test.js
index a66f115624e65..5fa2a4f9f2e6e 100644
--- a/packages/react-dom/src/__tests__/ReactComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactComponent-test.js
@@ -471,7 +471,7 @@ describe('ReactComponent', () => {
root.render();
});
}).toErrorDev(
- 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
+ 'React.jsx: type is invalid -- expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined.',
),
).rejects.toThrowError(
@@ -492,7 +492,7 @@ describe('ReactComponent', () => {
root.render();
});
}).toErrorDev(
- 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
+ 'React.jsx: type is invalid -- expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: null.',
),
).rejects.toThrowError(
@@ -528,7 +528,7 @@ describe('ReactComponent', () => {
root.render();
});
}).toErrorDev(
- 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
+ 'React.jsx: type is invalid -- expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: undefined.',
),
).rejects.toThrowError(
diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
index 3bbdd379813e1..ea30b45e3d61c 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
@@ -1007,7 +1007,7 @@ describe('ReactDOMServerIntegration', () => {
expect(() => {
EmptyComponent = ;
}).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string ' +
+ 'Warning: React.jsx: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: object. You likely forgot to export your ' +
"component from the file it's defined in, or you might have mixed up " +
@@ -1031,7 +1031,7 @@ describe('ReactDOMServerIntegration', () => {
expect(() => {
NullComponent = ;
}).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string ' +
+ 'Warning: React.jsx: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.',
{withoutStack: true},
@@ -1049,7 +1049,7 @@ describe('ReactDOMServerIntegration', () => {
expect(() => {
UndefinedComponent = ;
}).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string ' +
+ 'Warning: React.jsx: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
"component from the file it's defined in, or you might have mixed up " +
diff --git a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js
index a263c18153644..f8b318b2f995e 100644
--- a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js
+++ b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js
@@ -95,7 +95,10 @@ describe('ReactDeprecationWarnings', () => {
}
class Component extends React.Component {
render() {
- return ;
+ return React.createElement(RefComponent, {
+ ref: 'refComponent',
+ __self: this,
+ });
}
}
expect(() => {
@@ -114,7 +117,10 @@ describe('ReactDeprecationWarnings', () => {
}
class Component extends React.Component {
render() {
- return ;
+ return React.createElement(RefComponent, {
+ ref: 'refComponent',
+ __self: {},
+ });
}
}
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
index 2f6b225f2f60e..d60e0b5426401 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
@@ -1244,9 +1244,9 @@ describe('ReactIncrementalErrorHandling', () => {
,
);
await expect(async () => await waitForAll([])).toErrorDev([
- 'Warning: React.createElement: type is invalid -- expected a string',
+ 'Warning: React.jsx: type is invalid -- expected a string',
// React retries once on error
- 'Warning: React.createElement: type is invalid -- expected a string',
+ 'Warning: React.jsx: type is invalid -- expected a string',
]);
expect(ReactNoop).toMatchRenderedOutput(
{
,
);
await expect(async () => await waitForAll([])).toErrorDev([
- 'Warning: React.createElement: type is invalid -- expected a string',
+ 'Warning: React.jsx: type is invalid -- expected a string',
// React retries once on error
- 'Warning: React.createElement: type is invalid -- expected a string',
+ 'Warning: React.jsx: type is invalid -- expected a string',
]);
expect(ReactNoop).toMatchRenderedOutput(
{
it('recovers from uncaught reconciler errors', async () => {
const InvalidType = undefined;
expect(() => ReactNoop.render()).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string',
+ 'Warning: React.jsx: type is invalid -- expected a string',
{withoutStack: true},
);
await waitForThrow(
diff --git a/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js b/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
index aca7ad9e0ed37..ed8c56072a217 100644
--- a/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
+++ b/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
@@ -86,13 +86,22 @@ describe('ReactFreshIntegration', () => {
// eslint-disable-next-line no-new-func
new Function(
'global',
+ 'require',
'React',
'Scheduler',
'exports',
'$RefreshReg$',
'$RefreshSig$',
compiled,
- )(global, React, Scheduler, exportsObj, $RefreshReg$, $RefreshSig$);
+ )(
+ global,
+ require,
+ React,
+ Scheduler,
+ exportsObj,
+ $RefreshReg$,
+ $RefreshSig$,
+ );
// Module systems will register exports as a fallback.
// This is useful for cases when e.g. a class is exported,
// and we don't want to propagate the update beyond this module.
diff --git a/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js b/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
index c8095b8239627..4d885c9eff3fc 100644
--- a/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
+++ b/packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
@@ -288,8 +288,7 @@ describe('ReactTestRenderer', () => {
expect(() => ReactTestRenderer.create()).toErrorDev(
'Warning: Function components cannot be given refs. Attempts ' +
'to access this ref will fail. ' +
- 'Did you mean to use React.forwardRef()?\n\n' +
- 'Check the render method of `Foo`.\n' +
+ 'Did you mean to use React.forwardRef()?\n' +
' in Bar (at **)\n' +
' in Foo (at **)',
);
diff --git a/packages/react/src/__tests__/ReactElement-test.js b/packages/react/src/__tests__/ReactCreateElement-test.js
similarity index 90%
rename from packages/react/src/__tests__/ReactElement-test.js
rename to packages/react/src/__tests__/ReactCreateElement-test.js
index 05aab3c326db7..6d1a16e3bb145 100644
--- a/packages/react/src/__tests__/ReactElement-test.js
+++ b/packages/react/src/__tests__/ReactCreateElement-test.js
@@ -14,7 +14,9 @@ let act;
let React;
let ReactDOMClient;
-describe('ReactElement', () => {
+// NOTE: This module tests the old, "classic" JSX runtime, React.createElement.
+// Do not use JSX syntax in this module; call React.createElement directly.
+describe('ReactCreateElement', () => {
let ComponentClass;
beforeEach(() => {
@@ -24,8 +26,6 @@ describe('ReactElement', () => {
React = require('react');
ReactDOMClient = require('react-dom/client');
- // NOTE: We're explicitly not using JSX here. This is intended to test
- // classic JS without JSX.
ComponentClass = class extends React.Component {
render() {
return React.createElement('div');
@@ -48,24 +48,24 @@ describe('ReactElement', () => {
it('should warn when `key` is being accessed on composite element', async () => {
class Child extends React.Component {
render() {
- return {this.props.key}
;
+ return React.createElement('div', null, this.props.key);
}
}
class Parent extends React.Component {
render() {
- return (
-
-
-
-
-
+ return React.createElement(
+ 'div',
+ null,
+ React.createElement(Child, {key: '0'}),
+ React.createElement(Child, {key: '1'}),
+ React.createElement(Child, {key: '2'}),
);
}
}
const root = ReactDOMClient.createRoot(document.createElement('div'));
await expect(async () => {
await act(() => {
- root.render();
+ root.render(React.createElement(Parent));
});
}).toErrorDev(
'Child: `key` is not a prop. Trying to access it will result ' +
@@ -76,7 +76,7 @@ describe('ReactElement', () => {
});
it('should warn when `key` is being accessed on a host element', () => {
- const element = ;
+ const element = React.createElement('div', {key: '3'});
expect(() => void element.props.key).toErrorDev(
'div: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
@@ -89,15 +89,15 @@ describe('ReactElement', () => {
it('should warn when `ref` is being accessed', async () => {
class Child extends React.Component {
render() {
- return {this.props.ref}
;
+ return React.createElement('div', null, this.props.ref);
}
}
class Parent extends React.Component {
render() {
- return (
-
-
-
+ return React.createElement(
+ 'div',
+ null,
+ React.createElement(Child, {ref: React.createRef()}),
);
}
}
@@ -105,7 +105,7 @@ describe('ReactElement', () => {
await expect(async () => {
await act(() => {
- root.render();
+ root.render(React.createElement(Parent));
});
}).toErrorDev(
'Child: `ref` is not a prop. Trying to access it will result ' +
@@ -277,8 +277,6 @@ describe('ReactElement', () => {
expect(element.props.children).toEqual([1, 2, 3]);
});
- // NOTE: We're explicitly not using JSX here. This is intended to test
- // classic JS without JSX.
it('allows static methods to be called using the type property', () => {
class StaticMethodComponentClass extends React.Component {
render() {
@@ -291,16 +289,12 @@ describe('ReactElement', () => {
expect(element.type.someStaticMethod()).toBe('someReturnValue');
});
- // NOTE: We're explicitly not using JSX here. This is intended to test
- // classic JS without JSX.
it('is indistinguishable from a plain object', () => {
const element = React.createElement('div', {className: 'foo'});
const object = {};
expect(element.constructor).toBe(object.constructor);
});
- // NOTE: We're explicitly not using JSX here. This is intended to test
- // classic JS without JSX.
it('should use default prop value when removing a prop', async () => {
class Component extends React.Component {
render() {
@@ -325,8 +319,6 @@ describe('ReactElement', () => {
expect(instance.props.fruit).toBe('persimmon');
});
- // NOTE: We're explicitly not using JSX here. This is intended to test
- // classic JS without JSX.
it('should normalize props with default values', async () => {
let instance;
class Component extends React.Component {
@@ -354,7 +346,7 @@ describe('ReactElement', () => {
it('throws when changing a prop (in dev) after element creation', async () => {
class Outer extends React.Component {
render() {
- const el = ;
+ const el = React.createElement('div', {className: 'moo'});
if (__DEV__) {
expect(function () {
@@ -374,7 +366,7 @@ describe('ReactElement', () => {
const root = ReactDOMClient.createRoot(container);
await act(() => {
- root.render();
+ root.render(React.createElement(Outer, {color: 'orange'}));
});
if (__DEV__) {
expect(container.firstChild.className).toBe('moo');
@@ -387,7 +379,7 @@ describe('ReactElement', () => {
const container = document.createElement('div');
class Outer extends React.Component {
render() {
- const el = {this.props.sound}
;
+ const el = React.createElement('div', null, this.props.sound);
if (__DEV__) {
expect(function () {
@@ -405,7 +397,7 @@ describe('ReactElement', () => {
Outer.defaultProps = {sound: 'meow'};
const root = ReactDOMClient.createRoot(container);
await act(() => {
- root.render();
+ root.render(React.createElement(Outer));
});
expect(container.firstChild.textContent).toBe('meow');
if (__DEV__) {
@@ -422,12 +414,12 @@ describe('ReactElement', () => {
test = this;
}
render() {
- return ;
+ return React.createElement('div');
}
}
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => {
- root.render();
+ root.render(React.createElement(Test, {value: +undefined}));
});
expect(test.props.value).toBeNaN();
});
diff --git a/packages/react/src/__tests__/ReactElementValidator-test.internal.js b/packages/react/src/__tests__/ReactElementValidator-test.internal.js
index dccee54a35f62..cd1259de19c02 100644
--- a/packages/react/src/__tests__/ReactElementValidator-test.internal.js
+++ b/packages/react/src/__tests__/ReactElementValidator-test.internal.js
@@ -10,7 +10,10 @@
'use strict';
// NOTE: We're explicitly not using JSX in this file. This is intended to test
-// classic JS without JSX.
+// classic React.createElement without JSX.
+// TODO: ^ the above note is a bit stale because there are tests in this file
+// that do use JSX syntax. We should port them to React.createElement, and also
+// confirm there's a corresponding test that uses JSX syntax.
let PropTypes;
let React;
@@ -548,7 +551,7 @@ describe('ReactElementValidator', () => {
expect(() => {
void ({[]});
}).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string ' +
+ 'Warning: React.jsx: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
"component from the file it's defined in, or you might have mixed up " +
diff --git a/packages/react/src/__tests__/ReactJSXElementValidator-test.js b/packages/react/src/__tests__/ReactJSXElementValidator-test.js
index 62d29ee68fefb..b7cdff0ce0aa1 100644
--- a/packages/react/src/__tests__/ReactJSXElementValidator-test.js
+++ b/packages/react/src/__tests__/ReactJSXElementValidator-test.js
@@ -220,7 +220,7 @@ describe('ReactJSXElementValidator', () => {
const True = true;
const Div = 'div';
expect(() => void ()).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string ' +
+ 'Warning: React.jsx: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: undefined. You likely forgot to export your ' +
"component from the file it's defined in, or you might have mixed up " +
@@ -229,14 +229,14 @@ describe('ReactJSXElementValidator', () => {
{withoutStack: true},
);
expect(() => void ()).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string ' +
+ 'Warning: React.jsx: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: null.' +
'\n\nCheck your code at **.',
{withoutStack: true},
);
expect(() => void ()).toErrorDev(
- 'Warning: React.createElement: type is invalid -- expected a string ' +
+ 'Warning: React.jsx: type is invalid -- expected a string ' +
'(for built-in components) or a class/function (for composite ' +
'components) but got: boolean.' +
'\n\nCheck your code at **.',
diff --git a/packages/react/src/__tests__/ReactElementJSX-test.js b/packages/react/src/__tests__/ReactJSXRuntime-test.js
similarity index 97%
rename from packages/react/src/__tests__/ReactElementJSX-test.js
rename to packages/react/src/__tests__/ReactJSXRuntime-test.js
index fcb1a1d9b10ce..713430fa5ead9 100644
--- a/packages/react/src/__tests__/ReactElementJSX-test.js
+++ b/packages/react/src/__tests__/ReactJSXRuntime-test.js
@@ -17,11 +17,10 @@ let JSXRuntime;
let JSXDEVRuntime;
let act;
-// NOTE: We're explicitly not using JSX here. This is intended to test
-// a new React.jsx api which does not have a JSX transformer yet.
-// A lot of these tests are pulled from ReactElement-test because
-// this api is meant to be backwards compatible.
-describe('ReactElement.jsx', () => {
+// NOTE: Prefer to call the JSXRuntime directly in these tests so we can be
+// certain that we are testing the runtime behavior, as opposed to the Babel
+// transform that we use in our tests configuration.
+describe('ReactJSXRuntime', () => {
beforeEach(() => {
jest.resetModules();
diff --git a/packages/react/src/__tests__/ReactJSXElement-test.js b/packages/react/src/__tests__/ReactJSXTransformIntegration-test.js
similarity index 86%
rename from packages/react/src/__tests__/ReactJSXElement-test.js
rename to packages/react/src/__tests__/ReactJSXTransformIntegration-test.js
index 39f2eae0dc566..6f764db3b11ce 100644
--- a/packages/react/src/__tests__/ReactJSXElement-test.js
+++ b/packages/react/src/__tests__/ReactJSXTransformIntegration-test.js
@@ -14,7 +14,15 @@ let ReactDOMClient;
let ReactTestUtils;
let act;
-describe('ReactJSXElement', () => {
+// TODO: Historically this module was used to confirm that the JSX transform
+// produces the correct output. However, most users (and indeed our own test
+// suite) use a tool like Babel or TypeScript to transform JSX; unlike the
+// runtime, the transform is not part of React itself. So this is really just an
+// integration suite for the Babel transform. We might consider deleting it. We
+// should prefer to test the JSX runtime directly, in ReactCreateElement-test
+// and ReactJsxRuntime-test. In the meantime, there's lots of overlap between
+// those modules and this one.
+describe('ReactJSXTransformIntegration', () => {
let Component;
beforeEach(() => {
@@ -32,6 +40,19 @@ describe('ReactJSXElement', () => {
};
});
+ it('sanity check: test environment is configured to compile JSX to the jsx() runtime', async () => {
+ function App() {
+ return ;
+ }
+ const source = App.toString();
+ if (__DEV__) {
+ expect(source).toContain('jsxDEV(');
+ } else {
+ expect(source).toContain('jsx(');
+ }
+ expect(source).not.toContain('React.createElement');
+ });
+
it('returns a complete element according to spec', () => {
const element = ;
expect(element.type).toBe(Component);
diff --git a/packages/shared/__tests__/describeComponentFrame-test.js b/packages/shared/__tests__/describeComponentFrame-test.js
index 37eaafb5ed279..6fc5bef2c9945 100644
--- a/packages/shared/__tests__/describeComponentFrame-test.js
+++ b/packages/shared/__tests__/describeComponentFrame-test.js
@@ -12,15 +12,18 @@
let React;
let ReactDOMClient;
let act;
+let jsxDEV;
describe('Component stack trace displaying', () => {
beforeEach(() => {
React = require('react');
- ReactDOMClient = require('react-dom');
+ ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
+ jsxDEV = require('react/jsx-dev-runtime').jsxDEV;
});
- // @gate !enableComponentStackLocations || !__DEV__
+ // @gate !enableComponentStackLocations
+ // @gate __DEV__
it('should provide filenames in stack traces', async () => {
class Component extends React.Component {
render() {
@@ -98,7 +101,18 @@ describe('Component stack trace displaying', () => {
Component.displayName = 'Component ' + i;
await act(() => {
- root.render();
+ root.render(
+ // Intentionally inlining a manual jsxDEV() instead of relying on the
+ // compiler so that we can pass a custom source location.
+ jsxDEV(
+ Component,
+ {},
+ undefined,
+ false,
+ {fileName, lineNumber: i},
+ this,
+ ),
+ );
});
i++;
diff --git a/scripts/jest/devtools/setupEnv.js b/scripts/jest/devtools/setupEnv.js
index b3ef1b9387dcf..beaf9c3044ae2 100644
--- a/scripts/jest/devtools/setupEnv.js
+++ b/scripts/jest/devtools/setupEnv.js
@@ -57,3 +57,41 @@ global._test_react_version_focus = (range, testName, callback) => {
global._test_ignore_for_react_version = (testName, callback) => {
test.skip(testName, callback);
};
+
+// Most of our tests call jest.resetModules in a beforeEach and the
+// re-require all the React modules. However, the JSX runtime is injected by
+// the compiler, so those bindings don't get updated. This causes warnings
+// logged by the JSX runtime to not have a component stack, because component
+// stack relies on the the secret internals object that lives on the React
+// module, which because of the resetModules call is longer the same one.
+//
+// To workaround this issue, we use a proxy that re-requires the latest
+// JSX Runtime from the require cache on every function invocation.
+//
+// Longer term we should migrate all our tests away from using require() and
+// resetModules, and use import syntax instead so this kind of thing doesn't
+// happen.
+lazyRequireFunctionExports('react/jsx-dev-runtime');
+
+// TODO: We shouldn't need to do this in the production runtime, but until
+// we remove string refs they also depend on the shared state object. Remove
+// once we remove string refs.
+lazyRequireFunctionExports('react/jsx-runtime');
+
+function lazyRequireFunctionExports(moduleName) {
+ jest.mock(moduleName, () => {
+ return new Proxy(jest.requireActual(moduleName), {
+ get(originalModule, prop) {
+ // If this export is a function, return a wrapper function that lazily
+ // requires the implementation from the current module cache.
+ if (typeof originalModule[prop] === 'function') {
+ return function () {
+ return jest.requireActual(moduleName)[prop].apply(this, arguments);
+ };
+ } else {
+ return originalModule[prop];
+ }
+ },
+ });
+ });
+}
diff --git a/scripts/jest/preprocessor.js b/scripts/jest/preprocessor.js
index 7b9f9e9e9abdf..bf9fe7fed3604 100644
--- a/scripts/jest/preprocessor.js
+++ b/scripts/jest/preprocessor.js
@@ -34,12 +34,6 @@ const babelOptions = {
// For Node environment only. For builds, Rollup takes care of ESM.
require.resolve('@babel/plugin-transform-modules-commonjs'),
- // Keep stacks detailed in tests.
- // Don't put this in .babelrc so that we don't embed filenames
- // into ReactART builds that include JSX.
- // TODO: I have not verified that this actually works.
- require.resolve('@babel/plugin-transform-react-jsx-source'),
-
pathToTransformInfiniteLoops,
pathToTransformTestGatePragma,
@@ -86,6 +80,16 @@ module.exports = {
if (isTestFile && isInDevToolsPackages) {
plugins.push(pathToTransformReactVersionPragma);
}
+
+ plugins.push([
+ process.env.NODE_ENV === 'development'
+ ? require.resolve('@babel/plugin-transform-react-jsx-development')
+ : require.resolve('@babel/plugin-transform-react-jsx'),
+ // The "automatic" runtime corresponds to react/jsx-runtime. "classic"
+ // would be React.createElement.
+ {runtime: 'automatic'},
+ ]);
+
let sourceAst = hermesParser.parse(src, {babel: true});
return {
code: babel.transformFromAstSync(
diff --git a/scripts/jest/setupTests.js b/scripts/jest/setupTests.js
index 09712d587f93f..71a13489cf5f1 100644
--- a/scripts/jest/setupTests.js
+++ b/scripts/jest/setupTests.js
@@ -313,3 +313,41 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
return fn(flags);
};
}
+
+// Most of our tests call jest.resetModules in a beforeEach and the
+// re-require all the React modules. However, the JSX runtime is injected by
+// the compiler, so those bindings don't get updated. This causes warnings
+// logged by the JSX runtime to not have a component stack, because component
+// stack relies on the the secret internals object that lives on the React
+// module, which because of the resetModules call is longer the same one.
+//
+// To workaround this issue, we use a proxy that re-requires the latest
+// JSX Runtime from the require cache on every function invocation.
+//
+// Longer term we should migrate all our tests away from using require() and
+// resetModules, and use import syntax instead so this kind of thing doesn't
+// happen.
+lazyRequireFunctionExports('react/jsx-dev-runtime');
+
+// TODO: We shouldn't need to do this in the production runtime, but until
+// we remove string refs they also depend on the shared state object. Remove
+// once we remove string refs.
+lazyRequireFunctionExports('react/jsx-runtime');
+
+function lazyRequireFunctionExports(moduleName) {
+ jest.mock(moduleName, () => {
+ return new Proxy(jest.requireActual(moduleName), {
+ get(originalModule, prop) {
+ // If this export is a function, return a wrapper function that lazily
+ // requires the implementation from the current module cache.
+ if (typeof originalModule[prop] === 'function') {
+ return function () {
+ return jest.requireActual(moduleName)[prop].apply(this, arguments);
+ };
+ } else {
+ return originalModule[prop];
+ }
+ },
+ });
+ });
+}
diff --git a/yarn.lock b/yarn.lock
index 75b90870c449d..ba16987b85283 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -220,6 +220,13 @@
dependencies:
"@babel/types" "^7.18.6"
+"@babel/helper-annotate-as-pure@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882"
+ integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3"
@@ -465,6 +472,13 @@
dependencies:
"@babel/types" "^7.18.6"
+"@babel/helper-module-imports@^7.22.15":
+ version "7.22.15"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0"
+ integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
+ dependencies:
+ "@babel/types" "^7.22.15"
+
"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359"
@@ -547,6 +561,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629"
integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==
+"@babel/helper-plugin-utils@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
+ integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
+
"@babel/helper-regex@^7.10.4":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0"
@@ -673,6 +692,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
+"@babel/helper-string-parser@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
+ integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
+
"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288"
@@ -688,6 +712,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+"@babel/helper-validator-identifier@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
+ integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
+
"@babel/helper-validator-option@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
@@ -698,6 +727,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
+"@babel/helper-validator-option@^7.22.15":
+ version "7.23.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
+ integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
+
"@babel/helper-wrap-function@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
@@ -1089,6 +1123,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-syntax-jsx@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473"
+ integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
@@ -1509,6 +1550,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-transform-react-display-name@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200"
+ integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
"@babel/plugin-transform-react-jsx-development@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.4.tgz#6ec90f244394604623880e15ebc3c34c356258ba"
@@ -1518,6 +1566,13 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-jsx" "^7.10.4"
+"@babel/plugin-transform-react-jsx-development@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87"
+ integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==
+ dependencies:
+ "@babel/plugin-transform-react-jsx" "^7.22.5"
+
"@babel/plugin-transform-react-jsx-self@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369"
@@ -1555,6 +1610,17 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-jsx" "^7.10.4"
+"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5", "@babel/plugin-transform-react-jsx@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312"
+ integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-module-imports" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-jsx" "^7.23.3"
+ "@babel/types" "^7.23.4"
+
"@babel/plugin-transform-react-pure-annotations@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501"
@@ -1563,6 +1629,14 @@
"@babel/helper-annotate-as-pure" "^7.10.4"
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-transform-react-pure-annotations@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz#fabedbdb8ee40edf5da96f3ecfc6958e3783b93c"
+ integrity sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
"@babel/plugin-transform-regenerator@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63"
@@ -1775,6 +1849,18 @@
"@babel/plugin-transform-react-jsx-source" "^7.10.4"
"@babel/plugin-transform-react-pure-annotations" "^7.10.4"
+"@babel/preset-react@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.23.3.tgz#f73ca07e7590f977db07eb54dbe46538cc015709"
+ integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-validator-option" "^7.22.15"
+ "@babel/plugin-transform-react-display-name" "^7.23.3"
+ "@babel/plugin-transform-react-jsx" "^7.22.15"
+ "@babel/plugin-transform-react-jsx-development" "^7.22.5"
+ "@babel/plugin-transform-react-pure-annotations" "^7.23.3"
+
"@babel/preset-typescript@^7.14.5":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.0.tgz#b0b4f105b855fb3d631ec036cdc9d1ffd1fa5eac"
@@ -1979,6 +2065,15 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
+"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.4":
+ version "7.23.9"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002"
+ integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==
+ dependencies:
+ "@babel/helper-string-parser" "^7.23.4"
+ "@babel/helper-validator-identifier" "^7.22.20"
+ to-fast-properties "^2.0.0"
+
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"