UNPKG

react-docgen

Version:

A CLI and toolkit to extract information from React components for documentation generation.

273 lines (220 loc) 7.31 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = getPropType; var _docblock = require("../utils/docblock"); var _getMembers = _interopRequireDefault(require("./getMembers")); var _getPropertyName = _interopRequireDefault(require("./getPropertyName")); var _isRequiredPropType = _interopRequireDefault(require("../utils/isRequiredPropType")); var _printValue = _interopRequireDefault(require("./printValue")); var _recast = _interopRequireDefault(require("recast")); var _resolveToValue = _interopRequireDefault(require("./resolveToValue")); var _resolveObjectKeysToArray = _interopRequireDefault(require("./resolveObjectKeysToArray")); var _resolveObjectValuesToArray = _interopRequireDefault(require("./resolveObjectValuesToArray")); /* * Copyright (c) 2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * * */ /*eslint no-use-before-define: 0*/ const types = _recast.default.types.namedTypes; function getEnumValues(path) { const values = []; path.get('elements').each(function (elementPath) { if (types.SpreadElement.check(elementPath.node)) { const value = (0, _resolveToValue.default)(elementPath.get('argument')); if (types.ArrayExpression.check(value.node)) { // if the SpreadElement resolved to an Array, add all their elements too return values.push(...getEnumValues(value)); } else { // otherwise we'll just print the SpreadElement itself return values.push({ value: (0, _printValue.default)(elementPath), computed: !types.Literal.check(elementPath.node) }); } } // try to resolve the array element to it's value const value = (0, _resolveToValue.default)(elementPath); return values.push({ value: (0, _printValue.default)(value), computed: !types.Literal.check(value.node) }); }); return values; } function getPropTypeOneOf(argumentPath) { const type = { name: 'enum' }; let value = (0, _resolveToValue.default)(argumentPath); if (!types.ArrayExpression.check(value.node)) { value = (0, _resolveObjectKeysToArray.default)(value) || (0, _resolveObjectValuesToArray.default)(value); if (value) { type.value = getEnumValues(value); } else { // could not easily resolve to an Array, let's print the original value type.computed = true; type.value = (0, _printValue.default)(argumentPath); } } else { type.value = getEnumValues(value); } return type; } function getPropTypeOneOfType(argumentPath) { const type = { name: 'union' }; if (!types.ArrayExpression.check(argumentPath.node)) { type.computed = true; type.value = (0, _printValue.default)(argumentPath); } else { type.value = argumentPath.get('elements').map(function (itemPath) { const descriptor = getPropType(itemPath); const docs = (0, _docblock.getDocblock)(itemPath); if (docs) { descriptor.description = docs; } return descriptor; }); } return type; } function getPropTypeArrayOf(argumentPath) { const type = { name: 'arrayOf' }; const docs = (0, _docblock.getDocblock)(argumentPath); if (docs) { type.description = docs; } const subType = getPropType(argumentPath); if (subType.name === 'unknown') { type.value = (0, _printValue.default)(argumentPath); type.computed = true; } else { type.value = subType; } return type; } function getPropTypeObjectOf(argumentPath) { const type = { name: 'objectOf' }; const docs = (0, _docblock.getDocblock)(argumentPath); if (docs) { type.description = docs; } const subType = getPropType(argumentPath); if (subType.name === 'unknown') { type.value = (0, _printValue.default)(argumentPath); type.computed = true; } else { type.value = subType; } return type; } /** * Handles shape and exact prop types */ function getPropTypeShapish(name, argumentPath) { const type = { name }; if (!types.ObjectExpression.check(argumentPath.node)) { argumentPath = (0, _resolveToValue.default)(argumentPath); } if (types.ObjectExpression.check(argumentPath.node)) { const value = {}; argumentPath.get('properties').each(function (propertyPath) { if (propertyPath.get('type').value === types.SpreadElement.name) { // It is impossible to resolve a name for a spread element return; } const descriptor = getPropType(propertyPath.get('value')); const docs = (0, _docblock.getDocblock)(propertyPath); if (docs) { descriptor.description = docs; } descriptor.required = (0, _isRequiredPropType.default)(propertyPath.get('value')); value[(0, _getPropertyName.default)(propertyPath)] = descriptor; }); type.value = value; } if (!type.value) { type.value = (0, _printValue.default)(argumentPath); type.computed = true; } return type; } function getPropTypeInstanceOf(argumentPath) { return { name: 'instanceOf', value: (0, _printValue.default)(argumentPath) }; } const simplePropTypes = ['array', 'bool', 'func', 'number', 'object', 'string', 'any', 'element', 'node', 'symbol']; const propTypes = { oneOf: getPropTypeOneOf, oneOfType: getPropTypeOneOfType, instanceOf: getPropTypeInstanceOf, arrayOf: getPropTypeArrayOf, objectOf: getPropTypeObjectOf, shape: getPropTypeShapish.bind(null, 'shape'), exact: getPropTypeShapish.bind(null, 'exact') }; /** * Tries to identify the prop type by inspecting the path for known * prop type names. This method doesn't check whether the found type is actually * from React.PropTypes. It simply assumes that a match has the same meaning * as the React.PropTypes one. * * If there is no match, "custom" is returned. */ function getPropType(path) { let descriptor; (0, _getMembers.default)(path, true).some(member => { const node = member.path.node; let name; if (types.Literal.check(node)) { name = node.value; } else if (types.Identifier.check(node) && !member.computed) { name = node.name; } if (name) { if (simplePropTypes.includes(name)) { descriptor = { name }; return true; } else if (propTypes.hasOwnProperty(name) && member.argumentsPath) { descriptor = propTypes[name](member.argumentsPath.get(0)); return true; } } }); if (!descriptor) { const node = path.node; if (types.Identifier.check(node) && simplePropTypes.includes(node.name)) { descriptor = { name: node.name }; } else if (types.CallExpression.check(node) && types.Identifier.check(node.callee) && propTypes.hasOwnProperty(node.callee.name)) { descriptor = propTypes[node.callee.name](path.get('arguments', 0)); } else { descriptor = { name: 'custom', raw: (0, _printValue.default)(path) }; } } return descriptor; }