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;