source: node_modules/prop-types/factoryWithTypeCheckers.js@ 47f4eaf

Last change on this file since 47f4eaf was 47f4eaf, checked in by Marko <Marko@…>, 20 months ago

Final features implemented

  • Property mode set to 100644
File size: 22.2 KB
Line 
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
10var ReactIs = require('react-is');
11var assign = require('object-assign');
12
13var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
14var has = require('./lib/has');
15var checkPropTypes = require('./checkPropTypes');
16
17var printWarning = function() {};
18
19if (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
34function emptyFunctionThatReturnsNull() {
35 return null;
36}
37
38module.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};
Note: See TracBrowser for help on using the repository browser.