diff --git a/gulpfile.js b/gulpfile.js
index 71fd70c9ca1..731c459a951 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -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(
diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt
index 5152468f42d..b24e98e993e 100644
--- a/scripts/fiber/tests-passing.txt
+++ b/scripts/fiber/tests-passing.txt
@@ -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
diff --git a/src/isomorphic/React.js b/src/isomorphic/React.js
index cb2c5a8d9cb..1d2f1b6e8e2 100644
--- a/src/isomorphic/React.js
+++ b/src/isomorphic/React.js
@@ -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,
diff --git a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
index 08582a200dd..a5a04430aae 100644
--- a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
+++ b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
@@ -261,7 +261,7 @@ describe('ReactContextValidator', () => {
ReactTestUtils.renderIntoDocument();
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 **)'
diff --git a/src/isomorphic/classic/class/ReactClass.js b/src/isomorphic/classic/class/ReactClass.js
index bf2f425f8f3..a4efdd825a0 100644
--- a/src/isomorphic/classic/class/ReactClass.js
+++ b/src/isomorphic/classic/class/ReactClass.js
@@ -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
);
}
diff --git a/src/isomorphic/classic/element/ReactDebugCurrentFrame.js b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js
new file mode 100644
index 00000000000..f4efdd672b6
--- /dev/null
+++ b/src/isomorphic/classic/element/ReactDebugCurrentFrame.js
@@ -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;
diff --git a/src/isomorphic/classic/element/ReactElementValidator.js b/src/isomorphic/classic/element/ReactElementValidator.js
index b8146bfd86d..224ad49644a 100644
--- a/src/isomorphic/classic/element/ReactElementValidator.js
+++ b/src/isomorphic/classic/element/ReactElementValidator.js
@@ -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') {
@@ -248,6 +250,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.
@@ -261,6 +267,10 @@ var ReactElementValidator = {
validatePropTypes(element);
+ if (__DEV__) {
+ ReactDebugCurrentFrame.element = null;
+ }
+
return element;
},
@@ -301,10 +311,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;
},
diff --git a/src/isomorphic/classic/types/ReactPropTypes.js b/src/isomorphic/classic/types/ReactPropTypes.js
index dcd0dbc82a7..6f749170bc0 100644
--- a/src/isomorphic/classic/types/ReactPropTypes.js
+++ b/src/isomorphic/classic/types/ReactPropTypes.js
@@ -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\`.`
);
}
diff --git a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
index d482d3866df..8ed0aa07ec4 100644
--- a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+++ b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
@@ -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 = } />;
- ReactTestUtils.renderIntoDocument(instance);
+ var container = document.createElement('div');
+ ReactDOM.render(} />, 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 = ;
- ReactTestUtils.renderIntoDocument(instance);
+ var container = document.createElement('div');
+ ReactDOM.render(, container);
expectDev(console.error.calls.count()).toBe(1);
});
@@ -1037,8 +1066,8 @@ describe('ReactPropTypes', () => {
}
};
- var instance = ;
- ReactTestUtils.renderIntoDocument(instance);
+ var container = document.createElement('div');
+ ReactDOM.render(, container);
expect(spy.calls.count()).toBe(1);
expect(spy.calls.argsFor(0)[1]).toBe('num');
@@ -1054,8 +1083,8 @@ describe('ReactPropTypes', () => {
}
};
- var instance = ;
- ReactTestUtils.renderIntoDocument(instance);
+ var container = document.createElement('div');
+ ReactDOM.render(, container);
expect(spy.calls.count()).toBe(1);
expect(spy.calls.argsFor(0)[1]).toBe('num');
@@ -1078,8 +1107,8 @@ describe('ReactPropTypes', () => {
}
};
- var instance = ;
- ReactTestUtils.renderIntoDocument(instance);
+ var container = document.createElement('div');
+ ReactDOM.render(, 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 = ;
- ReactTestUtils.renderIntoDocument(instance);
+ var container = document.createElement('div');
+ ReactDOM.render(, container);
expectDev(console.error.calls.count()).toBe(0);
}
);
diff --git a/src/isomorphic/classic/types/__tests__/ReactPropTypesProduction-test.js b/src/isomorphic/classic/types/__tests__/ReactPropTypesProduction-test.js
index 076c1639349..8e6a868c9f9 100644
--- a/src/isomorphic/classic/types/__tests__/ReactPropTypesProduction-test.js
+++ b/src/isomorphic/classic/types/__tests__/ReactPropTypesProduction-test.js
@@ -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.'
diff --git a/src/isomorphic/classic/types/checkPropTypes.js b/src/isomorphic/classic/types/checkPropTypes.js
new file mode 100644
index 00000000000..23ebacaf09a
--- /dev/null
+++ b/src/isomorphic/classic/types/checkPropTypes.js
@@ -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;
diff --git a/src/renderers/shared/fiber/ReactFiberContext.js b/src/renderers/shared/fiber/ReactFiberContext.js
index 8b7383458b2..836bc79eb42 100644
--- a/src/renderers/shared/fiber/ReactFiberContext.js
+++ b/src/renderers/shared/fiber/ReactFiberContext.js
@@ -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};
}
diff --git a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
index 9a572a8f6f0..19ce8079f2c 100644
--- a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+++ b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
@@ -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;
}
},
diff --git a/src/shared/types/ReactPropTypeLocationNames.js b/src/shared/types/ReactPropTypeLocationNames.js
deleted file mode 100644
index 1b36b04cc89..00000000000
--- a/src/shared/types/ReactPropTypeLocationNames.js
+++ /dev/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;
diff --git a/src/shared/types/ReactPropTypeLocations.js b/src/shared/types/ReactPropTypeLocations.js
deleted file mode 100644
index a2cb905e191..00000000000
--- a/src/shared/types/ReactPropTypeLocations.js
+++ /dev/null
@@ -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';
diff --git a/src/shared/types/checkReactTypeSpec.js b/src/shared/types/checkReactTypeSpec.js
index e274c3e9f58..c7ed4d8167e 100644
--- a/src/shared/types/checkReactTypeSpec.js
+++ b/src/shared/types/checkReactTypeSpec.js
@@ -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;