Combine ReactJSXElementValidator with main module (#28317)
There are too many layers to the JSX runtime implementation. I think basically everything should be implemented in a single file, so that's what I'm going to do. As a first step, this deletes ReactJSXElementValidator and moves all the code into ReactJSXElement. I can already see how this will help us remove more indirections in the future. Next I'm going to do start moving the `createElement` runtime into this module as well, since there's a lot of duplicated code.
Andrew Clark committed
Feb 19, 2024 at 22:14 UTC
ec160f32c28ccab798c73ecccbb36ce121e1640e
4 files changed
+450
-466
packages/react/src/jsx/ReactJSX.js
+14
-8
@@ -8,15 +8,21 @@
8
*/
9
import {REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
10
import {
11
- jsxWithValidationStatic,
12
- jsxWithValidationDynamic,
13
- jsxWithValidation,
14
-} from './ReactJSXElementValidator';
15
-import {jsx as jsxProd} from './ReactJSXElement';
16
-const jsx: any = __DEV__ ? jsxWithValidationDynamic : jsxProd;
11
+ jsxProd,
12
+ jsxProdSignatureRunningInDevWithDynamicChildren,
13
+ jsxProdSignatureRunningInDevWithStaticChildren,
14
+ jsxDEV as _jsxDEV,
15
+} from './ReactJSXElement';
16
+
17
+const jsx: any = __DEV__
18
+ ? jsxProdSignatureRunningInDevWithDynamicChildren
19
+ : jsxProd;
20
// we may want to special case jsxs internally to take advantage of static children.
21
// for now we can ship identical prod functions
19
-const jsxs: any = __DEV__ ? jsxWithValidationStatic : jsxProd;
20
-const jsxDEV: any = __DEV__ ? jsxWithValidation : undefined;
22
+const jsxs: any = __DEV__
23
+ ? jsxProdSignatureRunningInDevWithStaticChildren
24
+ : jsxProd;
25
+
26
+const jsxDEV: any = __DEV__ ? _jsxDEV : undefined;
27
28
export {REACT_FRAGMENT_TYPE as Fragment, jsx, jsxs, jsxDEV};
packages/react/src/jsx/ReactJSXElement.js
+421
-4
@@ -8,10 +8,23 @@
8
import getComponentNameFromType from 'shared/getComponentNameFromType';
9
import ReactSharedInternals from 'shared/ReactSharedInternals';
10
import hasOwnProperty from 'shared/hasOwnProperty';
11
-import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
11
+import {
12
+ getIteratorFn,
13
+ REACT_ELEMENT_TYPE,
14
+ REACT_FORWARD_REF_TYPE,
15
+ REACT_MEMO_TYPE,
16
+ REACT_FRAGMENT_TYPE,
17
+} from 'shared/ReactSymbols';
18
import {checkKeyStringCoercion} from 'shared/CheckStringCoercion';
19
+import isValidElementType from 'shared/isValidElementType';
20
+import isArray from 'shared/isArray';
21
+import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
22
+import checkPropTypes from 'shared/checkPropTypes';
23
24
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
25
+const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
26
+
27
+const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
28
29
let specialPropKeyWarningShown;
30
let specialPropRefWarningShown;
@@ -192,7 +205,7 @@ function ReactElement(type, key, ref, self, source, owner, props) {
205
* @param {object} props
206
* @param {string} key
207
*/
195
-export function jsx(type, config, maybeKey) {
208
+export function jsxProd(type, config, maybeKey) {
209
let propName;
210
211
// Reserved names are extracted
@@ -259,14 +272,157 @@ export function jsx(type, config, maybeKey) {
272
);
273
}
274
275
+// While `jsxDEV` should never be called when running in production, we do
276
+// support `jsx` and `jsxs` when running in development. This supports the case
277
+// where a third-party dependency ships code that was compiled for production;
278
+// we want to still provide warnings in development.
279
+//
280
+// So these functions are the _dev_ implementations of the _production_
281
+// API signatures.
282
+//
283
+// Since these functions are dev-only, it's ok to add an indirection here. They
284
+// only exist to provide different versions of `isStaticChildren`. (We shouldn't
285
+// use this pattern for the prod versions, though, because it will add an call
286
+// frame.)
287
+export function jsxProdSignatureRunningInDevWithDynamicChildren(
288
+ type,
289
+ config,
290
+ maybeKey,
291
+ source,
292
+ self,
293
+) {
294
+ if (__DEV__) {
295
+ const isStaticChildren = false;
296
+ return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
297
+ }
298
+}
299
+
300
+export function jsxProdSignatureRunningInDevWithStaticChildren(
301
+ type,
302
+ config,
303
+ maybeKey,
304
+ source,
305
+ self,
306
+) {
307
+ if (__DEV__) {
308
+ const isStaticChildren = true;
309
+ return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
310
+ }
311
+}
312
+
313
+const didWarnAboutKeySpread = {};
314
+
315
/**
316
* https://github.com/reactjs/rfcs/pull/107
317
* @param {*} type
318
* @param {object} props
319
* @param {string} key
320
*/
268
-export function jsxDEV(type, config, maybeKey, source, self) {
321
+export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
322
if (__DEV__) {
323
+ if (!isValidElementType(type)) {
324
+ // This is an invalid element type.
325
+ //
326
+ // We warn in this case but don't throw. We expect the element creation to
327
+ // succeed and there will likely be errors in render.
328
+ let info = '';
329
+ if (
330
+ type === undefined ||
331
+ (typeof type === 'object' &&
332
+ type !== null &&
333
+ Object.keys(type).length === 0)
334
+ ) {
335
+ info +=
336
+ ' You likely forgot to export your component from the file ' +
337
+ "it's defined in, or you might have mixed up default and named imports.";
338
+ }
339
+
340
+ const sourceInfo = getSourceInfoErrorAddendum(source);
341
+ if (sourceInfo) {
342
+ info += sourceInfo;
343
+ } else {
344
+ info += getDeclarationErrorAddendum();
345
+ }
346
+
347
+ let typeString;
348
+ if (type === null) {
349
+ typeString = 'null';
350
+ } else if (isArray(type)) {
351
+ typeString = 'array';
352
+ } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
353
+ typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`;
354
+ info =
355
+ ' Did you accidentally export a JSX literal instead of a component?';
356
+ } else {
357
+ typeString = typeof type;
358
+ }
359
+
360
+ console.error(
361
+ 'React.jsx: type is invalid -- expected a string (for ' +
362
+ 'built-in components) or a class/function (for composite ' +
363
+ 'components) but got: %s.%s',
364
+ typeString,
365
+ info,
366
+ );
367
+ } else {
368
+ // This is a valid element type.
369
+
370
+ // Skip key warning if the type isn't valid since our key validation logic
371
+ // doesn't expect a non-string/function type and can throw confusing
372
+ // errors. We don't want exception behavior to differ between dev and
373
+ // prod. (Rendering will throw with a helpful message and as soon as the
374
+ // type is fixed, the key warnings will appear.)
375
+ const children = config.children;
376
+ if (children !== undefined) {
377
+ if (isStaticChildren) {
378
+ if (isArray(children)) {
379
+ for (let i = 0; i < children.length; i++) {
380
+ validateChildKeys(children[i], type);
381
+ }
382
+
383
+ if (Object.freeze) {
384
+ Object.freeze(children);
385
+ }
386
+ } else {
387
+ console.error(
388
+ 'React.jsx: Static children should always be an array. ' +
389
+ 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
390
+ 'Use the Babel transform instead.',
391
+ );
392
+ }
393
+ } else {
394
+ validateChildKeys(children, type);
395
+ }
396
+ }
397
+ }
398
+
399
+ // Warn about key spread regardless of whether the type is valid.
400
+ if (hasOwnProperty.call(config, 'key')) {
401
+ const componentName = getComponentNameFromType(type);
402
+ const keys = Object.keys(config).filter(k => k !== 'key');
403
+ const beforeExample =
404
+ keys.length > 0
405
+ ? '{key: someKey, ' + keys.join(': ..., ') + ': ...}'
406
+ : '{key: someKey}';
407
+ if (!didWarnAboutKeySpread[componentName + beforeExample]) {
408
+ const afterExample =
409
+ keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';
410
+ console.error(
411
+ 'A props object containing a "key" prop is being spread into JSX:\n' +
412
+ ' let props = %s;\n' +
413
+ ' <%s {...props} />\n' +
414
+ 'React keys must be passed directly to JSX without using spread:\n' +
415
+ ' let props = %s;\n' +
416
+ ' <%s key={someKey} {...props} />',
417
+ beforeExample,
418
+ componentName,
419
+ afterExample,
420
+ componentName,
421
+ );
422
+ didWarnAboutKeySpread[componentName + beforeExample] = true;
423
+ }
424
+ }
425
+
426
let propName;
427
428
// Reserved names are extracted
@@ -336,7 +492,7 @@ export function jsxDEV(type, config, maybeKey, source, self) {
492
}
493
}
494
339
- return ReactElement(
495
+ const element = ReactElement(
496
type,
497
key,
498
ref,
@@ -345,5 +501,266 @@ export function jsxDEV(type, config, maybeKey, source, self) {
501
ReactCurrentOwner.current,
502
props,
503
);
504
+
505
+ if (type === REACT_FRAGMENT_TYPE) {
506
+ validateFragmentProps(element);
507
+ } else {
508
+ validatePropTypes(element);
509
+ }
510
+
511
+ return element;
512
+ }
513
+}
514
+
515
+function getDeclarationErrorAddendum() {
516
+ if (__DEV__) {
517
+ if (ReactCurrentOwner.current) {
518
+ const name = getComponentNameFromType(ReactCurrentOwner.current.type);
519
+ if (name) {
520
+ return '\n\nCheck the render method of `' + name + '`.';
521
+ }
522
+ }
523
+ return '';
524
+ }
525
+}
526
+
527
+function getSourceInfoErrorAddendum(source) {
528
+ if (__DEV__) {
529
+ if (source !== undefined) {
530
+ const fileName = source.fileName.replace(/^.*[\\\/]/, '');
531
+ const lineNumber = source.lineNumber;
532
+ return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
533
+ }
534
+ return '';
535
+ }
536
+}
537
+
538
+/**
539
+ * Ensure that every element either is passed in a static location, in an
540
+ * array with an explicit keys property defined, or in an object literal
541
+ * with valid key property.
542
+ *
543
+ * @internal
544
+ * @param {ReactNode} node Statically passed child of any type.
545
+ * @param {*} parentType node's parent's type.
546
+ */
547
+function validateChildKeys(node, parentType) {
548
+ if (__DEV__) {
549
+ if (typeof node !== 'object' || !node) {
550
+ return;
551
+ }
552
+ if (node.$$typeof === REACT_CLIENT_REFERENCE) {
553
+ // This is a reference to a client component so it's unknown.
554
+ } else if (isArray(node)) {
555
+ for (let i = 0; i < node.length; i++) {
556
+ const child = node[i];
557
+ if (isValidElement(child)) {
558
+ validateExplicitKey(child, parentType);
559
+ }
560
+ }
561
+ } else if (isValidElement(node)) {
562
+ // This element was passed in a valid location.
563
+ if (node._store) {
564
+ node._store.validated = true;
565
+ }
566
+ } else {
567
+ const iteratorFn = getIteratorFn(node);
568
+ if (typeof iteratorFn === 'function') {
569
+ // Entry iterators used to provide implicit keys,
570
+ // but now we print a separate warning for them later.
571
+ if (iteratorFn !== node.entries) {
572
+ const iterator = iteratorFn.call(node);
573
+ let step;
574
+ while (!(step = iterator.next()).done) {
575
+ if (isValidElement(step.value)) {
576
+ validateExplicitKey(step.value, parentType);
577
+ }
578
+ }
579
+ }
580
+ }
581
+ }
582
+ }
583
+}
584
+
585
+/**
586
+ * Verifies the object is a ReactElement.
587
+ * See https://reactjs.org/docs/react-api.html#isvalidelement
588
+ * @param {?object} object
589
+ * @return {boolean} True if `object` is a ReactElement.
590
+ * @final
591
+ */
592
+export function isValidElement(object) {
593
+ if (__DEV__) {
594
+ return (
595
+ typeof object === 'object' &&
596
+ object !== null &&
597
+ object.$$typeof === REACT_ELEMENT_TYPE
598
+ );
599
+ }
600
+}
601
+
602
+const ownerHasKeyUseWarning = {};
603
+
604
+/**
605
+ * Warn if the element doesn't have an explicit key assigned to it.
606
+ * This element is in an array. The array could grow and shrink or be
607
+ * reordered. All children that haven't already been validated are required to
608
+ * have a "key" property assigned to it. Error statuses are cached so a warning
609
+ * will only be shown once.
610
+ *
611
+ * @internal
612
+ * @param {ReactElement} element Element that requires a key.
613
+ * @param {*} parentType element's parent's type.
614
+ */
615
+function validateExplicitKey(element, parentType) {
616
+ if (__DEV__) {
617
+ if (!element._store || element._store.validated || element.key != null) {
618
+ return;
619
+ }
620
+ element._store.validated = true;
621
+
622
+ const currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
623
+ if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
624
+ return;
625
+ }
626
+ ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
627
+
628
+ // Usually the current owner is the offender, but if it accepts children as a
629
+ // property, it may be the creator of the child that's responsible for
630
+ // assigning it a key.
631
+ let childOwner = '';
632
+ if (
633
+ element &&
634
+ element._owner &&
635
+ element._owner !== ReactCurrentOwner.current
636
+ ) {
637
+ // Give the component that originally created this child.
638
+ childOwner = ` It was passed a child from ${getComponentNameFromType(
639
+ element._owner.type,
640
+ )}.`;
641
+ }
642
+
643
+ setCurrentlyValidatingElement(element);
644
+ console.error(
645
+ 'Each child in a list should have a unique "key" prop.' +
646
+ '%s%s See https://reactjs.org/link/warning-keys for more information.',
647
+ currentComponentErrorInfo,
648
+ childOwner,
649
+ );
650
+ setCurrentlyValidatingElement(null);
651
+ }
652
+}
653
+
654
+function setCurrentlyValidatingElement(element) {
655
+ if (__DEV__) {
656
+ if (element) {
657
+ const owner = element._owner;
658
+ const stack = describeUnknownElementTypeFrameInDEV(
659
+ element.type,
660
+ owner ? owner.type : null,
661
+ );
662
+ ReactDebugCurrentFrame.setExtraStackFrame(stack);
663
+ } else {
664
+ ReactDebugCurrentFrame.setExtraStackFrame(null);
665
+ }
666
+ }
667
+}
668
+
669
+function getCurrentComponentErrorInfo(parentType) {
670
+ if (__DEV__) {
671
+ let info = getDeclarationErrorAddendum();
672
+
673
+ if (!info) {
674
+ const parentName = getComponentNameFromType(parentType);
675
+ if (parentName) {
676
+ info = `\n\nCheck the top-level render call using <${parentName}>.`;
677
+ }
678
+ }
679
+ return info;
680
+ }
681
+}
682
+
683
+/**
684
+ * Given a fragment, validate that it can only be provided with fragment props
685
+ * @param {ReactElement} fragment
686
+ */
687
+function validateFragmentProps(fragment) {
688
+ if (__DEV__) {
689
+ const keys = Object.keys(fragment.props);
690
+ for (let i = 0; i < keys.length; i++) {
691
+ const key = keys[i];
692
+ if (key !== 'children' && key !== 'key') {
693
+ setCurrentlyValidatingElement(fragment);
694
+ console.error(
695
+ 'Invalid prop `%s` supplied to `React.Fragment`. ' +
696
+ 'React.Fragment can only have `key` and `children` props.',
697
+ key,
698
+ );
699
+ setCurrentlyValidatingElement(null);
700
+ break;
701
+ }
702
+ }
703
+
704
+ if (fragment.ref !== null) {
705
+ setCurrentlyValidatingElement(fragment);
706
+ console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
707
+ setCurrentlyValidatingElement(null);
708
+ }
709
+ }
710
+}
711
+
712
+let propTypesMisspellWarningShown = false;
713
+
714
+/**
715
+ * Given an element, validate that its props follow the propTypes definition,
716
+ * provided by the type.
717
+ *
718
+ * @param {ReactElement} element
719
+ */
720
+function validatePropTypes(element) {
721
+ if (__DEV__) {
722
+ const type = element.type;
723
+ if (type === null || type === undefined || typeof type === 'string') {
724
+ return;
725
+ }
726
+ if (type.$$typeof === REACT_CLIENT_REFERENCE) {
727
+ return;
728
+ }
729
+ let propTypes;
730
+ if (typeof type === 'function') {
731
+ propTypes = type.propTypes;
732
+ } else if (
733
+ typeof type === 'object' &&
734
+ (type.$$typeof === REACT_FORWARD_REF_TYPE ||
735
+ // Note: Memo only checks outer props here.
736
+ // Inner props are checked in the reconciler.
737
+ type.$$typeof === REACT_MEMO_TYPE)
738
+ ) {
739
+ propTypes = type.propTypes;
740
+ } else {
741
+ return;
742
+ }
743
+ if (propTypes) {
744
+ // Intentionally inside to avoid triggering lazy initializers:
745
+ const name = getComponentNameFromType(type);
746
+ checkPropTypes(propTypes, element.props, 'prop', name, element);
747
+ } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
748
+ propTypesMisspellWarningShown = true;
749
+ // Intentionally inside to avoid triggering lazy initializers:
750
+ const name = getComponentNameFromType(type);
751
+ console.error(
752
+ 'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?',
753
+ name || 'Unknown',
754
+ );
755
+ }
756
+ if (
757
+ typeof type.getDefaultProps === 'function' &&
758
+ !type.getDefaultProps.isReactClassApproved
759
+ ) {
760
+ console.error(
761
+ 'getDefaultProps is only used on classic React.createClass ' +
762
+ 'definitions. Use a static property named `defaultProps` instead.',
763
+ );
764
+ }
765
}
766
}
packages/react/src/jsx/ReactJSXElementValidator.js
deleted
-445
@@ -1,445 +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 a 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
-import isValidElementType from 'shared/isValidElementType';
15
-import getComponentNameFromType from 'shared/getComponentNameFromType';
16
-import checkPropTypes from 'shared/checkPropTypes';
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 hasOwnProperty from 'shared/hasOwnProperty';
25
-import isArray from 'shared/isArray';
26
-import {jsxDEV} from './ReactJSXElement';
27
-
28
-import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
29
-
30
-import ReactSharedInternals from 'shared/ReactSharedInternals';
31
-
32
-const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
33
-const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
34
-
35
-const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
36
-
37
-function setCurrentlyValidatingElement(element) {
38
- if (__DEV__) {
39
- if (element) {
40
- const owner = element._owner;
41
- const stack = describeUnknownElementTypeFrameInDEV(
42
- element.type,
43
- owner ? owner.type : null,
44
- );
45
- ReactDebugCurrentFrame.setExtraStackFrame(stack);
46
- } else {
47
- ReactDebugCurrentFrame.setExtraStackFrame(null);
48
- }
49
- }
50
-}
51
-
52
-let propTypesMisspellWarningShown;
53
-
54
-if (__DEV__) {
55
- propTypesMisspellWarningShown = false;
56
-}
57
-
58
-/**
59
- * Verifies the object is a ReactElement.
60
- * See https://reactjs.org/docs/react-api.html#isvalidelement
61
- * @param {?object} object
62
- * @return {boolean} True if `object` is a ReactElement.
63
- * @final
64
- */
65
-export function isValidElement(object) {
66
- if (__DEV__) {
67
- return (
68
- typeof object === 'object' &&
69
- object !== null &&
70
- object.$$typeof === REACT_ELEMENT_TYPE
71
- );
72
- }
73
-}
74
-
75
-function getDeclarationErrorAddendum() {
76
- if (__DEV__) {
77
- if (ReactCurrentOwner.current) {
78
- const name = getComponentNameFromType(ReactCurrentOwner.current.type);
79
- if (name) {
80
- return '\n\nCheck the render method of `' + name + '`.';
81
- }
82
- }
83
- return '';
84
- }
85
-}
86
-
87
-function getSourceInfoErrorAddendum(source) {
88
- if (__DEV__) {
89
- if (source !== undefined) {
90
- const fileName = source.fileName.replace(/^.*[\\\/]/, '');
91
- const lineNumber = source.lineNumber;
92
- return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
93
- }
94
- return '';
95
- }
96
-}
97
-
98
-/**
99
- * Warn if there's no key explicitly set on dynamic arrays of children or
100
- * object keys are not valid. This allows us to keep track of children between
101
- * updates.
102
- */
103
-const ownerHasKeyUseWarning = {};
104
-
105
-function getCurrentComponentErrorInfo(parentType) {
106
- if (__DEV__) {
107
- let info = getDeclarationErrorAddendum();
108
-
109
- if (!info) {
110
- const parentName = getComponentNameFromType(parentType);
111
- if (parentName) {
112
- info = `\n\nCheck the top-level render call using <${parentName}>.`;
113
- }
114
- }
115
- return info;
116
- }
117
-}
118
-
119
-/**
120
- * Warn if the element doesn't have an explicit key assigned to it.
121
- * This element is in an array. The array could grow and shrink or be
122
- * reordered. All children that haven't already been validated are required to
123
- * have a "key" property assigned to it. Error statuses are cached so a warning
124
- * will only be shown once.
125
- *
126
- * @internal
127
- * @param {ReactElement} element Element that requires a key.
128
- * @param {*} parentType element's parent's type.
129
- */
130
-function validateExplicitKey(element, parentType) {
131
- if (__DEV__) {
132
- if (!element._store || element._store.validated || element.key != null) {
133
- return;
134
- }
135
- element._store.validated = true;
136
-
137
- const currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
138
- if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
139
- return;
140
- }
141
- ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
142
-
143
- // Usually the current owner is the offender, but if it accepts children as a
144
- // property, it may be the creator of the child that's responsible for
145
- // assigning it a key.
146
- let childOwner = '';
147
- if (
148
- element &&
149
- element._owner &&
150
- element._owner !== ReactCurrentOwner.current
151
- ) {
152
- // Give the component that originally created this child.
153
- childOwner = ` It was passed a child from ${getComponentNameFromType(
154
- element._owner.type,
155
- )}.`;
156
- }
157
-
158
- setCurrentlyValidatingElement(element);
159
- console.error(
160
- 'Each child in a list should have a unique "key" prop.' +
161
- '%s%s See https://reactjs.org/link/warning-keys for more information.',
162
- currentComponentErrorInfo,
163
- childOwner,
164
- );
165
- setCurrentlyValidatingElement(null);
166
- }
167
-}
168
-
169
-/**
170
- * Ensure that every element either is passed in a static location, in an
171
- * array with an explicit keys property defined, or in an object literal
172
- * with valid key property.
173
- *
174
- * @internal
175
- * @param {ReactNode} node Statically passed child of any type.
176
- * @param {*} parentType node's parent's type.
177
- */
178
-function validateChildKeys(node, parentType) {
179
- if (__DEV__) {
180
- if (typeof node !== 'object' || !node) {
181
- return;
182
- }
183
- if (node.$$typeof === REACT_CLIENT_REFERENCE) {
184
- // This is a reference to a client component so it's unknown.
185
- } else if (isArray(node)) {
186
- for (let i = 0; i < node.length; i++) {
187
- const child = node[i];
188
- if (isValidElement(child)) {
189
- validateExplicitKey(child, parentType);
190
- }
191
- }
192
- } else if (isValidElement(node)) {
193
- // This element was passed in a valid location.
194
- if (node._store) {
195
- node._store.validated = true;
196
- }
197
- } else {
198
- const iteratorFn = getIteratorFn(node);
199
- if (typeof iteratorFn === 'function') {
200
- // Entry iterators used to provide implicit keys,
201
- // but now we print a separate warning for them later.
202
- if (iteratorFn !== node.entries) {
203
- const iterator = iteratorFn.call(node);
204
- let step;
205
- while (!(step = iterator.next()).done) {
206
- if (isValidElement(step.value)) {
207
- validateExplicitKey(step.value, parentType);
208
- }
209
- }
210
- }
211
- }
212
- }
213
- }
214
-}
215
-
216
-/**
217
- * Given an element, validate that its props follow the propTypes definition,
218
- * provided by the type.
219
- *
220
- * @param {ReactElement} element
221
- */
222
-function validatePropTypes(element) {
223
- if (__DEV__) {
224
- const type = element.type;
225
- if (type === null || type === undefined || typeof type === 'string') {
226
- return;
227
- }
228
- if (type.$$typeof === REACT_CLIENT_REFERENCE) {
229
- return;
230
- }
231
- let propTypes;
232
- if (typeof type === 'function') {
233
- propTypes = type.propTypes;
234
- } else if (
235
- typeof type === 'object' &&
236
- (type.$$typeof === REACT_FORWARD_REF_TYPE ||
237
- // Note: Memo only checks outer props here.
238
- // Inner props are checked in the reconciler.
239
- type.$$typeof === REACT_MEMO_TYPE)
240
- ) {
241
- propTypes = type.propTypes;
242
- } else {
243
- return;
244
- }
245
- if (propTypes) {
246
- // Intentionally inside to avoid triggering lazy initializers:
247
- const name = getComponentNameFromType(type);
248
- checkPropTypes(propTypes, element.props, 'prop', name, element);
249
- } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
250
- propTypesMisspellWarningShown = true;
251
- // Intentionally inside to avoid triggering lazy initializers:
252
- const name = getComponentNameFromType(type);
253
- console.error(
254
- 'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?',
255
- name || 'Unknown',
256
- );
257
- }
258
- if (
259
- typeof type.getDefaultProps === 'function' &&
260
- !type.getDefaultProps.isReactClassApproved
261
- ) {
262
- console.error(
263
- 'getDefaultProps is only used on classic React.createClass ' +
264
- 'definitions. Use a static property named `defaultProps` instead.',
265
- );
266
- }
267
- }
268
-}
269
-
270
-/**
271
- * Given a fragment, validate that it can only be provided with fragment props
272
- * @param {ReactElement} fragment
273
- */
274
-function validateFragmentProps(fragment) {
275
- if (__DEV__) {
276
- const keys = Object.keys(fragment.props);
277
- for (let i = 0; i < keys.length; i++) {
278
- const key = keys[i];
279
- if (key !== 'children' && key !== 'key') {
280
- setCurrentlyValidatingElement(fragment);
281
- console.error(
282
- 'Invalid prop `%s` supplied to `React.Fragment`. ' +
283
- 'React.Fragment can only have `key` and `children` props.',
284
- key,
285
- );
286
- setCurrentlyValidatingElement(null);
287
- break;
288
- }
289
- }
290
-
291
- if (fragment.ref !== null) {
292
- setCurrentlyValidatingElement(fragment);
293
- console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
294
- setCurrentlyValidatingElement(null);
295
- }
296
- }
297
-}
298
-
299
-const didWarnAboutKeySpread = {};
300
-
301
-export function jsxWithValidation(
302
- type,
303
- props,
304
- key,
305
- isStaticChildren,
306
- source,
307
- self,
308
-) {
309
- if (__DEV__) {
310
- const validType = isValidElementType(type);
311
-
312
- // We warn in this case but don't throw. We expect the element creation to
313
- // succeed and there will likely be errors in render.
314
- if (!validType) {
315
- let info = '';
316
- if (
317
- type === undefined ||
318
- (typeof type === 'object' &&
319
- type !== null &&
320
- Object.keys(type).length === 0)
321
- ) {
322
- info +=
323
- ' You likely forgot to export your component from the file ' +
324
- "it's defined in, or you might have mixed up default and named imports.";
325
- }
326
-
327
- const sourceInfo = getSourceInfoErrorAddendum(source);
328
- if (sourceInfo) {
329
- info += sourceInfo;
330
- } else {
331
- info += getDeclarationErrorAddendum();
332
- }
333
-
334
- let typeString;
335
- if (type === null) {
336
- typeString = 'null';
337
- } else if (isArray(type)) {
338
- typeString = 'array';
339
- } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
340
- typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`;
341
- info =
342
- ' Did you accidentally export a JSX literal instead of a component?';
343
- } else {
344
- typeString = typeof type;
345
- }
346
-
347
- console.error(
348
- 'React.jsx: type is invalid -- expected a string (for ' +
349
- 'built-in components) or a class/function (for composite ' +
350
- 'components) but got: %s.%s',
351
- typeString,
352
- info,
353
- );
354
- }
355
-
356
- const element = jsxDEV(type, props, key, source, self);
357
-
358
- // The result can be nullish if a mock or a custom function is used.
359
- // TODO: Drop this when these are no longer allowed as the type argument.
360
- if (element == null) {
361
- return element;
362
- }
363
-
364
- // Skip key warning if the type isn't valid since our key validation logic
365
- // doesn't expect a non-string/function type and can throw confusing errors.
366
- // We don't want exception behavior to differ between dev and prod.
367
- // (Rendering will throw with a helpful message and as soon as the type is
368
- // fixed, the key warnings will appear.)
369
-
370
- if (validType) {
371
- const children = props.children;
372
- if (children !== undefined) {
373
- if (isStaticChildren) {
374
- if (isArray(children)) {
375
- for (let i = 0; i < children.length; i++) {
376
- validateChildKeys(children[i], type);
377
- }
378
-
379
- if (Object.freeze) {
380
- Object.freeze(children);
381
- }
382
- } else {
383
- console.error(
384
- 'React.jsx: Static children should always be an array. ' +
385
- 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
386
- 'Use the Babel transform instead.',
387
- );
388
- }
389
- } else {
390
- validateChildKeys(children, type);
391
- }
392
- }
393
- }
394
-
395
- if (hasOwnProperty.call(props, 'key')) {
396
- const componentName = getComponentNameFromType(type);
397
- const keys = Object.keys(props).filter(k => k !== 'key');
398
- const beforeExample =
399
- keys.length > 0
400
- ? '{key: someKey, ' + keys.join(': ..., ') + ': ...}'
401
- : '{key: someKey}';
402
- if (!didWarnAboutKeySpread[componentName + beforeExample]) {
403
- const afterExample =
404
- keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';
405
- console.error(
406
- 'A props object containing a "key" prop is being spread into JSX:\n' +
407
- ' let props = %s;\n' +
408
- ' <%s {...props} />\n' +
409
- 'React keys must be passed directly to JSX without using spread:\n' +
410
- ' let props = %s;\n' +
411
- ' <%s key={someKey} {...props} />',
412
- beforeExample,
413
- componentName,
414
- afterExample,
415
- componentName,
416
- );
417
- didWarnAboutKeySpread[componentName + beforeExample] = true;
418
- }
419
- }
420
-
421
- if (type === REACT_FRAGMENT_TYPE) {
422
- validateFragmentProps(element);
423
- } else {
424
- validatePropTypes(element);
425
- }
426
-
427
- return element;
428
- }
429
-}
430
-
431
-// These two functions exist to still get child warnings in dev
432
-// even with the prod transform. This means that jsxDEV is purely
433
-// opt-in behavior for better messages but that we won't stop
434
-// giving you warnings if you use production apis.
435
-export function jsxWithValidationStatic(type, props, key) {
436
- if (__DEV__) {
437
- return jsxWithValidation(type, props, key, true);
438
- }
439
-}
440
-
441
-export function jsxWithValidationDynamic(type, props, key) {
442
- if (__DEV__) {
443
- return jsxWithValidation(type, props, key, false);
444
- }
445
-}
packages/react/src/jsx/ReactJSXServer.js
+15
-9
@@ -6,17 +6,23 @@
6
*
7
* @flow
8
*/
9
-
10
-// These are implementations of the jsx APIs for React Server runtimes.
9
import {REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
10
import {
13
- jsxWithValidationStatic,
14
- jsxWithValidationDynamic,
15
-} from './ReactJSXElementValidator';
16
-import {jsx as jsxProd} from './ReactJSXElement';
17
-const jsx: any = __DEV__ ? jsxWithValidationDynamic : jsxProd;
11
+ jsxProd,
12
+ jsxProdSignatureRunningInDevWithDynamicChildren,
13
+ jsxProdSignatureRunningInDevWithStaticChildren,
14
+ jsxDEV as _jsxDEV,
15
+} from './ReactJSXElement';
16
+
17
+const jsx: any = __DEV__
18
+ ? jsxProdSignatureRunningInDevWithDynamicChildren
19
+ : jsxProd;
20
// we may want to special case jsxs internally to take advantage of static children.
21
// for now we can ship identical prod functions
20
-const jsxs: any = __DEV__ ? jsxWithValidationStatic : jsxProd;
22
+const jsxs: any = __DEV__
23
+ ? jsxProdSignatureRunningInDevWithStaticChildren
24
+ : jsxProd;
25
+
26
+const jsxDEV: any = __DEV__ ? _jsxDEV : undefined;
27
22
-export {REACT_FRAGMENT_TYPE as Fragment, jsx, jsxs};
28
+export {REACT_FRAGMENT_TYPE as Fragment, jsx, jsxs, jsxDEV};