mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
add jsx-runtime and jsx-dev-runtime (#18299)
This PR adds the jsx-runtime and jsx-dev-runtime modules for the JSX Babel Plugin. WWW still relies on jsx/jsxs/jsxDEV from the "react" module, so once we refactor the code to point to the runtime modules we will remove jsx/jsxs/jsxDEV from the "react" module.
This commit is contained in:
@@ -50,8 +50,5 @@ export {
|
||||
DEPRECATED_createResponder,
|
||||
// enableScopeAPI
|
||||
unstable_createScope,
|
||||
// enableJSXTransformAPI
|
||||
jsx,
|
||||
jsxs,
|
||||
jsxDEV,
|
||||
} from './src/React';
|
||||
export {jsx, jsxs, jsxDEV} from './src/jsx/ReactJSX';
|
||||
|
||||
@@ -45,8 +45,4 @@ export {
|
||||
unstable_withSuspenseConfig,
|
||||
// enableBlocksAPI
|
||||
block,
|
||||
// enableJSXTransformAPI
|
||||
jsx,
|
||||
jsxs,
|
||||
jsxDEV,
|
||||
} from './src/React';
|
||||
|
||||
@@ -76,7 +76,4 @@ export {
|
||||
DEPRECATED_createResponder,
|
||||
unstable_createFundamental,
|
||||
unstable_createScope,
|
||||
jsx,
|
||||
jsxs,
|
||||
jsxDEV,
|
||||
} from './src/React';
|
||||
|
||||
@@ -49,8 +49,5 @@ export {
|
||||
DEPRECATED_createResponder,
|
||||
// enableScopeAPI
|
||||
unstable_createScope,
|
||||
// enableJSXTransformAPI
|
||||
jsx,
|
||||
jsxs,
|
||||
jsxDEV,
|
||||
} from './src/React';
|
||||
export {jsx, jsxs, jsxDEV} from './src/jsx/ReactJSX';
|
||||
|
||||
10
packages/react/jsx-dev-runtime.js
Normal file
10
packages/react/jsx-dev-runtime.js
Normal file
@@ -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/jsx/ReactJSX';
|
||||
9
packages/react/jsx-runtime.js
Normal file
9
packages/react/jsx-runtime.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 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/jsx/ReactJSX';
|
||||
7
packages/react/npm/jsx-dev-runtime.js
Normal file
7
packages/react/npm/jsx-dev-runtime.js
Normal file
@@ -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
packages/react/npm/jsx-runtime.js
Normal file
7
packages/react/npm/jsx-runtime.js
Normal file
@@ -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');
|
||||
}
|
||||
@@ -14,7 +14,9 @@
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/",
|
||||
"umd/"
|
||||
"umd/",
|
||||
"jsx-runtime.js",
|
||||
"jsx-dev-runtime.js"
|
||||
],
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
createFactory as createFactoryProd,
|
||||
cloneElement as cloneElementProd,
|
||||
isValidElement,
|
||||
jsx as jsxProd,
|
||||
} from './ReactElement';
|
||||
import {createContext} from './ReactContext';
|
||||
import {lazy} from './ReactLazy';
|
||||
@@ -52,9 +51,6 @@ import {
|
||||
createElementWithValidation,
|
||||
createFactoryWithValidation,
|
||||
cloneElementWithValidation,
|
||||
jsxWithValidation,
|
||||
jsxWithValidationStatic,
|
||||
jsxWithValidationDynamic,
|
||||
} from './ReactElementValidator';
|
||||
import createMutableSource from './createMutableSource';
|
||||
import ReactSharedInternals from './ReactSharedInternals';
|
||||
@@ -67,12 +63,6 @@ const createElement = __DEV__ ? createElementWithValidation : createElementProd;
|
||||
const cloneElement = __DEV__ ? cloneElementWithValidation : cloneElementProd;
|
||||
const createFactory = __DEV__ ? createFactoryWithValidation : createFactoryProd;
|
||||
|
||||
const jsxDEV = __DEV__ ? jsxWithValidation : undefined;
|
||||
const jsx = __DEV__ ? jsxWithValidationDynamic : jsxProd;
|
||||
// we may want to special case jsxs internally to take advantage of static children.
|
||||
// for now we can ship identical prod functions
|
||||
const jsxs = __DEV__ ? jsxWithValidationStatic : jsxProd;
|
||||
|
||||
const Children = {
|
||||
map,
|
||||
forEach,
|
||||
@@ -127,9 +117,4 @@ export {
|
||||
createFundamental as unstable_createFundamental,
|
||||
// enableScopeAPI
|
||||
createScope as unstable_createScope,
|
||||
// enableJSXTransformAPI
|
||||
jsx,
|
||||
jsxs,
|
||||
// TODO: jsxDEV should not be exposed as a name. We might want to move it to a different entry point.
|
||||
jsxDEV,
|
||||
};
|
||||
|
||||
@@ -23,6 +23,13 @@ export function setCurrentlyValidatingElement(element: null | ReactElement) {
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
ReactDebugCurrentFrame.setCurrentlyValidatingElement = function(
|
||||
element: null | ReactElement,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
currentlyValidatingElement = element;
|
||||
}
|
||||
};
|
||||
// Stack implementation injected by the current renderer.
|
||||
ReactDebugCurrentFrame.getCurrentStack = (null: null | (() => string));
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
let JSXRuntime;
|
||||
let JSXDEVRuntime;
|
||||
|
||||
// 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 +31,8 @@ describe('ReactElement.jsx', () => {
|
||||
global.Symbol = undefined;
|
||||
|
||||
React = require('react');
|
||||
JSXRuntime = require('react/jsx-runtime');
|
||||
JSXDEVRuntime = require('react/jsx-dev-runtime');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
@@ -37,32 +41,35 @@ describe('ReactElement.jsx', () => {
|
||||
global.Symbol = originalSymbol;
|
||||
});
|
||||
|
||||
if (!__EXPERIMENTAL__) {
|
||||
it("empty test so Jest doesn't complain", () => {});
|
||||
return;
|
||||
}
|
||||
|
||||
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(JSXRuntime.jsx(JSXRuntime.Fragment, {})),
|
||||
).toEqual(true);
|
||||
if (__DEV__) {
|
||||
expect(React.isValidElement(JSXDEVRuntime.jsxDEV('div', {}))).toEqual(
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
expect(React.isValidElement(null)).toEqual(false);
|
||||
expect(React.isValidElement(true)).toEqual(false);
|
||||
@@ -83,12 +90,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 +103,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 +141,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 +157,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 +170,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 +186,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 +198,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 +211,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 +238,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 +251,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 +267,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 +306,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,29 +336,32 @@ 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' +
|
||||
@@ -356,18 +374,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 +398,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);
|
||||
});
|
||||
});
|
||||
|
||||
22
packages/react/src/jsx/ReactJSX.js
Normal file
22
packages/react/src/jsx/ReactJSX.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
import {REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
|
||||
import {
|
||||
jsxWithValidationStatic,
|
||||
jsxWithValidationDynamic,
|
||||
jsxWithValidation,
|
||||
} from './ReactJSXElementValidator';
|
||||
import {jsx as jsxProd} from './ReactJSXElement';
|
||||
const jsx = __DEV__ ? jsxWithValidationDynamic : jsxProd;
|
||||
// we may want to special case jsxs internally to take advantage of static children.
|
||||
// for now we can ship identical prod functions
|
||||
const jsxs = __DEV__ ? jsxWithValidationStatic : jsxProd;
|
||||
const jsxDEV = __DEV__ ? jsxWithValidation : undefined;
|
||||
|
||||
export {REACT_FRAGMENT_TYPE as Fragment, jsx, jsxs, jsxDEV};
|
||||
345
packages/react/src/jsx/ReactJSXElement.js
Normal file
345
packages/react/src/jsx/ReactJSXElement.js
Normal file
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
||||
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
const RESERVED_PROPS = {
|
||||
key: true,
|
||||
ref: true,
|
||||
__self: true,
|
||||
__source: true,
|
||||
};
|
||||
|
||||
let specialPropKeyWarningShown;
|
||||
let specialPropRefWarningShown;
|
||||
let didWarnAboutStringRefs;
|
||||
|
||||
if (__DEV__) {
|
||||
didWarnAboutStringRefs = {};
|
||||
}
|
||||
|
||||
function hasValidRef(config) {
|
||||
if (__DEV__) {
|
||||
if (hasOwnProperty.call(config, 'ref')) {
|
||||
const getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
|
||||
if (getter && getter.isReactWarning) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return config.ref !== undefined;
|
||||
}
|
||||
|
||||
function hasValidKey(config) {
|
||||
if (__DEV__) {
|
||||
if (hasOwnProperty.call(config, 'key')) {
|
||||
const getter = Object.getOwnPropertyDescriptor(config, 'key').get;
|
||||
if (getter && getter.isReactWarning) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return config.key !== undefined;
|
||||
}
|
||||
|
||||
function warnIfStringRefCannotBeAutoConverted(config) {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
typeof config.ref === 'string' &&
|
||||
ReactCurrentOwner.current &&
|
||||
config.__self &&
|
||||
ReactCurrentOwner.current.stateNode !== config.__self
|
||||
) {
|
||||
const componentName = getComponentName(ReactCurrentOwner.current.type);
|
||||
|
||||
if (!didWarnAboutStringRefs[componentName]) {
|
||||
console.error(
|
||||
'Component "%s" contains the string ref "%s". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'This case cannot be automatically converted to an arrow function. ' +
|
||||
'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://fb.me/react-strict-mode-string-ref',
|
||||
getComponentName(ReactCurrentOwner.current.type),
|
||||
config.ref,
|
||||
);
|
||||
didWarnAboutStringRefs[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function defineKeyPropWarningGetter(props, displayName) {
|
||||
if (__DEV__) {
|
||||
const warnAboutAccessingKey = function() {
|
||||
if (!specialPropKeyWarningShown) {
|
||||
specialPropKeyWarningShown = true;
|
||||
console.error(
|
||||
'%s: `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)',
|
||||
displayName,
|
||||
);
|
||||
}
|
||||
};
|
||||
warnAboutAccessingKey.isReactWarning = true;
|
||||
Object.defineProperty(props, 'key', {
|
||||
get: warnAboutAccessingKey,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function defineRefPropWarningGetter(props, displayName) {
|
||||
if (__DEV__) {
|
||||
const warnAboutAccessingRef = function() {
|
||||
if (!specialPropRefWarningShown) {
|
||||
specialPropRefWarningShown = true;
|
||||
console.error(
|
||||
'%s: `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)',
|
||||
displayName,
|
||||
);
|
||||
}
|
||||
};
|
||||
warnAboutAccessingRef.isReactWarning = true;
|
||||
Object.defineProperty(props, 'ref', {
|
||||
get: warnAboutAccessingRef,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new React element. This no longer adheres to
|
||||
* the class pattern, so do not use new to call it. Also, instanceof check
|
||||
* will not work. Instead test $$typeof field against Symbol.for('react.element') to check
|
||||
* 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
|
||||
* functions, and as long as `this` and owner are the same, there will be no
|
||||
* change in behavior.
|
||||
* @param {*} source An annotation object (added by a transpiler or otherwise)
|
||||
* indicating filename, line number, and/or other information.
|
||||
* @internal
|
||||
*/
|
||||
const ReactElement = function(type, key, ref, self, source, owner, props) {
|
||||
const element = {
|
||||
// This tag allows us to uniquely identify this as a React Element
|
||||
$$typeof: REACT_ELEMENT_TYPE,
|
||||
|
||||
// Built-in properties that belong on the element
|
||||
type: type,
|
||||
key: key,
|
||||
ref: ref,
|
||||
props: props,
|
||||
|
||||
// Record the component responsible for creating this element.
|
||||
_owner: owner,
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
// The validation flag is currently mutative. We put it on
|
||||
// an external backing store so that we can freeze the whole object.
|
||||
// This can be replaced with a WeakMap once they are implemented in
|
||||
// commonly used development environments.
|
||||
element._store = {};
|
||||
|
||||
// To make comparing ReactElements easier for testing purposes, we make
|
||||
// the validation flag non-enumerable (where possible, which should
|
||||
// include every environment we run tests in), so the test framework
|
||||
// ignores it.
|
||||
Object.defineProperty(element._store, 'validated', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: false,
|
||||
});
|
||||
// self and source are DEV only properties.
|
||||
Object.defineProperty(element, '_self', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: self,
|
||||
});
|
||||
// Two elements created in two different places should be considered
|
||||
// equal for testing purposes and therefore we hide it from enumeration.
|
||||
Object.defineProperty(element, '_source', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: source,
|
||||
});
|
||||
if (Object.freeze) {
|
||||
Object.freeze(element.props);
|
||||
Object.freeze(element);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Currently, key can be spread in as a prop. This causes a potential
|
||||
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
|
||||
// or <div key="Hi" {...props} /> ). We want to deprecate key spread,
|
||||
// but as an intermediary step, we will use jsxDEV for everything except
|
||||
// <div {...props} key="Hi" />, because we aren't currently able to tell if
|
||||
// key is explicitly declared to be undefined or not.
|
||||
if (maybeKey !== undefined) {
|
||||
key = '' + maybeKey;
|
||||
}
|
||||
|
||||
if (hasValidKey(config)) {
|
||||
key = '' + config.key;
|
||||
}
|
||||
|
||||
if (hasValidRef(config)) {
|
||||
ref = config.ref;
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (__DEV__) {
|
||||
let propName;
|
||||
|
||||
// Reserved names are extracted
|
||||
const props = {};
|
||||
|
||||
let key = null;
|
||||
let ref = null;
|
||||
|
||||
// Currently, key can be spread in as a prop. This causes a potential
|
||||
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
|
||||
// or <div key="Hi" {...props} /> ). We want to deprecate key spread,
|
||||
// but as an intermediary step, we will use jsxDEV for everything except
|
||||
// <div {...props} key="Hi" />, because we aren't currently able to tell if
|
||||
// key is explicitly declared to be undefined or not.
|
||||
if (maybeKey !== undefined) {
|
||||
key = '' + maybeKey;
|
||||
}
|
||||
|
||||
if (hasValidKey(config)) {
|
||||
key = '' + config.key;
|
||||
}
|
||||
|
||||
if (hasValidRef(config)) {
|
||||
ref = config.ref;
|
||||
warnIfStringRefCannotBeAutoConverted(config);
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
407
packages/react/src/jsx/ReactJSXElementValidator.js
Normal file
407
packages/react/src/jsx/ReactJSXElementValidator.js
Normal file
@@ -0,0 +1,407 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ReactElementValidator provides a wrapper around a element factory
|
||||
* which validates the props passed to the element. This is intended to be
|
||||
* used only in DEV and could be replaced by a static type checker for languages
|
||||
* that support it.
|
||||
*/
|
||||
import isValidElementType from 'shared/isValidElementType';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import checkPropTypes from 'shared/checkPropTypes';
|
||||
import {
|
||||
getIteratorFn,
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
REACT_MEMO_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_ELEMENT_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
import {warnAboutSpreadingKeyToJSX} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {jsxDEV} from './ReactJSXElement';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
||||
|
||||
let propTypesMisspellWarningShown;
|
||||
|
||||
if (__DEV__) {
|
||||
propTypesMisspellWarningShown = false;
|
||||
}
|
||||
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Verifies the object is a ReactElement.
|
||||
* See https://reactjs.org/docs/react-api.html#isvalidelement
|
||||
* @param {?object} object
|
||||
* @return {boolean} True if `object` is a ReactElement.
|
||||
* @final
|
||||
*/
|
||||
export function isValidElement(object) {
|
||||
if (__DEV__) {
|
||||
return (
|
||||
typeof object === 'object' &&
|
||||
object !== null &&
|
||||
object.$$typeof === REACT_ELEMENT_TYPE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getDeclarationErrorAddendum() {
|
||||
if (__DEV__) {
|
||||
if (ReactCurrentOwner.current) {
|
||||
const name = getComponentName(ReactCurrentOwner.current.type);
|
||||
if (name) {
|
||||
return '\n\nCheck the render method of `' + name + '`.';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceInfoErrorAddendum(source) {
|
||||
if (__DEV__) {
|
||||
if (source !== undefined) {
|
||||
const fileName = source.fileName.replace(/^.*[\\\/]/, '');
|
||||
const lineNumber = source.lineNumber;
|
||||
return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
|
||||
}
|
||||
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
|
||||
* updates.
|
||||
*/
|
||||
const ownerHasKeyUseWarning = {};
|
||||
|
||||
function getCurrentComponentErrorInfo(parentType) {
|
||||
if (__DEV__) {
|
||||
let info = getDeclarationErrorAddendum();
|
||||
|
||||
if (!info) {
|
||||
const parentName =
|
||||
typeof parentType === 'string'
|
||||
? parentType
|
||||
: parentType.displayName || parentType.name;
|
||||
if (parentName) {
|
||||
info = `\n\nCheck the top-level render call using <${parentName}>.`;
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn if the element doesn't have an explicit key assigned to it.
|
||||
* This element is in an array. The array could grow and shrink or be
|
||||
* reordered. All children that haven't already been validated are required to
|
||||
* have a "key" property assigned to it. Error statuses are cached so a warning
|
||||
* will only be shown once.
|
||||
*
|
||||
* @internal
|
||||
* @param {ReactElement} element Element that requires a key.
|
||||
* @param {*} parentType element's parent's type.
|
||||
*/
|
||||
function validateExplicitKey(element, parentType) {
|
||||
if (__DEV__) {
|
||||
if (!element._store || element._store.validated || element.key != null) {
|
||||
return;
|
||||
}
|
||||
element._store.validated = true;
|
||||
|
||||
const currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
|
||||
if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
|
||||
return;
|
||||
}
|
||||
ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
|
||||
|
||||
// Usually the current owner is the offender, but if it accepts children as a
|
||||
// property, it may be the creator of the child that's responsible for
|
||||
// assigning it a key.
|
||||
let childOwner = '';
|
||||
if (
|
||||
element &&
|
||||
element._owner &&
|
||||
element._owner !== ReactCurrentOwner.current
|
||||
) {
|
||||
// Give the component that originally created this child.
|
||||
childOwner = ` It was passed a child from ${getComponentName(
|
||||
element._owner.type,
|
||||
)}.`;
|
||||
}
|
||||
|
||||
ReactDebugCurrentFrame.setCurrentlyValidatingElement(element);
|
||||
console.error(
|
||||
'Each child in a list should have a unique "key" prop.' +
|
||||
'%s%s See https://fb.me/react-warning-keys for more information.',
|
||||
currentComponentErrorInfo,
|
||||
childOwner,
|
||||
);
|
||||
ReactDebugCurrentFrame.setCurrentlyValidatingElement(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that every element either is passed in a static location, in an
|
||||
* array with an explicit keys property defined, or in an object literal
|
||||
* with valid key property.
|
||||
*
|
||||
* @internal
|
||||
* @param {ReactNode} node Statically passed child of any type.
|
||||
* @param {*} parentType node's parent's type.
|
||||
*/
|
||||
function validateChildKeys(node, parentType) {
|
||||
if (__DEV__) {
|
||||
if (typeof node !== 'object') {
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(node)) {
|
||||
for (let i = 0; i < node.length; i++) {
|
||||
const child = node[i];
|
||||
if (isValidElement(child)) {
|
||||
validateExplicitKey(child, parentType);
|
||||
}
|
||||
}
|
||||
} else if (isValidElement(node)) {
|
||||
// This element was passed in a valid location.
|
||||
if (node._store) {
|
||||
node._store.validated = true;
|
||||
}
|
||||
} else if (node) {
|
||||
const iteratorFn = getIteratorFn(node);
|
||||
if (typeof iteratorFn === 'function') {
|
||||
// Entry iterators used to provide implicit keys,
|
||||
// but now we print a separate warning for them later.
|
||||
if (iteratorFn !== node.entries) {
|
||||
const iterator = iteratorFn.call(node);
|
||||
let step;
|
||||
while (!(step = iterator.next()).done) {
|
||||
if (isValidElement(step.value)) {
|
||||
validateExplicitKey(step.value, parentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element, validate that its props follow the propTypes definition,
|
||||
* provided by the type.
|
||||
*
|
||||
* @param {ReactElement} element
|
||||
*/
|
||||
function validatePropTypes(element) {
|
||||
if (__DEV__) {
|
||||
const type = element.type;
|
||||
if (type === null || type === undefined || typeof type === 'string') {
|
||||
return;
|
||||
}
|
||||
const name = getComponentName(type);
|
||||
let propTypes;
|
||||
if (typeof type === 'function') {
|
||||
propTypes = type.propTypes;
|
||||
} else if (
|
||||
typeof type === 'object' &&
|
||||
(type.$$typeof === REACT_FORWARD_REF_TYPE ||
|
||||
// Note: Memo only checks outer props here.
|
||||
// Inner props are checked in the reconciler.
|
||||
type.$$typeof === REACT_MEMO_TYPE)
|
||||
) {
|
||||
propTypes = type.propTypes;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (propTypes) {
|
||||
ReactDebugCurrentFrame.setCurrentlyValidatingElement(element);
|
||||
checkPropTypes(propTypes, element.props, 'prop', name);
|
||||
ReactDebugCurrentFrame.setCurrentlyValidatingElement(null);
|
||||
} else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
|
||||
propTypesMisspellWarningShown = true;
|
||||
console.error(
|
||||
'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?',
|
||||
name || 'Unknown',
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof type.getDefaultProps === 'function' &&
|
||||
!type.getDefaultProps.isReactClassApproved
|
||||
) {
|
||||
console.error(
|
||||
'getDefaultProps is only used on classic React.createClass ' +
|
||||
'definitions. Use a static property named `defaultProps` instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a fragment, validate that it can only be provided with fragment props
|
||||
* @param {ReactElement} fragment
|
||||
*/
|
||||
function validateFragmentProps(fragment) {
|
||||
if (__DEV__) {
|
||||
ReactDebugCurrentFrame.setCurrentlyValidatingElement(fragment);
|
||||
|
||||
const keys = Object.keys(fragment.props);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (key !== 'children' && key !== 'key') {
|
||||
console.error(
|
||||
'Invalid prop `%s` supplied to `React.Fragment`. ' +
|
||||
'React.Fragment can only have `key` and `children` props.',
|
||||
key,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fragment.ref !== null) {
|
||||
console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
|
||||
}
|
||||
|
||||
ReactDebugCurrentFrame.setCurrentlyValidatingElement(null);
|
||||
}
|
||||
}
|
||||
|
||||
export function jsxWithValidation(
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
isStaticChildren,
|
||||
source,
|
||||
self,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
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;
|
||||
}
|
||||
|
||||
console.error(
|
||||
'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) {
|
||||
if (Array.isArray(children)) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
validateChildKeys(children[i], type);
|
||||
}
|
||||
|
||||
if (Object.freeze) {
|
||||
Object.freeze(children);
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
'React.jsx: Static children should always be an array. ' +
|
||||
'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
|
||||
'Use the Babel transform instead.',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
validateChildKeys(children, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (warnAboutSpreadingKeyToJSX) {
|
||||
if (hasOwnProperty.call(props, 'key')) {
|
||||
console.error(
|
||||
'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
|
||||
'Explicitly pass a key after spreading props in your JSX call. ' +
|
||||
'E.g. <%s {...props} key={key} />',
|
||||
getComponentName(type) || 'ComponentName',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (__DEV__) {
|
||||
return jsxWithValidation(type, props, key, true);
|
||||
}
|
||||
}
|
||||
|
||||
export function jsxWithValidationDynamic(type, props, key) {
|
||||
if (__DEV__) {
|
||||
return jsxWithValidation(type, props, key, false);
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,38 @@ const bundles = [
|
||||
externals: [],
|
||||
},
|
||||
|
||||
/******* React JSX Runtime *******/
|
||||
{
|
||||
bundleTypes: [
|
||||
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 JSX DEV Runtime *******/
|
||||
{
|
||||
bundleTypes: [
|
||||
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: [
|
||||
|
||||
@@ -58,7 +58,7 @@ const forks = Object.freeze({
|
||||
if (entry === 'react') {
|
||||
return 'react/src/ReactSharedInternals';
|
||||
}
|
||||
if (dependencies.indexOf('react') === -1) {
|
||||
if (!entry.startsWith('react/') && dependencies.indexOf('react') === -1) {
|
||||
// React internals are unavailable if we can't reference the package.
|
||||
// We return an error because we only want to throw if this module gets used.
|
||||
return new Error(
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user