@samitouri / QOS-React-2 / commits / 5fb2c93f39

Combine createElement and JSX modules (#28320)

Depends on: - #28317 --- There's a ton of overlap between the createElement implementation and the JSX implementation, so I combined them into a single module. In the actual build output, the shared code between JSX and createElement will get duplicated anyway, because react/jsx-runtime and react (where createElement lives) are separate, flat build artifacts. So this is more about code organization — with a few key exceptions, the implementations of createElement and jsx are highly coupled.

Andrew Clark committed Feb 19, 2024 at 22:45 UTC 5fb2c93f3924ba980444da5698f60651b5ef0689
9 files changed +351 -845
packages/react-devtools-shared/src/__tests__/utils-test.js
+1 -1
@@ -23,7 +23,7 @@ import {
23 REACT_SUSPENSE_LIST_TYPE as SuspenseList,
24 REACT_STRICT_MODE_TYPE as StrictMode,
25 } from 'shared/ReactSymbols';
26 -import {createElement} from 'react/src/ReactElement';
26 +import {createElement} from 'react';
27
28 describe('utils', () => {
29 describe('getDisplayName', () => {
packages/react/src/ReactChildren.js
+1 -1
@@ -24,7 +24,7 @@ import {
24 } from 'shared/ReactSymbols';
25 import {checkKeyStringCoercion} from 'shared/CheckStringCoercion';
26
27 -import {isValidElement, cloneAndReplaceKey} from './ReactElement';
27 +import {isValidElement, cloneAndReplaceKey} from './jsx/ReactJSXElement';
28
29 const SEPARATOR = '.';
30 const SUBSEPARATOR = ':';
packages/react/src/ReactClient.js
+1 -1
@@ -30,7 +30,7 @@ import {
30 createFactory,
31 cloneElement,
32 isValidElement,
33 -} from './ReactElement';
33 +} from './jsx/ReactJSXElement';
34 import {createContext} from './ReactContext';
35 import {lazy} from './ReactLazy';
36 import {forwardRef} from './ReactForwardRef';
packages/react/src/ReactElement.js deleted
-32
@@ -1,32 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import {
11 - createElement as createElementProd,
12 - createFactory as createFactoryProd,
13 - cloneElement as cloneElementProd,
14 -} from './ReactElementProd';
15 -
16 -import {
17 - createElementWithValidation,
18 - createFactoryWithValidation,
19 - cloneElementWithValidation,
20 -} from './ReactElementValidator';
21 -
22 -export {isValidElement, cloneAndReplaceKey} from './ReactElementProd';
23 -
24 -export const createElement: any = __DEV__
25 - ? createElementWithValidation
26 - : createElementProd;
27 -export const cloneElement: any = __DEV__
28 - ? cloneElementWithValidation
29 - : cloneElementProd;
30 -export const createFactory: any = __DEV__
31 - ? createFactoryWithValidation
32 - : createFactoryProd;
packages/react/src/ReactElementProd.js deleted
-406
@@ -1,406 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -import getComponentNameFromType from 'shared/getComponentNameFromType';
9 -import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
10 -import assign from 'shared/assign';
11 -import hasOwnProperty from 'shared/hasOwnProperty';
12 -import {checkKeyStringCoercion} from 'shared/CheckStringCoercion';
13 -
14 -import ReactCurrentOwner from './ReactCurrentOwner';
15 -
16 -let specialPropKeyWarningShown,
17 - specialPropRefWarningShown,
18 - didWarnAboutStringRefs;
19 -
20 -if (__DEV__) {
21 - didWarnAboutStringRefs = {};
22 -}
23 -
24 -function hasValidRef(config) {
25 - if (__DEV__) {
26 - if (hasOwnProperty.call(config, 'ref')) {
27 - const getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
28 - if (getter && getter.isReactWarning) {
29 - return false;
30 - }
31 - }
32 - }
33 - return config.ref !== undefined;
34 -}
35 -
36 -function hasValidKey(config) {
37 - if (__DEV__) {
38 - if (hasOwnProperty.call(config, 'key')) {
39 - const getter = Object.getOwnPropertyDescriptor(config, 'key').get;
40 - if (getter && getter.isReactWarning) {
41 - return false;
42 - }
43 - }
44 - }
45 - return config.key !== undefined;
46 -}
47 -
48 -function defineKeyPropWarningGetter(props, displayName) {
49 - const warnAboutAccessingKey = function () {
50 - if (__DEV__) {
51 - if (!specialPropKeyWarningShown) {
52 - specialPropKeyWarningShown = true;
53 - console.error(
54 - '%s: `key` is not a prop. Trying to access it will result ' +
55 - 'in `undefined` being returned. If you need to access the same ' +
56 - 'value within the child component, you should pass it as a different ' +
57 - 'prop. (https://reactjs.org/link/special-props)',
58 - displayName,
59 - );
60 - }
61 - }
62 - };
63 - warnAboutAccessingKey.isReactWarning = true;
64 - Object.defineProperty(props, 'key', {
65 - get: warnAboutAccessingKey,
66 - configurable: true,
67 - });
68 -}
69 -
70 -function defineRefPropWarningGetter(props, displayName) {
71 - const warnAboutAccessingRef = function () {
72 - if (__DEV__) {
73 - if (!specialPropRefWarningShown) {
74 - specialPropRefWarningShown = true;
75 - console.error(
76 - '%s: `ref` is not a prop. Trying to access it will result ' +
77 - 'in `undefined` being returned. If you need to access the same ' +
78 - 'value within the child component, you should pass it as a different ' +
79 - 'prop. (https://reactjs.org/link/special-props)',
80 - displayName,
81 - );
82 - }
83 - }
84 - };
85 - warnAboutAccessingRef.isReactWarning = true;
86 - Object.defineProperty(props, 'ref', {
87 - get: warnAboutAccessingRef,
88 - configurable: true,
89 - });
90 -}
91 -
92 -function warnIfStringRefCannotBeAutoConverted(config) {
93 - if (__DEV__) {
94 - if (
95 - typeof config.ref === 'string' &&
96 - ReactCurrentOwner.current &&
97 - config.__self &&
98 - ReactCurrentOwner.current.stateNode !== config.__self
99 - ) {
100 - const componentName = getComponentNameFromType(
101 - ReactCurrentOwner.current.type,
102 - );
103 -
104 - if (!didWarnAboutStringRefs[componentName]) {
105 - console.error(
106 - 'Component "%s" contains the string ref "%s". ' +
107 - 'Support for string refs will be removed in a future major release. ' +
108 - 'This case cannot be automatically converted to an arrow function. ' +
109 - 'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
110 - 'Learn more about using refs safely here: ' +
111 - 'https://reactjs.org/link/strict-mode-string-ref',
112 - componentName,
113 - config.ref,
114 - );
115 - didWarnAboutStringRefs[componentName] = true;
116 - }
117 - }
118 - }
119 -}
120 -
121 -/**
122 - * Factory method to create a new React element. This no longer adheres to
123 - * the class pattern, so do not use new to call it. Also, instanceof check
124 - * will not work. Instead test $$typeof field against Symbol.for('react.element') to check
125 - * if something is a React Element.
126 - *
127 - * @param {*} type
128 - * @param {*} props
129 - * @param {*} key
130 - * @param {string|object} ref
131 - * @param {*} owner
132 - * @param {*} self A *temporary* helper to detect places where `this` is
133 - * different from the `owner` when React.createElement is called, so that we
134 - * can warn. We want to get rid of owner and replace string `ref`s with arrow
135 - * functions, and as long as `this` and owner are the same, there will be no
136 - * change in behavior.
137 - * @param {*} source An annotation object (added by a transpiler or otherwise)
138 - * indicating filename, line number, and/or other information.
139 - * @internal
140 - */
141 -function ReactElement(type, key, ref, owner, props) {
142 - const element = {
143 - // This tag allows us to uniquely identify this as a React Element
144 - $$typeof: REACT_ELEMENT_TYPE,
145 -
146 - // Built-in properties that belong on the element
147 - type: type,
148 - key: key,
149 - ref: ref,
150 - props: props,
151 -
152 - // Record the component responsible for creating this element.
153 - _owner: owner,
154 - };
155 -
156 - if (__DEV__) {
157 - // The validation flag is currently mutative. We put it on
158 - // an external backing store so that we can freeze the whole object.
159 - // This can be replaced with a WeakMap once they are implemented in
160 - // commonly used development environments.
161 - element._store = {};
162 -
163 - // To make comparing ReactElements easier for testing purposes, we make
164 - // the validation flag non-enumerable (where possible, which should
165 - // include every environment we run tests in), so the test framework
166 - // ignores it.
167 - Object.defineProperty(element._store, 'validated', {
168 - configurable: false,
169 - enumerable: false,
170 - writable: true,
171 - value: false,
172 - });
173 - // debugInfo contains Server Component debug information.
174 - Object.defineProperty(element, '_debugInfo', {
175 - configurable: false,
176 - enumerable: false,
177 - writable: true,
178 - value: null,
179 - });
180 - if (Object.freeze) {
181 - Object.freeze(element.props);
182 - Object.freeze(element);
183 - }
184 - }
185 -
186 - return element;
187 -}
188 -
189 -/**
190 - * Create and return a new ReactElement of the given type.
191 - * See https://reactjs.org/docs/react-api.html#createelement
192 - */
193 -export function createElement(type, config, children) {
194 - let propName;
195 -
196 - // Reserved names are extracted
197 - const props = {};
198 -
199 - let key = null;
200 - let ref = null;
201 -
202 - if (config != null) {
203 - if (hasValidRef(config)) {
204 - ref = config.ref;
205 -
206 - if (__DEV__) {
207 - warnIfStringRefCannotBeAutoConverted(config);
208 - }
209 - }
210 - if (hasValidKey(config)) {
211 - if (__DEV__) {
212 - checkKeyStringCoercion(config.key);
213 - }
214 - key = '' + config.key;
215 - }
216 -
217 - // Remaining properties are added to a new props object
218 - for (propName in config) {
219 - if (
220 - hasOwnProperty.call(config, propName) &&
221 - // Skip over reserved prop names
222 - propName !== 'key' &&
223 - // TODO: `ref` will no longer be reserved in the next major
224 - propName !== 'ref' &&
225 - // ...and maybe these, too, though we currently rely on them for
226 - // warnings and debug information in dev. Need to decide if we're OK
227 - // with dropping them. In the jsx() runtime it's not an issue because
228 - // the data gets passed as separate arguments instead of props, but
229 - // it would be nice to stop relying on them entirely so we can drop
230 - // them from the internal Fiber field.
231 - propName !== '__self' &&
232 - propName !== '__source'
233 - ) {
234 - props[propName] = config[propName];
235 - }
236 - }
237 - }
238 -
239 - // Children can be more than one argument, and those are transferred onto
240 - // the newly allocated props object.
241 - const childrenLength = arguments.length - 2;
242 - if (childrenLength === 1) {
243 - props.children = children;
244 - } else if (childrenLength > 1) {
245 - const childArray = Array(childrenLength);
246 - for (let i = 0; i < childrenLength; i++) {
247 - childArray[i] = arguments[i + 2];
248 - }
249 - if (__DEV__) {
250 - if (Object.freeze) {
251 - Object.freeze(childArray);
252 - }
253 - }
254 - props.children = childArray;
255 - }
256 -
257 - // Resolve default props
258 - if (type && type.defaultProps) {
259 - const defaultProps = type.defaultProps;
260 - for (propName in defaultProps) {
261 - if (props[propName] === undefined) {
262 - props[propName] = defaultProps[propName];
263 - }
264 - }
265 - }
266 - if (__DEV__) {
267 - if (key || ref) {
268 - const displayName =
269 - typeof type === 'function'
270 - ? type.displayName || type.name || 'Unknown'
271 - : type;
272 - if (key) {
273 - defineKeyPropWarningGetter(props, displayName);
274 - }
275 - if (ref) {
276 - defineRefPropWarningGetter(props, displayName);
277 - }
278 - }
279 - }
280 - return ReactElement(type, key, ref, ReactCurrentOwner.current, props);
281 -}
282 -
283 -/**
284 - * Return a function that produces ReactElements of a given type.
285 - * See https://reactjs.org/docs/react-api.html#createfactory
286 - */
287 -export function createFactory(type) {
288 - const factory = createElement.bind(null, type);
289 - // Expose the type on the factory and the prototype so that it can be
290 - // easily accessed on elements. E.g. `<Foo />.type === Foo`.
291 - // This should not be named `constructor` since this may not be the function
292 - // that created the element, and it may not even be a constructor.
293 - // Legacy hook: remove it
294 - factory.type = type;
295 - return factory;
296 -}
297 -
298 -export function cloneAndReplaceKey(oldElement, newKey) {
299 - const newElement = ReactElement(
300 - oldElement.type,
301 - newKey,
302 - oldElement.ref,
303 - oldElement._owner,
304 - oldElement.props,
305 - );
306 -
307 - return newElement;
308 -}
309 -
310 -/**
311 - * Clone and return a new ReactElement using element as the starting point.
312 - * See https://reactjs.org/docs/react-api.html#cloneelement
313 - */
314 -export function cloneElement(element, config, children) {
315 - if (element === null || element === undefined) {
316 - throw new Error(
317 - `React.cloneElement(...): The argument must be a React element, but you passed ${element}.`,
318 - );
319 - }
320 -
321 - let propName;
322 -
323 - // Original props are copied
324 - const props = assign({}, element.props);
325 -
326 - // Reserved names are extracted
327 - let key = element.key;
328 - let ref = element.ref;
329 -
330 - // Owner will be preserved, unless ref is overridden
331 - let owner = element._owner;
332 -
333 - if (config != null) {
334 - if (hasValidRef(config)) {
335 - // Silently steal the ref from the parent.
336 - ref = config.ref;
337 - owner = ReactCurrentOwner.current;
338 - }
339 - if (hasValidKey(config)) {
340 - if (__DEV__) {
341 - checkKeyStringCoercion(config.key);
342 - }
343 - key = '' + config.key;
344 - }
345 -
346 - // Remaining properties override existing props
347 - let defaultProps;
348 - if (element.type && element.type.defaultProps) {
349 - defaultProps = element.type.defaultProps;
350 - }
351 - for (propName in config) {
352 - if (
353 - hasOwnProperty.call(config, propName) &&
354 - // Skip over reserved prop names
355 - propName !== 'key' &&
356 - // TODO: `ref` will no longer be reserved in the next major
357 - propName !== 'ref' &&
358 - // ...and maybe these, too, though we currently rely on them for
359 - // warnings and debug information in dev. Need to decide if we're OK
360 - // with dropping them. In the jsx() runtime it's not an issue because
361 - // the data gets passed as separate arguments instead of props, but
362 - // it would be nice to stop relying on them entirely so we can drop
363 - // them from the internal Fiber field.
364 - propName !== '__self' &&
365 - propName !== '__source'
366 - ) {
367 - if (config[propName] === undefined && defaultProps !== undefined) {
368 - // Resolve default props
369 - props[propName] = defaultProps[propName];
370 - } else {
371 - props[propName] = config[propName];
372 - }
373 - }
374 - }
375 - }
376 -
377 - // Children can be more than one argument, and those are transferred onto
378 - // the newly allocated props object.
379 - const childrenLength = arguments.length - 2;
380 - if (childrenLength === 1) {
381 - props.children = children;
382 - } else if (childrenLength > 1) {
383 - const childArray = Array(childrenLength);
384 - for (let i = 0; i < childrenLength; i++) {
385 - childArray[i] = arguments[i + 2];
386 - }
387 - props.children = childArray;
388 - }
389 -
390 - return ReactElement(element.type, key, ref, owner, props);
391 -}
392 -
393 -/**
394 - * Verifies the object is a ReactElement.
395 - * See https://reactjs.org/docs/react-api.html#isvalidelement
396 - * @param {?object} object
397 - * @return {boolean} True if `object` is a ReactElement.
398 - * @final
399 - */
400 -export function isValidElement(object) {
401 - return (
402 - typeof object === 'object' &&
403 - object !== null &&
404 - object.$$typeof === REACT_ELEMENT_TYPE
405 - );
406 -}
packages/react/src/ReactElementValidator.js deleted
-395
@@ -1,395 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -/**
9 - * ReactElementValidator provides a wrapper around an element factory
10 - * which validates the props passed to the element. This is intended to be
11 - * used only in DEV and could be replaced by a static type checker for languages
12 - * that support it.
13 - */
14 -
15 -import isValidElementType from 'shared/isValidElementType';
16 -import getComponentNameFromType from 'shared/getComponentNameFromType';
17 -import {
18 - getIteratorFn,
19 - REACT_FORWARD_REF_TYPE,
20 - REACT_MEMO_TYPE,
21 - REACT_FRAGMENT_TYPE,
22 - REACT_ELEMENT_TYPE,
23 -} from 'shared/ReactSymbols';
24 -import checkPropTypes from 'shared/checkPropTypes';
25 -import isArray from 'shared/isArray';
26 -
27 -import ReactCurrentOwner from './ReactCurrentOwner';
28 -import {isValidElement, createElement, cloneElement} from './ReactElementProd';
29 -import {setExtraStackFrame} from './ReactDebugCurrentFrame';
30 -import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
31 -
32 -const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
33 -
34 -function setCurrentlyValidatingElement(element) {
35 - if (__DEV__) {
36 - if (element) {
37 - const owner = element._owner;
38 - const stack = describeUnknownElementTypeFrameInDEV(
39 - element.type,
40 - owner ? owner.type : null,
41 - );
42 - setExtraStackFrame(stack);
43 - } else {
44 - setExtraStackFrame(null);
45 - }
46 - }
47 -}
48 -
49 -let propTypesMisspellWarningShown;
50 -
51 -if (__DEV__) {
52 - propTypesMisspellWarningShown = false;
53 -}
54 -
55 -function getDeclarationErrorAddendum() {
56 - if (ReactCurrentOwner.current) {
57 - const name = getComponentNameFromType(ReactCurrentOwner.current.type);
58 - if (name) {
59 - return '\n\nCheck the render method of `' + name + '`.';
60 - }
61 - }
62 - return '';
63 -}
64 -
65 -function getSourceInfoErrorAddendum(source) {
66 - if (source !== undefined) {
67 - const fileName = source.fileName.replace(/^.*[\\\/]/, '');
68 - const lineNumber = source.lineNumber;
69 - return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
70 - }
71 - return '';
72 -}
73 -
74 -function getSourceInfoErrorAddendumForProps(elementProps) {
75 - if (elementProps !== null && elementProps !== undefined) {
76 - return getSourceInfoErrorAddendum(elementProps.__source);
77 - }
78 - return '';
79 -}
80 -
81 -/**
82 - * Warn if there's no key explicitly set on dynamic arrays of children or
83 - * object keys are not valid. This allows us to keep track of children between
84 - * updates.
85 - */
86 -const ownerHasKeyUseWarning = {};
87 -
88 -function getCurrentComponentErrorInfo(parentType) {
89 - let info = getDeclarationErrorAddendum();
90 -
91 - if (!info) {
92 - const parentName = getComponentNameFromType(parentType);
93 - if (parentName) {
94 - info = `\n\nCheck the top-level render call using <${parentName}>.`;
95 - }
96 - }
97 - return info;
98 -}
99 -
100 -/**
101 - * Warn if the element doesn't have an explicit key assigned to it.
102 - * This element is in an array. The array could grow and shrink or be
103 - * reordered. All children that haven't already been validated are required to
104 - * have a "key" property assigned to it. Error statuses are cached so a warning
105 - * will only be shown once.
106 - *
107 - * @internal
108 - * @param {ReactElement} element Element that requires a key.
109 - * @param {*} parentType element's parent's type.
110 - */
111 -function validateExplicitKey(element, parentType) {
112 - if (!element._store || element._store.validated || element.key != null) {
113 - return;
114 - }
115 - element._store.validated = true;
116 -
117 - const currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
118 - if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
119 - return;
120 - }
121 - ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
122 -
123 - // Usually the current owner is the offender, but if it accepts children as a
124 - // property, it may be the creator of the child that's responsible for
125 - // assigning it a key.
126 - let childOwner = '';
127 - if (
128 - element &&
129 - element._owner &&
130 - element._owner !== ReactCurrentOwner.current
131 - ) {
132 - // Give the component that originally created this child.
133 - childOwner = ` It was passed a child from ${getComponentNameFromType(
134 - element._owner.type,
135 - )}.`;
136 - }
137 -
138 - if (__DEV__) {
139 - setCurrentlyValidatingElement(element);
140 - console.error(
141 - 'Each child in a list should have a unique "key" prop.' +
142 - '%s%s See https://reactjs.org/link/warning-keys for more information.',
143 - currentComponentErrorInfo,
144 - childOwner,
145 - );
146 - setCurrentlyValidatingElement(null);
147 - }
148 -}
149 -
150 -/**
151 - * Ensure that every element either is passed in a static location, in an
152 - * array with an explicit keys property defined, or in an object literal
153 - * with valid key property.
154 - *
155 - * @internal
156 - * @param {ReactNode} node Statically passed child of any type.
157 - * @param {*} parentType node's parent's type.
158 - */
159 -function validateChildKeys(node, parentType) {
160 - if (typeof node !== 'object' || !node) {
161 - return;
162 - }
163 - if (node.$$typeof === REACT_CLIENT_REFERENCE) {
164 - // This is a reference to a client component so it's unknown.
165 - } else if (isArray(node)) {
166 - for (let i = 0; i < node.length; i++) {
167 - const child = node[i];
168 - if (isValidElement(child)) {
169 - validateExplicitKey(child, parentType);
170 - }
171 - }
172 - } else if (isValidElement(node)) {
173 - // This element was passed in a valid location.
174 - if (node._store) {
175 - node._store.validated = true;
176 - }
177 - } else {
178 - const iteratorFn = getIteratorFn(node);
179 - if (typeof iteratorFn === 'function') {
180 - // Entry iterators used to provide implicit keys,
181 - // but now we print a separate warning for them later.
182 - if (iteratorFn !== node.entries) {
183 - const iterator = iteratorFn.call(node);
184 - let step;
185 - while (!(step = iterator.next()).done) {
186 - if (isValidElement(step.value)) {
187 - validateExplicitKey(step.value, parentType);
188 - }
189 - }
190 - }
191 - }
192 - }
193 -}
194 -
195 -/**
196 - * Given an element, validate that its props follow the propTypes definition,
197 - * provided by the type.
198 - *
199 - * @param {ReactElement} element
200 - */
201 -function validatePropTypes(element) {
202 - if (__DEV__) {
203 - const type = element.type;
204 - if (type === null || type === undefined || typeof type === 'string') {
205 - return;
206 - }
207 - if (type.$$typeof === REACT_CLIENT_REFERENCE) {
208 - return;
209 - }
210 - let propTypes;
211 - if (typeof type === 'function') {
212 - propTypes = type.propTypes;
213 - } else if (
214 - typeof type === 'object' &&
215 - (type.$$typeof === REACT_FORWARD_REF_TYPE ||
216 - // Note: Memo only checks outer props here.
217 - // Inner props are checked in the reconciler.
218 - type.$$typeof === REACT_MEMO_TYPE)
219 - ) {
220 - propTypes = type.propTypes;
221 - } else {
222 - return;
223 - }
224 - if (propTypes) {
225 - // Intentionally inside to avoid triggering lazy initializers:
226 - const name = getComponentNameFromType(type);
227 - checkPropTypes(propTypes, element.props, 'prop', name, element);
228 - } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
229 - propTypesMisspellWarningShown = true;
230 - // Intentionally inside to avoid triggering lazy initializers:
231 - const name = getComponentNameFromType(type);
232 - console.error(
233 - 'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?',
234 - name || 'Unknown',
235 - );
236 - }
237 - if (
238 - typeof type.getDefaultProps === 'function' &&
239 - !type.getDefaultProps.isReactClassApproved
240 - ) {
241 - console.error(
242 - 'getDefaultProps is only used on classic React.createClass ' +
243 - 'definitions. Use a static property named `defaultProps` instead.',
244 - );
245 - }
246 - }
247 -}
248 -
249 -/**
250 - * Given a fragment, validate that it can only be provided with fragment props
251 - * @param {ReactElement} fragment
252 - */
253 -function validateFragmentProps(fragment) {
254 - if (__DEV__) {
255 - const keys = Object.keys(fragment.props);
256 - for (let i = 0; i < keys.length; i++) {
257 - const key = keys[i];
258 - if (key !== 'children' && key !== 'key') {
259 - setCurrentlyValidatingElement(fragment);
260 - console.error(
261 - 'Invalid prop `%s` supplied to `React.Fragment`. ' +
262 - 'React.Fragment can only have `key` and `children` props.',
263 - key,
264 - );
265 - setCurrentlyValidatingElement(null);
266 - break;
267 - }
268 - }
269 -
270 - if (fragment.ref !== null) {
271 - setCurrentlyValidatingElement(fragment);
272 - console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
273 - setCurrentlyValidatingElement(null);
274 - }
275 - }
276 -}
277 -
278 -export function createElementWithValidation(type, props, children) {
279 - const validType = isValidElementType(type);
280 -
281 - // We warn in this case but don't throw. We expect the element creation to
282 - // succeed and there will likely be errors in render.
283 - if (!validType) {
284 - let info = '';
285 - if (
286 - type === undefined ||
287 - (typeof type === 'object' &&
288 - type !== null &&
289 - Object.keys(type).length === 0)
290 - ) {
291 - info +=
292 - ' You likely forgot to export your component from the file ' +
293 - "it's defined in, or you might have mixed up default and named imports.";
294 - }
295 -
296 - const sourceInfo = getSourceInfoErrorAddendumForProps(props);
297 - if (sourceInfo) {
298 - info += sourceInfo;
299 - } else {
300 - info += getDeclarationErrorAddendum();
301 - }
302 -
303 - let typeString;
304 - if (type === null) {
305 - typeString = 'null';
306 - } else if (isArray(type)) {
307 - typeString = 'array';
308 - } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
309 - typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`;
310 - info =
311 - ' Did you accidentally export a JSX literal instead of a component?';
312 - } else {
313 - typeString = typeof type;
314 - }
315 -
316 - if (__DEV__) {
317 - console.error(
318 - 'React.createElement: type is invalid -- expected a string (for ' +
319 - 'built-in components) or a class/function (for composite ' +
320 - 'components) but got: %s.%s',
321 - typeString,
322 - info,
323 - );
324 - }
325 - }
326 -
327 - const element = createElement.apply(this, arguments);
328 -
329 - // The result can be nullish if a mock or a custom function is used.
330 - // TODO: Drop this when these are no longer allowed as the type argument.
331 - if (element == null) {
332 - return element;
333 - }
334 -
335 - // Skip key warning if the type isn't valid since our key validation logic
336 - // doesn't expect a non-string/function type and can throw confusing errors.
337 - // We don't want exception behavior to differ between dev and prod.
338 - // (Rendering will throw with a helpful message and as soon as the type is
339 - // fixed, the key warnings will appear.)
340 - if (validType) {
341 - for (let i = 2; i < arguments.length; i++) {
342 - validateChildKeys(arguments[i], type);
343 - }
344 - }
345 -
346 - if (type === REACT_FRAGMENT_TYPE) {
347 - validateFragmentProps(element);
348 - } else {
349 - validatePropTypes(element);
350 - }
351 -
352 - return element;
353 -}
354 -
355 -let didWarnAboutDeprecatedCreateFactory = false;
356 -
357 -export function createFactoryWithValidation(type) {
358 - const validatedFactory = createElementWithValidation.bind(null, type);
359 - validatedFactory.type = type;
360 - if (__DEV__) {
361 - if (!didWarnAboutDeprecatedCreateFactory) {
362 - didWarnAboutDeprecatedCreateFactory = true;
363 - console.warn(
364 - 'React.createFactory() is deprecated and will be removed in ' +
365 - 'a future major release. Consider using JSX ' +
366 - 'or use React.createElement() directly instead.',
367 - );
368 - }
369 - // Legacy hook: remove it
370 - Object.defineProperty(validatedFactory, 'type', {
371 - enumerable: false,
372 - get: function () {
373 - console.warn(
374 - 'Factory.type is deprecated. Access the class directly ' +
375 - 'before passing it to createFactory.',
376 - );
377 - Object.defineProperty(this, 'type', {
378 - value: type,
379 - });
380 - return type;
381 - },
382 - });
383 - }
384 -
385 - return validatedFactory;
386 -}
387 -
388 -export function cloneElementWithValidation(element, props, children) {
389 - const newElement = cloneElement.apply(this, arguments);
390 - for (let i = 2; i < arguments.length; i++) {
391 - validateChildKeys(arguments[i], newElement.type);
392 - }
393 - validatePropTypes(newElement);
394 - return newElement;
395 -}
packages/react/src/ReactServer.experimental.js
+5 -1
@@ -22,7 +22,11 @@ import {
22 REACT_SUSPENSE_TYPE,
23 REACT_DEBUG_TRACING_MODE_TYPE,
24 } from 'shared/ReactSymbols';
25 -import {cloneElement, createElement, isValidElement} from './ReactElement';
25 +import {
26 + cloneElement,
27 + createElement,
28 + isValidElement,
29 +} from './jsx/ReactJSXElement';
30 import {createRef} from './ReactCreateRef';
31 import {
32 use,
packages/react/src/ReactServer.js
+5 -1
@@ -21,7 +21,11 @@ import {
21 REACT_STRICT_MODE_TYPE,
22 REACT_SUSPENSE_TYPE,
23 } from 'shared/ReactSymbols';
24 -import {cloneElement, createElement, isValidElement} from './ReactElement';
24 +import {
25 + cloneElement,
26 + createElement,
27 + isValidElement,
28 +} from './jsx/ReactJSXElement';
29 import {createRef} from './ReactCreateRef';
30 import {use, useId, useCallback, useDebugValue, useMemo} from './ReactHooks';
31 import {forwardRef} from './ReactForwardRef';
packages/react/src/jsx/ReactJSXElement.js
+338 -7
@@ -8,6 +8,7 @@
8 import getComponentNameFromType from 'shared/getComponentNameFromType';
9 import ReactSharedInternals from 'shared/ReactSharedInternals';
10 import hasOwnProperty from 'shared/hasOwnProperty';
11 +import assign from 'shared/assign';
12 import {
13 getIteratorFn,
14 REACT_ELEMENT_TYPE,
@@ -512,6 +513,331 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
513 }
514 }
515
516 +/**
517 + * Create and return a new ReactElement of the given type.
518 + * See https://reactjs.org/docs/react-api.html#createelement
519 + */
520 +export function createElement(type, config, children) {
521 + if (__DEV__) {
522 + if (!isValidElementType(type)) {
523 + // This is an invalid element type.
524 + //
525 + // We warn in this case but don't throw. We expect the element creation to
526 + // succeed and there will likely be errors in render.
527 + let info = '';
528 + if (
529 + type === undefined ||
530 + (typeof type === 'object' &&
531 + type !== null &&
532 + Object.keys(type).length === 0)
533 + ) {
534 + info +=
535 + ' You likely forgot to export your component from the file ' +
536 + "it's defined in, or you might have mixed up default and named imports.";
537 + }
538 +
539 + const sourceInfo = getSourceInfoErrorAddendumForProps(config);
540 + if (sourceInfo) {
541 + info += sourceInfo;
542 + } else {
543 + info += getDeclarationErrorAddendum();
544 + }
545 +
546 + let typeString;
547 + if (type === null) {
548 + typeString = 'null';
549 + } else if (isArray(type)) {
550 + typeString = 'array';
551 + } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
552 + typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`;
553 + info =
554 + ' Did you accidentally export a JSX literal instead of a component?';
555 + } else {
556 + typeString = typeof type;
557 + }
558 +
559 + console.error(
560 + 'React.createElement: type is invalid -- expected a string (for ' +
561 + 'built-in components) or a class/function (for composite ' +
562 + 'components) but got: %s.%s',
563 + typeString,
564 + info,
565 + );
566 + } else {
567 + // This is a valid element type.
568 +
569 + // Skip key warning if the type isn't valid since our key validation logic
570 + // doesn't expect a non-string/function type and can throw confusing
571 + // errors. We don't want exception behavior to differ between dev and
572 + // prod. (Rendering will throw with a helpful message and as soon as the
573 + // type is fixed, the key warnings will appear.)
574 + for (let i = 2; i < arguments.length; i++) {
575 + validateChildKeys(arguments[i], type);
576 + }
577 + }
578 +
579 + // Unlike the jsx() runtime, createElement() doesn't warn about key spread.
580 + }
581 +
582 + let propName;
583 +
584 + // Reserved names are extracted
585 + const props = {};
586 +
587 + let key = null;
588 + let ref = null;
589 +
590 + if (config != null) {
591 + if (hasValidRef(config)) {
592 + ref = config.ref;
593 +
594 + if (__DEV__) {
595 + warnIfStringRefCannotBeAutoConverted(config, config.__self);
596 + }
597 + }
598 + if (hasValidKey(config)) {
599 + if (__DEV__) {
600 + checkKeyStringCoercion(config.key);
601 + }
602 + key = '' + config.key;
603 + }
604 +
605 + // Remaining properties are added to a new props object
606 + for (propName in config) {
607 + if (
608 + hasOwnProperty.call(config, propName) &&
609 + // Skip over reserved prop names
610 + propName !== 'key' &&
611 + // TODO: `ref` will no longer be reserved in the next major
612 + propName !== 'ref' &&
613 + // ...and maybe these, too, though we currently rely on them for
614 + // warnings and debug information in dev. Need to decide if we're OK
615 + // with dropping them. In the jsx() runtime it's not an issue because
616 + // the data gets passed as separate arguments instead of props, but
617 + // it would be nice to stop relying on them entirely so we can drop
618 + // them from the internal Fiber field.
619 + propName !== '__self' &&
620 + propName !== '__source'
621 + ) {
622 + props[propName] = config[propName];
623 + }
624 + }
625 + }
626 +
627 + // Children can be more than one argument, and those are transferred onto
628 + // the newly allocated props object.
629 + const childrenLength = arguments.length - 2;
630 + if (childrenLength === 1) {
631 + props.children = children;
632 + } else if (childrenLength > 1) {
633 + const childArray = Array(childrenLength);
634 + for (let i = 0; i < childrenLength; i++) {
635 + childArray[i] = arguments[i + 2];
636 + }
637 + if (__DEV__) {
638 + if (Object.freeze) {
639 + Object.freeze(childArray);
640 + }
641 + }
642 + props.children = childArray;
643 + }
644 +
645 + // Resolve default props
646 + if (type && type.defaultProps) {
647 + const defaultProps = type.defaultProps;
648 + for (propName in defaultProps) {
649 + if (props[propName] === undefined) {
650 + props[propName] = defaultProps[propName];
651 + }
652 + }
653 + }
654 + if (__DEV__) {
655 + if (key || ref) {
656 + const displayName =
657 + typeof type === 'function'
658 + ? type.displayName || type.name || 'Unknown'
659 + : type;
660 + if (key) {
661 + defineKeyPropWarningGetter(props, displayName);
662 + }
663 + if (ref) {
664 + defineRefPropWarningGetter(props, displayName);
665 + }
666 + }
667 + }
668 +
669 + const element = ReactElement(
670 + type,
671 + key,
672 + ref,
673 + undefined,
674 + undefined,
675 + ReactCurrentOwner.current,
676 + props,
677 + );
678 +
679 + if (type === REACT_FRAGMENT_TYPE) {
680 + validateFragmentProps(element);
681 + } else {
682 + validatePropTypes(element);
683 + }
684 +
685 + return element;
686 +}
687 +
688 +let didWarnAboutDeprecatedCreateFactory = false;
689 +
690 +/**
691 + * Return a function that produces ReactElements of a given type.
692 + * See https://reactjs.org/docs/react-api.html#createfactory
693 + */
694 +export function createFactory(type) {
695 + const factory = createElement.bind(null, type);
696 + // Expose the type on the factory and the prototype so that it can be
697 + // easily accessed on elements. E.g. `<Foo />.type === Foo`.
698 + // This should not be named `constructor` since this may not be the function
699 + // that created the element, and it may not even be a constructor.
700 + // Legacy hook: remove it
701 + factory.type = type;
702 +
703 + if (__DEV__) {
704 + if (!didWarnAboutDeprecatedCreateFactory) {
705 + didWarnAboutDeprecatedCreateFactory = true;
706 + console.warn(
707 + 'React.createFactory() is deprecated and will be removed in ' +
708 + 'a future major release. Consider using JSX ' +
709 + 'or use React.createElement() directly instead.',
710 + );
711 + }
712 + // Legacy hook: remove it
713 + Object.defineProperty(factory, 'type', {
714 + enumerable: false,
715 + get: function () {
716 + console.warn(
717 + 'Factory.type is deprecated. Access the class directly ' +
718 + 'before passing it to createFactory.',
719 + );
720 + Object.defineProperty(this, 'type', {
721 + value: type,
722 + });
723 + return type;
724 + },
725 + });
726 + }
727 +
728 + return factory;
729 +}
730 +
731 +export function cloneAndReplaceKey(oldElement, newKey) {
732 + return ReactElement(
733 + oldElement.type,
734 + newKey,
735 + oldElement.ref,
736 + undefined,
737 + undefined,
738 + oldElement._owner,
739 + oldElement.props,
740 + );
741 +}
742 +
743 +/**
744 + * Clone and return a new ReactElement using element as the starting point.
745 + * See https://reactjs.org/docs/react-api.html#cloneelement
746 + */
747 +export function cloneElement(element, config, children) {
748 + if (element === null || element === undefined) {
749 + throw new Error(
750 + `React.cloneElement(...): The argument must be a React element, but you passed ${element}.`,
751 + );
752 + }
753 +
754 + let propName;
755 +
756 + // Original props are copied
757 + const props = assign({}, element.props);
758 +
759 + // Reserved names are extracted
760 + let key = element.key;
761 + let ref = element.ref;
762 +
763 + // Owner will be preserved, unless ref is overridden
764 + let owner = element._owner;
765 +
766 + if (config != null) {
767 + if (hasValidRef(config)) {
768 + // Silently steal the ref from the parent.
769 + ref = config.ref;
770 + owner = ReactCurrentOwner.current;
771 + }
772 + if (hasValidKey(config)) {
773 + if (__DEV__) {
774 + checkKeyStringCoercion(config.key);
775 + }
776 + key = '' + config.key;
777 + }
778 +
779 + // Remaining properties override existing props
780 + let defaultProps;
781 + if (element.type && element.type.defaultProps) {
782 + defaultProps = element.type.defaultProps;
783 + }
784 + for (propName in config) {
785 + if (
786 + hasOwnProperty.call(config, propName) &&
787 + // Skip over reserved prop names
788 + propName !== 'key' &&
789 + // TODO: `ref` will no longer be reserved in the next major
790 + propName !== 'ref' &&
791 + // ...and maybe these, too, though we currently rely on them for
792 + // warnings and debug information in dev. Need to decide if we're OK
793 + // with dropping them. In the jsx() runtime it's not an issue because
794 + // the data gets passed as separate arguments instead of props, but
795 + // it would be nice to stop relying on them entirely so we can drop
796 + // them from the internal Fiber field.
797 + propName !== '__self' &&
798 + propName !== '__source'
799 + ) {
800 + if (config[propName] === undefined && defaultProps !== undefined) {
801 + // Resolve default props
802 + props[propName] = defaultProps[propName];
803 + } else {
804 + props[propName] = config[propName];
805 + }
806 + }
807 + }
808 + }
809 +
810 + // Children can be more than one argument, and those are transferred onto
811 + // the newly allocated props object.
812 + const childrenLength = arguments.length - 2;
813 + if (childrenLength === 1) {
814 + props.children = children;
815 + } else if (childrenLength > 1) {
816 + const childArray = Array(childrenLength);
817 + for (let i = 0; i < childrenLength; i++) {
818 + childArray[i] = arguments[i + 2];
819 + }
820 + props.children = childArray;
821 + }
822 +
823 + const clonedElement = ReactElement(
824 + element.type,
825 + key,
826 + ref,
827 + undefined,
828 + undefined,
829 + owner,
830 + props,
831 + );
832 +
833 + for (let i = 2; i < arguments.length; i++) {
834 + validateChildKeys(arguments[i], clonedElement.type);
835 + }
836 + validatePropTypes(clonedElement);
837 +
838 + return clonedElement;
839 +}
840 +
841 function getDeclarationErrorAddendum() {
842 if (__DEV__) {
843 if (ReactCurrentOwner.current) {
@@ -524,6 +850,13 @@ function getDeclarationErrorAddendum() {
850 }
851 }
852
853 +function getSourceInfoErrorAddendumForProps(elementProps) {
854 + if (elementProps !== null && elementProps !== undefined) {
855 + return getSourceInfoErrorAddendum(elementProps.__source);
856 + }
857 + return '';
858 +}
859 +
860 function getSourceInfoErrorAddendum(source) {
861 if (__DEV__) {
862 if (source !== undefined) {
@@ -590,13 +923,11 @@ function validateChildKeys(node, parentType) {
923 * @final
924 */
925 export function isValidElement(object) {
593 - if (__DEV__) {
594 - return (
595 - typeof object === 'object' &&
596 - object !== null &&
597 - object.$$typeof === REACT_ELEMENT_TYPE
598 - );
599 - }
926 + return (
927 + typeof object === 'object' &&
928 + object !== null &&
929 + object.$$typeof === REACT_ELEMENT_TYPE
930 + );
931 }
932
933 const ownerHasKeyUseWarning = {};