[47f4eaf] | 1 | /**
|
---|
| 2 | * Copyright (c) 2013-present, Facebook, Inc.
|
---|
| 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 | 'use strict';
|
---|
| 9 |
|
---|
| 10 | var ReactIs = require('react-is');
|
---|
| 11 | var assign = require('object-assign');
|
---|
| 12 |
|
---|
| 13 | var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
|
---|
| 14 | var has = require('./lib/has');
|
---|
| 15 | var checkPropTypes = require('./checkPropTypes');
|
---|
| 16 |
|
---|
| 17 | var printWarning = function() {};
|
---|
| 18 |
|
---|
| 19 | if (process.env.NODE_ENV !== 'production') {
|
---|
| 20 | printWarning = function(text) {
|
---|
| 21 | var message = 'Warning: ' + text;
|
---|
| 22 | if (typeof console !== 'undefined') {
|
---|
| 23 | console.error(message);
|
---|
| 24 | }
|
---|
| 25 | try {
|
---|
| 26 | // --- Welcome to debugging React ---
|
---|
| 27 | // This error was thrown as a convenience so that you can use this stack
|
---|
| 28 | // to find the callsite that caused this warning to fire.
|
---|
| 29 | throw new Error(message);
|
---|
| 30 | } catch (x) {}
|
---|
| 31 | };
|
---|
| 32 | }
|
---|
| 33 |
|
---|
| 34 | function emptyFunctionThatReturnsNull() {
|
---|
| 35 | return null;
|
---|
| 36 | }
|
---|
| 37 |
|
---|
| 38 | module.exports = function(isValidElement, throwOnDirectAccess) {
|
---|
| 39 | /* global Symbol */
|
---|
| 40 | var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
|
---|
| 41 | var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
|
---|
| 42 |
|
---|
| 43 | /**
|
---|
| 44 | * Returns the iterator method function contained on the iterable object.
|
---|
| 45 | *
|
---|
| 46 | * Be sure to invoke the function with the iterable as context:
|
---|
| 47 | *
|
---|
| 48 | * var iteratorFn = getIteratorFn(myIterable);
|
---|
| 49 | * if (iteratorFn) {
|
---|
| 50 | * var iterator = iteratorFn.call(myIterable);
|
---|
| 51 | * ...
|
---|
| 52 | * }
|
---|
| 53 | *
|
---|
| 54 | * @param {?object} maybeIterable
|
---|
| 55 | * @return {?function}
|
---|
| 56 | */
|
---|
| 57 | function getIteratorFn(maybeIterable) {
|
---|
| 58 | var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
|
---|
| 59 | if (typeof iteratorFn === 'function') {
|
---|
| 60 | return iteratorFn;
|
---|
| 61 | }
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 | /**
|
---|
| 65 | * Collection of methods that allow declaration and validation of props that are
|
---|
| 66 | * supplied to React components. Example usage:
|
---|
| 67 | *
|
---|
| 68 | * var Props = require('ReactPropTypes');
|
---|
| 69 | * var MyArticle = React.createClass({
|
---|
| 70 | * propTypes: {
|
---|
| 71 | * // An optional string prop named "description".
|
---|
| 72 | * description: Props.string,
|
---|
| 73 | *
|
---|
| 74 | * // A required enum prop named "category".
|
---|
| 75 | * category: Props.oneOf(['News','Photos']).isRequired,
|
---|
| 76 | *
|
---|
| 77 | * // A prop named "dialog" that requires an instance of Dialog.
|
---|
| 78 | * dialog: Props.instanceOf(Dialog).isRequired
|
---|
| 79 | * },
|
---|
| 80 | * render: function() { ... }
|
---|
| 81 | * });
|
---|
| 82 | *
|
---|
| 83 | * A more formal specification of how these methods are used:
|
---|
| 84 | *
|
---|
| 85 | * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
|
---|
| 86 | * decl := ReactPropTypes.{type}(.isRequired)?
|
---|
| 87 | *
|
---|
| 88 | * Each and every declaration produces a function with the same signature. This
|
---|
| 89 | * allows the creation of custom validation functions. For example:
|
---|
| 90 | *
|
---|
| 91 | * var MyLink = React.createClass({
|
---|
| 92 | * propTypes: {
|
---|
| 93 | * // An optional string or URI prop named "href".
|
---|
| 94 | * href: function(props, propName, componentName) {
|
---|
| 95 | * var propValue = props[propName];
|
---|
| 96 | * if (propValue != null && typeof propValue !== 'string' &&
|
---|
| 97 | * !(propValue instanceof URI)) {
|
---|
| 98 | * return new Error(
|
---|
| 99 | * 'Expected a string or an URI for ' + propName + ' in ' +
|
---|
| 100 | * componentName
|
---|
| 101 | * );
|
---|
| 102 | * }
|
---|
| 103 | * }
|
---|
| 104 | * },
|
---|
| 105 | * render: function() {...}
|
---|
| 106 | * });
|
---|
| 107 | *
|
---|
| 108 | * @internal
|
---|
| 109 | */
|
---|
| 110 |
|
---|
| 111 | var ANONYMOUS = '<<anonymous>>';
|
---|
| 112 |
|
---|
| 113 | // Important!
|
---|
| 114 | // Keep this list in sync with production version in `./factoryWithThrowingShims.js`.
|
---|
| 115 | var ReactPropTypes = {
|
---|
| 116 | array: createPrimitiveTypeChecker('array'),
|
---|
| 117 | bigint: createPrimitiveTypeChecker('bigint'),
|
---|
| 118 | bool: createPrimitiveTypeChecker('boolean'),
|
---|
| 119 | func: createPrimitiveTypeChecker('function'),
|
---|
| 120 | number: createPrimitiveTypeChecker('number'),
|
---|
| 121 | object: createPrimitiveTypeChecker('object'),
|
---|
| 122 | string: createPrimitiveTypeChecker('string'),
|
---|
| 123 | symbol: createPrimitiveTypeChecker('symbol'),
|
---|
| 124 |
|
---|
| 125 | any: createAnyTypeChecker(),
|
---|
| 126 | arrayOf: createArrayOfTypeChecker,
|
---|
| 127 | element: createElementTypeChecker(),
|
---|
| 128 | elementType: createElementTypeTypeChecker(),
|
---|
| 129 | instanceOf: createInstanceTypeChecker,
|
---|
| 130 | node: createNodeChecker(),
|
---|
| 131 | objectOf: createObjectOfTypeChecker,
|
---|
| 132 | oneOf: createEnumTypeChecker,
|
---|
| 133 | oneOfType: createUnionTypeChecker,
|
---|
| 134 | shape: createShapeTypeChecker,
|
---|
| 135 | exact: createStrictShapeTypeChecker,
|
---|
| 136 | };
|
---|
| 137 |
|
---|
| 138 | /**
|
---|
| 139 | * inlined Object.is polyfill to avoid requiring consumers ship their own
|
---|
| 140 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
---|
| 141 | */
|
---|
| 142 | /*eslint-disable no-self-compare*/
|
---|
| 143 | function is(x, y) {
|
---|
| 144 | // SameValue algorithm
|
---|
| 145 | if (x === y) {
|
---|
| 146 | // Steps 1-5, 7-10
|
---|
| 147 | // Steps 6.b-6.e: +0 != -0
|
---|
| 148 | return x !== 0 || 1 / x === 1 / y;
|
---|
| 149 | } else {
|
---|
| 150 | // Step 6.a: NaN == NaN
|
---|
| 151 | return x !== x && y !== y;
|
---|
| 152 | }
|
---|
| 153 | }
|
---|
| 154 | /*eslint-enable no-self-compare*/
|
---|
| 155 |
|
---|
| 156 | /**
|
---|
| 157 | * We use an Error-like object for backward compatibility as people may call
|
---|
| 158 | * PropTypes directly and inspect their output. However, we don't use real
|
---|
| 159 | * Errors anymore. We don't inspect their stack anyway, and creating them
|
---|
| 160 | * is prohibitively expensive if they are created too often, such as what
|
---|
| 161 | * happens in oneOfType() for any type before the one that matched.
|
---|
| 162 | */
|
---|
| 163 | function PropTypeError(message, data) {
|
---|
| 164 | this.message = message;
|
---|
| 165 | this.data = data && typeof data === 'object' ? data: {};
|
---|
| 166 | this.stack = '';
|
---|
| 167 | }
|
---|
| 168 | // Make `instanceof Error` still work for returned errors.
|
---|
| 169 | PropTypeError.prototype = Error.prototype;
|
---|
| 170 |
|
---|
| 171 | function createChainableTypeChecker(validate) {
|
---|
| 172 | if (process.env.NODE_ENV !== 'production') {
|
---|
| 173 | var manualPropTypeCallCache = {};
|
---|
| 174 | var manualPropTypeWarningCount = 0;
|
---|
| 175 | }
|
---|
| 176 | function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
|
---|
| 177 | componentName = componentName || ANONYMOUS;
|
---|
| 178 | propFullName = propFullName || propName;
|
---|
| 179 |
|
---|
| 180 | if (secret !== ReactPropTypesSecret) {
|
---|
| 181 | if (throwOnDirectAccess) {
|
---|
| 182 | // New behavior only for users of `prop-types` package
|
---|
| 183 | var err = new Error(
|
---|
| 184 | 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +
|
---|
| 185 | 'Use `PropTypes.checkPropTypes()` to call them. ' +
|
---|
| 186 | 'Read more at http://fb.me/use-check-prop-types'
|
---|
| 187 | );
|
---|
| 188 | err.name = 'Invariant Violation';
|
---|
| 189 | throw err;
|
---|
| 190 | } else if (process.env.NODE_ENV !== 'production' && typeof console !== 'undefined') {
|
---|
| 191 | // Old behavior for people using React.PropTypes
|
---|
| 192 | var cacheKey = componentName + ':' + propName;
|
---|
| 193 | if (
|
---|
| 194 | !manualPropTypeCallCache[cacheKey] &&
|
---|
| 195 | // Avoid spamming the console because they are often not actionable except for lib authors
|
---|
| 196 | manualPropTypeWarningCount < 3
|
---|
| 197 | ) {
|
---|
| 198 | printWarning(
|
---|
| 199 | 'You are manually calling a React.PropTypes validation ' +
|
---|
| 200 | 'function for the `' + propFullName + '` prop on `' + componentName + '`. This is deprecated ' +
|
---|
| 201 | 'and will throw in the standalone `prop-types` package. ' +
|
---|
| 202 | 'You may be seeing this warning due to a third-party PropTypes ' +
|
---|
| 203 | 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.'
|
---|
| 204 | );
|
---|
| 205 | manualPropTypeCallCache[cacheKey] = true;
|
---|
| 206 | manualPropTypeWarningCount++;
|
---|
| 207 | }
|
---|
| 208 | }
|
---|
| 209 | }
|
---|
| 210 | if (props[propName] == null) {
|
---|
| 211 | if (isRequired) {
|
---|
| 212 | if (props[propName] === null) {
|
---|
| 213 | return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));
|
---|
| 214 | }
|
---|
| 215 | return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));
|
---|
| 216 | }
|
---|
| 217 | return null;
|
---|
| 218 | } else {
|
---|
| 219 | return validate(props, propName, componentName, location, propFullName);
|
---|
| 220 | }
|
---|
| 221 | }
|
---|
| 222 |
|
---|
| 223 | var chainedCheckType = checkType.bind(null, false);
|
---|
| 224 | chainedCheckType.isRequired = checkType.bind(null, true);
|
---|
| 225 |
|
---|
| 226 | return chainedCheckType;
|
---|
| 227 | }
|
---|
| 228 |
|
---|
| 229 | function createPrimitiveTypeChecker(expectedType) {
|
---|
| 230 | function validate(props, propName, componentName, location, propFullName, secret) {
|
---|
| 231 | var propValue = props[propName];
|
---|
| 232 | var propType = getPropType(propValue);
|
---|
| 233 | if (propType !== expectedType) {
|
---|
| 234 | // `propValue` being instance of, say, date/regexp, pass the 'object'
|
---|
| 235 | // check, but we can offer a more precise error message here rather than
|
---|
| 236 | // 'of type `object`'.
|
---|
| 237 | var preciseType = getPreciseType(propValue);
|
---|
| 238 |
|
---|
| 239 | return new PropTypeError(
|
---|
| 240 | 'Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'),
|
---|
| 241 | {expectedType: expectedType}
|
---|
| 242 | );
|
---|
| 243 | }
|
---|
| 244 | return null;
|
---|
| 245 | }
|
---|
| 246 | return createChainableTypeChecker(validate);
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | function createAnyTypeChecker() {
|
---|
| 250 | return createChainableTypeChecker(emptyFunctionThatReturnsNull);
|
---|
| 251 | }
|
---|
| 252 |
|
---|
| 253 | function createArrayOfTypeChecker(typeChecker) {
|
---|
| 254 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 255 | if (typeof typeChecker !== 'function') {
|
---|
| 256 | return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.');
|
---|
| 257 | }
|
---|
| 258 | var propValue = props[propName];
|
---|
| 259 | if (!Array.isArray(propValue)) {
|
---|
| 260 | var propType = getPropType(propValue);
|
---|
| 261 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));
|
---|
| 262 | }
|
---|
| 263 | for (var i = 0; i < propValue.length; i++) {
|
---|
| 264 | var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret);
|
---|
| 265 | if (error instanceof Error) {
|
---|
| 266 | return error;
|
---|
| 267 | }
|
---|
| 268 | }
|
---|
| 269 | return null;
|
---|
| 270 | }
|
---|
| 271 | return createChainableTypeChecker(validate);
|
---|
| 272 | }
|
---|
| 273 |
|
---|
| 274 | function createElementTypeChecker() {
|
---|
| 275 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 276 | var propValue = props[propName];
|
---|
| 277 | if (!isValidElement(propValue)) {
|
---|
| 278 | var propType = getPropType(propValue);
|
---|
| 279 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.'));
|
---|
| 280 | }
|
---|
| 281 | return null;
|
---|
| 282 | }
|
---|
| 283 | return createChainableTypeChecker(validate);
|
---|
| 284 | }
|
---|
| 285 |
|
---|
| 286 | function createElementTypeTypeChecker() {
|
---|
| 287 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 288 | var propValue = props[propName];
|
---|
| 289 | if (!ReactIs.isValidElementType(propValue)) {
|
---|
| 290 | var propType = getPropType(propValue);
|
---|
| 291 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement type.'));
|
---|
| 292 | }
|
---|
| 293 | return null;
|
---|
| 294 | }
|
---|
| 295 | return createChainableTypeChecker(validate);
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | function createInstanceTypeChecker(expectedClass) {
|
---|
| 299 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 300 | if (!(props[propName] instanceof expectedClass)) {
|
---|
| 301 | var expectedClassName = expectedClass.name || ANONYMOUS;
|
---|
| 302 | var actualClassName = getClassName(props[propName]);
|
---|
| 303 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));
|
---|
| 304 | }
|
---|
| 305 | return null;
|
---|
| 306 | }
|
---|
| 307 | return createChainableTypeChecker(validate);
|
---|
| 308 | }
|
---|
| 309 |
|
---|
| 310 | function createEnumTypeChecker(expectedValues) {
|
---|
| 311 | if (!Array.isArray(expectedValues)) {
|
---|
| 312 | if (process.env.NODE_ENV !== 'production') {
|
---|
| 313 | if (arguments.length > 1) {
|
---|
| 314 | printWarning(
|
---|
| 315 | 'Invalid arguments supplied to oneOf, expected an array, got ' + arguments.length + ' arguments. ' +
|
---|
| 316 | 'A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).'
|
---|
| 317 | );
|
---|
| 318 | } else {
|
---|
| 319 | printWarning('Invalid argument supplied to oneOf, expected an array.');
|
---|
| 320 | }
|
---|
| 321 | }
|
---|
| 322 | return emptyFunctionThatReturnsNull;
|
---|
| 323 | }
|
---|
| 324 |
|
---|
| 325 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 326 | var propValue = props[propName];
|
---|
| 327 | for (var i = 0; i < expectedValues.length; i++) {
|
---|
| 328 | if (is(propValue, expectedValues[i])) {
|
---|
| 329 | return null;
|
---|
| 330 | }
|
---|
| 331 | }
|
---|
| 332 |
|
---|
| 333 | var valuesString = JSON.stringify(expectedValues, function replacer(key, value) {
|
---|
| 334 | var type = getPreciseType(value);
|
---|
| 335 | if (type === 'symbol') {
|
---|
| 336 | return String(value);
|
---|
| 337 | }
|
---|
| 338 | return value;
|
---|
| 339 | });
|
---|
| 340 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + String(propValue) + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));
|
---|
| 341 | }
|
---|
| 342 | return createChainableTypeChecker(validate);
|
---|
| 343 | }
|
---|
| 344 |
|
---|
| 345 | function createObjectOfTypeChecker(typeChecker) {
|
---|
| 346 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 347 | if (typeof typeChecker !== 'function') {
|
---|
| 348 | return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.');
|
---|
| 349 | }
|
---|
| 350 | var propValue = props[propName];
|
---|
| 351 | var propType = getPropType(propValue);
|
---|
| 352 | if (propType !== 'object') {
|
---|
| 353 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));
|
---|
| 354 | }
|
---|
| 355 | for (var key in propValue) {
|
---|
| 356 | if (has(propValue, key)) {
|
---|
| 357 | var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
|
---|
| 358 | if (error instanceof Error) {
|
---|
| 359 | return error;
|
---|
| 360 | }
|
---|
| 361 | }
|
---|
| 362 | }
|
---|
| 363 | return null;
|
---|
| 364 | }
|
---|
| 365 | return createChainableTypeChecker(validate);
|
---|
| 366 | }
|
---|
| 367 |
|
---|
| 368 | function createUnionTypeChecker(arrayOfTypeCheckers) {
|
---|
| 369 | if (!Array.isArray(arrayOfTypeCheckers)) {
|
---|
| 370 | process.env.NODE_ENV !== 'production' ? printWarning('Invalid argument supplied to oneOfType, expected an instance of array.') : void 0;
|
---|
| 371 | return emptyFunctionThatReturnsNull;
|
---|
| 372 | }
|
---|
| 373 |
|
---|
| 374 | for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
|
---|
| 375 | var checker = arrayOfTypeCheckers[i];
|
---|
| 376 | if (typeof checker !== 'function') {
|
---|
| 377 | printWarning(
|
---|
| 378 | 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' +
|
---|
| 379 | 'received ' + getPostfixForTypeWarning(checker) + ' at index ' + i + '.'
|
---|
| 380 | );
|
---|
| 381 | return emptyFunctionThatReturnsNull;
|
---|
| 382 | }
|
---|
| 383 | }
|
---|
| 384 |
|
---|
| 385 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 386 | var expectedTypes = [];
|
---|
| 387 | for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
|
---|
| 388 | var checker = arrayOfTypeCheckers[i];
|
---|
| 389 | var checkerResult = checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret);
|
---|
| 390 | if (checkerResult == null) {
|
---|
| 391 | return null;
|
---|
| 392 | }
|
---|
| 393 | if (checkerResult.data && has(checkerResult.data, 'expectedType')) {
|
---|
| 394 | expectedTypes.push(checkerResult.data.expectedType);
|
---|
| 395 | }
|
---|
| 396 | }
|
---|
| 397 | var expectedTypesMessage = (expectedTypes.length > 0) ? ', expected one of type [' + expectedTypes.join(', ') + ']': '';
|
---|
| 398 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`' + expectedTypesMessage + '.'));
|
---|
| 399 | }
|
---|
| 400 | return createChainableTypeChecker(validate);
|
---|
| 401 | }
|
---|
| 402 |
|
---|
| 403 | function createNodeChecker() {
|
---|
| 404 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 405 | if (!isNode(props[propName])) {
|
---|
| 406 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));
|
---|
| 407 | }
|
---|
| 408 | return null;
|
---|
| 409 | }
|
---|
| 410 | return createChainableTypeChecker(validate);
|
---|
| 411 | }
|
---|
| 412 |
|
---|
| 413 | function invalidValidatorError(componentName, location, propFullName, key, type) {
|
---|
| 414 | return new PropTypeError(
|
---|
| 415 | (componentName || 'React class') + ': ' + location + ' type `' + propFullName + '.' + key + '` is invalid; ' +
|
---|
| 416 | 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.'
|
---|
| 417 | );
|
---|
| 418 | }
|
---|
| 419 |
|
---|
| 420 | function createShapeTypeChecker(shapeTypes) {
|
---|
| 421 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 422 | var propValue = props[propName];
|
---|
| 423 | var propType = getPropType(propValue);
|
---|
| 424 | if (propType !== 'object') {
|
---|
| 425 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
|
---|
| 426 | }
|
---|
| 427 | for (var key in shapeTypes) {
|
---|
| 428 | var checker = shapeTypes[key];
|
---|
| 429 | if (typeof checker !== 'function') {
|
---|
| 430 | return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
|
---|
| 431 | }
|
---|
| 432 | var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
|
---|
| 433 | if (error) {
|
---|
| 434 | return error;
|
---|
| 435 | }
|
---|
| 436 | }
|
---|
| 437 | return null;
|
---|
| 438 | }
|
---|
| 439 | return createChainableTypeChecker(validate);
|
---|
| 440 | }
|
---|
| 441 |
|
---|
| 442 | function createStrictShapeTypeChecker(shapeTypes) {
|
---|
| 443 | function validate(props, propName, componentName, location, propFullName) {
|
---|
| 444 | var propValue = props[propName];
|
---|
| 445 | var propType = getPropType(propValue);
|
---|
| 446 | if (propType !== 'object') {
|
---|
| 447 | return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
|
---|
| 448 | }
|
---|
| 449 | // We need to check all keys in case some are required but missing from props.
|
---|
| 450 | var allKeys = assign({}, props[propName], shapeTypes);
|
---|
| 451 | for (var key in allKeys) {
|
---|
| 452 | var checker = shapeTypes[key];
|
---|
| 453 | if (has(shapeTypes, key) && typeof checker !== 'function') {
|
---|
| 454 | return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
|
---|
| 455 | }
|
---|
| 456 | if (!checker) {
|
---|
| 457 | return new PropTypeError(
|
---|
| 458 | 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' +
|
---|
| 459 | '\nBad object: ' + JSON.stringify(props[propName], null, ' ') +
|
---|
| 460 | '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ')
|
---|
| 461 | );
|
---|
| 462 | }
|
---|
| 463 | var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
|
---|
| 464 | if (error) {
|
---|
| 465 | return error;
|
---|
| 466 | }
|
---|
| 467 | }
|
---|
| 468 | return null;
|
---|
| 469 | }
|
---|
| 470 |
|
---|
| 471 | return createChainableTypeChecker(validate);
|
---|
| 472 | }
|
---|
| 473 |
|
---|
| 474 | function isNode(propValue) {
|
---|
| 475 | switch (typeof propValue) {
|
---|
| 476 | case 'number':
|
---|
| 477 | case 'string':
|
---|
| 478 | case 'undefined':
|
---|
| 479 | return true;
|
---|
| 480 | case 'boolean':
|
---|
| 481 | return !propValue;
|
---|
| 482 | case 'object':
|
---|
| 483 | if (Array.isArray(propValue)) {
|
---|
| 484 | return propValue.every(isNode);
|
---|
| 485 | }
|
---|
| 486 | if (propValue === null || isValidElement(propValue)) {
|
---|
| 487 | return true;
|
---|
| 488 | }
|
---|
| 489 |
|
---|
| 490 | var iteratorFn = getIteratorFn(propValue);
|
---|
| 491 | if (iteratorFn) {
|
---|
| 492 | var iterator = iteratorFn.call(propValue);
|
---|
| 493 | var step;
|
---|
| 494 | if (iteratorFn !== propValue.entries) {
|
---|
| 495 | while (!(step = iterator.next()).done) {
|
---|
| 496 | if (!isNode(step.value)) {
|
---|
| 497 | return false;
|
---|
| 498 | }
|
---|
| 499 | }
|
---|
| 500 | } else {
|
---|
| 501 | // Iterator will provide entry [k,v] tuples rather than values.
|
---|
| 502 | while (!(step = iterator.next()).done) {
|
---|
| 503 | var entry = step.value;
|
---|
| 504 | if (entry) {
|
---|
| 505 | if (!isNode(entry[1])) {
|
---|
| 506 | return false;
|
---|
| 507 | }
|
---|
| 508 | }
|
---|
| 509 | }
|
---|
| 510 | }
|
---|
| 511 | } else {
|
---|
| 512 | return false;
|
---|
| 513 | }
|
---|
| 514 |
|
---|
| 515 | return true;
|
---|
| 516 | default:
|
---|
| 517 | return false;
|
---|
| 518 | }
|
---|
| 519 | }
|
---|
| 520 |
|
---|
| 521 | function isSymbol(propType, propValue) {
|
---|
| 522 | // Native Symbol.
|
---|
| 523 | if (propType === 'symbol') {
|
---|
| 524 | return true;
|
---|
| 525 | }
|
---|
| 526 |
|
---|
| 527 | // falsy value can't be a Symbol
|
---|
| 528 | if (!propValue) {
|
---|
| 529 | return false;
|
---|
| 530 | }
|
---|
| 531 |
|
---|
| 532 | // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol'
|
---|
| 533 | if (propValue['@@toStringTag'] === 'Symbol') {
|
---|
| 534 | return true;
|
---|
| 535 | }
|
---|
| 536 |
|
---|
| 537 | // Fallback for non-spec compliant Symbols which are polyfilled.
|
---|
| 538 | if (typeof Symbol === 'function' && propValue instanceof Symbol) {
|
---|
| 539 | return true;
|
---|
| 540 | }
|
---|
| 541 |
|
---|
| 542 | return false;
|
---|
| 543 | }
|
---|
| 544 |
|
---|
| 545 | // Equivalent of `typeof` but with special handling for array and regexp.
|
---|
| 546 | function getPropType(propValue) {
|
---|
| 547 | var propType = typeof propValue;
|
---|
| 548 | if (Array.isArray(propValue)) {
|
---|
| 549 | return 'array';
|
---|
| 550 | }
|
---|
| 551 | if (propValue instanceof RegExp) {
|
---|
| 552 | // Old webkits (at least until Android 4.0) return 'function' rather than
|
---|
| 553 | // 'object' for typeof a RegExp. We'll normalize this here so that /bla/
|
---|
| 554 | // passes PropTypes.object.
|
---|
| 555 | return 'object';
|
---|
| 556 | }
|
---|
| 557 | if (isSymbol(propType, propValue)) {
|
---|
| 558 | return 'symbol';
|
---|
| 559 | }
|
---|
| 560 | return propType;
|
---|
| 561 | }
|
---|
| 562 |
|
---|
| 563 | // This handles more types than `getPropType`. Only used for error messages.
|
---|
| 564 | // See `createPrimitiveTypeChecker`.
|
---|
| 565 | function getPreciseType(propValue) {
|
---|
| 566 | if (typeof propValue === 'undefined' || propValue === null) {
|
---|
| 567 | return '' + propValue;
|
---|
| 568 | }
|
---|
| 569 | var propType = getPropType(propValue);
|
---|
| 570 | if (propType === 'object') {
|
---|
| 571 | if (propValue instanceof Date) {
|
---|
| 572 | return 'date';
|
---|
| 573 | } else if (propValue instanceof RegExp) {
|
---|
| 574 | return 'regexp';
|
---|
| 575 | }
|
---|
| 576 | }
|
---|
| 577 | return propType;
|
---|
| 578 | }
|
---|
| 579 |
|
---|
| 580 | // Returns a string that is postfixed to a warning about an invalid type.
|
---|
| 581 | // For example, "undefined" or "of type array"
|
---|
| 582 | function getPostfixForTypeWarning(value) {
|
---|
| 583 | var type = getPreciseType(value);
|
---|
| 584 | switch (type) {
|
---|
| 585 | case 'array':
|
---|
| 586 | case 'object':
|
---|
| 587 | return 'an ' + type;
|
---|
| 588 | case 'boolean':
|
---|
| 589 | case 'date':
|
---|
| 590 | case 'regexp':
|
---|
| 591 | return 'a ' + type;
|
---|
| 592 | default:
|
---|
| 593 | return type;
|
---|
| 594 | }
|
---|
| 595 | }
|
---|
| 596 |
|
---|
| 597 | // Returns class name of the object, if any.
|
---|
| 598 | function getClassName(propValue) {
|
---|
| 599 | if (!propValue.constructor || !propValue.constructor.name) {
|
---|
| 600 | return ANONYMOUS;
|
---|
| 601 | }
|
---|
| 602 | return propValue.constructor.name;
|
---|
| 603 | }
|
---|
| 604 |
|
---|
| 605 | ReactPropTypes.checkPropTypes = checkPropTypes;
|
---|
| 606 | ReactPropTypes.resetWarningCache = checkPropTypes.resetWarningCache;
|
---|
| 607 | ReactPropTypes.PropTypes = ReactPropTypes;
|
---|
| 608 |
|
---|
| 609 | return ReactPropTypes;
|
---|
| 610 | };
|
---|