react-immutable-proptypes
Version:
PropType validators that work with Immutable.js.
301 lines (260 loc) • 12.1 kB
JavaScript
/**
* This is a straight rip-off of the React.js ReactPropTypes.js proptype validators,
* modified to make it possible to validate Immutable.js data.
* ImmutableTypes.listOf is patterned after React.PropTypes.arrayOf, but for Immutable.List
* ImmutableTypes.shape is based on React.PropTypes.shape, but for any Immutable.Iterable
*/
;
var Immutable = require("immutable");
var ANONYMOUS = "<<anonymous>>";
var ImmutablePropTypes;
if (process.env.NODE_ENV !== "production") {
ImmutablePropTypes = {
listOf: createListOfTypeChecker,
mapOf: createMapOfTypeChecker,
orderedMapOf: createOrderedMapOfTypeChecker,
setOf: createSetOfTypeChecker,
orderedSetOf: createOrderedSetOfTypeChecker,
stackOf: createStackOfTypeChecker,
iterableOf: createIterableOfTypeChecker,
recordOf: createRecordOfTypeChecker,
shape: createShapeChecker,
contains: createShapeChecker,
mapContains: createMapContainsChecker,
orderedMapContains: createOrderedMapContainsChecker,
// Primitive Types
list: createImmutableTypeChecker("List", Immutable.List.isList),
map: createImmutableTypeChecker("Map", Immutable.Map.isMap),
orderedMap: createImmutableTypeChecker("OrderedMap", Immutable.OrderedMap.isOrderedMap),
set: createImmutableTypeChecker("Set", Immutable.Set.isSet),
orderedSet: createImmutableTypeChecker("OrderedSet", Immutable.OrderedSet.isOrderedSet),
stack: createImmutableTypeChecker("Stack", Immutable.Stack.isStack),
seq: createImmutableTypeChecker("Seq", Immutable.Seq.isSeq),
record: createImmutableTypeChecker("Record", function (isRecord) {
return isRecord instanceof Immutable.Record;
}),
iterable: createImmutableTypeChecker("Iterable", Immutable.Iterable.isIterable)
};
} else {
var productionTypeChecker = function productionTypeChecker() {
invariant(false, "ImmutablePropTypes type checking code is stripped in production.");
};
productionTypeChecker.isRequired = productionTypeChecker;
var getProductionTypeChecker = function getProductionTypeChecker() {
return productionTypeChecker;
};
ImmutablePropTypes = {
listOf: getProductionTypeChecker,
mapOf: getProductionTypeChecker,
orderedMapOf: getProductionTypeChecker,
setOf: getProductionTypeChecker,
orderedSetOf: getProductionTypeChecker,
stackOf: getProductionTypeChecker,
iterableOf: getProductionTypeChecker,
recordOf: getProductionTypeChecker,
shape: getProductionTypeChecker,
contains: getProductionTypeChecker,
mapContains: getProductionTypeChecker,
orderedMapContains: getProductionTypeChecker,
// Primitive Types
list: productionTypeChecker,
map: productionTypeChecker,
orderedMap: productionTypeChecker,
set: productionTypeChecker,
orderedSet: productionTypeChecker,
stack: productionTypeChecker,
seq: productionTypeChecker,
record: productionTypeChecker,
iterable: productionTypeChecker
};
}
ImmutablePropTypes.iterable.indexed = createIterableSubclassTypeChecker("Indexed", Immutable.Iterable.isIndexed);
ImmutablePropTypes.iterable.keyed = createIterableSubclassTypeChecker("Keyed", Immutable.Iterable.isKeyed);
function getPropType(propValue) {
var propType = typeof propValue;
if (Array.isArray(propValue)) {
return "array";
}
if (propValue instanceof RegExp) {
// Old webkits (at least until Android 4.0) return 'function' rather than
// 'object' for typeof a RegExp. We'll normalize this here so that /bla/
// passes PropTypes.object.
return "object";
}
if (propValue instanceof Immutable.Iterable) {
return "Immutable." + propValue.toSource().split(" ")[0];
}
return propType;
}
function createChainableTypeChecker(validate) {
function checkType(isRequired, props, propName, componentName, location, propFullName) {
for (var _len = arguments.length, rest = Array(_len > 6 ? _len - 6 : 0), _key = 6; _key < _len; _key++) {
rest[_key - 6] = arguments[_key];
}
propFullName = propFullName || propName;
componentName = componentName || ANONYMOUS;
if (props[propName] == null) {
var locationName = location;
if (isRequired) {
return new Error("Required " + locationName + " `" + propFullName + "` was not specified in " + ("`" + componentName + "`."));
}
} else {
return validate.apply(undefined, [props, propName, componentName, location, propFullName].concat(rest));
}
}
var chainedCheckType = checkType.bind(null, false);
chainedCheckType.isRequired = checkType.bind(null, true);
return chainedCheckType;
}
function createImmutableTypeChecker(immutableClassName, immutableClassTypeValidator) {
function validate(props, propName, componentName, location, propFullName) {
var propValue = props[propName];
if (!immutableClassTypeValidator(propValue)) {
var propType = getPropType(propValue);
return new Error("Invalid " + location + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected `" + immutableClassName + "`."));
}
return null;
}
return createChainableTypeChecker(validate);
}
function createIterableSubclassTypeChecker(subclassName, validator) {
return createImmutableTypeChecker("Iterable." + subclassName, function (propValue) {
return Immutable.Iterable.isIterable(propValue) && validator(propValue);
});
}
function createIterableTypeChecker(typeChecker, immutableClassName, immutableClassTypeValidator) {
function validate(props, propName, componentName, location, propFullName) {
for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
rest[_key - 5] = arguments[_key];
}
var propValue = props[propName];
if (!immutableClassTypeValidator(propValue)) {
var locationName = location;
var propType = getPropType(propValue);
return new Error("Invalid " + locationName + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected an Immutable.js " + immutableClassName + "."));
}
if (typeof typeChecker !== "function") {
return new Error("Invalid typeChecker supplied to `" + componentName + "` " + ("for propType `" + propFullName + "`, expected a function."));
}
var propValues = propValue.valueSeq().toArray();
for (var i = 0, len = propValues.length; i < len; i++) {
var error = typeChecker.apply(undefined, [propValues, i, componentName, location, "" + propFullName + "[" + i + "]"].concat(rest));
if (error instanceof Error) {
return error;
}
}
}
return createChainableTypeChecker(validate);
}
function createKeysTypeChecker(typeChecker) {
function validate(props, propName, componentName, location, propFullName) {
for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
rest[_key - 5] = arguments[_key];
}
var propValue = props[propName];
if (typeof typeChecker !== "function") {
return new Error("Invalid keysTypeChecker (optional second argument) supplied to `" + componentName + "` " + ("for propType `" + propFullName + "`, expected a function."));
}
var keys = propValue.keySeq().toArray();
for (var i = 0, len = keys.length; i < len; i++) {
var error = typeChecker.apply(undefined, [keys, i, componentName, location, "" + propFullName + " -> key(" + keys[i] + ")"].concat(rest));
if (error instanceof Error) {
return error;
}
}
}
return createChainableTypeChecker(validate);
}
function createListOfTypeChecker(typeChecker) {
return createIterableTypeChecker(typeChecker, "List", Immutable.List.isList);
}
function createMapOfTypeCheckerFactory(valuesTypeChecker, keysTypeChecker, immutableClassName, immutableClassTypeValidator) {
function validate() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return createIterableTypeChecker(valuesTypeChecker, immutableClassName, immutableClassTypeValidator).apply(undefined, args) || keysTypeChecker && createKeysTypeChecker(keysTypeChecker).apply(undefined, args);
}
return createChainableTypeChecker(validate);
}
function createMapOfTypeChecker(valuesTypeChecker, keysTypeChecker) {
return createMapOfTypeCheckerFactory(valuesTypeChecker, keysTypeChecker, "Map", Immutable.Map.isMap);
}
function createOrderedMapOfTypeChecker(valuesTypeChecker, keysTypeChecker) {
return createMapOfTypeCheckerFactory(valuesTypeChecker, keysTypeChecker, "OrderedMap", Immutable.OrderedMap.isOrderedMap);
}
function createSetOfTypeChecker(typeChecker) {
return createIterableTypeChecker(typeChecker, "Set", Immutable.Set.isSet);
}
function createOrderedSetOfTypeChecker(typeChecker) {
return createIterableTypeChecker(typeChecker, "OrderedSet", Immutable.OrderedSet.isOrderedSet);
}
function createStackOfTypeChecker(typeChecker) {
return createIterableTypeChecker(typeChecker, "Stack", Immutable.Stack.isStack);
}
function createIterableOfTypeChecker(typeChecker) {
return createIterableTypeChecker(typeChecker, "Iterable", Immutable.Iterable.isIterable);
}
function createRecordOfTypeChecker(recordKeys) {
function validate(props, propName, componentName, location, propFullName) {
for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
rest[_key - 5] = arguments[_key];
}
var propValue = props[propName];
if (!(propValue instanceof Immutable.Record)) {
var propType = getPropType(propValue);
var locationName = location;
return new Error("Invalid " + locationName + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected an Immutable.js Record."));
}
for (var key in recordKeys) {
var checker = recordKeys[key];
if (!checker) {
continue;
}
var mutablePropValue = propValue.toObject();
var error = checker.apply(undefined, [mutablePropValue, key, componentName, location, "" + propFullName + "." + key].concat(rest));
if (error) {
return error;
}
}
}
return createChainableTypeChecker(validate);
}
// there is some irony in the fact that shapeTypes is a standard hash and not an immutable collection
function createShapeTypeChecker(shapeTypes) {
var immutableClassName = arguments[1] === undefined ? "Iterable" : arguments[1];
var immutableClassTypeValidator = arguments[2] === undefined ? Immutable.Iterable.isIterable : arguments[2];
function validate(props, propName, componentName, location, propFullName) {
for (var _len = arguments.length, rest = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
rest[_key - 5] = arguments[_key];
}
var propValue = props[propName];
if (!immutableClassTypeValidator(propValue)) {
var propType = getPropType(propValue);
var locationName = location;
return new Error("Invalid " + locationName + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected an Immutable.js " + immutableClassName + "."));
}
var mutablePropValue = propValue.toObject();
for (var key in shapeTypes) {
var checker = shapeTypes[key];
if (!checker) {
continue;
}
var error = checker.apply(undefined, [mutablePropValue, key, componentName, location, "" + propFullName + "." + key].concat(rest));
if (error) {
return error;
}
}
}
return createChainableTypeChecker(validate);
}
function createShapeChecker(shapeTypes) {
return createShapeTypeChecker(shapeTypes);
}
function createMapContainsChecker(shapeTypes) {
return createShapeTypeChecker(shapeTypes, "Map", Immutable.Map.isMap);
}
function createOrderedMapContainsChecker(shapeTypes) {
return createShapeTypeChecker(shapeTypes, "OrderedMap", Immutable.OrderedMap.isOrderedMap);
}
module.exports = ImmutablePropTypes;