@@ -27,7 +27,12 @@ import warning from 'shared/warning';
2727import warningWithoutStack from 'shared/warningWithoutStack' ;
2828
2929import ReactCurrentOwner from './ReactCurrentOwner' ;
30- import { isValidElement , createElement , cloneElement } from './ReactElement' ;
30+ import {
31+ isValidElement ,
32+ createElement ,
33+ cloneElement ,
34+ jsxDEV ,
35+ } from './ReactElement' ;
3136import ReactDebugCurrentFrame , {
3237 setCurrentlyValidatingElement ,
3338} from './ReactDebugCurrentFrame' ;
@@ -48,20 +53,22 @@ function getDeclarationErrorAddendum() {
4853 return '' ;
4954}
5055
51- function getSourceInfoErrorAddendum ( elementProps ) {
52- if (
53- elementProps !== null &&
54- elementProps !== undefined &&
55- elementProps . __source !== undefined
56- ) {
57- const source = elementProps . __source ;
56+ function getSourceInfoErrorAddendum ( source ) {
57+ if ( source !== undefined ) {
5858 const fileName = source . fileName . replace ( / ^ .* [ \\ \/ ] / , '' ) ;
5959 const lineNumber = source . lineNumber ;
6060 return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.' ;
6161 }
6262 return '' ;
6363}
6464
65+ function getSourceInfoErrorAddendumForProps ( elementProps ) {
66+ if ( elementProps !== null && elementProps !== undefined ) {
67+ return getSourceInfoErrorAddendum ( elementProps . __source ) ;
68+ }
69+ return '' ;
70+ }
71+
6572/**
6673 * Warn if there's no key explicitly set on dynamic arrays of children or
6774 * object keys are not valid. This allows us to keep track of children between
@@ -259,6 +266,117 @@ function validateFragmentProps(fragment) {
259266 setCurrentlyValidatingElement ( null ) ;
260267}
261268
269+ export function jsxWithValidation (
270+ type ,
271+ props ,
272+ key ,
273+ isStaticChildren ,
274+ source ,
275+ self ,
276+ ) {
277+ const validType = isValidElementType ( type ) ;
278+
279+ // We warn in this case but don't throw. We expect the element creation to
280+ // succeed and there will likely be errors in render.
281+ if ( ! validType ) {
282+ let info = '' ;
283+ if (
284+ type === undefined ||
285+ ( typeof type === 'object' &&
286+ type !== null &&
287+ Object . keys ( type ) . length === 0 )
288+ ) {
289+ info +=
290+ ' You likely forgot to export your component from the file ' +
291+ "it's defined in, or you might have mixed up default and named imports." ;
292+ }
293+
294+ const sourceInfo = getSourceInfoErrorAddendum ( source ) ;
295+ if ( sourceInfo ) {
296+ info += sourceInfo ;
297+ } else {
298+ info += getDeclarationErrorAddendum ( ) ;
299+ }
300+
301+ let typeString ;
302+ if ( type === null ) {
303+ typeString = 'null' ;
304+ } else if ( Array . isArray ( type ) ) {
305+ typeString = 'array' ;
306+ } else if ( type !== undefined && type . $$typeof === REACT_ELEMENT_TYPE ) {
307+ typeString = `<${ getComponentName ( type . type ) || 'Unknown' } />` ;
308+ info =
309+ ' Did you accidentally export a JSX literal instead of a component?' ;
310+ } else {
311+ typeString = typeof type ;
312+ }
313+
314+ warning (
315+ false ,
316+ 'React.jsx: type is invalid -- expected a string (for ' +
317+ 'built-in components) or a class/function (for composite ' +
318+ 'components) but got: %s.%s' ,
319+ typeString ,
320+ info ,
321+ ) ;
322+ }
323+
324+ const element = jsxDEV ( type , props , key , source , self ) ;
325+
326+ // The result can be nullish if a mock or a custom function is used.
327+ // TODO: Drop this when these are no longer allowed as the type argument.
328+ if ( element == null ) {
329+ return element ;
330+ }
331+
332+ // Skip key warning if the type isn't valid since our key validation logic
333+ // doesn't expect a non-string/function type and can throw confusing errors.
334+ // We don't want exception behavior to differ between dev and prod.
335+ // (Rendering will throw with a helpful message and as soon as the type is
336+ // fixed, the key warnings will appear.)
337+ if ( validType ) {
338+ const children = props . children ;
339+ if ( children !== undefined ) {
340+ if ( isStaticChildren ) {
341+ for ( let i = 0 ; i < children . length ; i ++ ) {
342+ validateChildKeys ( children [ i ] , type ) ;
343+ }
344+ } else {
345+ validateChildKeys ( children , type ) ;
346+ }
347+ }
348+ }
349+
350+ if ( props . key !== undefined ) {
351+ warning (
352+ false ,
353+ 'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
354+ 'Explicitly pass a key after spreading props in your JSX call. ' +
355+ 'E.g. <ComponentName {...props} key={key} />' ,
356+ ) ;
357+ }
358+
359+ if ( type === REACT_FRAGMENT_TYPE ) {
360+ validateFragmentProps ( element ) ;
361+ } else {
362+ validatePropTypes ( element ) ;
363+ }
364+
365+ return element ;
366+ }
367+
368+ // These two functions exist to still get child warnings in dev
369+ // even with the prod transform. This means that jsxDEV is purely
370+ // opt-in behavior for better messages but that we won't stop
371+ // giving you warnings if you use production apis.
372+ export function jsxWithValidationStatic ( type , props , key ) {
373+ return jsxWithValidation ( type , props , key , true ) ;
374+ }
375+
376+ export function jsxWithValidationDynamic ( type , props , key ) {
377+ return jsxWithValidation ( type , props , key , false ) ;
378+ }
379+
262380export function createElementWithValidation ( type , props , children ) {
263381 const validType = isValidElementType ( type ) ;
264382
@@ -277,7 +395,7 @@ export function createElementWithValidation(type, props, children) {
277395 "it's defined in, or you might have mixed up default and named imports." ;
278396 }
279397
280- const sourceInfo = getSourceInfoErrorAddendum ( props ) ;
398+ const sourceInfo = getSourceInfoErrorAddendumForProps ( props ) ;
281399 if ( sourceInfo ) {
282400 info += sourceInfo ;
283401 } else {
0 commit comments