UNPKG

react-docgen

Version:

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

133 lines (107 loc) 4.62 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveObjectToPropMap = resolveObjectToPropMap; exports.default = resolveObjectValuesToArray; var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread")); var _recast = _interopRequireDefault(require("recast")); var _resolveToValue = _interopRequireDefault(require("./resolveToValue")); /* * Copyright (c) 2017, 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. * * * */ const _recast$types = _recast.default.types, ASTNode = _recast$types.ASTNode, NodePath = _recast$types.NodePath, builders = _recast$types.builders, types = _recast$types.namedTypes; function isObjectValuesCall(node) { return types.CallExpression.check(node) && node.arguments.length === 1 && types.MemberExpression.check(node.callee) && types.Identifier.check(node.callee.object) && node.callee.object.name === 'Object' && types.Identifier.check(node.callee.property) && node.callee.property.name === 'values'; } function isWhitelistedObjectProperty(prop) { return types.Property.check(prop) && (types.Identifier.check(prop.key) && !prop.computed || types.Literal.check(prop.key)) || types.SpreadElement.check(prop); } function isWhiteListedObjectTypeProperty(prop) { return types.ObjectTypeProperty.check(prop) || types.ObjectTypeSpreadProperty.check(prop); } // Resolves an ObjectExpression or an ObjectTypeAnnotation function resolveObjectToPropMap(object, raw = false) { if (types.ObjectExpression.check(object.value) && object.value.properties.every(isWhitelistedObjectProperty) || types.ObjectTypeAnnotation.check(object.value) && object.value.properties.every(isWhiteListedObjectTypeProperty)) { const properties = []; let values = {}; let error = false; object.get('properties').each(propPath => { if (error) return; const prop = propPath.value; if (prop.kind === 'get' || prop.kind === 'set') return; if (types.Property.check(prop) || types.ObjectTypeProperty.check(prop)) { // Key is either Identifier or Literal const name = prop.key.name || (raw ? prop.key.raw : prop.key.value); const propValue = propPath.get(name).parentPath.value; const value = propValue.value.value || (raw ? propValue.value.raw : propValue.value.value); if (properties.indexOf(name) === -1) { properties.push(name); } values[name] = value; } else if (types.SpreadElement.check(prop) || types.ObjectTypeSpreadProperty.check(prop)) { let spreadObject = (0, _resolveToValue.default)(propPath.get('argument')); if (types.GenericTypeAnnotation.check(spreadObject.value)) { const typeAlias = (0, _resolveToValue.default)(spreadObject.get('id')); if (types.ObjectTypeAnnotation.check(typeAlias.get('right').value)) { spreadObject = (0, _resolveToValue.default)(typeAlias.get('right')); } } const spreadValues = resolveObjectToPropMap(spreadObject); if (!spreadValues) { error = true; return; } spreadValues.properties.forEach(spreadProp => { if (properties.indexOf(spreadProp) === -1) { properties.push(spreadProp); } }); values = (0, _objectSpread2.default)({}, values, spreadValues.values); } }); if (!error) { return { properties: properties.sort(), values }; } } return null; } /** * Returns an ArrayExpression which contains all the keys resolved from an object * * Ignores setters in objects * * Returns null in case of * unresolvable spreads * computed identifier values */ function resolveObjectValuesToArray(path) { const node = path.node; if (isObjectValuesCall(node)) { const objectExpression = (0, _resolveToValue.default)(path.get('arguments').get(0)); const propMap = resolveObjectToPropMap(objectExpression); if (propMap) { const nodes = propMap.properties.map(prop => { const value = propMap.values[prop]; return typeof value === 'undefined' ? builders.literal(null) : builders.literal(value); }); return new NodePath(builders.arrayExpression(nodes)); } } return null; }