Skip to content

Commit

Permalink
Strip React.PropTypes type checking code in production
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Sep 3, 2016
1 parent 7b247f3 commit f4c9a04
Show file tree
Hide file tree
Showing 2 changed files with 272 additions and 19 deletions.
71 changes: 52 additions & 19 deletions src/isomorphic/classic/types/ReactPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var ReactPropTypesSecret = require('ReactPropTypesSecret');

var emptyFunction = require('emptyFunction');
var getIteratorFn = require('getIteratorFn');
var invariant = require('invariant');
var warning = require('warning');

/**
Expand Down Expand Up @@ -68,25 +69,57 @@ var warning = require('warning');

var ANONYMOUS = '<<anonymous>>';

var ReactPropTypes = {
array: createPrimitiveTypeChecker('array'),
bool: createPrimitiveTypeChecker('boolean'),
func: createPrimitiveTypeChecker('function'),
number: createPrimitiveTypeChecker('number'),
object: createPrimitiveTypeChecker('object'),
string: createPrimitiveTypeChecker('string'),
symbol: createPrimitiveTypeChecker('symbol'),

any: createAnyTypeChecker(),
arrayOf: createArrayOfTypeChecker,
element: createElementTypeChecker(),
instanceOf: createInstanceTypeChecker,
node: createNodeChecker(),
objectOf: createObjectOfTypeChecker,
oneOf: createEnumTypeChecker,
oneOfType: createUnionTypeChecker,
shape: createShapeTypeChecker,
};
if (__DEV__) {
// Keep in sync with production version below
var ReactPropTypes = {
array: createPrimitiveTypeChecker('array'),
bool: createPrimitiveTypeChecker('boolean'),
func: createPrimitiveTypeChecker('function'),
number: createPrimitiveTypeChecker('number'),
object: createPrimitiveTypeChecker('object'),
string: createPrimitiveTypeChecker('string'),
symbol: createPrimitiveTypeChecker('symbol'),

any: createAnyTypeChecker(),
arrayOf: createArrayOfTypeChecker,
element: createElementTypeChecker(),
instanceOf: createInstanceTypeChecker,
node: createNodeChecker(),
objectOf: createObjectOfTypeChecker,
oneOf: createEnumTypeChecker,
oneOfType: createUnionTypeChecker,
shape: createShapeTypeChecker,
};
} else {
var productionTypeChecker = function() {
invariant(
false,
'React.PropTypes type checking code is stripped in production.'
);
};
productionTypeChecker.isRequired = productionTypeChecker;
var getProductionTypeChecker = () => productionTypeChecker;
// Keep in sync with production version above
var ReactPropTypes = {
array: getProductionTypeChecker(),
bool: getProductionTypeChecker(),
func: getProductionTypeChecker(),
number: getProductionTypeChecker(),
object: getProductionTypeChecker(),
string: getProductionTypeChecker(),
symbol: getProductionTypeChecker(),

any: getProductionTypeChecker(),
arrayOf: getProductionTypeChecker,
element: getProductionTypeChecker(),
instanceOf: getProductionTypeChecker,
node: getProductionTypeChecker(),
objectOf: getProductionTypeChecker,
oneOf: getProductionTypeChecker,
oneOfType: getProductionTypeChecker,
shape: getProductionTypeChecker,
};
}

/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/**
* 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.
*
* @emails react-core
*/

'use strict';

describe('ReactPropTypesProduction', function() {
var oldProcess;

var PropTypes;
var React;
var ReactPropTypeLocations;
var ReactTestUtils;

beforeEach(function() {
__DEV__ = false;
oldProcess = process;
global.process = {env: {NODE_ENV: 'production'}};

jest.resetModuleRegistry();
PropTypes = require('ReactPropTypes');
React = require('React');
ReactPropTypeLocations = require('ReactPropTypeLocations');
ReactTestUtils = require('ReactTestUtils');
});

afterEach(function() {
__DEV__ = true;
global.process = oldProcess;
});

function expectThrowsInProduction(declaration, value) {
var props = {testProp: value};
expect(() => {
declaration(
props,
'testProp',
'testComponent',
ReactPropTypeLocations.prop
);
}).toThrow(
/React.PropTypes type checking code is stripped in production./
);
}

describe('Primitive Types', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.array, /please/);
expectThrowsInProduction(PropTypes.array.isRequired, /please/);
expectThrowsInProduction(PropTypes.array.isRequired, null);
expectThrowsInProduction(PropTypes.array.isRequired, undefined);
expectThrowsInProduction(PropTypes.bool, []);
expectThrowsInProduction(PropTypes.bool.isRequired, []);
expectThrowsInProduction(PropTypes.bool.isRequired, null);
expectThrowsInProduction(PropTypes.bool.isRequired, undefined);
expectThrowsInProduction(PropTypes.func, false);
expectThrowsInProduction(PropTypes.func.isRequired, false);
expectThrowsInProduction(PropTypes.func.isRequired, null);
expectThrowsInProduction(PropTypes.func.isRequired, undefined);
expectThrowsInProduction(PropTypes.number, function() {});
expectThrowsInProduction(PropTypes.number.isRequired, function() {});
expectThrowsInProduction(PropTypes.number.isRequired, null);
expectThrowsInProduction(PropTypes.number.isRequired, undefined);
expectThrowsInProduction(PropTypes.string, 0);
expectThrowsInProduction(PropTypes.string.isRequired, 0);
expectThrowsInProduction(PropTypes.string.isRequired, null);
expectThrowsInProduction(PropTypes.string.isRequired, undefined);
expectThrowsInProduction(PropTypes.symbol, 0);
expectThrowsInProduction(PropTypes.symbol.isRequired, 0);
expectThrowsInProduction(PropTypes.symbol.isRequired, null);
expectThrowsInProduction(PropTypes.symbol.isRequired, undefined);
expectThrowsInProduction(PropTypes.object, '');
expectThrowsInProduction(PropTypes.object.isRequired, '');
expectThrowsInProduction(PropTypes.object.isRequired, null);
expectThrowsInProduction(PropTypes.object.isRequired, undefined);
});
});

describe('Any Type', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.any, null);
expectThrowsInProduction(PropTypes.any.isRequired, null);
expectThrowsInProduction(PropTypes.any.isRequired, undefined);
});
});

describe('ArrayOf Type', function() {
it('should be a no-op', function() {
expectThrowsInProduction(
PropTypes.arrayOf({ foo: PropTypes.string }),
{ foo: 'bar' }
);
expectThrowsInProduction(
PropTypes.arrayOf(PropTypes.number),
[1, 2, 'b']
);
expectThrowsInProduction(
PropTypes.arrayOf(PropTypes.number),
{'0': 'maybe-array', length: 1}
);
expectThrowsInProduction(PropTypes.arrayOf(PropTypes.number).isRequired, null);
expectThrowsInProduction(PropTypes.arrayOf(PropTypes.number).isRequired, undefined);
});
});

describe('Component Type', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.element, [<div />, <div />]);
expectThrowsInProduction(PropTypes.element, 123);
expectThrowsInProduction(PropTypes.element, 'foo');
expectThrowsInProduction(PropTypes.element, false);
expectThrowsInProduction(PropTypes.element.isRequired, null);
expectThrowsInProduction(PropTypes.element.isRequired, undefined);
});
});

describe('Instance Types', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.instanceOf(Date), {});
expectThrowsInProduction(PropTypes.instanceOf(Date).isRequired, {});
});
});

describe('React Component Types', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.node, {});
expectThrowsInProduction(PropTypes.node.isRequired, null);
expectThrowsInProduction(PropTypes.node.isRequired, undefined);
});
});

describe('ObjectOf Type', function() {
it('should be a no-op', function() {
expectThrowsInProduction(
PropTypes.objectOf({ foo: PropTypes.string }),
{ foo: 'bar' }
);
expectThrowsInProduction(
PropTypes.objectOf(PropTypes.number),
{a: 1, b: 2, c: 'b'}
);
expectThrowsInProduction(PropTypes.objectOf(PropTypes.number), [1, 2]);
expectThrowsInProduction(PropTypes.objectOf(PropTypes.number), null);
expectThrowsInProduction(PropTypes.objectOf(PropTypes.number), undefined);
});
});

describe('OneOf Types', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.oneOf('red', 'blue'), 'red');
expectThrowsInProduction(PropTypes.oneOf(['red', 'blue']), true);
expectThrowsInProduction(PropTypes.oneOf(['red', 'blue']), null);
expectThrowsInProduction(PropTypes.oneOf(['red', 'blue']), undefined);
});
});

describe('Union Types', function() {
it('should be a no-op', function() {
expectThrowsInProduction(
PropTypes.oneOfType(PropTypes.string, PropTypes.number),
'red'
);
expectThrowsInProduction(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
[]
);
expectThrowsInProduction(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
null
);
expectThrowsInProduction(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
undefined
);
});
});

describe('Shape Types', function() {
it('should be a no-op', function() {
expectThrowsInProduction(PropTypes.shape({}), 'some string');
expectThrowsInProduction(
PropTypes.shape({key: PropTypes.number}).isRequired,
null
);
expectThrowsInProduction(
PropTypes.shape({key: PropTypes.number}).isRequired,
undefined
);
});
});

describe('Custom validator', function() {
beforeEach(function() {
jest.resetModuleRegistry();
});

it('should not have been called', function() {
var spy = jasmine.createSpy();
var Component = React.createClass({
propTypes: {num: spy},

render: function() {
return <div />;
},
});

var instance = <Component num={5} />;
instance = ReactTestUtils.renderIntoDocument(instance);

expect(spy.calls.count()).toBe(0);
});
});
});

0 comments on commit f4c9a04

Please sign in to comment.