Merge pull request #9004 from acdlite/proptypescheck

API for checking external objects against React prop types
This commit is contained in:
Andrew Clark
2017-02-23 14:37:19 -08:00
committed by GitHub
16 changed files with 312 additions and 277 deletions
+2
View File
@@ -130,7 +130,9 @@ var rendererSharedState = {
React: 'react/lib/React',
// Shared state
ReactCurrentOwner: 'react/lib/ReactCurrentOwner',
checkPropTypes: 'react/lib/checkPropTypes',
ReactComponentTreeHook: 'react/lib/ReactComponentTreeHook',
ReactDebugCurrentFrame: 'react/lib/ReactDebugCurrentFrame',
};
var moduleMapReactDOM = Object.assign(
+2
View File
@@ -307,6 +307,8 @@ src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
* does not blow up on key warning with undefined type
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
* does not return a value from a validator
* does not throw if validator throws
* should warn for invalid strings
* should fail date and regexp correctly
* should not warn for valid values
+3
View File
@@ -21,6 +21,7 @@ var ReactVersion = require('ReactVersion');
var onlyChild = require('onlyChild');
var warning = require('warning');
var checkPropTypes = require('checkPropTypes');
var createElement = ReactElement.createElement;
var createFactory = ReactElement.createFactory;
@@ -71,6 +72,8 @@ var React = {
cloneElement: cloneElement,
isValidElement: ReactElement.isValidElement,
checkPropTypes: checkPropTypes,
// Classic
PropTypes: ReactPropTypes,
@@ -261,7 +261,7 @@ describe('ReactContextValidator', () => {
ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />);
expectDev(console.error.calls.count()).toBe(1);
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Failed childContext type: ' +
'Warning: Failed child context type: ' +
'The child context `foo` is marked as required in `Component`, but its ' +
'value is `undefined`.\n' +
' in Component (at **)'
@@ -271,7 +271,7 @@ describe('ReactContextValidator', () => {
expectDev(console.error.calls.count()).toBe(2);
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
'Warning: Failed childContext type: ' +
'Warning: Failed child context type: ' +
'Invalid child context `foo` of type `number` ' +
'supplied to `Component`, expected `string`.\n' +
' in Component (at **)'
+3 -6
View File
@@ -13,7 +13,6 @@
var ReactBaseClasses = require('ReactBaseClasses');
var ReactElement = require('ReactElement');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
var emptyObject = require('emptyObject');
@@ -22,8 +21,6 @@ var warning = require('warning');
var ReactComponent = ReactBaseClasses.Component;
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
var MIXINS_KEY = 'mixins';
// Helper function to allow the creation of anonymous functions which do not
@@ -330,7 +327,7 @@ var RESERVED_SPEC_KEYS = {
validateTypeDef(
Constructor,
childContextTypes,
'childContext'
'child context'
);
}
Constructor.childContextTypes = Object.assign(
@@ -390,7 +387,7 @@ var RESERVED_SPEC_KEYS = {
function validateTypeDef(
Constructor,
typeDef,
location: ReactPropTypeLocations,
location: string,
) {
for (var propName in typeDef) {
if (typeDef.hasOwnProperty(propName)) {
@@ -401,7 +398,7 @@ function validateTypeDef(
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
Constructor.displayName || 'ReactClass',
ReactPropTypeLocationNames[location],
location,
propName
);
}
@@ -0,0 +1,56 @@
/**
* Copyright 2013-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.
*
* @providesModule ReactDebugCurrentFrame
* @flow
*/
'use strict';
import type { Fiber } from 'ReactFiber';
import type { DebugID } from 'ReactInstanceType';
const ReactDebugCurrentFrame = {};
if (__DEV__) {
var {
getStackAddendumByID,
getStackAddendumByWorkInProgressFiber,
getCurrentStackAddendum,
} = require('ReactComponentTreeHook');
// Component that is being worked on
ReactDebugCurrentFrame.current = (null : Fiber | DebugID | null);
// Element that is being cloned or created
ReactDebugCurrentFrame.element = (null : *);
ReactDebugCurrentFrame.getStackAddendum = function() : string | null {
let stack = null;
const current = ReactDebugCurrentFrame.current;
const element = ReactDebugCurrentFrame.element;
if (current !== null) {
if (typeof current === 'number') {
// DebugID from Stack.
const debugID = current;
stack = getStackAddendumByID(debugID);
} else if (typeof current.tag === 'number') {
// This is a Fiber.
// The stack will only be correct if this is a work in progress
// version and we're calling it during reconciliation.
const workInProgress = current;
stack = getStackAddendumByWorkInProgressFiber(workInProgress);
}
} else if (element !== null) {
stack = getCurrentStackAddendum(element);
}
return stack;
};
}
module.exports = ReactDebugCurrentFrame;
@@ -27,7 +27,11 @@ var checkReactTypeSpec = require('checkReactTypeSpec');
var canDefineProperty = require('canDefineProperty');
var getComponentName = require('getComponentName');
var getIteratorFn = require('getIteratorFn');
var warning = require('warning');
if (__DEV__) {
var warning = require('warning');
var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
}
function getDeclarationErrorAddendum() {
if (ReactCurrentOwner.current) {
@@ -181,9 +185,7 @@ function validatePropTypes(element) {
componentClass.propTypes,
element.props,
'prop',
name,
element,
null
name
);
}
if (typeof componentClass.getDefaultProps === 'function') {
@@ -243,6 +245,10 @@ var ReactElementValidator = {
return element;
}
if (__DEV__) {
ReactDebugCurrentFrame.element = 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.
@@ -256,6 +262,10 @@ var ReactElementValidator = {
validatePropTypes(element);
if (__DEV__) {
ReactDebugCurrentFrame.element = null;
}
return element;
},
@@ -296,10 +306,16 @@ var ReactElementValidator = {
cloneElement: function(element, props, children) {
var newElement = ReactElement.cloneElement.apply(this, arguments);
if (__DEV__) {
ReactDebugCurrentFrame.element = newElement;
}
for (var i = 2; i < arguments.length; i++) {
validateChildKeys(arguments[i], newElement.type);
}
validatePropTypes(newElement);
if (__DEV__) {
ReactDebugCurrentFrame.element = null;
}
return newElement;
},
+12 -22
View File
@@ -12,7 +12,6 @@
'use strict';
var ReactElement = require('ReactElement');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactPropTypesSecret = require('ReactPropTypesSecret');
var emptyFunction = require('emptyFunction');
@@ -123,6 +122,7 @@ if (__DEV__) {
};
}
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
@@ -192,16 +192,15 @@ function createChainableTypeChecker(validate) {
}
}
if (props[propName] == null) {
var locationName = ReactPropTypeLocationNames[location];
if (isRequired) {
if (props[propName] === null) {
return new PropTypeError(
`The ${locationName} \`${propFullName}\` is marked as required ` +
`The ${location} \`${propFullName}\` is marked as required ` +
`in \`${componentName}\`, but its value is \`null\`.`
);
}
return new PropTypeError(
`The ${locationName} \`${propFullName}\` is marked as required in ` +
`The ${location} \`${propFullName}\` is marked as required in ` +
`\`${componentName}\`, but its value is \`undefined\`.`
);
}
@@ -235,14 +234,13 @@ function createPrimitiveTypeChecker(expectedType) {
var propValue = props[propName];
var propType = getPropType(propValue);
if (propType !== expectedType) {
var locationName = ReactPropTypeLocationNames[location];
// `propValue` being instance of, say, date/regexp, pass the 'object'
// check, but we can offer a more precise error message here rather than
// 'of type `object`'.
var preciseType = getPreciseType(propValue);
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` of type ` +
`Invalid ${location} \`${propFullName}\` of type ` +
`\`${preciseType}\` supplied to \`${componentName}\`, expected ` +
`\`${expectedType}\`.`
);
@@ -265,10 +263,9 @@ function createArrayOfTypeChecker(typeChecker) {
}
var propValue = props[propName];
if (!Array.isArray(propValue)) {
var locationName = ReactPropTypeLocationNames[location];
var propType = getPropType(propValue);
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` of type ` +
`Invalid ${location} \`${propFullName}\` of type ` +
`\`${propType}\` supplied to \`${componentName}\`, expected an array.`
);
}
@@ -294,10 +291,9 @@ function createElementTypeChecker() {
function validate(props, propName, componentName, location, propFullName) {
var propValue = props[propName];
if (!ReactElement.isValidElement(propValue)) {
var locationName = ReactPropTypeLocationNames[location];
var propType = getPropType(propValue);
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` of type ` +
`Invalid ${location} \`${propFullName}\` of type ` +
`\`${propType}\` supplied to \`${componentName}\`, expected a single ReactElement.`
);
}
@@ -309,11 +305,10 @@ function createElementTypeChecker() {
function createInstanceTypeChecker(expectedClass) {
function validate(props, propName, componentName, location, propFullName) {
if (!(props[propName] instanceof expectedClass)) {
var locationName = ReactPropTypeLocationNames[location];
var expectedClassName = expectedClass.name || ANONYMOUS;
var actualClassName = getClassName(props[propName]);
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` of type ` +
`Invalid ${location} \`${propFullName}\` of type ` +
`\`${actualClassName}\` supplied to \`${componentName}\`, expected ` +
`instance of \`${expectedClassName}\`.`
);
@@ -337,10 +332,9 @@ function createEnumTypeChecker(expectedValues) {
}
}
var locationName = ReactPropTypeLocationNames[location];
var valuesString = JSON.stringify(expectedValues);
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` of value \`${propValue}\` ` +
`Invalid ${location} \`${propFullName}\` of value \`${propValue}\` ` +
`supplied to \`${componentName}\`, expected one of ${valuesString}.`
);
}
@@ -357,9 +351,8 @@ function createObjectOfTypeChecker(typeChecker) {
var propValue = props[propName];
var propType = getPropType(propValue);
if (propType !== 'object') {
var locationName = ReactPropTypeLocationNames[location];
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` of type ` +
`Invalid ${location} \`${propFullName}\` of type ` +
`\`${propType}\` supplied to \`${componentName}\`, expected an object.`
);
}
@@ -406,9 +399,8 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
}
}
var locationName = ReactPropTypeLocationNames[location];
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` supplied to ` +
`Invalid ${location} \`${propFullName}\` supplied to ` +
`\`${componentName}\`.`
);
}
@@ -418,9 +410,8 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
function createNodeChecker() {
function validate(props, propName, componentName, location, propFullName) {
if (!isNode(props[propName])) {
var locationName = ReactPropTypeLocationNames[location];
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` supplied to ` +
`Invalid ${location} \`${propFullName}\` supplied to ` +
`\`${componentName}\`, expected a ReactNode.`
);
}
@@ -434,9 +425,8 @@ function createShapeTypeChecker(shapeTypes) {
var propValue = props[propName];
var propType = getPropType(propValue);
if (propType !== 'object') {
var locationName = ReactPropTypeLocationNames[location];
return new PropTypeError(
`Invalid ${locationName} \`${propFullName}\` of type \`${propType}\` ` +
`Invalid ${location} \`${propFullName}\` of type \`${propType}\` ` +
`supplied to \`${componentName}\`, expected \`object\`.`
);
}
@@ -12,26 +12,48 @@
'use strict';
var PropTypes;
var checkPropTypes;
var checkReactTypeSpec;
var React;
var ReactDOM;
var ReactFragment;
var ReactTestUtils;
var ReactPropTypesSecret;
var Component;
var MyComponent;
function typeCheckFail(declaration, value, message) {
var props = {testProp: value};
var error = declaration(
props,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error instanceof Error).toBe(true);
expect(error.message).toBe(message);
function resetWarningCache() {
jest.resetModules();
checkReactTypeSpec = require('checkReactTypeSpec');
checkPropTypes = require('checkPropTypes');
}
function getPropTypeWarningMessage(propTypes, object, componentName) {
if (!console.error.calls) {
spyOn(console, 'error');
} else {
console.error.calls.reset();
}
resetWarningCache();
checkReactTypeSpec(propTypes, object, 'prop', 'testComponent');
const callCount = console.error.calls.count();
if (callCount > 1) {
throw new Error('Too many warnings.');
}
const message = console.error.calls.argsFor(0)[0] || null;
console.error.calls.reset();
return message;
}
function typeCheckFail(declaration, value, expectedMessage) {
const propTypes = {
testProp: declaration,
};
const props = {
testProp: value,
};
const message = getPropTypeWarningMessage(propTypes, props, 'testComponent');
expect(message).toContain(expectedMessage);
}
function typeCheckFailRequiredValues(declaration) {
@@ -39,52 +61,31 @@ function typeCheckFailRequiredValues(declaration) {
'`testComponent`, but its value is `null`.';
var unspecifiedMsg = 'The prop `testProp` is marked as required in ' +
'`testComponent`, but its value is \`undefined\`.';
var props1 = {testProp: null};
var error1 = declaration(
props1,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error1 instanceof Error).toBe(true);
expect(error1.message).toBe(specifiedButIsNullMsg);
var props2 = {testProp: undefined};
var error2 = declaration(
props2,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error2 instanceof Error).toBe(true);
expect(error2.message).toBe(unspecifiedMsg);
var props3 = {};
var error3 = declaration(
props3,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error3 instanceof Error).toBe(true);
expect(error3.message).toBe(unspecifiedMsg);
var propTypes = { testProp: declaration };
// Required prop is null
var message1 = getPropTypeWarningMessage(propTypes, { testProp: null }, 'testComponent');
expect(message1).toContain(specifiedButIsNullMsg);
// Required prop is undefined
var message2 = getPropTypeWarningMessage(propTypes, { testProp: undefined }, 'testComponent');
expect(message2).toContain(unspecifiedMsg);
// Required prop is not a member of props object
var message3 = getPropTypeWarningMessage(propTypes, {}, 'testComponent');
expect(message3).toContain(unspecifiedMsg);
}
function typeCheckPass(declaration, value) {
var props = {testProp: value};
var error = declaration(
props,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error).toBe(null);
const propTypes = {
testProp: declaration,
};
const props = {
testProp: value,
};
const message = getPropTypeWarningMessage(propTypes, props, 'testComponent');
expect(message).toBe(null);
}
function expectWarningInDevelopment(declaration, value) {
@@ -110,9 +111,37 @@ describe('ReactPropTypes', () => {
beforeEach(() => {
PropTypes = require('ReactPropTypes');
React = require('React');
ReactDOM = require('ReactDOM');
ReactFragment = require('ReactFragment');
ReactTestUtils = require('ReactTestUtils');
ReactPropTypesSecret = require('ReactPropTypesSecret');
resetWarningCache();
});
describe('checkPropTypes', () => {
it('does not return a value from a validator', () => {
spyOn(console, 'error');
const propTypes = {
foo(props, propName, componentName) {
return new Error('some error');
},
};
const props = { foo: 'foo' };
const returnValue = checkPropTypes(propTypes, props, 'prop', 'testComponent', null);
expect(console.error.calls.argsFor(0)[0]).toContain('some error');
expect(returnValue).toBe(undefined);
});
it('does not throw if validator throws', () => {
spyOn(console, 'error');
const propTypes = {
foo(props, propName, componentName) {
throw new Error('some error');
},
};
const props = { foo: 'foo' };
const returnValue = checkPropTypes(propTypes, props, 'prop', 'testComponent', null);
expect(console.error.calls.argsFor(0)[0]).toContain('some error');
expect(returnValue).toBe(undefined);
});
});
describe('Primitive Types', () => {
@@ -409,8 +438,8 @@ describe('ReactPropTypes', () => {
it('should be able to define a single child as label', () => {
spyOn(console, 'error');
var instance = <Component label={<div />} />;
ReactTestUtils.renderIntoDocument(instance);
var container = document.createElement('div');
ReactDOM.render(<Component label={<div />} />, container);
expectDev(console.error.calls.count()).toBe(0);
});
@@ -418,8 +447,8 @@ describe('ReactPropTypes', () => {
it('should warn when passing no label and isRequired is set', () => {
spyOn(console, 'error');
var instance = <Component />;
ReactTestUtils.renderIntoDocument(instance);
var container = document.createElement('div');
ReactDOM.render(<Component />, container);
expectDev(console.error.calls.count()).toBe(1);
});
@@ -1037,8 +1066,8 @@ describe('ReactPropTypes', () => {
}
};
var instance = <Component num={5} />;
ReactTestUtils.renderIntoDocument(instance);
var container = document.createElement('div');
ReactDOM.render(<Component num={5} />, container);
expect(spy.calls.count()).toBe(1);
expect(spy.calls.argsFor(0)[1]).toBe('num');
@@ -1054,8 +1083,8 @@ describe('ReactPropTypes', () => {
}
};
var instance = <Component bla={5} />;
ReactTestUtils.renderIntoDocument(instance);
var container = document.createElement('div');
ReactDOM.render(<Component bla={5} />, container);
expect(spy.calls.count()).toBe(1);
expect(spy.calls.argsFor(0)[1]).toBe('num');
@@ -1078,8 +1107,8 @@ describe('ReactPropTypes', () => {
}
};
var instance = <Component num={6} />;
ReactTestUtils.renderIntoDocument(instance);
var container = document.createElement('div');
ReactDOM.render(<Component num={6} />, container);
expectDev(console.error.calls.count()).toBe(1);
expect(
console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
@@ -1105,8 +1134,8 @@ describe('ReactPropTypes', () => {
}
};
var instance = <Component num={5} />;
ReactTestUtils.renderIntoDocument(instance);
var container = document.createElement('div');
ReactDOM.render(<Component num={5} />, container);
expectDev(console.error.calls.count()).toBe(0);
}
);
@@ -14,7 +14,6 @@
describe('ReactPropTypesProduction', function() {
var PropTypes;
var React;
var ReactPropTypeLocations;
var ReactTestUtils;
var oldProcess;
@@ -32,7 +31,6 @@ describe('ReactPropTypesProduction', function() {
jest.resetModules();
PropTypes = require('ReactPropTypes');
React = require('React');
ReactPropTypeLocations = require('ReactPropTypeLocations');
ReactTestUtils = require('ReactTestUtils');
});
@@ -48,7 +46,7 @@ describe('ReactPropTypesProduction', function() {
props,
'testProp',
'testComponent',
ReactPropTypeLocations.prop
'prop'
);
}).toThrowError(
'React.PropTypes type checking code is stripped in production.'
@@ -0,0 +1,87 @@
/**
* Copyright 2013-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.
*
* @providesModule checkPropTypes
*/
'use strict';
var ReactPropTypesSecret = require('ReactPropTypesSecret');
var invariant = require('invariant');
var warning = require('warning');
var loggedTypeFailures = {};
/**
* Assert that the values match with the type specs.
* Error messages are memorized and will only be shown once.
*
* @param {object} typeSpecs Map of name to a ReactPropType
* @param {object} values Runtime values that need to be type-checked
* @param {string} location e.g. "prop", "context", "child context"
* @param {string} componentName Name of the component for error messages.
* @param {?Function} getStack Returns the component stack.
* @private
*/
function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
if (__DEV__) {
for (var typeSpecName in typeSpecs) {
if (typeSpecs.hasOwnProperty(typeSpecName)) {
var error;
// Prop type validation may throw. In case they do, we don't want to
// fail the render phase where it didn't fail before. So we log it.
// After these have been cleaned up, we'll let them throw.
try {
// This is intentionally an invariant that gets caught. It's the same
// behavior as without this statement except with a better message.
invariant(
typeof typeSpecs[typeSpecName] === 'function',
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
componentName || 'React class',
location,
typeSpecName
);
error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
} catch (ex) {
error = ex;
}
warning(
!error || error instanceof Error,
'%s: type specification of %s `%s` is invalid; the type checker ' +
'function must return `null` or an `Error` but returned a %s. ' +
'You may have forgotten to pass an argument to the type checker ' +
'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
'shape all require an argument).',
componentName || 'React class',
location,
typeSpecName,
typeof error
);
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error.message] = true;
var stack = getStack ? getStack() : '';
warning(
false,
'Failed %s type: %s%s',
location,
error.message,
stack != null ? stack : '',
);
}
}
}
}
}
module.exports = checkPropTypes;
@@ -34,6 +34,7 @@ const {
if (__DEV__) {
var checkReactTypeSpec = require('checkReactTypeSpec');
var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
var warnedAboutMissingGetChildContext = {};
}
@@ -91,7 +92,9 @@ exports.getMaskedContext = function(workInProgress : Fiber, unmaskedContext : Ob
if (__DEV__) {
const name = getComponentName(workInProgress);
checkReactTypeSpec(contextTypes, context, 'context', name, null, workInProgress);
ReactDebugCurrentFrame.current = workInProgress;
checkReactTypeSpec(contextTypes, context, 'context', name);
ReactDebugCurrentFrame.current = null;
}
// Cache unmasked context so we can avoid recreating masked context unless necessary.
@@ -182,7 +185,9 @@ function processChildContext(fiber : Fiber, parentContext : Object, isReconcilin
// assume anything about the given fiber. We won't pass it down if we aren't sure.
// TODO: remove this hack when we delete unstable_renderSubtree in Fiber.
const workInProgress = isReconciling ? fiber : null;
checkReactTypeSpec(childContextTypes, childContext, 'childContext', name, null, workInProgress);
ReactDebugCurrentFrame.current = workInProgress;
checkReactTypeSpec(childContextTypes, childContext, 'child context', name);
ReactDebugCurrentFrame.current = null;
}
return {...parentContext, ...childContext};
}
@@ -24,6 +24,7 @@ var ReactReconciler = require('ReactReconciler');
if (__DEV__) {
var checkReactTypeSpec = require('checkReactTypeSpec');
var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
var warningAboutMissingGetChildContext = {};
}
@@ -33,8 +34,6 @@ var shallowEqual = require('shallowEqual');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
var warning = require('warning');
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
function StatelessComponent(Component) {
}
StatelessComponent.prototype.render = function() {
@@ -687,7 +686,7 @@ var ReactCompositeComponent = {
this._checkContextTypes(
Component.childContextTypes,
childContext,
'childContext'
'child context'
);
}
for (var name in childContext) {
@@ -730,17 +729,17 @@ var ReactCompositeComponent = {
_checkContextTypes: function(
typeSpecs,
values,
location: ReactPropTypeLocations,
location: string,
) {
if (__DEV__) {
ReactDebugCurrentFrame.current = this._debugID;
checkReactTypeSpec(
typeSpecs,
values,
location,
this.getName(),
null,
this._debugID
this.getName()
);
ReactDebugCurrentFrame.current = null;
}
},
@@ -1,29 +0,0 @@
/**
* Copyright 2013-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.
*
* @flow
* @providesModule ReactPropTypeLocationNames
*/
'use strict';
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
type NamesType = {[key: ReactPropTypeLocations]: string};
var ReactPropTypeLocationNames: NamesType = {};
if (__DEV__) {
ReactPropTypeLocationNames = {
prop: 'prop',
context: 'context',
childContext: 'child context',
};
}
module.exports = ReactPropTypeLocationNames;
@@ -1,18 +0,0 @@
/**
* Copyright 2013-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.
*
* @flow
* @providesModule ReactPropTypeLocations
*/
'use strict';
export type ReactPropTypeLocations =
'prop' |
'context' |
'childContext';
+11 -113
View File
@@ -11,124 +11,22 @@
'use strict';
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactPropTypesSecret = require('ReactPropTypesSecret');
var checkPropTypes = require('checkPropTypes');
var { getStackAddendum } = require('ReactDebugCurrentFrame');
var invariant = require('invariant');
var warning = require('warning');
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
var ReactComponentTreeHook;
if (
typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV === 'test'
) {
// Temporary hack.
// Inline requires don't work well with Jest:
// https://github.com/facebook/react/issues/7240
// Remove the inline requires when we don't need them anymore:
// https://github.com/facebook/react/pull/7178
ReactComponentTreeHook = require('ReactComponentTreeHook');
}
var loggedTypeFailures = {};
/**
* Assert that the values match with the type specs.
* Error messages are memorized and will only be shown once.
*
* @param {object} typeSpecs Map of name to a ReactPropType
* @param {object} values Runtime values that need to be type-checked
* @param {string} location e.g. "prop", "context", "child context"
* @param {string} componentName Name of the component for error messages.
* @param {?object} element The React element that is being type-checked
* @param {?number} workInProgressOrDebugID The React component instance that is being type-checked
* @private
*/
function checkReactTypeSpec(
typeSpecs,
values,
location: ReactPropTypeLocations,
componentName,
element,
// It is only safe to pass fiber if it is the work-in-progress version, and
// only during reconciliation (begin and complete phase).
workInProgressOrDebugID,
location: string,
componentName
) {
for (var typeSpecName in typeSpecs) {
if (typeSpecs.hasOwnProperty(typeSpecName)) {
var error;
// Prop type validation may throw. In case they do, we don't want to
// fail the render phase where it didn't fail before. So we log it.
// After these have been cleaned up, we'll let them throw.
try {
// This is intentionally an invariant that gets caught. It's the same
// behavior as without this statement except with a better message.
invariant(
typeof typeSpecs[typeSpecName] === 'function',
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
componentName || 'React class',
ReactPropTypeLocationNames[location],
typeSpecName
);
error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
} catch (ex) {
error = ex;
}
warning(
!error || error instanceof Error,
'%s: type specification of %s `%s` is invalid; the type checker ' +
'function must return `null` or an `Error` but returned a %s. ' +
'You may have forgotten to pass an argument to the type checker ' +
'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
'shape all require an argument).',
componentName || 'React class',
ReactPropTypeLocationNames[location],
typeSpecName,
typeof error
);
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error.message] = true;
var componentStackInfo = '';
if (__DEV__) {
if (!ReactComponentTreeHook) {
ReactComponentTreeHook = require('ReactComponentTreeHook');
}
if (workInProgressOrDebugID != null) {
if (typeof workInProgressOrDebugID === 'number') {
// DebugID from Stack.
const debugID = workInProgressOrDebugID;
componentStackInfo = ReactComponentTreeHook.getStackAddendumByID(debugID);
} else if (typeof workInProgressOrDebugID.tag === 'number') {
// This is a Fiber.
// The stack will only be correct if this is a work in progress
// version and we're calling it during reconciliation.
const workInProgress = workInProgressOrDebugID;
componentStackInfo = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
}
} else if (element !== null) {
componentStackInfo = ReactComponentTreeHook.getCurrentStackAddendum(element);
}
}
warning(
false,
'Failed %s type: %s%s',
location,
error.message,
componentStackInfo
);
}
}
}
checkPropTypes(
typeSpecs,
values,
location,
componentName,
getStackAddendum
);
}
module.exports = checkReactTypeSpec;