UNPKG

react-immutable-proptypes

Version:
301 lines (260 loc) 12.1 kB
/** * 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 */ "use strict"; 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;