UNPKG

babel-plugin-flow-react-proptypes

Version:
447 lines (389 loc) 17.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = convertToPropTypes; var _util = require("./util"); function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function getObjectTypePropertyKey(node) { if (node.key.type === 'StringLiteral') { return node.key.value; } return node.key.name; } function mapGenericToRealType(node, typeParamMapping) { if (node.type === 'GenericTypeAnnotation' && node.id.type === 'Identifier') { return typeParamMapping[node.id.name] || node; } else if (node.type === 'NullableTypeAnnotation') { var realTypeAnnotation = mapGenericToRealType(node.typeAnnotation, typeParamMapping); var newNode = Object.assign({}, node, { typeAnnotation: realTypeAnnotation }); return newNode; } else if (node.type === 'UnionTypeAnnotation') { var realTypes = node.types.map(function (n) { return mapGenericToRealType(n, typeParamMapping); }); var _newNode = Object.assign({}, node, { types: realTypes }); return _newNode; } else if (node.type === 'ObjectTypeProperty') { var realValue = mapGenericToRealType(node.value, typeParamMapping); var _newNode2 = Object.assign({}, node, { value: realValue }); return _newNode2; } else if (node.type === 'ObjectTypeAnnotation') { var realProperties = node.properties.map(function (p) { return mapGenericToRealType(p, typeParamMapping); }); var _newNode3 = Object.assign({}, node, { properties: realProperties }); return _newNode3; } return node; } function convertGenericToPropTypes(node, typeParamMapping, importedTypes, internalTypes) { if (node.type === 'GenericTypeAnnotation' && node.id.type === 'Identifier' && !node.typeParameters) { return convertToPropTypes(typeParamMapping[node.id.name], importedTypes, internalTypes); } else if (node.type === 'GenericTypeAnnotation' && node.typeParameters) { var realTypeParameters = node.typeParameters.params.map(function (p) { return mapGenericToRealType(p, typeParamMapping); }); return internalTypes[node.id.name](realTypeParameters); } else if (node.type === 'ArrayTypeAnnotation') { var realElementType = mapGenericToRealType(node.elementType, typeParamMapping); var realNode = Object.assign({}, node, { elementType: realElementType }); return convertToPropTypes(realNode, importedTypes, internalTypes); } else if (node.type === 'NullableTypeAnnotation') { var result = convertGenericToPropTypes(node.typeAnnotation, typeParamMapping, importedTypes, internalTypes); result.optional = true; return result; } else if (node.type === 'UnionTypeAnnotation') { var types = node.types.map(function (n) { return mapGenericToRealType(n, typeParamMapping); }); var _realNode = Object.assign({}, node, { types: types }); return convertToPropTypes(_realNode, importedTypes, internalTypes); } else if (node.type === 'ObjectTypeAnnotation') { var properties = node.properties.map(function (n) { return mapGenericToRealType(n, typeParamMapping); }); var _realNode2 = Object.assign({}, node, { properties: properties }); return convertToPropTypes(_realNode2, importedTypes, internalTypes); } } function convertToPropTypes(node, importedTypes, internalTypes) { (0, _util.$debug)('convertToPropTypes', node); var resultPropType; if (node.type === 'TypeAlias' && node.typeParameters) { return function (types) { var typeParams = node.typeParameters.params.map(function (t) { return t.name; }); var typeParamMapping = {}; for (var i = 0; i < typeParams.length; i++) { typeParamMapping[typeParams[i]] = types[i]; } return convertGenericToPropTypes(node.right, typeParamMapping, importedTypes, internalTypes); }; } else if (node.right) { node = node.right; } if (node.type === 'OpaqueType') { node = node.impltype; } if (node.type === 'InterfaceDeclaration') { node = node.body; } if (node.type === 'ObjectTypeAnnotation') { var properties = []; if (node.indexers.length === 1 && node.properties.length === 0) { // indexed object, like `{[name: string]: number}` // recur on indexer's value type (`number` in the example above) // and emit an `objectOf` var indexer = node.indexers[0]; var subnode = indexer.value; var subresult = convertToPropTypes(subnode, importedTypes, internalTypes); subresult.isRequired = !subresult.optional; resultPropType = { type: 'objectOf', of: subresult }; } else { // not an indexed object // recurse on object properties node.properties.forEach(function (subnode) { // result may be from: // ObjectTypeProperty - {key, value} // ObjectTypeSpreadProperty - Array<{key, value}> var result = convertToPropTypes(subnode, importedTypes, internalTypes); if (subnode.leadingComments && subnode.leadingComments.length) { result.leadingComments = subnode.leadingComments; } if (Array.isArray(result)) { result.forEach(function (prop) { return properties.push(prop); }); } else { properties.push(result); } }); // return a shape resultPropType = { type: 'shape', properties: properties, isExact: node.exact }; } } else if (node.type === 'ObjectTypeProperty') { var key = getObjectTypePropertyKey(node); var value = node.value; // recurse value = convertToPropTypes(value, importedTypes, internalTypes); // handles id?: string if (value) { value.isRequired = !node.optional && !value.optional; } return { key: key, value: value }; } else if (node.type === 'ObjectTypeSpreadProperty') { var exact = (0, _util.isExact)(node.argument); var _subnode; if (exact) { _subnode = node.argument.typeParameters.params[0]; } else { _subnode = node.argument; } var spreadShape = convertToPropTypes(_subnode, importedTypes, internalTypes); if (spreadShape.type === 'raw') { return []; } var _properties = spreadShape.properties; // Unless or until the strange default behavior changes in flow (https://github.com/facebook/flow/issues/3214) // every property from spread becomes optional unless it uses `...$Exact<T>` // @see also explanation of behavior - https://github.com/facebook/flow/issues/3534#issuecomment-287580240 // @returns flattened properties from shape if (!exact) { _properties.forEach(function (prop) { return prop.value.isRequired = false; }); } return _properties; } else if (node.type === 'FunctionTypeAnnotation') resultPropType = { type: 'func' };else if (node.type === 'AnyTypeAnnotation') resultPropType = { type: 'any' }; // babylon6 Node Name else if (node.type === 'ExistentialTypeParam') resultPropType = { type: 'any' }; // babylon7 Node Name else if (node.type === 'ExistsTypeAnnotation') resultPropType = { type: 'any' };else if (node.type === 'MixedTypeAnnotation') resultPropType = { type: 'any' };else if (node.type === 'TypeofTypeAnnotation') resultPropType = { type: 'any' };else if (node.type === 'EmptyTypeAnnotation') resultPropType = { type: 'any' };else if (node.type === 'NumberTypeAnnotation') resultPropType = { type: 'number' };else if (node.type === 'StringTypeAnnotation') resultPropType = { type: 'string' };else if (node.type === 'BooleanTypeAnnotation') resultPropType = { type: 'bool' };else if (node.type === 'VoidTypeAnnotation') resultPropType = { type: 'void' };else if (node.type === 'TupleTypeAnnotation') resultPropType = { type: 'arrayOf', of: { type: 'any' } };else if (node.type === 'NullableTypeAnnotation') { resultPropType = convertToPropTypes(node.typeAnnotation, importedTypes, internalTypes); resultPropType.optional = true; } else if (node.type === 'IntersectionTypeAnnotation') { var _ref; var objectTypeAnnotations = node.types.filter(function (annotation) { return annotation.type === 'ObjectTypeAnnotation' || annotation.type === 'GenericTypeAnnotation'; }); var propTypes = objectTypeAnnotations.map(function (node) { return convertToPropTypes(node, importedTypes, internalTypes); }); var shapes = propTypes.filter(function (propType) { return propType.type === 'shape'; }); var requiresRuntimeMerge = propTypes.filter(function (propType) { return propType.type === 'raw' || propType.type === 'shape-intersect-runtime'; }); var mergedProperties = (_ref = []).concat.apply(_ref, _toConsumableArray(shapes.map(function (propType) { return propType.properties; }))); if (mergedProperties.length === 0 && requiresRuntimeMerge.length === 0) { resultPropType = { type: 'any' }; } else if (requiresRuntimeMerge.length === 0) { resultPropType = { type: 'shape', properties: mergedProperties }; } else { // TODO: properties may be a misnomer - that probably means a list of object // property specifications resultPropType = { 'type': 'shape-intersect-runtime', properties: propTypes }; } } // https://github.com/brigand/babel-plugin-flow-react-proptypes/issues/147 else if (node.type === 'GenericTypeAnnotation' && node.id.type === 'QualifiedTypeIdentifier' && node.id.qualification && node.id.qualification.name === 'React' && node.id.id && node.id.id.name === 'ElementProps') { var tp = node.typeParameters && node.typeParameters.params; if (!tp || tp.length !== 1) { throw new TypeError("babel-plugin-flow-react-proptypes expected React.ElementProps to have one type parameter"); } if (tp[0].type === 'StringLiteralTypeAnnotation') { resultPropType = { type: 'object', properties: [] }; } else if (tp[0].type === 'TypeofTypeAnnotation') { var argument = tp[0].argument; var id = argument.id; if (id.type !== 'Identifier') { throw new TypeError("babel-plugin-flow-react-proptypes expected React.ElementProps<typeof OneIdentifier>, but found some other type parameter"); } var name = id.name; resultPropType = { type: 'reference', propertyPath: [name, 'propTypes'] }; } else { throw new TypeError("babel-plugin-flow-react-proptypes expected React.ElementProps to either be e.g. React.ElementProps<'div'> or React.ElementProps<typeof SomeComponent> "); } } // Exact else if (node.type === 'GenericTypeAnnotation' && node.id.name === '$Exact') { resultPropType = { type: 'exact', properties: convertToPropTypes(node.typeParameters.params[0], importedTypes, internalTypes) }; } else if (node.type === 'GenericTypeAnnotation' || node.type === 'ArrayTypeAnnotation') { if (node.type === 'ArrayTypeAnnotation' || node.id.name === 'Array' || node.id.name === '$ReadOnlyArray') { var arrayType; if (node.type === 'ArrayTypeAnnotation') { arrayType = node.elementType; } else if (!node.typeParameters) { resultPropType = { type: 'array' }; arrayType = null; } else { arrayType = node.typeParameters.params[0]; } if (arrayType && arrayType.type === 'GenericTypeAnnotation' && arrayType.id.type === 'QualifiedTypeIdentifier' && arrayType.id.qualification.name === 'React' && (arrayType.id.id.name === 'Element' || arrayType.id.id.name === 'Node')) { resultPropType = { type: 'node' }; } else if (arrayType) { var elementType = convertToPropTypes(arrayType, importedTypes, internalTypes); elementType.isRequired = !elementType.optional; resultPropType = { type: 'arrayOf', of: elementType }; } } else if (node.type === 'GenericTypeAnnotation' && node.id.type === 'QualifiedTypeIdentifier' && node.id.qualification.name === 'React' && (node.id.id.name === 'Element' || node.id.id.name === 'Node')) { resultPropType = { type: 'node' }; } else if (node.type === 'GenericTypeAnnotation' && node.typeParameters && typeof internalTypes[node.id.name] === 'function') { resultPropType = Object.assign({}, internalTypes[node.id.name](node.typeParameters.params)); } else if (node.id && node.id.name && internalTypes[node.id.name]) { resultPropType = Object.assign({}, internalTypes[node.id.name]); } else if (node.id && node.id.name && importedTypes[node.id.name]) { resultPropType = { type: 'raw', value: importedTypes[node.id.name].accessNode }; } else if (node.id.name === 'Object') { resultPropType = { type: 'object' }; } else if (node.id.name === 'Function') { resultPropType = { type: 'func' }; } else { resultPropType = { type: 'possible-class', value: node.id }; } } else if (node.type === 'UnionTypeAnnotation') { var _node = node, types = _node.types; var typesWithoutNulls = types.filter(function (t) { return t.type !== 'NullLiteralTypeAnnotation' && t.type !== 'VoidTypeAnnotation'; }); // If a NullLiteralTypeAnnotation we know that this union type is not required. var optional = typesWithoutNulls.length !== types.length; // e.g. null | string // 'foo' | null if (typesWithoutNulls.length === 1) { resultPropType = convertToPropTypes(typesWithoutNulls[0], importedTypes, internalTypes); resultPropType.optional = optional; } else if (typesWithoutNulls.every(function (t) { return /Literal/.test(t.type); })) { // e.g. 'hello' | 5 resultPropType = { type: 'oneOf', optional: optional, options: typesWithoutNulls.map(function (_ref2) { var value = _ref2.value; return value; }) }; } else { // e.g. string | number resultPropType = { type: 'oneOfType', optional: optional, options: typesWithoutNulls.map(function (node) { return convertToPropTypes(node, importedTypes, internalTypes); }) }; } } else if (node.type in { 'StringLiteralTypeAnnotation': 0, // Babylon6 'NumericLiteralTypeAnnotation': 0, // Babylon7 'NumberLiteralTypeAnnotation': 0, 'BooleanLiteralTypeAnnotation': 0, 'NullLiteralTypeAnnotation': 0 }) { var _value = node.value; // babylon7 does not provide value for NullLiteralTypeAnnotation if (node.type === 'NullLiteralTypeAnnotation') { _value = true; } resultPropType = { type: 'oneOf', options: [_value] }; } if (resultPropType) { return resultPropType; } else { throw new Error("".concat(_util.PLUGIN_NAME, ": Encountered an unknown node in the type definition. Node: ").concat(JSON.stringify(node))); } }