From 88ea8d9789599395c621004982fd09dba19eb57d Mon Sep 17 00:00:00 2001 From: Luna Ruan Date: Thu, 12 Mar 2020 18:46:02 -0700 Subject: [PATCH] add jsx-runtime and jsx-dev-runtime --- packages/react/jsx-dev-runtime.js | 10 ++ packages/react/jsx-runtime.js | 10 ++ packages/react/npm/jsx-dev-runtime.js | 7 ++ packages/react/npm/jsx-runtime.js | 7 ++ packages/react/package.json | 4 +- .../src/__tests__/ReactElementJSX-test.js | 114 ++++++++++-------- scripts/rollup/bundles.js | 38 ++++++ scripts/rollup/forks.js | 12 +- scripts/rollup/modules.js | 1 + 9 files changed, 149 insertions(+), 54 deletions(-) create mode 100644 packages/react/jsx-dev-runtime.js create mode 100644 packages/react/jsx-runtime.js create mode 100644 packages/react/npm/jsx-dev-runtime.js create mode 100644 packages/react/npm/jsx-runtime.js diff --git a/packages/react/jsx-dev-runtime.js b/packages/react/jsx-dev-runtime.js new file mode 100644 index 0000000000000..225f4bffa65d2 --- /dev/null +++ b/packages/react/jsx-dev-runtime.js @@ -0,0 +1,10 @@ +/** + * 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. + * + * @flow + */ + +export {Fragment, jsxDEV} from './src/React'; diff --git a/packages/react/jsx-runtime.js b/packages/react/jsx-runtime.js new file mode 100644 index 0000000000000..ec8987e8318ae --- /dev/null +++ b/packages/react/jsx-runtime.js @@ -0,0 +1,10 @@ +/** + * 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. + * + * @flow + */ + +export {Fragment, jsx, jsxs} from './src/React'; diff --git a/packages/react/npm/jsx-dev-runtime.js b/packages/react/npm/jsx-dev-runtime.js new file mode 100644 index 0000000000000..207ae62954872 --- /dev/null +++ b/packages/react/npm/jsx-dev-runtime.js @@ -0,0 +1,7 @@ +'use strict'; + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./cjs/react/jsx-dev-runtime.production.min.js'); +} else { + module.exports = require('./cjs/react/jsx-dev-runtime.development.js'); +} diff --git a/packages/react/npm/jsx-runtime.js b/packages/react/npm/jsx-runtime.js new file mode 100644 index 0000000000000..878d9d21190e6 --- /dev/null +++ b/packages/react/npm/jsx-runtime.js @@ -0,0 +1,7 @@ +'use strict'; + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./cjs/react/jsx-runtime.production.min.js'); +} else { + module.exports = require('./cjs/react/jsx-runtime.development.js'); +} diff --git a/packages/react/package.json b/packages/react/package.json index 2ffa23333ad2a..466c97ec0efa5 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -14,7 +14,9 @@ "build-info.json", "index.js", "cjs/", - "umd/" + "umd/", + "jsx-runtime.js", + "jsx-dev-runtime.js" ], "main": "index.js", "repository": { diff --git a/packages/react/src/__tests__/ReactElementJSX-test.js b/packages/react/src/__tests__/ReactElementJSX-test.js index 881e72256784a..c946b9ef4f675 100644 --- a/packages/react/src/__tests__/ReactElementJSX-test.js +++ b/packages/react/src/__tests__/ReactElementJSX-test.js @@ -12,6 +12,7 @@ let React; let ReactDOM; let ReactTestUtils; +let jsxRuntime; // 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. @@ -29,6 +30,7 @@ describe('ReactElement.jsx', () => { global.Symbol = undefined; React = require('react'); + jsxRuntime = require('react/jsx-runtime'); ReactDOM = require('react-dom'); ReactTestUtils = require('react-dom/test-utils'); }); @@ -45,24 +47,24 @@ describe('ReactElement.jsx', () => { it('allows static methods to be called using the type property', () => { class StaticMethodComponentClass extends React.Component { render() { - return React.jsx('div', {}); + return jsxRuntime.jsx('div', {}); } } StaticMethodComponentClass.someStaticMethod = () => 'someReturnValue'; - const element = React.jsx(StaticMethodComponentClass, {}); + const element = jsxRuntime.jsx(StaticMethodComponentClass, {}); expect(element.type.someStaticMethod()).toBe('someReturnValue'); }); it('identifies valid elements', () => { class Component extends React.Component { render() { - return React.jsx('div', {}); + return jsxRuntime.jsx('div', {}); } } - expect(React.isValidElement(React.jsx('div', {}))).toEqual(true); - expect(React.isValidElement(React.jsx(Component, {}))).toEqual(true); + expect(React.isValidElement(jsxRuntime.jsx('div', {}))).toEqual(true); + expect(React.isValidElement(jsxRuntime.jsx(Component, {}))).toEqual(true); expect(React.isValidElement(null)).toEqual(false); expect(React.isValidElement(true)).toEqual(false); @@ -83,12 +85,12 @@ describe('ReactElement.jsx', () => { expect(React.isValidElement(Component)).toEqual(false); expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); - const jsonElement = JSON.stringify(React.jsx('div', {})); + const jsonElement = JSON.stringify(jsxRuntime.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 element = jsxRuntime.jsx('div', {className: 'foo'}); const object = {}; expect(element.constructor).toBe(object.constructor); }); @@ -96,37 +98,37 @@ describe('ReactElement.jsx', () => { it('should use default prop value when removing a prop', () => { class Component extends React.Component { render() { - return React.jsx('span', {}); + return jsxRuntime.jsx('span', {}); } } Component.defaultProps = {fruit: 'persimmon'}; const container = document.createElement('div'); const instance = ReactDOM.render( - React.jsx(Component, {fruit: 'mango'}), + jsxRuntime.jsx(Component, {fruit: 'mango'}), container, ); expect(instance.props.fruit).toBe('mango'); - ReactDOM.render(React.jsx(Component, {}), container); + ReactDOM.render(jsxRuntime.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}); + return jsxRuntime.jsx('span', {children: this.props.prop}); } } Component.defaultProps = {prop: 'testKey'}; const instance = ReactTestUtils.renderIntoDocument( - React.jsx(Component, {}), + jsxRuntime.jsx(Component, {}), ); expect(instance.props.prop).toBe('testKey'); const inst2 = ReactTestUtils.renderIntoDocument( - React.jsx(Component, {prop: null}), + jsxRuntime.jsx(Component, {prop: null}), ); expect(inst2.props.prop).toBe(null); }); @@ -134,7 +136,7 @@ describe('ReactElement.jsx', () => { it('throws when changing a prop (in dev) after element creation', () => { class Outer extends React.Component { render() { - const el = React.jsx('div', {className: 'moo'}); + const el = jsxRuntime.jsx('div', {className: 'moo'}); if (__DEV__) { expect(function() { @@ -150,7 +152,7 @@ describe('ReactElement.jsx', () => { } } const outer = ReactTestUtils.renderIntoDocument( - React.jsx(Outer, {color: 'orange'}), + jsxRuntime.jsx(Outer, {color: 'orange'}), ); if (__DEV__) { expect(ReactDOM.findDOMNode(outer).className).toBe('moo'); @@ -163,7 +165,7 @@ describe('ReactElement.jsx', () => { const container = document.createElement('div'); class Outer extends React.Component { render() { - const el = React.jsx('div', {children: this.props.sound}); + const el = jsxRuntime.jsx('div', {children: this.props.sound}); if (__DEV__) { expect(function() { @@ -179,7 +181,7 @@ describe('ReactElement.jsx', () => { } } Outer.defaultProps = {sound: 'meow'}; - const outer = ReactDOM.render(React.jsx(Outer, {}), container); + const outer = ReactDOM.render(jsxRuntime.jsx(Outer, {}), container); expect(ReactDOM.findDOMNode(outer).textContent).toBe('meow'); if (__DEV__) { expect(ReactDOM.findDOMNode(outer).className).toBe(''); @@ -191,11 +193,11 @@ describe('ReactElement.jsx', () => { it('does not warn for NaN props', () => { class Test extends React.Component { render() { - return React.jsx('div', {}); + return jsxRuntime.jsx('div', {}); } } const test = ReactTestUtils.renderIntoDocument( - React.jsx(Test, {value: +undefined}), + jsxRuntime.jsx(Test, {value: +undefined}), ); expect(test.props.value).toBeNaN(); }); @@ -204,21 +206,23 @@ describe('ReactElement.jsx', () => { const container = document.createElement('div'); class Child extends React.Component { render() { - return React.jsx('div', {children: this.props.key}); + return jsxRuntime.jsx('div', {children: this.props.key}); } } class Parent extends React.Component { render() { - return React.jsxs('div', { + return jsxRuntime.jsxs('div', { children: [ - React.jsx(Child, {}, '0'), - React.jsx(Child, {}, '1'), - React.jsx(Child, {}, '2'), + jsxRuntime.jsx(Child, {}, '0'), + jsxRuntime.jsx(Child, {}, '1'), + jsxRuntime.jsx(Child, {}, '2'), ], }); } } - expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toErrorDev( + expect(() => + ReactDOM.render(jsxRuntime.jsx(Parent, {}), container), + ).toErrorDev( '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 ' + @@ -229,7 +233,10 @@ describe('ReactElement.jsx', () => { it('warns when a jsxs is passed something that is not an array', () => { const container = document.createElement('div'); expect(() => - ReactDOM.render(React.jsxs('div', {children: 'foo'}, null), container), + ReactDOM.render( + jsxRuntime.jsxs('div', {children: 'foo'}, null), + container, + ), ).toErrorDev( 'React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + @@ -239,7 +246,7 @@ describe('ReactElement.jsx', () => { }); it('should warn when `key` is being accessed on a host element', () => { - const element = React.jsxs('div', {}, '3'); + const element = jsxRuntime.jsxs('div', {}, '3'); expect( () => void element.props.key, ).toErrorDev( @@ -255,17 +262,19 @@ describe('ReactElement.jsx', () => { const container = document.createElement('div'); class Child extends React.Component { render() { - return React.jsx('div', {children: this.props.ref}); + return jsxRuntime.jsx('div', {children: this.props.ref}); } } class Parent extends React.Component { render() { - return React.jsx('div', { - children: React.jsx(Child, {ref: 'childElement'}), + return jsxRuntime.jsx('div', { + children: jsxRuntime.jsx(Child, {ref: 'childElement'}), }); } } - expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toErrorDev( + expect(() => + ReactDOM.render(jsxRuntime.jsx(Parent, {}), container), + ).toErrorDev( '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 ' + @@ -292,15 +301,16 @@ describe('ReactElement.jsx', () => { jest.resetModules(); React = require('react'); + jsxRuntime = require('react/jsx-runtime'); class Component extends React.Component { render() { - return React.jsx('div'); + return jsxRuntime.jsx('div'); } } - expect(React.isValidElement(React.jsx('div', {}))).toEqual(true); - expect(React.isValidElement(React.jsx(Component, {}))).toEqual(true); + expect(React.isValidElement(jsxRuntime.jsx('div', {}))).toEqual(true); + expect(React.isValidElement(jsxRuntime.jsx(Component, {}))).toEqual(true); expect(React.isValidElement(null)).toEqual(false); expect(React.isValidElement(true)).toEqual(false); @@ -321,7 +331,7 @@ describe('ReactElement.jsx', () => { expect(React.isValidElement(Component)).toEqual(false); expect(React.isValidElement({type: 'div', props: {}})).toEqual(false); - const jsonElement = JSON.stringify(React.jsx('div', {})); + const jsonElement = JSON.stringify(jsxRuntime.jsx('div', {})); expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false); }); @@ -329,21 +339,23 @@ describe('ReactElement.jsx', () => { const container = document.createElement('div'); class Child extends React.Component { render() { - return React.jsx('div', {}); + return jsxRuntime.jsx('div', {}); } } class Parent extends React.Component { render() { - return React.jsx('div', { + return jsxRuntime.jsx('div', { children: [ - React.jsx(Child, {}), - React.jsx(Child, {}), - React.jsx(Child, {}), + jsxRuntime.jsx(Child, {}), + jsxRuntime.jsx(Child, {}), + jsxRuntime.jsx(Child, {}), ], }); } } - expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toErrorDev( + expect(() => + ReactDOM.render(jsxRuntime.jsx(Parent, {}), container), + ).toErrorDev( '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' + @@ -356,18 +368,18 @@ describe('ReactElement.jsx', () => { const container = document.createElement('div'); class Child extends React.Component { render() { - return React.jsx('div', {}); + return jsxRuntime.jsx('div', {}); } } class Parent extends React.Component { render() { - return React.jsx('div', { - children: [React.jsx(Child, {key: '0'})], + return jsxRuntime.jsx('div', { + children: [jsxRuntime.jsx(Child, {key: '0'})], }); } } expect(() => - ReactDOM.render(React.jsx(Parent, {}), container), + ReactDOM.render(jsxRuntime.jsx(Parent, {}), container), ).toErrorDev( 'Warning: React.jsx: Spreading a key to JSX is a deprecated pattern. ' + 'Explicitly pass a key after spreading props in your JSX call. ' + @@ -380,21 +392,21 @@ describe('ReactElement.jsx', () => { const container = document.createElement('div'); class Child extends React.Component { render() { - return React.jsx('div', {}); + return jsxRuntime.jsx('div', {}); } } class Parent extends React.Component { render() { - return React.jsxs('div', { + return jsxRuntime.jsxs('div', { children: [ - React.jsx(Child, {}), - React.jsx(Child, {}), - React.jsx(Child, {}), + jsxRuntime.jsx(Child, {}), + jsxRuntime.jsx(Child, {}), + jsxRuntime.jsx(Child, {}), ], }); } } // TODO: an explicit expect for no warning? - ReactDOM.render(React.jsx(Parent, {}), container); + ReactDOM.render(jsxRuntime.jsx(Parent, {}), container); }); }); diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 379b3fcbbaa69..8f5244730282d 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -76,6 +76,44 @@ const bundles = [ externals: [], }, + /******* React Runtime *******/ + { + bundleTypes: [ + UMD_DEV, + UMD_PROD, + UMD_PROFILING, + NODE_DEV, + NODE_PROD, + NODE_PROFILING, + FB_WWW_DEV, + FB_WWW_PROD, + FB_WWW_PROFILING, + ], + moduleType: ISOMORPHIC, + entry: 'react/jsx-runtime', + global: 'JSXRuntime', + externals: ['react'], + }, + + /******* React DEV Runtime *******/ + { + bundleTypes: [ + UMD_DEV, + UMD_PROD, + UMD_PROFILING, + NODE_DEV, + NODE_PROD, + NODE_PROFILING, + FB_WWW_DEV, + FB_WWW_PROD, + FB_WWW_PROFILING, + ], + moduleType: ISOMORPHIC, + entry: 'react/jsx-dev-runtime', + global: 'JSXDEVRuntime', + externals: ['react'], + }, + /******* React DOM *******/ { bundleTypes: [ diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index 01fd4fee5aade..9f8dc89e944c9 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -34,7 +34,11 @@ const forks = Object.freeze({ // happens. Other bundles just require('object-assign') anyway. return null; } - if (entry === 'react') { + if ( + entry === 'react' || + entry === 'react/jsx-runtime' || + entry === 'react/jsx-dev-runtime' + ) { // Use the forked version that uses ES modules instead of CommonJS. return 'shared/forks/object-assign.inline-umd.js'; } @@ -50,7 +54,11 @@ const forks = Object.freeze({ // Without this fork, importing `shared/ReactSharedInternals` inside // the `react` package itself would not work due to a cyclical dependency. 'shared/ReactSharedInternals': (bundleType, entry, dependencies) => { - if (entry === 'react') { + if ( + entry === 'react' || + entry === 'react/jsx-runtime' || + entry === 'react/jsx-dev-runtime' + ) { return 'react/src/ReactSharedInternals'; } if (dependencies.indexOf('react') === -1) { diff --git a/scripts/rollup/modules.js b/scripts/rollup/modules.js index 8b188c30ac18c..59b29457294c8 100644 --- a/scripts/rollup/modules.js +++ b/scripts/rollup/modules.js @@ -14,6 +14,7 @@ const importSideEffects = Object.freeze({ scheduler: HAS_NO_SIDE_EFFECTS_ON_IMPORT, 'scheduler/tracing': HAS_NO_SIDE_EFFECTS_ON_IMPORT, 'react-dom/server': HAS_NO_SIDE_EFFECTS_ON_IMPORT, + 'react/jsx-dev-runtime': HAS_NO_SIDE_EFFECTS_ON_IMPORT, }); // Bundles exporting globals that other modules rely on.