diff --git a/packages/react/src/React.js b/packages/react/src/React.js
index ba696073f82a5..0a96310a11925 100644
--- a/packages/react/src/React.js
+++ b/packages/react/src/React.js
@@ -22,6 +22,7 @@ import {
createFactory,
cloneElement,
isValidElement,
+ jsx,
} from './ReactElement';
import {createContext} from './ReactContext';
import {lazy} from './ReactLazy';
@@ -43,10 +44,16 @@ import {
createElementWithValidation,
createFactoryWithValidation,
cloneElementWithValidation,
+ jsxWithValidation,
+ jsxWithValidationStatic,
+ jsxWithValidationDynamic,
} from './ReactElementValidator';
import ReactSharedInternals from './ReactSharedInternals';
import {error, warn} from './withComponentStack';
-import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
+import {
+ enableStableConcurrentModeAPIs,
+ enableJSXTransformAPI,
+} from 'shared/ReactFeatureFlags';
const React = {
Children: {
@@ -107,4 +114,17 @@ if (enableStableConcurrentModeAPIs) {
React.unstable_ConcurrentMode = undefined;
}
+if (enableJSXTransformAPI) {
+ if (__DEV__) {
+ React.jsxDEV = jsxWithValidation;
+ React.jsx = jsxWithValidationDynamic;
+ React.jsxs = jsxWithValidationStatic;
+ } else {
+ React.jsx = jsx;
+ // we may want to special case jsxs internally to take advantage of static children.
+ // for now we can ship identical prod functions
+ React.jsxs = jsx;
+ }
+}
+
export default React;
diff --git a/packages/react/src/ReactElement.js b/packages/react/src/ReactElement.js
index 50561efe3aace..9416a313b9648 100644
--- a/packages/react/src/ReactElement.js
+++ b/packages/react/src/ReactElement.js
@@ -95,8 +95,10 @@ function defineRefPropWarningGetter(props, displayName) {
* if something is a React Element.
*
* @param {*} type
+ * @param {*} props
* @param {*} key
* @param {string|object} ref
+ * @param {*} owner
* @param {*} self A *temporary* helper to detect places where `this` is
* different from the `owner` when React.createElement is called, so that we
* can warn. We want to get rid of owner and replace string `ref`s with arrow
@@ -104,8 +106,6 @@ function defineRefPropWarningGetter(props, displayName) {
* change in behavior.
* @param {*} source An annotation object (added by a transpiler or otherwise)
* indicating filename, line number, and/or other information.
- * @param {*} owner
- * @param {*} props
* @internal
*/
const ReactElement = function(type, key, ref, self, source, owner, props) {
@@ -164,6 +164,139 @@ const ReactElement = function(type, key, ref, self, source, owner, props) {
return element;
};
+/**
+ * https://github.com/reactjs/rfcs/pull/107
+ * @param {*} type
+ * @param {object} props
+ * @param {string} key
+ */
+export function jsx(type, config, maybeKey) {
+ let propName;
+
+ // Reserved names are extracted
+ const props = {};
+
+ let key = null;
+ let ref = null;
+
+ if (hasValidRef(config)) {
+ ref = config.ref;
+ }
+
+ if (hasValidKey(config)) {
+ key = '' + config.key;
+ }
+
+ // Remaining properties are added to a new props object
+ for (propName in config) {
+ if (
+ hasOwnProperty.call(config, propName) &&
+ !RESERVED_PROPS.hasOwnProperty(propName)
+ ) {
+ props[propName] = config[propName];
+ }
+ }
+
+ // intentionally not checking if key was set above
+ // this key is higher priority as it's static
+ if (maybeKey !== undefined) {
+ key = '' + maybeKey;
+ }
+
+ // Resolve default props
+ if (type && type.defaultProps) {
+ const defaultProps = type.defaultProps;
+ for (propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
+ }
+ }
+
+ return ReactElement(
+ type,
+ key,
+ ref,
+ undefined,
+ undefined,
+ ReactCurrentOwner.current,
+ props,
+ );
+}
+
+/**
+ * https://github.com/reactjs/rfcs/pull/107
+ * @param {*} type
+ * @param {object} props
+ * @param {string} key
+ */
+export function jsxDEV(type, config, maybeKey, source, self) {
+ let propName;
+
+ // Reserved names are extracted
+ const props = {};
+
+ let key = null;
+ let ref = null;
+
+ if (hasValidRef(config)) {
+ ref = config.ref;
+ }
+
+ if (hasValidKey(config)) {
+ key = '' + config.key;
+ }
+
+ // Remaining properties are added to a new props object
+ for (propName in config) {
+ if (
+ hasOwnProperty.call(config, propName) &&
+ !RESERVED_PROPS.hasOwnProperty(propName)
+ ) {
+ props[propName] = config[propName];
+ }
+ }
+
+ // intentionally not checking if key was set above
+ // this key is higher priority as it's static
+ if (maybeKey !== undefined) {
+ key = '' + maybeKey;
+ }
+
+ // Resolve default props
+ if (type && type.defaultProps) {
+ const defaultProps = type.defaultProps;
+ for (propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
+ }
+ }
+
+ if (key || ref) {
+ const displayName =
+ typeof type === 'function'
+ ? type.displayName || type.name || 'Unknown'
+ : type;
+ if (key) {
+ defineKeyPropWarningGetter(props, displayName);
+ }
+ if (ref) {
+ defineRefPropWarningGetter(props, displayName);
+ }
+ }
+
+ return ReactElement(
+ type,
+ key,
+ ref,
+ self,
+ source,
+ ReactCurrentOwner.current,
+ props,
+ );
+}
+
/**
* Create and return a new ReactElement of the given type.
* See https://reactjs.org/docs/react-api.html#createelement
diff --git a/packages/react/src/ReactElementValidator.js b/packages/react/src/ReactElementValidator.js
index fa83aea4ef28e..6a1626a2e27e8 100644
--- a/packages/react/src/ReactElementValidator.js
+++ b/packages/react/src/ReactElementValidator.js
@@ -27,7 +27,12 @@ import warning from 'shared/warning';
import warningWithoutStack from 'shared/warningWithoutStack';
import ReactCurrentOwner from './ReactCurrentOwner';
-import {isValidElement, createElement, cloneElement} from './ReactElement';
+import {
+ isValidElement,
+ createElement,
+ cloneElement,
+ jsxDEV,
+} from './ReactElement';
import ReactDebugCurrentFrame, {
setCurrentlyValidatingElement,
} from './ReactDebugCurrentFrame';
@@ -48,13 +53,8 @@ function getDeclarationErrorAddendum() {
return '';
}
-function getSourceInfoErrorAddendum(elementProps) {
- if (
- elementProps !== null &&
- elementProps !== undefined &&
- elementProps.__source !== undefined
- ) {
- const source = elementProps.__source;
+function getSourceInfoErrorAddendum(source) {
+ if (source !== undefined) {
const fileName = source.fileName.replace(/^.*[\\\/]/, '');
const lineNumber = source.lineNumber;
return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
@@ -62,6 +62,13 @@ function getSourceInfoErrorAddendum(elementProps) {
return '';
}
+function getSourceInfoErrorAddendumForProps(elementProps) {
+ if (elementProps !== null && elementProps !== undefined) {
+ return getSourceInfoErrorAddendum(elementProps.__source);
+ }
+ return '';
+}
+
/**
* Warn if there's no key explicitly set on dynamic arrays of children or
* object keys are not valid. This allows us to keep track of children between
@@ -259,6 +266,117 @@ function validateFragmentProps(fragment) {
setCurrentlyValidatingElement(null);
}
+export function jsxWithValidation(
+ type,
+ props,
+ key,
+ isStaticChildren,
+ source,
+ self,
+) {
+ const validType = isValidElementType(type);
+
+ // We warn in this case but don't throw. We expect the element creation to
+ // succeed and there will likely be errors in render.
+ if (!validType) {
+ let info = '';
+ if (
+ type === undefined ||
+ (typeof type === 'object' &&
+ type !== null &&
+ Object.keys(type).length === 0)
+ ) {
+ info +=
+ ' You likely forgot to export your component from the file ' +
+ "it's defined in, or you might have mixed up default and named imports.";
+ }
+
+ const sourceInfo = getSourceInfoErrorAddendum(source);
+ if (sourceInfo) {
+ info += sourceInfo;
+ } else {
+ info += getDeclarationErrorAddendum();
+ }
+
+ let typeString;
+ if (type === null) {
+ typeString = 'null';
+ } else if (Array.isArray(type)) {
+ typeString = 'array';
+ } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
+ typeString = `<${getComponentName(type.type) || 'Unknown'} />`;
+ info =
+ ' Did you accidentally export a JSX literal instead of a component?';
+ } else {
+ typeString = typeof type;
+ }
+
+ warning(
+ false,
+ 'React.jsx: type is invalid -- expected a string (for ' +
+ 'built-in components) or a class/function (for composite ' +
+ 'components) but got: %s.%s',
+ typeString,
+ info,
+ );
+ }
+
+ const element = jsxDEV(type, props, key, source, self);
+
+ // The result can be nullish if a mock or a custom function is used.
+ // TODO: Drop this when these are no longer allowed as the type argument.
+ if (element == null) {
+ return element;
+ }
+
+ // Skip key warning if the type isn't valid since our key validation logic
+ // doesn't expect a non-string/function type and can throw confusing errors.
+ // We don't want exception behavior to differ between dev and prod.
+ // (Rendering will throw with a helpful message and as soon as the type is
+ // fixed, the key warnings will appear.)
+ if (validType) {
+ const children = props.children;
+ if (children !== undefined) {
+ if (isStaticChildren) {
+ for (let i = 0; i < children.length; i++) {
+ validateChildKeys(children[i], type);
+ }
+ } else {
+ validateChildKeys(children, type);
+ }
+ }
+ }
+
+ if (props.key !== undefined) {
+ warning(
+ false,
+ 'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
+ 'Explicitly pass a key after spreading props in your JSX call. ' +
+ 'E.g. ',
+ );
+ }
+
+ if (type === REACT_FRAGMENT_TYPE) {
+ validateFragmentProps(element);
+ } else {
+ validatePropTypes(element);
+ }
+
+ return element;
+}
+
+// These two functions exist to still get child warnings in dev
+// even with the prod transform. This means that jsxDEV is purely
+// opt-in behavior for better messages but that we won't stop
+// giving you warnings if you use production apis.
+export function jsxWithValidationStatic(type, props, key) {
+ return jsxWithValidation(type, props, key, true);
+}
+
+export function jsxWithValidationDynamic(type, props, key) {
+ return jsxWithValidation(type, props, key, false);
+}
+
export function createElementWithValidation(type, props, children) {
const validType = isValidElementType(type);
@@ -277,7 +395,7 @@ export function createElementWithValidation(type, props, children) {
"it's defined in, or you might have mixed up default and named imports.";
}
- const sourceInfo = getSourceInfoErrorAddendum(props);
+ const sourceInfo = getSourceInfoErrorAddendumForProps(props);
if (sourceInfo) {
info += sourceInfo;
} else {
diff --git a/packages/react/src/__tests__/ReactElementJSX-test.internal.js b/packages/react/src/__tests__/ReactElementJSX-test.internal.js
new file mode 100644
index 0000000000000..db62a2c51ec15
--- /dev/null
+++ b/packages/react/src/__tests__/ReactElementJSX-test.internal.js
@@ -0,0 +1,364 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+let React;
+let ReactDOM;
+let ReactFeatureFlags;
+let ReactTestUtils;
+
+// 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', () => {
+ let originalSymbol;
+
+ beforeEach(() => {
+ jest.resetModules();
+
+ // Delete the native Symbol if we have one to ensure we test the
+ // unpolyfilled environment.
+ originalSymbol = global.Symbol;
+ global.Symbol = undefined;
+
+ ReactFeatureFlags = require('shared/ReactFeatureFlags');
+ ReactFeatureFlags.enableJSXTransformAPI = true;
+
+ React = require('react');
+ ReactDOM = require('react-dom');
+ ReactTestUtils = require('react-dom/test-utils');
+ });
+
+ afterEach(() => {
+ global.Symbol = originalSymbol;
+ });
+
+ it('allows static methods to be called using the type property', () => {
+ class StaticMethodComponentClass extends React.Component {
+ render() {
+ return React.jsx('div', {});
+ }
+ }
+ StaticMethodComponentClass.someStaticMethod = () => 'someReturnValue';
+
+ const element = React.jsx(StaticMethodComponentClass, {});
+ expect(element.type.someStaticMethod()).toBe('someReturnValue');
+ });
+
+ it('identifies valid elements', () => {
+ class Component extends React.Component {
+ render() {
+ return React.jsx('div', {});
+ }
+ }
+
+ expect(React.isValidElement(React.jsx('div', {}))).toEqual(true);
+ expect(React.isValidElement(React.jsx(Component, {}))).toEqual(true);
+
+ expect(React.isValidElement(null)).toEqual(false);
+ expect(React.isValidElement(true)).toEqual(false);
+ expect(React.isValidElement({})).toEqual(false);
+ expect(React.isValidElement('string')).toEqual(false);
+ expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
+ expect(React.isValidElement(Component)).toEqual(false);
+ expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
+
+ const jsonElement = JSON.stringify(React.jsx('div', {}));
+ expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true);
+ });
+
+ it('is indistinguishable from a plain object', () => {
+ const element = React.jsx('div', {className: 'foo'});
+ const object = {};
+ expect(element.constructor).toBe(object.constructor);
+ });
+
+ it('should use default prop value when removing a prop', () => {
+ class Component extends React.Component {
+ render() {
+ return React.jsx('span', {});
+ }
+ }
+ Component.defaultProps = {fruit: 'persimmon'};
+
+ const container = document.createElement('div');
+ const instance = ReactDOM.render(
+ React.jsx(Component, {fruit: 'mango'}),
+ container,
+ );
+ expect(instance.props.fruit).toBe('mango');
+
+ ReactDOM.render(React.jsx(Component, {}), container);
+ expect(instance.props.fruit).toBe('persimmon');
+ });
+
+ it('should normalize props with default values', () => {
+ class Component extends React.Component {
+ render() {
+ return React.jsx('span', {children: this.props.prop});
+ }
+ }
+ Component.defaultProps = {prop: 'testKey'};
+
+ const instance = ReactTestUtils.renderIntoDocument(
+ React.jsx(Component, {}),
+ );
+ expect(instance.props.prop).toBe('testKey');
+
+ const inst2 = ReactTestUtils.renderIntoDocument(
+ React.jsx(Component, {prop: null}),
+ );
+ expect(inst2.props.prop).toBe(null);
+ });
+
+ it('throws when changing a prop (in dev) after element creation', () => {
+ class Outer extends React.Component {
+ render() {
+ const el = React.jsx('div', {className: 'moo'});
+
+ if (__DEV__) {
+ expect(function() {
+ el.props.className = 'quack';
+ }).toThrow();
+ expect(el.props.className).toBe('moo');
+ } else {
+ el.props.className = 'quack';
+ expect(el.props.className).toBe('quack');
+ }
+
+ return el;
+ }
+ }
+ const outer = ReactTestUtils.renderIntoDocument(
+ React.jsx(Outer, {color: 'orange'}),
+ );
+ if (__DEV__) {
+ expect(ReactDOM.findDOMNode(outer).className).toBe('moo');
+ } else {
+ expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
+ }
+ });
+
+ it('throws when adding a prop (in dev) after element creation', () => {
+ const container = document.createElement('div');
+ class Outer extends React.Component {
+ render() {
+ const el = React.jsx('div', {children: this.props.sound});
+
+ if (__DEV__) {
+ expect(function() {
+ el.props.className = 'quack';
+ }).toThrow();
+ expect(el.props.className).toBe(undefined);
+ } else {
+ el.props.className = 'quack';
+ expect(el.props.className).toBe('quack');
+ }
+
+ return el;
+ }
+ }
+ Outer.defaultProps = {sound: 'meow'};
+ const outer = ReactDOM.render(React.jsx(Outer, {}), container);
+ expect(ReactDOM.findDOMNode(outer).textContent).toBe('meow');
+ if (__DEV__) {
+ expect(ReactDOM.findDOMNode(outer).className).toBe('');
+ } else {
+ expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
+ }
+ });
+
+ it('does not warn for NaN props', () => {
+ class Test extends React.Component {
+ render() {
+ return React.jsx('div', {});
+ }
+ }
+ const test = ReactTestUtils.renderIntoDocument(
+ React.jsx(Test, {value: +undefined}),
+ );
+ expect(test.props.value).toBeNaN();
+ });
+
+ it('should warn when `key` is being accessed on composite element', () => {
+ const container = document.createElement('div');
+ class Child extends React.Component {
+ render() {
+ return React.jsx('div', {children: this.props.key});
+ }
+ }
+ class Parent extends React.Component {
+ render() {
+ return React.jsxs('div', {
+ children: [
+ React.jsx(Child, {}, '0'),
+ React.jsx(Child, {}, '1'),
+ React.jsx(Child, {}, '2'),
+ ],
+ });
+ }
+ }
+ expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
+ 'Child: `key` is not a prop. Trying to access it will result ' +
+ 'in `undefined` being returned. If you need to access the same ' +
+ 'value within the child component, you should pass it as a different ' +
+ 'prop. (https://fb.me/react-special-props)',
+ {withoutStack: true},
+ );
+ });
+
+ it('should warn when `key` is being accessed on a host element', () => {
+ const element = React.jsxs('div', {}, '3');
+ expect(() => void element.props.key).toWarnDev(
+ 'div: `key` is not a prop. Trying to access it will result ' +
+ 'in `undefined` being returned. If you need to access the same ' +
+ 'value within the child component, you should pass it as a different ' +
+ 'prop. (https://fb.me/react-special-props)',
+ {withoutStack: true},
+ );
+ });
+
+ it('should warn when `ref` is being accessed', () => {
+ const container = document.createElement('div');
+ class Child extends React.Component {
+ render() {
+ return React.jsx('div', {children: this.props.ref});
+ }
+ }
+ class Parent extends React.Component {
+ render() {
+ return React.jsx('div', {
+ children: React.jsx(Child, {ref: 'childElement'}),
+ });
+ }
+ }
+ expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
+ 'Child: `ref` is not a prop. Trying to access it will result ' +
+ 'in `undefined` being returned. If you need to access the same ' +
+ 'value within the child component, you should pass it as a different ' +
+ 'prop. (https://fb.me/react-special-props)',
+ {withoutStack: true},
+ );
+ });
+
+ it('identifies elements, but not JSON, if Symbols are supported', () => {
+ // Rudimentary polyfill
+ // Once all jest engines support Symbols natively we can swap this to test
+ // WITH native Symbols by default.
+ const REACT_ELEMENT_TYPE = function() {}; // fake Symbol
+ const OTHER_SYMBOL = function() {}; // another fake Symbol
+ global.Symbol = function(name) {
+ return OTHER_SYMBOL;
+ };
+ global.Symbol.for = function(key) {
+ if (key === 'react.element') {
+ return REACT_ELEMENT_TYPE;
+ }
+ return OTHER_SYMBOL;
+ };
+
+ jest.resetModules();
+
+ ReactFeatureFlags = require('shared/ReactFeatureFlags');
+ ReactFeatureFlags.enableJSXTransformAPI = true;
+
+ React = require('react');
+
+ class Component extends React.Component {
+ render() {
+ return React.jsx('div');
+ }
+ }
+
+ expect(React.isValidElement(React.jsx('div', {}))).toEqual(true);
+ expect(React.isValidElement(React.jsx(Component, {}))).toEqual(true);
+
+ expect(React.isValidElement(null)).toEqual(false);
+ expect(React.isValidElement(true)).toEqual(false);
+ expect(React.isValidElement({})).toEqual(false);
+ expect(React.isValidElement('string')).toEqual(false);
+ expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
+ expect(React.isValidElement(Component)).toEqual(false);
+ expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
+
+ const jsonElement = JSON.stringify(React.jsx('div', {}));
+ expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
+ });
+
+ it('should warn when unkeyed children are passed to jsx', () => {
+ const container = document.createElement('div');
+ class Child extends React.Component {
+ render() {
+ return React.jsx('div', {});
+ }
+ }
+ class Parent extends React.Component {
+ render() {
+ return React.jsx('div', {
+ children: [
+ React.jsx(Child, {}),
+ React.jsx(Child, {}),
+ React.jsx(Child, {}),
+ ],
+ });
+ }
+ }
+ expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
+ 'Warning: Each child in a list should have a unique "key" prop.\n\n' +
+ 'Check the render method of `Parent`. See https://fb.me/react-warning-keys for more information.\n' +
+ ' in Child (created by Parent)\n' +
+ ' in Parent',
+ );
+ });
+
+ it('should warn when keys are passed as part of props', () => {
+ const container = document.createElement('div');
+ class Child extends React.Component {
+ render() {
+ return React.jsx('div', {});
+ }
+ }
+ class Parent extends React.Component {
+ render() {
+ return React.jsx('div', {
+ children: [React.jsx(Child, {key: '0'})],
+ });
+ }
+ }
+ expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
+ 'Warning: React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
+ 'Explicitly pass a key after spreading props in your JSX call. ' +
+ 'E.g. ',
+ );
+ });
+
+ it('should not warn when unkeyed children are passed to jsxs', () => {
+ const container = document.createElement('div');
+ class Child extends React.Component {
+ render() {
+ return React.jsx('div', {});
+ }
+ }
+ class Parent extends React.Component {
+ render() {
+ return React.jsxs('div', {
+ children: [
+ React.jsx(Child, {}),
+ React.jsx(Child, {}),
+ React.jsx(Child, {}),
+ ],
+ });
+ }
+ }
+ // TODO: an explicit expect for no warning?
+ ReactDOM.render(React.jsx(Parent, {}), container);
+ });
+});
diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js
index 990dedf247649..bab2d9a2cfff0 100644
--- a/packages/shared/ReactFeatureFlags.js
+++ b/packages/shared/ReactFeatureFlags.js
@@ -68,3 +68,6 @@ export const enableEventAPI = false;
// Enables rewritten version of ReactFiberScheduler. Added in case we need to
// quickly revert it.
export const enableNewScheduler = false;
+
+// New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107
+export const enableJSXTransformAPI = false;
diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js
index 658f7781ff313..16b811bbbab2f 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-fb.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js
@@ -32,6 +32,7 @@ export const warnAboutDeprecatedLifecycles = true;
export const warnAboutDeprecatedSetNativeProps = true;
export const enableEventAPI = false;
export const enableNewScheduler = false;
+export const enableJSXTransformAPI = false;
// Only used in www builds.
export function addUserTimingListener() {
diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js
index 38e036682caac..3d26ebea941d8 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-oss.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js
@@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableEventAPI = false;
export const enableNewScheduler = false;
+export const enableJSXTransformAPI = false;
// Only used in www builds.
export function addUserTimingListener() {
diff --git a/packages/shared/forks/ReactFeatureFlags.new-scheduler.js b/packages/shared/forks/ReactFeatureFlags.new-scheduler.js
index a2a3bfdfe786d..ab43d3f3081f4 100644
--- a/packages/shared/forks/ReactFeatureFlags.new-scheduler.js
+++ b/packages/shared/forks/ReactFeatureFlags.new-scheduler.js
@@ -26,5 +26,6 @@ export const enableStableConcurrentModeAPIs = false;
export const warnAboutShorthandPropertyCollision = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableEventAPI = false;
+export const enableJSXTransformAPI = false;
export const enableNewScheduler = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.persistent.js b/packages/shared/forks/ReactFeatureFlags.persistent.js
index e5dc81c58b32d..8e96b43623d20 100644
--- a/packages/shared/forks/ReactFeatureFlags.persistent.js
+++ b/packages/shared/forks/ReactFeatureFlags.persistent.js
@@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableEventAPI = false;
export const enableNewScheduler = false;
+export const enableJSXTransformAPI = false;
// Only used in www builds.
export function addUserTimingListener() {
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index 08fe309d20b79..178a542430a5f 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableEventAPI = false;
export const enableNewScheduler = false;
+export const enableJSXTransformAPI = false;
// Only used in www builds.
export function addUserTimingListener() {
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
index 97fc3164eb68c..dd3bfd274f44a 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
@@ -27,6 +27,7 @@ export const disableJavaScriptURLs = false;
export const disableYielding = false;
export const enableEventAPI = true;
export const enableNewScheduler = false;
+export const enableJSXTransformAPI = true;
// Only used in www builds.
export function addUserTimingListener() {
diff --git a/packages/shared/forks/ReactFeatureFlags.www-new-scheduler.js b/packages/shared/forks/ReactFeatureFlags.www-new-scheduler.js
index 0d87b53fe3bd9..cd6b303a75660 100644
--- a/packages/shared/forks/ReactFeatureFlags.www-new-scheduler.js
+++ b/packages/shared/forks/ReactFeatureFlags.www-new-scheduler.js
@@ -31,6 +31,7 @@ export {
} from './ReactFeatureFlags.www';
export const enableNewScheduler = true;
+export const enableJSXTransformAPI = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js
index a5049339166f3..e77c28389d027 100644
--- a/packages/shared/forks/ReactFeatureFlags.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -74,6 +74,8 @@ function updateFlagOutsideOfReactCallStack() {
export const enableEventAPI = true;
+export const enableJSXTransformAPI = true;
+
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
type Check<_X, Y: _X, X: Y = _X> = null;