Skip to content

Commit

Permalink
add jsx-runtime and jsx-dev-runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
lunaruan committed Mar 13, 2020
1 parent 0705b72 commit 9565d1b
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 54 deletions.
10 changes: 10 additions & 0 deletions packages/react/jsx-dev-runtime.js
Original file line number Diff line number Diff line change
@@ -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';
10 changes: 10 additions & 0 deletions packages/react/jsx-runtime.js
Original file line number Diff line number Diff line change
@@ -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';
7 changes: 7 additions & 0 deletions packages/react/npm/jsx-dev-runtime.js
Original file line number Diff line number Diff line change
@@ -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');
}
7 changes: 7 additions & 0 deletions packages/react/npm/jsx-runtime.js
Original file line number Diff line number Diff line change
@@ -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');
}
4 changes: 3 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"build-info.json",
"index.js",
"cjs/",
"umd/"
"umd/",
"jsx-runtime.js",
"jsx-dev-runtime.js"
],
"main": "index.js",
"repository": {
Expand Down
114 changes: 63 additions & 51 deletions packages/react/src/__tests__/ReactElementJSX-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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');
});
Expand All @@ -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);
Expand All @@ -83,58 +85,58 @@ 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);
});

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);
});

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() {
Expand All @@ -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');
Expand All @@ -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() {
Expand All @@ -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('');
Expand All @@ -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();
});
Expand All @@ -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 ' +
Expand All @@ -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. ' +
Expand All @@ -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(
Expand All @@ -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 ' +
Expand All @@ -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);
Expand All @@ -321,29 +331,31 @@ 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);
});

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', {});
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' +
Expand All @@ -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. ' +
Expand All @@ -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);
});
});
Loading

0 comments on commit 9565d1b

Please sign in to comment.