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 = {};