@playlyfe/babel-plugin-flow-react-proptypes
Advanced tools
Comparing version 3.4.3-patch.1 to 13.0.1-patch.1
@@ -19,2 +19,74 @@ 'use strict'; | ||
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) { | ||
@@ -24,2 +96,18 @@ (0, _util.$debug)('convertToPropTypes', node); | ||
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]; | ||
} | ||
// console.log(node.right, typeParamMapping); | ||
return convertGenericToPropTypes(node.right, typeParamMapping, importedTypes, internalTypes); | ||
}; | ||
} else if (node.right) { | ||
node = node.right; | ||
} | ||
if (node.type === 'ObjectTypeAnnotation') { | ||
@@ -34,3 +122,5 @@ var properties = []; | ||
var result = convertToPropTypes(subnode, importedTypes, internalTypes); | ||
if (subnode.leadingComments && subnode.leadingComments.length) { | ||
result.leadingComments = subnode.leadingComments; | ||
} | ||
if (Array.isArray(result)) { | ||
@@ -83,114 +173,162 @@ result.forEach(function (prop) { | ||
return _properties; | ||
} else if (node.type === 'FunctionTypeAnnotation') resultPropType = { type: 'func' };else if (node.type === 'AnyTypeAnnotation') resultPropType = { type: 'any' };else if (node.type === 'ExistentialTypeParam') resultPropType = { type: 'any' };else if (node.type === 'MixedTypeAnnotation') resultPropType = { type: 'any' };else if (node.type === 'TypeofTypeAnnotation') 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; | ||
} 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 === '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 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 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; | ||
}))); | ||
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 }; | ||
} | ||
} | ||
// 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') { | ||
var arrayType = void 0; | ||
if (node.type === 'ArrayTypeAnnotation') { | ||
arrayType = node.elementType; | ||
if (mergedProperties.length === 0 && requiresRuntimeMerge.length === 0) { | ||
resultPropType = { type: 'any' }; | ||
} else if (requiresRuntimeMerge.length === 0) { | ||
resultPropType = { 'type': 'shape', properties: mergedProperties }; | ||
} else { | ||
arrayType = node.typeParameters.params[0]; | ||
// TODO: properties may be a misnomer - that probably means a list of object | ||
// property specifications | ||
resultPropType = { 'type': 'shape-intersect-runtime', properties: propTypes }; | ||
} | ||
if (arrayType.type === 'GenericTypeAnnotation' && arrayType.id.type === 'QualifiedTypeIdentifier' && arrayType.id.qualification.name === 'React' && arrayType.id.id.name === 'Element') { | ||
resultPropType = { type: 'node' }; | ||
} else { | ||
resultPropType = { type: 'arrayOf', of: convertToPropTypes(arrayType, importedTypes, internalTypes) }; | ||
} | ||
} 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] }; | ||
} 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 types = node.types; | ||
// 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; | ||
var typesWithoutNulls = types.filter(function (t) { | ||
return t.type !== 'NullLiteralTypeAnnotation'; | ||
}); | ||
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') { | ||
var arrayType = void 0; | ||
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) { | ||
resultPropType = { type: 'arrayOf', of: convertToPropTypes(arrayType, importedTypes, internalTypes) }; | ||
} | ||
} 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] }; | ||
} 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; | ||
// 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, | ||
'NumericLiteralTypeAnnotation': 0, | ||
'BooleanLiteralTypeAnnotation': 0, | ||
'NullLiteralTypeAnnotation': 0 | ||
}) { | ||
resultPropType = { type: 'oneOf', options: [node.value] }; | ||
} | ||
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) { | ||
@@ -197,0 +335,0 @@ return resultPropType; |
282
lib/index.js
@@ -18,5 +18,14 @@ 'use strict'; | ||
var importedTypes = {}; | ||
var exportedTypes = {}; | ||
var suppress = false; | ||
var omitRuntimeTypeExport = false; | ||
var SUPPRESS_STRING = 'no babel-plugin-flow-react-proptypes'; | ||
// The template to use for the dead code elimination check | ||
// if it passes, the prop types will be removed | ||
// currently the 'deadCode' option is off by default, but this may change | ||
var DEFAULT_DCE = 'process.env.NODE_ENV === \'production\''; | ||
// General control flow: | ||
@@ -45,18 +54,34 @@ // Parse flow type annotations in index.js | ||
var getFunctionalComponentTypeProps = function getFunctionalComponentTypeProps(path) { | ||
// Check if this looks like a stateless react component with PropType reference: | ||
var firstParam = path.node.params[0]; | ||
var typeAnnotation = firstParam && firstParam.typeAnnotation && firstParam.typeAnnotation.typeAnnotation; | ||
module.exports = function flowReactPropTypes(babel) { | ||
var t = babel.types; | ||
if (!typeAnnotation) { | ||
(0, _util.$debug)('Found stateless component without type definition'); | ||
return; | ||
var opts = {}; | ||
var _templateCache = {}; | ||
function getDcePredicate() { | ||
// opts.deadCode could be a boolean (true for DEFAULT_DCE), or a string to be | ||
// used as a template | ||
// if it's falsy, then just return node without any wrapper | ||
if (!opts.deadCode) return null; | ||
// cache the template since it's going to be used a lot | ||
var templateCode = typeof opts.deadCode === 'string' ? opts.deadCode : DEFAULT_DCE; | ||
if (!_templateCache[templateCode]) { | ||
_templateCache[templateCode] = babel.template(templateCode); | ||
} | ||
// return a ternary | ||
var predicate = _templateCache[templateCode]({}).expression; | ||
return predicate; | ||
} | ||
return getPropsForTypeAnnotation(typeAnnotation); | ||
}; | ||
function wrapInDceCheck(node) { | ||
var predicate = getDcePredicate(node); | ||
if (!predicate) return node; | ||
module.exports = function flowReactPropTypes(babel) { | ||
var t = babel.types; | ||
var conditional = t.conditionalExpression(predicate, t.nullLiteral(), node); | ||
return conditional; | ||
} | ||
var isFunctionalReactComponent = function isFunctionalReactComponent(path) { | ||
@@ -77,2 +102,51 @@ if ((path.type === 'ArrowFunctionExpression' || path.type === 'FunctionExpression') && !path.parent.id) { | ||
/** | ||
* Adds propTypes or contextTypes annotations to code | ||
* | ||
* Extracts some shared logic from `annotate`. | ||
* | ||
* @param path | ||
* @param name | ||
* @param attribute - target member name ('propTypes' or 'contextTypes') | ||
* @param typesOrVar - propsOrVar / contextOrVar value | ||
*/ | ||
var addAnnotationsToAST = function addAnnotationsToAST(path, name, attribute, typesOrVar) { | ||
var attachPropTypesAST = void 0; | ||
// if type was exported, use the declared variable | ||
var valueNode = null; | ||
if (typeof typesOrVar === 'string') { | ||
valueNode = t.identifier(typesOrVar); | ||
var inner = t.assignmentExpression('=', t.memberExpression(t.identifier(name), t.identifier(attribute)), valueNode); | ||
if (attribute === 'propTypes') { | ||
inner = wrapInDceCheck(inner); | ||
} | ||
attachPropTypesAST = t.expressionStatement(inner); | ||
} | ||
// type was not exported, generate | ||
else { | ||
var propTypesAST = (0, _makePropTypesAst.makePropTypesAstForPropTypesAssignment)(typesOrVar); | ||
valueNode = propTypesAST; | ||
if (attribute === 'propTypes') { | ||
valueNode = wrapInDceCheck(valueNode); | ||
} | ||
if (propTypesAST == null) { | ||
return; | ||
} | ||
attachPropTypesAST = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(name), t.identifier(attribute)), valueNode)); | ||
} | ||
if (opts.useStatic && (path.type === 'ClassDeclaration' || path.type === 'ClassExpression')) { | ||
var newNode = t.classProperty(t.identifier(attribute), valueNode); | ||
newNode.static = true; | ||
path.node.body.body.push(newNode); | ||
} else { | ||
path.insertAfter(attachPropTypesAST); | ||
} | ||
}; | ||
/** | ||
* Called when visiting a node. | ||
@@ -85,27 +159,31 @@ * | ||
* @param path | ||
* @param props | ||
* @param propsOrVar - props or exported props variable reference | ||
* @param contextOrVar - context or exported context variable reference | ||
*/ | ||
var annotate = function annotate(path, props) { | ||
var annotate = function annotate(path, propsOrVar) { | ||
var contextOrVar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | ||
var name = void 0; | ||
var targetPath = void 0; | ||
if (path.type === 'ArrowFunctionExpression' || path.type === 'FunctionExpression') { | ||
if (opts.useStatic && (path.type === 'ClassDeclaration' || path.type === 'ClassExpression')) { | ||
targetPath = path; | ||
} else if (path.type === 'ArrowFunctionExpression' || path.type === 'FunctionExpression') { | ||
name = path.parent.id.name; | ||
var basePath = path.parentPath.parentPath; | ||
targetPath = t.isExportDeclaration(basePath.parent) ? basePath.parentPath : basePath; | ||
} else { | ||
} else if (path.node.id) { | ||
name = path.node.id.name; | ||
targetPath = ['Program', 'BlockStatement'].indexOf(path.parent.type) >= 0 ? path : path.parentPath; | ||
} else { | ||
throw new Error('babel-plugin-flow-react-proptypes attempted to add propTypes to a function/class with no name'); | ||
} | ||
if (!props) { | ||
throw new Error('Did not find type annotation for ' + name); | ||
if (propsOrVar) { | ||
addAnnotationsToAST(targetPath, name, 'propTypes', propsOrVar); | ||
} | ||
var propTypesAST = (0, _makePropTypesAst.makePropTypesAstForPropTypesAssignment)(props); | ||
if (propTypesAST == null) { | ||
return; | ||
if (contextOrVar) { | ||
addAnnotationsToAST(targetPath, name, 'contextTypes', contextOrVar); | ||
} | ||
var attachPropTypesAST = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(name), t.identifier('propTypes')), propTypesAST)); | ||
targetPath.insertAfter(attachPropTypesAST); | ||
}; | ||
@@ -117,3 +195,3 @@ | ||
* Determines if a function is a functional react component and | ||
* inserts the proptypes AST via `annotate`. | ||
* inserts the proptypes and contexttypes AST via `annotate`. | ||
* | ||
@@ -126,6 +204,29 @@ * @param path | ||
} | ||
var props = getFunctionalComponentTypeProps(path); | ||
if (props) { | ||
annotate(path, props); | ||
// Check if this looks like a stateless react component with PropType reference: | ||
var firstParam = path.node.params[0]; | ||
var typeAnnotation = firstParam && firstParam.typeAnnotation && firstParam.typeAnnotation.typeAnnotation; | ||
// Check if the component has context annotations | ||
var secondParam = path.node.params[1]; | ||
var contextAnnotation = secondParam && secondParam.typeAnnotation && secondParam.typeAnnotation.typeAnnotation; | ||
var propsOrVar = null; | ||
if (!typeAnnotation) { | ||
(0, _util.$debug)('Found stateless component without type definition'); | ||
} else { | ||
propsOrVar = typeAnnotation.id && exportedTypes[typeAnnotation.id.name] ? exportedTypes[typeAnnotation.id.name] : getPropsForTypeAnnotation(typeAnnotation); | ||
} | ||
var contextOrVar = void 0; | ||
if (contextAnnotation) { | ||
contextOrVar = contextAnnotation.id && exportedTypes[contextAnnotation.id.name] ? exportedTypes[contextAnnotation.id.name] : getPropsForTypeAnnotation(contextAnnotation); | ||
} else { | ||
contextOrVar = null; | ||
} | ||
if (propsOrVar) { | ||
annotate(path, propsOrVar, contextOrVar); | ||
} | ||
}; | ||
@@ -136,7 +237,10 @@ | ||
Program: function Program(path, _ref) { | ||
var opts = _ref.opts; | ||
var _opts = _ref.opts; | ||
opts = _opts; | ||
internalTypes = {}; | ||
importedTypes = {}; | ||
exportedTypes = {}; | ||
suppress = false; | ||
omitRuntimeTypeExport = opts.omitRuntimeTypeExport || false; | ||
var directives = path.node.directives; | ||
@@ -160,5 +264,3 @@ if (directives && directives.length) { | ||
(0, _util.$debug)('TypeAlias found'); | ||
var right = path.node.right; | ||
var typeAliasName = path.node.id.name; | ||
@@ -169,6 +271,11 @@ if (!typeAliasName) { | ||
var propTypes = convertNodeToPropTypes(right); | ||
var propTypes = convertNodeToPropTypes(path.node); | ||
internalTypes[typeAliasName] = propTypes; | ||
}, | ||
ClassDeclaration: function ClassDeclaration(path) { | ||
"ClassExpression|ClassDeclaration": function ClassExpressionClassDeclaration(path) { | ||
if (!opts.useStatic && path.node.type === 'ClassExpression') return; | ||
if (path.node.BPFRP_HIT_FOR_CLASS_ANNOTATION) return; | ||
path.node.BPFRP_HIT_FOR_CLASS_ANNOTATION = true; | ||
if (suppress) return; | ||
@@ -186,17 +293,59 @@ var superClass = path.node.superClass; | ||
// And have type as property annotations or Component<void, Props, void> | ||
var propTypes = null, | ||
contextTypes = null; | ||
// And have type as property annotations | ||
path.node.body.body.forEach(function (bodyNode) { | ||
if (bodyNode && bodyNode.key.name === 'props' && bodyNode.typeAnnotation) { | ||
var props = getPropsForTypeAnnotation(bodyNode.typeAnnotation.typeAnnotation); | ||
return annotate(path, props); | ||
var annotation = bodyNode.typeAnnotation.typeAnnotation; | ||
var props = getPropsForTypeAnnotation(annotation); | ||
if (!props) { | ||
throw new TypeError('Couldn\'t process \`class { props: This }`'); | ||
} | ||
propTypes = props; | ||
return; | ||
} | ||
if (bodyNode && bodyNode.key.name === 'context' && bodyNode.typeAnnotation) { | ||
var _annotation = bodyNode.typeAnnotation.typeAnnotation; | ||
var context = getPropsForTypeAnnotation(_annotation); | ||
if (!context) { | ||
throw new TypeError('Couldn\'t process \`class { context: This }`'); | ||
} | ||
contextTypes = context; | ||
} | ||
}); | ||
// super type parameter | ||
var secondSuperParam = path.node.superTypeParameters && path.node.superTypeParameters.params[1]; | ||
// or Component<void, Props, Context> | ||
var secondSuperParam = getPropsTypeParam(path.node); | ||
if (secondSuperParam && secondSuperParam.type === 'GenericTypeAnnotation') { | ||
var typeAliasName = secondSuperParam.id.name; | ||
var props = internalTypes[typeAliasName]; | ||
return annotate(path, props); | ||
if (typeAliasName === 'Object') return; | ||
var props = internalTypes[typeAliasName] || importedTypes[typeAliasName]; | ||
if (!props) { | ||
throw new TypeError('Couldn\'t find type "' + typeAliasName + '"'); | ||
} | ||
propTypes = props; | ||
} | ||
if (secondSuperParam && secondSuperParam.type === 'ObjectTypeAnnotation') { | ||
propTypes = (0, _convertToPropTypes2.default)(secondSuperParam, importedTypes, internalTypes); | ||
} | ||
var thirdSuperParam = getContextTypeParam(path.node); | ||
if (thirdSuperParam && thirdSuperParam.type === 'GenericTypeAnnotation') { | ||
var _typeAliasName = thirdSuperParam.id.name; | ||
if (_typeAliasName === 'Object') return; | ||
var _props = internalTypes[_typeAliasName] || importedTypes[_typeAliasName]; | ||
if (!_props) { | ||
throw new TypeError('Couldn\'t find type "' + _typeAliasName + '"'); | ||
} | ||
contextTypes = _props; | ||
} | ||
annotate(path, propTypes, contextTypes); | ||
}, | ||
@@ -244,5 +393,22 @@ FunctionExpression: function FunctionExpression(path) { | ||
var exportAst = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('defineProperty')), [t.identifier('exports'), t.stringLiteral((0, _util.getExportNameForType)(name)), t.objectExpression([t.objectProperty(t.identifier('value'), propTypesAst), t.objectProperty(t.identifier('configurable'), t.booleanLiteral(true))])])); | ||
var conditionalExportsAst = t.ifStatement(t.binaryExpression('!==', t.unaryExpression('typeof', t.identifier('exports')), t.stringLiteral('undefined')), exportAst); | ||
path.insertAfter(conditionalExportsAst); | ||
// create a variable for reuse | ||
var exportedName = (0, _util.getExportNameForType)(name); | ||
exportedTypes[name] = exportedName; | ||
var variableDeclarationAst = t.variableDeclaration('var', [t.variableDeclarator(t.identifier(exportedName), wrapInDceCheck(propTypesAst))]); | ||
path.insertBefore(variableDeclarationAst); | ||
if (!omitRuntimeTypeExport) { | ||
// add the variable to the exports | ||
var exportAst = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('defineProperty')), [t.identifier('exports'), t.stringLiteral((0, _util.getExportNameForType)(name)), t.objectExpression([t.objectProperty(t.identifier('value'), t.identifier(exportedName)), t.objectProperty(t.identifier('configurable'), t.booleanLiteral(true))])])); | ||
var exportsDefinedCondition = t.binaryExpression('!==', t.unaryExpression('typeof', t.identifier('exports')), t.stringLiteral('undefined')); | ||
var ifCond = exportsDefinedCondition; | ||
if (opts.deadCode) { | ||
var dceConditional = t.unaryExpression('!', getDcePredicate()); | ||
ifCond = t.logicalExpression('&&', dceConditional, ifCond); | ||
} | ||
var conditionalExportsAst = t.ifStatement(ifCond, exportAst); | ||
path.insertAfter(conditionalExportsAst); | ||
} | ||
}, | ||
@@ -271,2 +437,10 @@ ImportDeclaration: function ImportDeclaration(path) { | ||
importedTypes[typeName] = (0, _util.getExportNameForType)(typeName); | ||
// https://github.com/brigand/babel-plugin-flow-react-proptypes/issues/129 | ||
if (node.source.value === 'react' && typeName === 'ComponentType') { | ||
var ast = t.variableDeclaration('var', [t.variableDeclarator(t.identifier((0, _util.getExportNameForType)(typeName)), t.memberExpression(t.callExpression(t.identifier('require'), [t.stringLiteral('prop-types')]), t.identifier('func')))]); | ||
path.insertAfter(ast); | ||
return; | ||
} | ||
var variableDeclarationAst = t.variableDeclaration('var', [t.variableDeclarator( | ||
@@ -316,2 +490,26 @@ // TODO: use local import name? | ||
}; | ||
}; | ||
}; | ||
function getPropsTypeParam(node) { | ||
if (!node) return null; | ||
if (!node.superTypeParameters) return null; | ||
var superTypes = node.superTypeParameters; | ||
if (superTypes.params.length === 2) { | ||
return superTypes.params[0]; | ||
} else if (superTypes.params.length === 3) { | ||
return superTypes.params[0]; | ||
} else if (superTypes.params.length === 1) { | ||
return superTypes.params[0]; | ||
} | ||
return null; | ||
} | ||
function getContextTypeParam(node) { | ||
if (!node) return null; | ||
if (!node.superTypeParameters) return null; | ||
var superTypes = node.superTypeParameters; | ||
if (superTypes.params.length === 3) { | ||
return superTypes.params[2]; | ||
} | ||
return null; | ||
} |
@@ -29,2 +29,4 @@ 'use strict'; | ||
var anyTemplate = (0, _babelTemplate2.default)('\n(props, propName, componentName) => {\n if (!Object.prototype.hasOwnProperty.call(props, propName)) {\n throw new Error(`Prop \\`${propName}\\` has type \'any\' or \'mixed\', but was not provided to \\`${componentName}\\`. Pass undefined or any other value.`);\n }\n}\n'); | ||
/** | ||
@@ -43,11 +45,14 @@ * Top-level function to generate prop-types AST. | ||
function makePropTypesAstForPropTypesAssignment(propTypeData) { | ||
var node = null; | ||
if (propTypeData.type === 'shape-intersect-runtime') { | ||
// For top-level usage, e.g. Foo.proptype, return | ||
// an expression returning an object. | ||
return makeObjectMergeAstForShapeIntersectRuntime(propTypeData); | ||
node = makeObjectMergeAstForShapeIntersectRuntime(propTypeData); | ||
} else if (propTypeData.type === 'shape') { | ||
return makeObjectAstForShape(propTypeData); | ||
node = makeObjectAstForShape(propTypeData); | ||
} else if (propTypeData.type === 'raw') { | ||
return makeObjectAstForRaw(propTypeData); | ||
node = makeObjectAstForRaw(propTypeData); | ||
} | ||
return node; | ||
}; | ||
@@ -164,5 +169,10 @@ | ||
var key = _ref.key, | ||
value = _ref.value; | ||
value = _ref.value, | ||
leadingComments = _ref.leadingComments; | ||
return t.objectProperty(t.identifier(key), makePropType(value)); | ||
var node = t.objectProperty(t.identifier(key), makePropType(value)); | ||
if (leadingComments) { | ||
node.leadingComments = leadingComments; | ||
} | ||
return node; | ||
}); | ||
@@ -241,4 +251,12 @@ return t.objectExpression(rootProperties); | ||
if (method === 'any' || method === 'string' || method === 'number' || method === 'bool' || method === 'object' || method === 'array' || method === 'func' || method === 'node') { | ||
if (method === 'string' || method === 'number' || method === 'bool' || method === 'object' || method === 'array' || method === 'func' || method === 'node') { | ||
node = t.memberExpression(node, t.identifier(method)); | ||
} else if (method === 'any') { | ||
markFullExpressionAsRequired = false; | ||
if (data.isRequired) { | ||
node = anyTemplate().expression; | ||
} else { | ||
node = t.memberExpression(node, t.identifier(method)); | ||
} | ||
} else if (method === 'raw') { | ||
@@ -263,5 +281,10 @@ markFullExpressionAsRequired = false; | ||
var key = _ref2.key, | ||
value = _ref2.value; | ||
value = _ref2.value, | ||
leadingComments = _ref2.leadingComments; | ||
return t.objectProperty(t.identifier(key), makePropType(value)); | ||
var node = t.objectProperty(t.identifier(key), makePropType(value)); | ||
if (leadingComments) { | ||
node.leadingComments = leadingComments; | ||
} | ||
return node; | ||
}); | ||
@@ -289,2 +312,9 @@ if (isExact || data.isExact) { | ||
}))]); | ||
} else if (method === 'reference') { | ||
var pp = data.propertyPath.slice(); | ||
var valueNode = t.identifier(pp.shift()); | ||
while (pp.length) { | ||
valueNode = t.memberExpression(valueNode, t.identifier(pp.shift())); | ||
} | ||
node = t.callExpression(t.memberExpression(node, t.identifier('shape')), [valueNode]); | ||
} else if (method === 'void') { | ||
@@ -291,0 +321,0 @@ markFullExpressionAsRequired = false; |
{ | ||
"name": "@playlyfe/babel-plugin-flow-react-proptypes", | ||
"version": "3.4.3-patch.1", | ||
"version": "13.0.1-patch.1", | ||
"description": "converts flow types to react proptypes", | ||
@@ -9,3 +9,3 @@ "main": "lib/index.js", | ||
"test": "npm run build && jest --coverage", | ||
"build": "babel src/ --out-dir lib/ --ignore src/__tests__ --presets es2015,stage-1,react", | ||
"build": "cross-env BABEL_ENV=production babel src/ --out-dir lib/ --ignore src/__tests__ --presets es2015,stage-1,react", | ||
"predeploy": "npm run build && npm run lint", | ||
@@ -44,11 +44,15 @@ "prepublish": "npm run build && npm run lint", | ||
"babel-preset-react": "^6.24.1", | ||
"babel-preset-react-native": "^4.0.0", | ||
"babel-preset-stage-1": "^6.24.1", | ||
"chalk": "^1.1.3", | ||
"coveralls": "^2.13.1", | ||
"cross-env": "^5.0.5", | ||
"eslint": "^4.0.0", | ||
"eslint-plugin-react": "^7.0.1", | ||
"jest": "^20.0.4", | ||
"jest-environment-node-debug": "^2.0.0", | ||
"prettier": "^1.4.4", | ||
"react": "^15.5.4", | ||
"react-test-renderer": "^15.5.4" | ||
"react-test-renderer": "^15.5.4", | ||
"uglify-js": "^3.2.2" | ||
}, | ||
@@ -55,0 +59,0 @@ "dependencies": { |
@@ -119,2 +119,3 @@ A babel plugin to generate React PropTypes definitions from Flow type declarations. | ||
}; | ||
exports.default = Foo; | ||
@@ -137,3 +138,3 @@ ``` | ||
Also install the prop-types package. This is required for React `>=15.5.0`. For earlier React versions | ||
you can use version `0.21.0` of this plugin, which doesn't use the prop-types pacakge. | ||
you can use version `0.21.0` of this plugin, which doesn't use the prop-types package. | ||
@@ -166,2 +167,40 @@ ```sh | ||
## useStatic | ||
When combining it with some plugins, such as react-transform (also used by react-native in development mode), the way we normally add `propTypes` doesn't work. You can enable the `useStatic` option to cause us to generate code like: | ||
```js | ||
class C extends Component<Props> { | ||
static propTypes = { x: require('prop-types').string } | ||
} | ||
``` | ||
Add the option to your babel config. | ||
```json | ||
{ | ||
"presets": ["..."], | ||
"plugins": [["flow-react-proptypes", { "useStatic": true }]] | ||
} | ||
``` | ||
## deadCode | ||
The deadCode option (disabled by default) adds a predicate to the code allowing both your propTypes definitions and potentially the | ||
entire 'prop-types' package to be excluded in certain builds. Unlike specifying this plugin in the development env, mentioned above, | ||
this also works for packages published to npm. | ||
```json | ||
"plugins": [["flow-react-proptypes", { "deadCode": true }]] | ||
``` | ||
The value of `true` is short for `process.env.NODE_ENV === 'production'`. You can alternatively pass any JavaScript expression. If the expression | ||
returns a truthy value, then the propTypes will be removed. This works because e.g. webpack will subsitute the value of `process.env.NODE_ENV` with `'production'`, resulting in the condition being `'production' === 'production'`, and then a minifer sees that the code we're generating can't be executed, and strips it, and the `require('prop-types')` code out of the final bundle. | ||
Example of specifying a custom expression: | ||
```json | ||
"plugins": [["flow-react-proptypes", { "deadCode": "__PROD__" }]] | ||
``` | ||
## Suppression | ||
@@ -174,3 +213,3 @@ This plugin isn't perfect. You can disable it for an entire file with this directive (including quotes): | ||
Specifically for react-native you can disable this for files in `node_modules` with the `ignoreNodeModules` config option. | ||
Specifically for react-native you can disable this for files in `node_modules` with the `ignoreNodeModules` config option. In react-native, you'll also want the `useStatic` option. | ||
@@ -180,3 +219,3 @@ ```json | ||
"presets": ["..."], | ||
"plugins": [["flow-react-proptypes", {"ignoreNodeModules": true}]] | ||
"plugins": [["flow-react-proptypes", { "ignoreNodeModules": true }]] | ||
} | ||
@@ -193,2 +232,28 @@ ``` | ||
## Minimizing production bundle size | ||
In production, omitting props and minimizing bundle size can be done with the additon of two things: | ||
1. Add the [transform-react-remove-prop-types](https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types) plugin | ||
2. Omit exported types to allow for dead code pruning | ||
There are cases where a library wishes to `export type` making types available in `*.js.flow` shadow files, | ||
but these may have no other purpose during runtime. If you wish to omit the corresponding export of | ||
the generated flow types, using this option with the | ||
[transform-react-remove-prop-types](https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types) | ||
plugin will allow for the smallest production bundle size. | ||
An example snippet from a `.babelrc`: | ||
```json | ||
"production": { | ||
"plugins": [ | ||
["transform-react-remove-prop-types", { | ||
"mode": "wrap", | ||
"plugins": [ | ||
["babel-plugin-flow-react-proptypes", {"omitRuntimeTypeExport": true}], | ||
"babel-plugin-transform-flow-strip-types", | ||
] | ||
}] | ||
] | ||
} | ||
``` | ||
## Custom generator function | ||
@@ -211,2 +276,1 @@ ```js | ||
const FooPropTypes = toPropTypes((t: FooProps) => t); | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
67656
1114
270
19