@samitouri / QOS-React-2 / commits / ec160f32c2

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