@samitouri / QOS-React / commits / 1d90894f93

Re-add the warning if PropType function is called manually (#8903)

* Revert "Revert "Warn if PropType function is called manually (#7132)"" This reverts commit be71f76ed8b7dbad25e3519455605252d3daf683. In other words, now we again have the warning if you attempt to call PropTypes manually. It was removed in #8066 but we shouldn't have done this since we still want to avoid people accidentally calling them in production (and even more so since now it would throw). Fixes #8080. * Pass secret in ReactControlledValuePropTypes * Record tests

Dan Abramov committed Feb 8, 2017 at 16:29 UTC 1d90894f93d9564cdb6aec9ca3e3da7e1eb01b5a
6 files changed +276 -8
scripts/fiber/tests-passing.txt
+10
@@ -313,9 +313,11 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
313 * should not warn for valid values
314 * should be implicitly optional and not warn without values
315 * should warn for missing required values
316 +* should warn if called manually in development
317 * should should accept any value
318 * should be implicitly optional and not warn without values
319 * should warn for missing required values
320 +* should warn if called manually in development
321 * should fail for invalid argument
322 * should support the arrayOf propTypes
323 * should support arrayOf with complex types
@@ -325,16 +327,19 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
327 * should not warn when passing an empty array
328 * should be implicitly optional and not warn without values
329 * should warn for missing required values
330 +* should warn if called manually in development
331 * should support components
332 * should not support multiple components or scalar values
333 * should be able to define a single child as label
334 * should warn when passing no label and isRequired is set
335 * should be implicitly optional and not warn without values
336 * should warn for missing required values
337 +* should warn if called manually in development
338 * should warn for invalid instances
339 * should not warn for valid values
340 * should be implicitly optional and not warn without values
341 * should warn for missing required values
342 +* should warn if called manually in development
343 * should warn for invalid values
344 * should not warn for valid values
345 * should not warn for iterables
@@ -342,6 +347,7 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
347 * should not warn for null/undefined if not required
348 * should warn for missing required values
349 * should accept empty array for required props
350 +* should warn if called manually in development
351 * should fail for invalid argument
352 * should support the objectOf propTypes
353 * should support objectOf with complex types
@@ -351,16 +357,19 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
357 * should not warn when passing an empty object
358 * should be implicitly optional and not warn without values
359 * should warn for missing required values
360 +* should warn if called manually in development
361 * should warn but not error for invalid argument
362 * should warn for invalid values
363 * should not warn for valid values
364 * should be implicitly optional and not warn without values
365 * should warn for missing required values
366 +* should warn if called manually in development
367 * should warn but not error for invalid argument
368 * should warn if none of the types are valid
369 * should not warn if one of the types are valid
370 * should be implicitly optional and not warn without values
371 * should warn for missing required values
372 +* should warn if called manually in development
373 * should warn for non objects
374 * should not warn for empty values
375 * should not warn for an empty object
@@ -371,6 +380,7 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
380 * should warn for invalid key types
381 * should be implicitly optional and not warn without values
382 * should warn for missing required values
383 +* should warn if called manually in development
384 * should warn for non-symbol
385 * should not warn for a polyfilled Symbol
386 * should have been called with the right params
src/isomorphic/classic/types/ReactPropTypes.js
+57 -7
@@ -13,6 +13,7 @@
13
14 var ReactElement = require('ReactElement');
15 var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
16 +var ReactPropTypesSecret = require('ReactPropTypesSecret');
17
18 var emptyFunction = require('emptyFunction');
19 var getIteratorFn = require('getIteratorFn');
@@ -154,16 +155,42 @@ function PropTypeError(message) {
155 PropTypeError.prototype = Error.prototype;
156
157 function createChainableTypeChecker(validate) {
158 + if (__DEV__) {
159 + var manualPropTypeCallCache = {};
160 + }
161 function checkType(
162 isRequired,
163 props,
164 propName,
165 componentName,
166 location,
163 - propFullName
167 + propFullName,
168 + secret
169 ) {
170 componentName = componentName || ANONYMOUS;
171 propFullName = propFullName || propName;
172 + if (__DEV__) {
173 + if (
174 + secret !== ReactPropTypesSecret &&
175 + typeof console !== 'undefined'
176 + ) {
177 + var cacheKey = `${componentName}:${propName}`;
178 + if (!manualPropTypeCallCache[cacheKey]) {
179 + warning(
180 + false,
181 + 'You are manually calling a React.PropTypes validation ' +
182 + 'function for the `%s` prop on `%s`. This is deprecated ' +
183 + 'and will not work in production with the next major version. ' +
184 + 'You may be seeing this warning due to a third-party PropTypes ' +
185 + 'library. See https://fb.me/react-warning-dont-call-proptypes ' +
186 + 'for details.',
187 + propFullName,
188 + componentName
189 + );
190 + manualPropTypeCallCache[cacheKey] = true;
191 + }
192 + }
193 + }
194 if (props[propName] == null) {
195 var locationName = ReactPropTypeLocationNames[location];
196 if (isRequired) {
@@ -180,7 +207,13 @@ function createChainableTypeChecker(validate) {
207 }
208 return null;
209 } else {
183 - return validate(props, propName, componentName, location, propFullName);
210 + return validate(
211 + props,
212 + propName,
213 + componentName,
214 + location,
215 + propFullName,
216 + );
217 }
218 }
219
@@ -191,7 +224,14 @@ function createChainableTypeChecker(validate) {
224 }
225
226 function createPrimitiveTypeChecker(expectedType) {
194 - function validate(props, propName, componentName, location, propFullName) {
227 + function validate(
228 + props,
229 + propName,
230 + componentName,
231 + location,
232 + propFullName,
233 + secret
234 + ) {
235 var propValue = props[propName];
236 var propType = getPropType(propValue);
237 if (propType !== expectedType) {
@@ -238,7 +278,8 @@ function createArrayOfTypeChecker(typeChecker) {
278 i,
279 componentName,
280 location,
241 - `${propFullName}[${i}]`
281 + `${propFullName}[${i}]`,
282 + ReactPropTypesSecret
283 );
284 if (error instanceof Error) {
285 return error;
@@ -329,7 +370,8 @@ function createObjectOfTypeChecker(typeChecker) {
370 key,
371 componentName,
372 location,
332 - `${propFullName}.${key}`
373 + `${propFullName}.${key}`,
374 + ReactPropTypesSecret
375 );
376 if (error instanceof Error) {
377 return error;
@@ -351,7 +393,14 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
393 for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
394 var checker = arrayOfTypeCheckers[i];
395 if (
354 - checker(props, propName, componentName, location, propFullName) == null
396 + checker(
397 + props,
398 + propName,
399 + componentName,
400 + location,
401 + propFullName,
402 + ReactPropTypesSecret
403 + ) == null
404 ) {
405 return null;
406 }
@@ -401,7 +450,8 @@ function createShapeTypeChecker(shapeTypes) {
450 key,
451 componentName,
452 location,
404 - `${propFullName}.${key}`
453 + `${propFullName}.${key}`,
454 + ReactPropTypesSecret
455 );
456 if (error) {
457 return error;
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+187
@@ -15,6 +15,7 @@ var PropTypes;
15 var React;
16 var ReactFragment;
17 var ReactTestUtils;
18 +var ReactPropTypesSecret;
19
20 var Component;
21 var MyComponent;
@@ -26,6 +27,8 @@ function typeCheckFail(declaration, value, message) {
27 'testProp',
28 'testComponent',
29 'prop',
30 + null,
31 + ReactPropTypesSecret
32 );
33 expect(error instanceof Error).toBe(true);
34 expect(error.message).toBe(message);
@@ -42,6 +45,8 @@ function typeCheckFailRequiredValues(declaration) {
45 'testProp',
46 'testComponent',
47 'prop',
48 + null,
49 + ReactPropTypesSecret
50 );
51 expect(error1 instanceof Error).toBe(true);
52 expect(error1.message).toBe(specifiedButIsNullMsg);
@@ -51,6 +56,8 @@ function typeCheckFailRequiredValues(declaration) {
56 'testProp',
57 'testComponent',
58 'prop',
59 + null,
60 + ReactPropTypesSecret
61 );
62 expect(error2 instanceof Error).toBe(true);
63 expect(error2.message).toBe(unspecifiedMsg);
@@ -60,6 +67,8 @@ function typeCheckFailRequiredValues(declaration) {
67 'testProp',
68 'testComponent',
69 'prop',
70 + null,
71 + ReactPropTypesSecret
72 );
73 expect(error3 instanceof Error).toBe(true);
74 expect(error3.message).toBe(unspecifiedMsg);
@@ -72,16 +81,38 @@ function typeCheckPass(declaration, value) {
81 'testProp',
82 'testComponent',
83 'prop',
84 + null,
85 + ReactPropTypesSecret
86 );
87 expect(error).toBe(null);
88 }
89
90 +function expectWarningInDevelopment(declaration, value) {
91 + var props = {testProp: value};
92 + var propName = 'testProp' + Math.random().toString();
93 + var componentName = 'testComponent' + Math.random().toString();
94 + for (var i = 0; i < 3; i ++) {
95 + declaration(
96 + props,
97 + propName,
98 + componentName,
99 + 'prop'
100 + );
101 + }
102 + expect(console.error.calls.count()).toBe(1);
103 + expect(console.error.calls.argsFor(0)[0]).toContain(
104 + 'You are manually calling a React.PropTypes validation '
105 + );
106 + console.error.calls.reset();
107 +}
108 +
109 describe('ReactPropTypes', () => {
110 beforeEach(() => {
111 PropTypes = require('ReactPropTypes');
112 React = require('React');
113 ReactFragment = require('ReactFragment');
114 ReactTestUtils = require('ReactTestUtils');
115 + ReactPropTypesSecret = require('ReactPropTypesSecret');
116 });
117
118 describe('Primitive Types', () => {
@@ -153,6 +184,52 @@ describe('ReactPropTypes', () => {
184 it('should warn for missing required values', () => {
185 typeCheckFailRequiredValues(PropTypes.string.isRequired);
186 });
187 +
188 + it('should warn if called manually in development', () => {
189 + spyOn(console, 'error');
190 + expectWarningInDevelopment(PropTypes.array, /please/);
191 + expectWarningInDevelopment(PropTypes.array, []);
192 + expectWarningInDevelopment(PropTypes.array.isRequired, /please/);
193 + expectWarningInDevelopment(PropTypes.array.isRequired, []);
194 + expectWarningInDevelopment(PropTypes.array.isRequired, null);
195 + expectWarningInDevelopment(PropTypes.array.isRequired, undefined);
196 + expectWarningInDevelopment(PropTypes.bool, []);
197 + expectWarningInDevelopment(PropTypes.bool, true);
198 + expectWarningInDevelopment(PropTypes.bool.isRequired, []);
199 + expectWarningInDevelopment(PropTypes.bool.isRequired, true);
200 + expectWarningInDevelopment(PropTypes.bool.isRequired, null);
201 + expectWarningInDevelopment(PropTypes.bool.isRequired, undefined);
202 + expectWarningInDevelopment(PropTypes.func, false);
203 + expectWarningInDevelopment(PropTypes.func, function() {});
204 + expectWarningInDevelopment(PropTypes.func.isRequired, false);
205 + expectWarningInDevelopment(PropTypes.func.isRequired, function() {});
206 + expectWarningInDevelopment(PropTypes.func.isRequired, null);
207 + expectWarningInDevelopment(PropTypes.func.isRequired, undefined);
208 + expectWarningInDevelopment(PropTypes.number, function() {});
209 + expectWarningInDevelopment(PropTypes.number, 42);
210 + expectWarningInDevelopment(PropTypes.number.isRequired, function() {});
211 + expectWarningInDevelopment(PropTypes.number.isRequired, 42);
212 + expectWarningInDevelopment(PropTypes.number.isRequired, null);
213 + expectWarningInDevelopment(PropTypes.number.isRequired, undefined);
214 + expectWarningInDevelopment(PropTypes.string, 0);
215 + expectWarningInDevelopment(PropTypes.string, 'foo');
216 + expectWarningInDevelopment(PropTypes.string.isRequired, 0);
217 + expectWarningInDevelopment(PropTypes.string.isRequired, 'foo');
218 + expectWarningInDevelopment(PropTypes.string.isRequired, null);
219 + expectWarningInDevelopment(PropTypes.string.isRequired, undefined);
220 + expectWarningInDevelopment(PropTypes.symbol, 0);
221 + expectWarningInDevelopment(PropTypes.symbol, Symbol('Foo'));
222 + expectWarningInDevelopment(PropTypes.symbol.isRequired, 0);
223 + expectWarningInDevelopment(PropTypes.symbol.isRequired, Symbol('Foo'));
224 + expectWarningInDevelopment(PropTypes.symbol.isRequired, null);
225 + expectWarningInDevelopment(PropTypes.symbol.isRequired, undefined);
226 + expectWarningInDevelopment(PropTypes.object, '');
227 + expectWarningInDevelopment(PropTypes.object, {foo: 'bar'});
228 + expectWarningInDevelopment(PropTypes.object.isRequired, '');
229 + expectWarningInDevelopment(PropTypes.object.isRequired, {foo: 'bar'});
230 + expectWarningInDevelopment(PropTypes.object.isRequired, null);
231 + expectWarningInDevelopment(PropTypes.object.isRequired, undefined);
232 + });
233 });
234
235 describe('Any type', () => {
@@ -171,6 +248,13 @@ describe('ReactPropTypes', () => {
248 it('should warn for missing required values', () => {
249 typeCheckFailRequiredValues(PropTypes.any.isRequired);
250 });
251 +
252 + it('should warn if called manually in development', () => {
253 + spyOn(console, 'error');
254 + expectWarningInDevelopment(PropTypes.any, null);
255 + expectWarningInDevelopment(PropTypes.any.isRequired, null);
256 + expectWarningInDevelopment(PropTypes.any.isRequired, undefined);
257 + });
258 });
259
260 describe('ArrayOf Type', () => {
@@ -258,6 +342,24 @@ describe('ReactPropTypes', () => {
342 PropTypes.arrayOf(PropTypes.number).isRequired
343 );
344 });
345 +
346 + it('should warn if called manually in development', () => {
347 + spyOn(console, 'error');
348 + expectWarningInDevelopment(
349 + PropTypes.arrayOf({ foo: PropTypes.string }),
350 + { foo: 'bar' }
351 + );
352 + expectWarningInDevelopment(
353 + PropTypes.arrayOf(PropTypes.number),
354 + [1, 2, 'b']
355 + );
356 + expectWarningInDevelopment(
357 + PropTypes.arrayOf(PropTypes.number),
358 + {'0': 'maybe-array', length: 1}
359 + );
360 + expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, null);
361 + expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, undefined);
362 + });
363 });
364
365 describe('Component Type', () => {
@@ -330,6 +432,18 @@ describe('ReactPropTypes', () => {
432 it('should warn for missing required values', () => {
433 typeCheckFailRequiredValues(PropTypes.element.isRequired);
434 });
435 +
436 + it('should warn if called manually in development', () => {
437 + spyOn(console, 'error');
438 + expectWarningInDevelopment(PropTypes.element, [<div />, <div />]);
439 + expectWarningInDevelopment(PropTypes.element, <div />);
440 + expectWarningInDevelopment(PropTypes.element, 123);
441 + expectWarningInDevelopment(PropTypes.element, 'foo');
442 + expectWarningInDevelopment(PropTypes.element, false);
443 + expectWarningInDevelopment(PropTypes.element.isRequired, null);
444 + expectWarningInDevelopment(PropTypes.element.isRequired, undefined);
445 + });
446 +
447 });
448
449 describe('Instance Types', () => {
@@ -404,6 +518,15 @@ describe('ReactPropTypes', () => {
518 it('should warn for missing required values', () => {
519 typeCheckFailRequiredValues(PropTypes.instanceOf(String).isRequired);
520 });
521 +
522 + it('should warn if called manually in development', () => {
523 + spyOn(console, 'error');
524 + expectWarningInDevelopment(PropTypes.instanceOf(Date), {});
525 + expectWarningInDevelopment(PropTypes.instanceOf(Date), new Date());
526 + expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, {});
527 + expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, new Date());
528 + });
529 +
530 });
531
532 describe('React Component Types', () => {
@@ -502,6 +625,16 @@ describe('ReactPropTypes', () => {
625 it('should accept empty array for required props', () => {
626 typeCheckPass(PropTypes.node.isRequired, []);
627 });
628 +
629 + it('should warn if called manually in development', () => {
630 + spyOn(console, 'error');
631 + expectWarningInDevelopment(PropTypes.node, 'node');
632 + expectWarningInDevelopment(PropTypes.node, {});
633 + expectWarningInDevelopment(PropTypes.node.isRequired, 'node');
634 + expectWarningInDevelopment(PropTypes.node.isRequired, undefined);
635 + expectWarningInDevelopment(PropTypes.node.isRequired, undefined);
636 + });
637 +
638 });
639
640 describe('ObjectOf Type', () => {
@@ -604,6 +737,21 @@ describe('ReactPropTypes', () => {
737 PropTypes.objectOf(PropTypes.number).isRequired
738 );
739 });
740 +
741 + it('should warn if called manually in development', () => {
742 + spyOn(console, 'error');
743 + expectWarningInDevelopment(
744 + PropTypes.objectOf({ foo: PropTypes.string }),
745 + { foo: 'bar' }
746 + );
747 + expectWarningInDevelopment(
748 + PropTypes.objectOf(PropTypes.number),
749 + {a: 1, b: 2, c: 'b'}
750 + );
751 + expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), [1, 2]);
752 + expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), null);
753 + expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), undefined);
754 + });
755 });
756
757 describe('OneOf Types', () => {
@@ -660,6 +808,13 @@ describe('ReactPropTypes', () => {
808 it('should warn for missing required values', () => {
809 typeCheckFailRequiredValues(PropTypes.oneOf(['red', 'blue']).isRequired);
810 });
811 +
812 + it('should warn if called manually in development', () => {
813 + spyOn(console, 'error');
814 + expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), true);
815 + expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), null);
816 + expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), undefined);
817 + });
818 });
819
820 describe('Union Types', () => {
@@ -724,6 +879,23 @@ describe('ReactPropTypes', () => {
879 PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
880 );
881 });
882 +
883 + it('should warn if called manually in development', () => {
884 + spyOn(console, 'error');
885 + expectWarningInDevelopment(
886 + PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
887 + []
888 + );
889 + expectWarningInDevelopment(
890 + PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
891 + null
892 + );
893 + expectWarningInDevelopment(
894 + PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
895 + undefined
896 + );
897 + });
898 +
899 });
900
901 describe('Shape Types', () => {
@@ -803,6 +975,21 @@ describe('ReactPropTypes', () => {
975 PropTypes.shape({key: PropTypes.number}).isRequired
976 );
977 });
978 +
979 + it('should warn if called manually in development', () => {
980 + spyOn(console, 'error');
981 + expectWarningInDevelopment(PropTypes.shape({}), 'some string');
982 + expectWarningInDevelopment(PropTypes.shape({ foo: PropTypes.number }), { foo: 42 });
983 + expectWarningInDevelopment(
984 + PropTypes.shape({key: PropTypes.number}).isRequired,
985 + null
986 + );
987 + expectWarningInDevelopment(
988 + PropTypes.shape({key: PropTypes.number}).isRequired,
989 + undefined
990 + );
991 + expectWarningInDevelopment(PropTypes.element, <div />);
992 + });
993 });
994
995 describe('Symbol Type', () => {
src/renderers/dom/shared/utils/ReactControlledValuePropTypes.js
+3
@@ -12,6 +12,7 @@
12 'use strict';
13
14 var React = require('React');
15 +var ReactPropTypesSecret = require('ReactPropTypesSecret');
16
17 var warning = require('warning');
18
@@ -79,6 +80,8 @@ var ReactControlledValuePropTypes = {
80 propName,
81 tagName,
82 'prop',
83 + null,
84 + ReactPropTypesSecret,
85 );
86 }
87 if (error instanceof Error && !(error.message in loggedTypeFailures)) {
src/shared/types/ReactPropTypesSecret.js new
+17
@@ -0,0 +1,17 @@
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 + * @flow
10 + * @providesModule ReactPropTypesSecret
11 + */
12 +
13 +'use strict';
14 +
15 +const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
16 +
17 +module.exports = ReactPropTypesSecret;
src/shared/types/checkReactTypeSpec.js
+2 -1
@@ -12,6 +12,7 @@
12 'use strict';
13
14 var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
15 +var ReactPropTypesSecret = require('ReactPropTypesSecret');
16
17 var invariant = require('invariant');
18 var warning = require('warning');
@@ -74,7 +75,7 @@ function checkReactTypeSpec(
75 ReactPropTypeLocationNames[location],
76 typeSpecName
77 );
77 - error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location);
78 + error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
79 } catch (ex) {
80 error = ex;
81 }