From d72700ff5a212262068df8ffa6bd2d68b04f9f54 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 28 Feb 2020 01:21:54 +0000 Subject: [PATCH] Remove runtime dependency on prop-types (#18127) * Remove runtime dep on prop-types * Fix test --- packages/react-art/package.json | 1 - packages/react-dom/package.json | 1 - .../src/server/ReactPartialRendererContext.js | 13 +-- .../shared/ReactControlledValuePropTypes.js | 2 +- packages/react-flight/package.json | 3 +- packages/react-native-renderer/package.json | 1 - packages/react-noop-renderer/package.json | 1 - packages/react-reconciler/package.json | 1 - .../src/ReactFiberBeginWork.js | 19 +---- .../react-reconciler/src/ReactFiberContext.js | 24 +----- packages/react-server/package.json | 3 +- packages/react-test-renderer/package.json | 1 - packages/react/package.json | 3 +- packages/react/src/ReactElementValidator.js | 14 +--- .../ReactElementValidator-test.internal.js | 1 - packages/shared/checkPropTypes.js | 80 +++++++++++++++++++ 16 files changed, 94 insertions(+), 74 deletions(-) create mode 100644 packages/shared/checkPropTypes.js diff --git a/packages/react-art/package.json b/packages/react-art/package.json index 08faba22ab7c5..628e6964758b9 100644 --- a/packages/react-art/package.json +++ b/packages/react-art/package.json @@ -26,7 +26,6 @@ "create-react-class": "^15.6.2", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", "scheduler": "^0.19.0" }, "peerDependencies": { diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json index 9620ce11317ff..fb5f49740caf4 100644 --- a/packages/react-dom/package.json +++ b/packages/react-dom/package.json @@ -19,7 +19,6 @@ "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", "scheduler": "^0.19.0" }, "peerDependencies": { diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index 808afae0bcdfd..74fdd8e321870 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -12,14 +12,11 @@ import type {ReactContext} from 'shared/ReactTypes'; import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; -import ReactSharedInternals from 'shared/ReactSharedInternals'; import getComponentName from 'shared/getComponentName'; -import checkPropTypes from 'prop-types/checkPropTypes'; +import checkPropTypes from 'shared/checkPropTypes'; -let ReactDebugCurrentFrame; let didWarnAboutInvalidateContextType; if (__DEV__) { - ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; didWarnAboutInvalidateContextType = new Set(); } @@ -42,13 +39,7 @@ function maskContext(type, context) { function checkContextTypes(typeSpecs, values, location: string) { if (__DEV__) { - checkPropTypes( - typeSpecs, - values, - location, - 'Component', - ReactDebugCurrentFrame.getCurrentStack, - ); + checkPropTypes(typeSpecs, values, location, 'Component'); } } diff --git a/packages/react-dom/src/shared/ReactControlledValuePropTypes.js b/packages/react-dom/src/shared/ReactControlledValuePropTypes.js index dfb6e0f715e1d..994419ba0c885 100644 --- a/packages/react-dom/src/shared/ReactControlledValuePropTypes.js +++ b/packages/react-dom/src/shared/ReactControlledValuePropTypes.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import checkPropTypes from 'prop-types/checkPropTypes'; +import checkPropTypes from 'shared/checkPropTypes'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import {enableDeprecatedFlareAPI} from 'shared/ReactFeatureFlags'; diff --git a/packages/react-flight/package.json b/packages/react-flight/package.json index e6eabd3c4b02e..88c50d4dc58de 100644 --- a/packages/react-flight/package.json +++ b/packages/react-flight/package.json @@ -29,8 +29,7 @@ }, "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" }, "browserify": { "transform": [ diff --git a/packages/react-native-renderer/package.json b/packages/react-native-renderer/package.json index e711f599b5c12..65efbfb11633a 100644 --- a/packages/react-native-renderer/package.json +++ b/packages/react-native-renderer/package.json @@ -9,7 +9,6 @@ }, "dependencies": { "object-assign": "^4.1.1", - "prop-types": "^15.6.2", "scheduler": "^0.11.0" }, "peerDependencies": { diff --git a/packages/react-noop-renderer/package.json b/packages/react-noop-renderer/package.json index 0f95472ae41e5..32630b190d94b 100644 --- a/packages/react-noop-renderer/package.json +++ b/packages/react-noop-renderer/package.json @@ -12,7 +12,6 @@ "license": "MIT", "dependencies": { "object-assign": "^4.1.1", - "prop-types": "^15.6.2", "regenerator-runtime": "^0.11.0", "react-reconciler": "*", "react-flight": "*", diff --git a/packages/react-reconciler/package.json b/packages/react-reconciler/package.json index f7cc0aa81fea0..1946a83f4f2f1 100644 --- a/packages/react-reconciler/package.json +++ b/packages/react-reconciler/package.json @@ -32,7 +32,6 @@ "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", "scheduler": "^0.19.0" }, "browserify": { diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index ddbdacd61a1dc..d9e88ecbab708 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -18,7 +18,7 @@ import type { } from './ReactFiberSuspenseComponent'; import type {SuspenseContext} from './ReactFiberSuspenseContext'; -import checkPropTypes from 'prop-types/checkPropTypes'; +import checkPropTypes from 'shared/checkPropTypes'; import { IndeterminateComponent, @@ -75,7 +75,6 @@ import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent'; import {REACT_LAZY_TYPE, getIteratorFn} from 'shared/ReactSymbols'; import { getCurrentFiberOwnerNameInDevOrNull, - getCurrentFiberStackInDev, setIsRendering, } from './ReactCurrentFiber'; import {startWorkTimer, cancelWorkTimer} from './ReactDebugFiberPerf'; @@ -296,7 +295,6 @@ function updateForwardRef( nextProps, // Resolved props 'prop', getComponentName(Component), - getCurrentFiberStackInDev, ); } } @@ -414,7 +412,6 @@ function updateMemoComponent( nextProps, // Resolved props 'prop', getComponentName(type), - getCurrentFiberStackInDev, ); } } @@ -442,7 +439,6 @@ function updateMemoComponent( nextProps, // Resolved props 'prop', getComponentName(type), - getCurrentFiberStackInDev, ); } } @@ -501,7 +497,6 @@ function updateSimpleMemoComponent( nextProps, // Resolved (SimpleMemoComponent has no defaultProps) 'prop', getComponentName(outerMemoType), - getCurrentFiberStackInDev, ); } // Inner propTypes will be validated in the function component path. @@ -626,7 +621,6 @@ function updateFunctionComponent( nextProps, // Resolved props 'prop', getComponentName(Component), - getCurrentFiberStackInDev, ); } } @@ -793,7 +787,6 @@ function updateClassComponent( nextProps, // Resolved props 'prop', getComponentName(Component), - getCurrentFiberStackInDev, ); } } @@ -1198,7 +1191,6 @@ function mountLazyComponent( resolvedProps, // Resolved for outer only 'prop', getComponentName(Component), - getCurrentFiberStackInDev, ); } } @@ -2622,13 +2614,7 @@ function updateContextProvider( const providerPropTypes = workInProgress.type.propTypes; if (providerPropTypes) { - checkPropTypes( - providerPropTypes, - newProps, - 'prop', - 'Context.Provider', - getCurrentFiberStackInDev, - ); + checkPropTypes(providerPropTypes, newProps, 'prop', 'Context.Provider'); } } @@ -3208,7 +3194,6 @@ function beginWork( resolvedProps, // Resolved for outer only 'prop', getComponentName(type), - getCurrentFiberStackInDev, ); } } diff --git a/packages/react-reconciler/src/ReactFiberContext.js b/packages/react-reconciler/src/ReactFiberContext.js index 5f4375b4c5c1f..f6009c2b9d3dc 100644 --- a/packages/react-reconciler/src/ReactFiberContext.js +++ b/packages/react-reconciler/src/ReactFiberContext.js @@ -15,9 +15,8 @@ import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {ClassComponent, HostRoot} from 'shared/ReactWorkTags'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; -import checkPropTypes from 'prop-types/checkPropTypes'; +import checkPropTypes from 'shared/checkPropTypes'; -import {getCurrentFiberStackInDev} from './ReactCurrentFiber'; import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf'; import {createCursor, push, pop} from './ReactFiberStack'; @@ -105,13 +104,7 @@ function getMaskedContext( if (__DEV__) { const name = getComponentName(type) || 'Unknown'; - checkPropTypes( - contextTypes, - context, - 'context', - name, - getCurrentFiberStackInDev, - ); + checkPropTypes(contextTypes, context, 'context', name); } // Cache unmasked context so we can avoid recreating masked context unless necessary. @@ -223,18 +216,7 @@ function processChildContext( } if (__DEV__) { const name = getComponentName(type) || 'Unknown'; - checkPropTypes( - childContextTypes, - childContext, - 'child context', - name, - // In practice, there is one case in which we won't get a stack. It's when - // somebody calls unstable_renderSubtreeIntoContainer() and we process - // context from the parent component instance. The stack will be missing - // because it's outside of the reconciliation, and so the pointer has not - // been set. This is rare and doesn't matter. We'll also remove that API. - getCurrentFiberStackInDev, - ); + checkPropTypes(childContextTypes, childContext, 'child context', name); } return {...parentContext, ...childContext}; diff --git a/packages/react-server/package.json b/packages/react-server/package.json index 77ff922fcd536..5745fd077e0eb 100644 --- a/packages/react-server/package.json +++ b/packages/react-server/package.json @@ -30,8 +30,7 @@ }, "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" }, "browserify": { "transform": [ diff --git a/packages/react-test-renderer/package.json b/packages/react-test-renderer/package.json index e0f95987080d7..b1ab08fe6089f 100644 --- a/packages/react-test-renderer/package.json +++ b/packages/react-test-renderer/package.json @@ -20,7 +20,6 @@ "homepage": "https://reactjs.org/", "dependencies": { "object-assign": "^4.1.1", - "prop-types": "^15.6.2", "react-is": "^16.8.6", "react-shallow-renderer": "^16.12.0", "scheduler": "^0.19.0" diff --git a/packages/react/package.json b/packages/react/package.json index f5b021f1c6dcd..2ffa23333ad2a 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -27,8 +27,7 @@ }, "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" }, "browserify": { "transform": [ diff --git a/packages/react/src/ReactElementValidator.js b/packages/react/src/ReactElementValidator.js index 2fead4c017576..c81ace3c6b2f7 100644 --- a/packages/react/src/ReactElementValidator.js +++ b/packages/react/src/ReactElementValidator.js @@ -22,7 +22,7 @@ import { REACT_ELEMENT_TYPE, } from 'shared/ReactSymbols'; import {warnAboutSpreadingKeyToJSX} from 'shared/ReactFeatureFlags'; -import checkPropTypes from 'prop-types/checkPropTypes'; +import checkPropTypes from 'shared/checkPropTypes'; import ReactCurrentOwner from './ReactCurrentOwner'; import { @@ -31,9 +31,7 @@ import { cloneElement, jsxDEV, } from './ReactElement'; -import ReactDebugCurrentFrame, { - setCurrentlyValidatingElement, -} from './ReactDebugCurrentFrame'; +import {setCurrentlyValidatingElement} from './ReactDebugCurrentFrame'; let propTypesMisspellWarningShown; @@ -213,13 +211,7 @@ function validatePropTypes(element) { } if (propTypes) { setCurrentlyValidatingElement(element); - checkPropTypes( - propTypes, - element.props, - 'prop', - name, - ReactDebugCurrentFrame.getStackAddendum, - ); + checkPropTypes(propTypes, element.props, 'prop', name); setCurrentlyValidatingElement(null); } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) { propTypesMisspellWarningShown = true; diff --git a/packages/react/src/__tests__/ReactElementValidator-test.internal.js b/packages/react/src/__tests__/ReactElementValidator-test.internal.js index 9380998460c11..c563e6ce6fea7 100644 --- a/packages/react/src/__tests__/ReactElementValidator-test.internal.js +++ b/packages/react/src/__tests__/ReactElementValidator-test.internal.js @@ -399,7 +399,6 @@ describe('ReactElementValidator', () => { 'returned a function. You may have forgotten to pass an argument to ' + 'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' + 'oneOfType, and shape all require an argument).', - {withoutStack: true}, ); }); diff --git a/packages/shared/checkPropTypes.js b/packages/shared/checkPropTypes.js new file mode 100644 index 0000000000000..f52cd4564231b --- /dev/null +++ b/packages/shared/checkPropTypes.js @@ -0,0 +1,80 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +let loggedTypeFailures = {}; + +export default function checkPropTypes( + typeSpecs: Object, + values: Object, + location: string, + componentName: ?string, +): void { + if (__DEV__) { + // $FlowFixMe This is okay but Flow doesn't know it. + let has = Function.call.bind(Object.prototype.hasOwnProperty); + for (let typeSpecName in typeSpecs) { + if (has(typeSpecs, typeSpecName)) { + let 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. + if (typeof typeSpecs[typeSpecName] !== 'function') { + let err = Error( + (componentName || 'React class') + + ': ' + + location + + ' type `' + + typeSpecName + + '` is invalid; ' + + 'it must be a function, usually from the `prop-types` package, but received `' + + typeof typeSpecs[typeSpecName] + + '`.' + + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.', + ); + err.name = 'Invariant Violation'; + throw err; + } + error = typeSpecs[typeSpecName]( + values, + typeSpecName, + componentName, + location, + null, + 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED', + ); + } catch (ex) { + error = ex; + } + if (error && !(error instanceof Error)) { + console.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; + console.error('Failed %s type: %s', location, error.message); + } + } + } + } +}