'use strict'; const docgen = require('react-docgen'); function stylePropTypeHandler(documentation, path) { let propTypesPath = docgen.utils.getMemberValuePath(path, 'propTypes'); if (!propTypesPath) { return; } propTypesPath = docgen.utils.resolveToValue(propTypesPath); if (!propTypesPath || propTypesPath.node.type !== 'ObjectExpression') { return; } // Check if the there is a style prop propTypesPath.get('properties').each(function(propertyPath) { if (propertyPath.node.type !== 'Property' || docgen.utils.getPropertyName(propertyPath) !== 'style') { return; } let valuePath = docgen.utils.resolveToValue(propertyPath.get('value')); // If it's a call to StyleSheetPropType, do stuff if (valuePath.node.type !== 'CallExpression' || valuePath.node.callee.name !== 'StyleSheetPropType') { return; } // Get type of style sheet let styleSheetModule = docgen.utils.resolveToModule( valuePath.get('arguments', 0) ); if (styleSheetModule) { let propDescriptor = documentation.getPropDescriptor('style'); propDescriptor.type = {name: 'stylesheet', value: styleSheetModule}; } }); } function deprecatedPropTypeHandler(documentation, path) { let propTypesPath = docgen.utils.getMemberValuePath(path, 'propTypes'); if (!propTypesPath) { return; } propTypesPath = docgen.utils.resolveToValue(propTypesPath); if (!propTypesPath || propTypesPath.node.type !== 'ObjectExpression') { return; } // Checks for deprecatedPropType function and add deprecation info. propTypesPath.get('properties').each(function(propertyPath) { let valuePath = docgen.utils.resolveToValue(propertyPath.get('value')); // If it's a call to deprecatedPropType, do stuff if (valuePath.node.type !== 'CallExpression' || valuePath.node.callee.name !== 'deprecatedPropType') { return; } let propDescriptor = documentation.getPropDescriptor( docgen.utils.getPropertyName(propertyPath) ); // The 2nd argument of deprecatedPropType is the deprecation message. // Used printValue to get the string otherwise there was issues with template // strings. propDescriptor.deprecationMessage = docgen.utils .printValue(valuePath.get('arguments', 1)) // Remove the quotes printValue adds. .slice(1, -1); // Get the actual prop type. propDescriptor.type = docgen.utils.getPropType( valuePath.get('arguments', 0) ); }); } function typedefHandler(documentation, path) { const declarationPath = path.get('declaration'); const typePath = declarationPath.get('right'); // Name, type, description of the typedef const name = declarationPath.value.id.name; const type = { names: [typePath.node.id.name] }; const description = docgen.utils.docblock.getDocblock(path); // Get the properties for the typedef let paramsDescriptions = []; let paramsTypes; if (typePath.node.typeParameters) { const paramsPath = typePath.get('typeParameters').get('params', 0); if (paramsPath) { const properties = paramsPath.get('properties'); // Get the descriptions inside each property (are inline leading comments) paramsDescriptions = properties.map(property => docgen.utils.docblock.getDocblock(property)); // Get the property type info paramsTypes = docgen.utils.getFlowType(paramsPath); } } // Get the property type, description and value info let values = []; if (paramsTypes && paramsTypes.signature && paramsTypes.signature.properties && paramsTypes.signature.properties.length !== 0) { values = paramsTypes.signature.properties.map((property, index) => { return { type: { names: [property.value.name] }, description: paramsDescriptions[index], name: property.key, }; }); } let typedef = { name: name, description: description, type: type, values: values, }; documentation.set('typedef', typedef); } function getTypeName(type) { let typeName; switch (type.name) { case 'signature': typeName = type.type; break; case 'union': typeName = type.elements.map(getTypeName); break; default: typeName = type.alias ? type.alias : type.name; break; } return typeName; } function jsDocFormatType(entities) { let modEntities = entities; if (entities) { if (typeof entities === 'object' && entities.length) { entities.map((entity, entityIndex) => { if (entity.type) { const typeNames = [].concat(getTypeName(entity.type)); modEntities[entityIndex].type = { names: typeNames }; } }); } else { modEntities.type = [].concat(getTypeName(entities)); } } return modEntities; } function jsDocFormatHandler(documentation, path) { const methods = documentation.get('methods'); if (!methods || methods.length === 0) { return; } let modMethods = methods; methods.map((method, methodIndex) => { modMethods[methodIndex].params = jsDocFormatType(method.params); modMethods[methodIndex].returns = jsDocFormatType(method.returns); }); documentation.set('methods', modMethods); } function findExportedOrFirst(node, recast) { return docgen.resolver.findExportedComponentDefinition(node, recast) || docgen.resolver.findAllComponentDefinitions(node, recast)[0]; } function findExportedObject(ast, recast) { let objPath; recast.visit(ast, { visitAssignmentExpression: function(path) { if (!objPath && docgen.utils.isExportsOrModuleAssignment(path)) { objPath = docgen.utils.resolveToValue(path.get('right')); } return false; } }); if (objPath) { // Hack: This is easier than replicating the default propType // handler. // This converts any expression, e.g. `foo` to an object expression of // the form `{propTypes: foo}` let b = recast.types.builders; let nt = recast.types.namedTypes; let obj = objPath.node; // Hack: This is converting calls like // // Object.apply(Object.create(foo), { bar: 42 }) // // to an AST representing an object literal: // // { ...foo, bar: 42 } if (nt.CallExpression.check(obj) && recast.print(obj.callee).code === 'Object.assign') { obj = objPath.node.arguments[1]; let firstArg = objPath.node.arguments[0]; if (recast.print(firstArg.callee).code === 'Object.create') { firstArg = firstArg.arguments[0]; } obj.properties.unshift( b.spreadProperty(firstArg) ); } objPath.replace(b.objectExpression([ b.property('init', b.literal('propTypes'), obj) ])); } return objPath; } function findExportedType(ast, recast) { let types = recast.types.namedTypes; let definitions; recast.visit(ast, { visitExportNamedDeclaration: function(path) { if (path.node.declaration) { if (types.TypeAlias.check(path.node.declaration)) { if (!definitions) { definitions = []; } definitions.push(path); } } return false; } }); return definitions; } exports.stylePropTypeHandler = stylePropTypeHandler; exports.deprecatedPropTypeHandler = deprecatedPropTypeHandler; exports.typedefHandler = typedefHandler; exports.jsDocFormatHandler = jsDocFormatHandler; exports.findExportedOrFirst = findExportedOrFirst; exports.findExportedObject = findExportedObject; exports.findExportedType = findExportedType;