2017-05-03 15:35:00 +02:00
/ * *
* Copyright 2014 - present , Facebook , Inc .
* All rights reserved .
*
* This source code is licensed under the BSD - style license found in the
* LICENSE file in the root directory of this source tree . An additional grant
* of patent rights can be found in the PATENTS file in the same directory .
*
* /
/ * *
* ReactElementValidator provides a wrapper around a element factory
* which validates the props passed to the element . This is intended to be
* used only in DEV and could be replaced by a static type checker for languages
* that support it .
* /
'use strict' ;
var ReactCurrentOwner = require ( './ReactCurrentOwner' ) ;
var ReactComponentTreeHook = require ( './ReactComponentTreeHook' ) ;
var ReactElement = require ( './ReactElement' ) ;
var checkReactTypeSpec = require ( './checkReactTypeSpec' ) ;
var canDefineProperty = require ( './canDefineProperty' ) ;
var getIteratorFn = require ( './getIteratorFn' ) ;
var warning = require ( 'fbjs/lib/warning' ) ;
2017-08-14 05:01:11 +02:00
var lowPriorityWarning = require ( './lowPriorityWarning' ) ;
2017-05-03 15:35:00 +02:00
function getDeclarationErrorAddendum ( ) {
if ( ReactCurrentOwner . current ) {
var name = ReactCurrentOwner . current . getName ( ) ;
if ( name ) {
return ' Check the render method of `' + name + '`.' ;
}
}
return '' ;
}
function getSourceInfoErrorAddendum ( elementProps ) {
if ( elementProps !== null && elementProps !== undefined && elementProps . _ _source !== undefined ) {
var source = elementProps . _ _source ;
var fileName = source . fileName . replace ( /^.*[\\\/]/ , '' ) ;
var lineNumber = source . lineNumber ;
return ' Check your code at ' + fileName + ':' + lineNumber + '.' ;
}
return '' ;
}
/ * *
* Warn if there ' s no key explicitly set on dynamic arrays of children or
* object keys are not valid . This allows us to keep track of children between
* updates .
* /
var ownerHasKeyUseWarning = { } ;
function getCurrentComponentErrorInfo ( parentType ) {
var info = getDeclarationErrorAddendum ( ) ;
if ( ! info ) {
var parentName = typeof parentType === 'string' ? parentType : parentType . displayName || parentType . name ;
if ( parentName ) {
info = ' Check the top-level render call using <' + parentName + '>.' ;
}
}
return info ;
}
/ * *
* Warn if the element doesn ' t have an explicit key assigned to it .
* This element is in an array . The array could grow and shrink or be
* reordered . All children that haven ' t already been validated are required to
* have a "key" property assigned to it . Error statuses are cached so a warning
* will only be shown once .
*
* @ internal
* @ param { ReactElement } element Element that requires a key .
* @ param { * } parentType element 's parent' s type .
* /
function validateExplicitKey ( element , parentType ) {
if ( ! element . _store || element . _store . validated || element . key != null ) {
return ;
}
element . _store . validated = true ;
var memoizer = ownerHasKeyUseWarning . uniqueKey || ( ownerHasKeyUseWarning . uniqueKey = { } ) ;
var currentComponentErrorInfo = getCurrentComponentErrorInfo ( parentType ) ;
if ( memoizer [ currentComponentErrorInfo ] ) {
return ;
}
memoizer [ currentComponentErrorInfo ] = true ;
// Usually the current owner is the offender, but if it accepts children as a
// property, it may be the creator of the child that's responsible for
// assigning it a key.
var childOwner = '' ;
if ( element && element . _owner && element . _owner !== ReactCurrentOwner . current ) {
// Give the component that originally created this child.
childOwner = ' It was passed a child from ' + element . _owner . getName ( ) + '.' ;
}
process . env . NODE _ENV !== 'production' ? warning ( false , 'Each child in an array or iterator should have a unique "key" prop.' + '%s%s See https://fb.me/react-warning-keys for more information.%s' , currentComponentErrorInfo , childOwner , ReactComponentTreeHook . getCurrentStackAddendum ( element ) ) : void 0 ;
}
/ * *
* Ensure that every element either is passed in a static location , in an
* array with an explicit keys property defined , or in an object literal
* with valid key property .
*
* @ internal
* @ param { ReactNode } node Statically passed child of any type .
* @ param { * } parentType node 's parent' s type .
* /
function validateChildKeys ( node , parentType ) {
if ( typeof node !== 'object' ) {
return ;
}
if ( Array . isArray ( node ) ) {
for ( var i = 0 ; i < node . length ; i ++ ) {
var child = node [ i ] ;
if ( ReactElement . isValidElement ( child ) ) {
validateExplicitKey ( child , parentType ) ;
}
}
} else if ( ReactElement . isValidElement ( node ) ) {
// This element was passed in a valid location.
if ( node . _store ) {
node . _store . validated = true ;
}
} else if ( node ) {
var iteratorFn = getIteratorFn ( node ) ;
// Entry iterators provide implicit keys.
if ( iteratorFn ) {
if ( iteratorFn !== node . entries ) {
var iterator = iteratorFn . call ( node ) ;
var step ;
while ( ! ( step = iterator . next ( ) ) . done ) {
if ( ReactElement . isValidElement ( step . value ) ) {
validateExplicitKey ( step . value , parentType ) ;
}
}
}
}
}
}
/ * *
* Given an element , validate that its props follow the propTypes definition ,
* provided by the type .
*
* @ param { ReactElement } element
* /
function validatePropTypes ( element ) {
var componentClass = element . type ;
if ( typeof componentClass !== 'function' ) {
return ;
}
var name = componentClass . displayName || componentClass . name ;
if ( componentClass . propTypes ) {
checkReactTypeSpec ( componentClass . propTypes , element . props , 'prop' , name , element , null ) ;
}
if ( typeof componentClass . getDefaultProps === 'function' ) {
process . env . NODE _ENV !== 'production' ? warning ( componentClass . getDefaultProps . isReactClassApproved , 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.' ) : void 0 ;
}
}
var ReactElementValidator = {
createElement : function ( type , props , children ) {
var validType = typeof type === 'string' || typeof type === 'function' ;
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
if ( ! validType ) {
if ( typeof type !== 'function' && typeof type !== 'string' ) {
var info = '' ;
if ( type === undefined || typeof type === 'object' && type !== null && Object . keys ( type ) . length === 0 ) {
2017-08-14 05:01:11 +02:00
info += ' You likely forgot to export your component from the file ' + "it's defined in." ;
2017-05-03 15:35:00 +02:00
}
var sourceInfo = getSourceInfoErrorAddendum ( props ) ;
if ( sourceInfo ) {
info += sourceInfo ;
} else {
info += getDeclarationErrorAddendum ( ) ;
}
info += ReactComponentTreeHook . getCurrentStackAddendum ( ) ;
2017-08-14 05:01:11 +02:00
var currentSource = props !== null && props !== undefined && props . _ _source !== undefined ? props . _ _source : null ;
ReactComponentTreeHook . pushNonStandardWarningStack ( true , currentSource ) ;
2017-05-03 15:35:00 +02:00
process . env . NODE _ENV !== 'production' ? warning ( false , 'React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s' , type == null ? type : typeof type , info ) : void 0 ;
2017-08-14 05:01:11 +02:00
ReactComponentTreeHook . popNonStandardWarningStack ( ) ;
2017-05-03 15:35:00 +02:00
}
}
var element = ReactElement . createElement . apply ( this , arguments ) ;
// The result can be nullish if a mock or a custom function is used.
// TODO: Drop this when these are no longer allowed as the type argument.
if ( element == null ) {
return element ;
}
// Skip key warning if the type isn't valid since our key validation logic
// doesn't expect a non-string/function type and can throw confusing errors.
// We don't want exception behavior to differ between dev and prod.
// (Rendering will throw with a helpful message and as soon as the type is
// fixed, the key warnings will appear.)
if ( validType ) {
for ( var i = 2 ; i < arguments . length ; i ++ ) {
validateChildKeys ( arguments [ i ] , type ) ;
}
}
validatePropTypes ( element ) ;
return element ;
} ,
createFactory : function ( type ) {
var validatedFactory = ReactElementValidator . createElement . bind ( null , type ) ;
// Legacy hook TODO: Warn if this is accessed
validatedFactory . type = type ;
if ( process . env . NODE _ENV !== 'production' ) {
if ( canDefineProperty ) {
Object . defineProperty ( validatedFactory , 'type' , {
enumerable : false ,
get : function ( ) {
2017-08-14 05:01:11 +02:00
lowPriorityWarning ( false , 'Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.' ) ;
2017-05-03 15:35:00 +02:00
Object . defineProperty ( this , 'type' , {
value : type
} ) ;
return type ;
}
} ) ;
}
}
return validatedFactory ;
} ,
cloneElement : function ( element , props , children ) {
var newElement = ReactElement . cloneElement . apply ( this , arguments ) ;
for ( var i = 2 ; i < arguments . length ; i ++ ) {
validateChildKeys ( arguments [ i ] , newElement . type ) ;
}
validatePropTypes ( newElement ) ;
return newElement ;
}
} ;
module . exports = ReactElementValidator ;