@samitouri / QOS-React / commits / 3746a9dafb

Invert dependency between checkPropTypes and checkReactTypeSpec

Expressing checkReactTypeSpec using the public API frees us to move it to a separate module in the future.

Andrew Clark committed Feb 15, 2017 at 16:08 UTC 3746a9dafb65e077632a79870a75120aa16c4c7f
4 files changed +114 -92
src/isomorphic/classic/types/ReactPropTypes.js
+1 -4
@@ -13,7 +13,7 @@
13
14 var ReactElement = require('ReactElement');
15 var ReactPropTypesSecret = require('ReactPropTypesSecret');
16 -var checkReactTypeSpec = require('checkReactTypeSpec');
16 +var checkPropTypes = require('checkPropTypes');
17
18 var emptyFunction = require('emptyFunction');
19 var getIteratorFn = require('getIteratorFn');
@@ -123,9 +123,6 @@ if (__DEV__) {
123 };
124 }
125
126 -function checkPropTypes(propTypes, object, location, componentName, warnOnRepeat) {
127 - checkReactTypeSpec(propTypes, object, location, componentName, null, null, warnOnRepeat);
128 -}
126 ReactPropTypes.checkPropTypes = checkPropTypes;
127
128
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+5 -3
@@ -12,6 +12,7 @@
12 'use strict';
13
14 var PropTypes;
15 +var checkReactTypeSpec;
16 var React;
17 var ReactFragment;
18 var ReactTestUtils;
@@ -25,7 +26,7 @@ function getPropTypeWarningMessage(propTypes, object, componentName) {
26 } else {
27 console.error.calls.reset();
28 }
28 - PropTypes.checkPropTypes(propTypes, object, 'prop', 'testComponent', true);
29 + checkReactTypeSpec(propTypes, object, 'prop', 'testComponent', null, null, true);
30 const callCount = console.error.calls.count();
31 if (callCount > 1) {
32 throw new Error('Too many warnings.');
@@ -101,6 +102,7 @@ function expectWarningInDevelopment(declaration, value) {
102 describe('ReactPropTypes', () => {
103 beforeEach(() => {
104 PropTypes = require('ReactPropTypes');
105 + checkReactTypeSpec = require('checkReactTypeSpec');
106 React = require('React');
107 ReactFragment = require('ReactFragment');
108 ReactTestUtils = require('ReactTestUtils');
@@ -115,7 +117,7 @@ describe('ReactPropTypes', () => {
117 },
118 };
119 const props = { foo: 'foo' };
118 - const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', true);
120 + const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', null, true);
121 expect(console.error.calls.argsFor(0)[0]).toContain('some error');
122 expect(returnValue).toBe(undefined);
123 });
@@ -128,7 +130,7 @@ describe('ReactPropTypes', () => {
130 },
131 };
132 const props = { foo: 'foo' };
131 - const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', true);
133 + const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', null, true);
134 expect(console.error.calls.argsFor(0)[0]).toContain('some error');
135 expect(returnValue).toBe(undefined);
136 });
src/isomorphic/classic/types/checkPropTypes.js new
+85
@@ -0,0 +1,85 @@
1 +/**
2 + * Copyright 2013-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule checkPropTypes
10 + */
11 +
12 +'use strict';
13 +
14 +var ReactPropTypesSecret = require('ReactPropTypesSecret');
15 +
16 +var invariant = require('invariant');
17 +var warning = require('warning');
18 +
19 +var loggedTypeFailures = {};
20 +
21 +/**
22 + * Assert that the values match with the type specs.
23 + * Error messages are memorized and will only be shown once.
24 + *
25 + * @param {object} typeSpecs Map of name to a ReactPropType
26 + * @param {object} values Runtime values that need to be type-checked
27 + * @param {string} location e.g. "prop", "context", "child context"
28 + * @param {string} componentName Name of the component for error messages.
29 + * @param {?Function} formatMessage Function that transforms the error message, to add additional info.
30 + * @param {?boolean} warnOnRepeat Whether or not repeated warnings should be skipped.
31 + * @private
32 + */
33 +function checkPropTypes(typeSpecs, values, location, componentName, formatMessage, warnOnRepeat) {
34 + for (var typeSpecName in typeSpecs) {
35 + if (typeSpecs.hasOwnProperty(typeSpecName)) {
36 + var error;
37 + // Prop type validation may throw. In case they do, we don't want to
38 + // fail the render phase where it didn't fail before. So we log it.
39 + // After these have been cleaned up, we'll let them throw.
40 + try {
41 + // This is intentionally an invariant that gets caught. It's the same
42 + // behavior as without this statement except with a better message.
43 + invariant(
44 + typeof typeSpecs[typeSpecName] === 'function',
45 + '%s: %s type `%s` is invalid; it must be a function, usually from ' +
46 + 'React.PropTypes.',
47 + componentName || 'React class',
48 + location,
49 + typeSpecName
50 + );
51 + error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
52 + } catch (ex) {
53 + error = ex;
54 + }
55 + warning(
56 + !error || error instanceof Error,
57 + '%s: type specification of %s `%s` is invalid; the type checker ' +
58 + 'function must return `null` or an `Error` but returned a %s. ' +
59 + 'You may have forgotten to pass an argument to the type checker ' +
60 + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
61 + 'shape all require an argument).',
62 + componentName || 'React class',
63 + location,
64 + typeSpecName,
65 + typeof error
66 + );
67 + if (error instanceof Error && (warnOnRepeat || !(error.message in loggedTypeFailures))) {
68 + // Only monitor this failure once because there tends to be a lot of the
69 + // same error.
70 + loggedTypeFailures[error.message] = true;
71 +
72 + var message = formatMessage ? formatMessage(error.message) : error.message;
73 +
74 + warning(
75 + false,
76 + 'Failed %s type: %s',
77 + location,
78 + message,
79 + );
80 + }
81 + }
82 + }
83 +}
84 +
85 +module.exports = checkPropTypes;
src/shared/types/checkReactTypeSpec.js
+23 -85
@@ -11,10 +11,7 @@
11
12 'use strict';
13
14 -var ReactPropTypesSecret = require('ReactPropTypesSecret');
15 -
16 -var invariant = require('invariant');
17 -var warning = require('warning');
14 +var checkPropTypes = require('checkPropTypes');
15
16 var ReactComponentTreeHook;
17
@@ -31,20 +28,6 @@ if (
28 ReactComponentTreeHook = require('ReactComponentTreeHook');
29 }
30
34 -var loggedTypeFailures = {};
35 -
36 -/**
37 - * Assert that the values match with the type specs.
38 - * Error messages are memorized and will only be shown once.
39 - *
40 - * @param {object} typeSpecs Map of name to a ReactPropType
41 - * @param {object} values Runtime values that need to be type-checked
42 - * @param {string} location e.g. "prop", "context", "child context"
43 - * @param {string} componentName Name of the component for error messages.
44 - * @param {?object} element The React element that is being type-checked
45 - * @param {?number} workInProgressOrDebugID The React component instance that is being type-checked
46 - * @private
47 - */
31 function checkReactTypeSpec(
32 typeSpecs,
33 values,
@@ -54,80 +37,35 @@ function checkReactTypeSpec(
37 // It is only safe to pass fiber if it is the work-in-progress version, and
38 // only during reconciliation (begin and complete phase).
39 workInProgressOrDebugID,
57 - // Default behavior is to suppress repeated warnings, but can be overridden
40 warnOnRepeat
41 ) {
60 - for (var typeSpecName in typeSpecs) {
61 - if (typeSpecs.hasOwnProperty(typeSpecName)) {
62 - var error;
63 - // Prop type validation may throw. In case they do, we don't want to
64 - // fail the render phase where it didn't fail before. So we log it.
65 - // After these have been cleaned up, we'll let them throw.
66 - try {
67 - // This is intentionally an invariant that gets caught. It's the same
68 - // behavior as without this statement except with a better message.
69 - invariant(
70 - typeof typeSpecs[typeSpecName] === 'function',
71 - '%s: %s type `%s` is invalid; it must be a function, usually from ' +
72 - 'React.PropTypes.',
73 - componentName || 'React class',
74 - location,
75 - typeSpecName
76 - );
77 - error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
78 - } catch (ex) {
79 - error = ex;
42 + function formatMessage(message) {
43 + if (__DEV__) {
44 + let componentStackInfo = '';
45 + if (!ReactComponentTreeHook) {
46 + ReactComponentTreeHook = require('ReactComponentTreeHook');
47 }
81 - warning(
82 - !error || error instanceof Error,
83 - '%s: type specification of %s `%s` is invalid; the type checker ' +
84 - 'function must return `null` or an `Error` but returned a %s. ' +
85 - 'You may have forgotten to pass an argument to the type checker ' +
86 - 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
87 - 'shape all require an argument).',
88 - componentName || 'React class',
89 - location,
90 - typeSpecName,
91 - typeof error
92 - );
93 - if (error instanceof Error && (warnOnRepeat || !(error.message in loggedTypeFailures))) {
94 - // Only monitor this failure once because there tends to be a lot of the
95 - // same error.
96 - loggedTypeFailures[error.message] = true;
97 -
98 - var componentStackInfo = '';
99 -
100 - if (__DEV__) {
101 - if (!ReactComponentTreeHook) {
102 - ReactComponentTreeHook = require('ReactComponentTreeHook');
103 - }
104 - if (workInProgressOrDebugID != null) {
105 - if (typeof workInProgressOrDebugID === 'number') {
106 - // DebugID from Stack.
107 - const debugID = workInProgressOrDebugID;
108 - componentStackInfo = ReactComponentTreeHook.getStackAddendumByID(debugID);
109 - } else if (typeof workInProgressOrDebugID.tag === 'number') {
110 - // This is a Fiber.
111 - // The stack will only be correct if this is a work in progress
112 - // version and we're calling it during reconciliation.
113 - const workInProgress = workInProgressOrDebugID;
114 - componentStackInfo = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
115 - }
116 - } else if (element !== null) {
117 - componentStackInfo = ReactComponentTreeHook.getCurrentStackAddendum(element);
118 - }
48 + if (workInProgressOrDebugID != null) {
49 + if (typeof workInProgressOrDebugID === 'number') {
50 + // DebugID from Stack.
51 + const debugID = workInProgressOrDebugID;
52 + componentStackInfo = ReactComponentTreeHook.getStackAddendumByID(debugID);
53 + } else if (typeof workInProgressOrDebugID.tag === 'number') {
54 + // This is a Fiber.
55 + // The stack will only be correct if this is a work in progress
56 + // version and we're calling it during reconciliation.
57 + const workInProgress = workInProgressOrDebugID;
58 + componentStackInfo = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
59 }
120 -
121 - warning(
122 - false,
123 - 'Failed %s type: %s%s',
124 - location,
125 - error.message,
126 - componentStackInfo
127 - );
60 + } else if (element !== null) {
61 + componentStackInfo = ReactComponentTreeHook.getCurrentStackAddendum(element);
62 }
63 + message += componentStackInfo;
64 }
65 + return message;
66 }
67 +
68 + checkPropTypes(typeSpecs, values, location, componentName, formatMessage, warnOnRepeat);
69 }
70
71 module.exports = checkReactTypeSpec;