"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractValuesFromTags = exports.describeDefault = exports.describeRequired = exports.getValuesFromTypeAnnotation = exports.getTypeFromTypePath = exports.describeType = exports.describePropsFromValue = exports.getRawValueParsedFromFunctionsBlockStatementNode = void 0; var bt = __importStar(require("@babel/types")); var recast_1 = require("recast"); var getDocblock_1 = __importDefault(require("../utils/getDocblock")); var getDoclets_1 = __importDefault(require("../utils/getDoclets")); var transformTagsIntoObject_1 = __importDefault(require("../utils/transformTagsIntoObject")); var getPropsFilter_1 = __importDefault(require("../utils/getPropsFilter")); var getTemplateExpressionAST_1 = __importDefault(require("../utils/getTemplateExpressionAST")); var parseValidator_1 = __importDefault(require("./utils/parseValidator")); function getRawValueParsedFromFunctionsBlockStatementNode(blockStatementNode) { var body = blockStatementNode.body; // if there is more than a return statement in the body, // we cannot resolve the new object, we let the function display as a function if (body.length !== 1 || !bt.isReturnStatement(body[0])) { return null; } var _a = __read(body, 1), ret = _a[0]; return ret.argument ? (0, recast_1.print)(ret.argument).code : null; } exports.getRawValueParsedFromFunctionsBlockStatementNode = getRawValueParsedFromFunctionsBlockStatementNode; /** * Extract props information form an object-style VueJs component * @param documentation * @param path */ function propHandler(documentation, path, ast, opt) { return __awaiter(this, void 0, void 0, function () { var propsPath, modelPropertyName, propsValuePath; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!bt.isObjectExpression(path.node)) return [3 /*break*/, 2]; propsPath = path .get('properties') .filter(function (p) { return bt.isObjectProperty(p.node) && (0, getPropsFilter_1.default)('props')(p); }); // if no prop return if (!propsPath.length) { return [2 /*return*/, Promise.resolve()]; } modelPropertyName = getModelPropName(path); propsValuePath = propsPath[0].get('value'); return [4 /*yield*/, describePropsFromValue(documentation, propsValuePath, ast, opt, modelPropertyName)]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/]; } }); }); } exports.default = propHandler; function describePropsFromValue(documentation, propsValuePath, ast, opt, modelPropertyName) { if (modelPropertyName === void 0) { modelPropertyName = null; } return __awaiter(this, void 0, void 0, function () { var objProp, objPropFiltered; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!bt.isObjectExpression(propsValuePath.node)) return [3 /*break*/, 2]; objProp = propsValuePath.get('properties'); objPropFiltered = objProp.filter(function (p) { return bt.isProperty(p.node); }); return [4 /*yield*/, Promise.all(objPropFiltered.map(function (prop) { return __awaiter(_this, void 0, void 0, function () { var propNode, docBlock, jsDoc, jsDocTags, propertyName, isPropertyModel, propName, propDescriptor, propValuePath, propPropertiesPath, literalType, propValuePathExpression, finalPropValuePathExpression, propPropertiesPath; return __generator(this, function (_a) { switch (_a.label) { case 0: propNode = prop.node; docBlock = (0, getDocblock_1.default)(prop); jsDoc = docBlock ? (0, getDoclets_1.default)(docBlock) : { description: '', tags: [] }; jsDocTags = jsDoc.tags ? jsDoc.tags : []; propertyName = bt.isIdentifier(propNode.key) ? propNode.key.name : bt.isStringLiteral(propNode.key) ? propNode.key.value : null; if (!propertyName) { return [2 /*return*/]; } isPropertyModel = jsDocTags.some(function (t) { return t.title === 'model'; }) || propertyName === modelPropertyName; propName = isPropertyModel ? 'v-model' : propertyName; propDescriptor = documentation.getPropDescriptor(propName); propValuePath = prop.get('value'); if (jsDoc.description) { propDescriptor.description = jsDoc.description; } if (jsDocTags.length) { propDescriptor.tags = (0, transformTagsIntoObject_1.default)(jsDocTags); } extractValuesFromTags(propDescriptor); if (!(bt.isArrayExpression(propValuePath.node) || bt.isIdentifier(propValuePath.node))) return [3 /*break*/, 1]; // if it's an immediately typed property, resolve its type immediately propDescriptor.type = getTypeFromTypePath(propValuePath); return [3 /*break*/, 4]; case 1: if (!bt.isObjectExpression(propValuePath.node)) return [3 /*break*/, 3]; propPropertiesPath = propValuePath .get('properties') .filter(function (p) { return bt.isObjectProperty(p.node) || bt.isObjectMethod(p.node); }); literalType = describeType(propPropertiesPath, propDescriptor); // required describeRequired(propPropertiesPath, propDescriptor); // default describeDefault(propPropertiesPath, propDescriptor, literalType || ''); // validator => values return [4 /*yield*/, describeValues(propPropertiesPath, propDescriptor, ast, opt)]; case 2: // validator => values _a.sent(); return [3 /*break*/, 4]; case 3: if (bt.isTSAsExpression(propValuePath.node)) { propValuePathExpression = propValuePath.get('expression'); finalPropValuePathExpression = bt.isTSAsExpression(propValuePathExpression.node) && bt.isTSUnknownKeyword(propValuePathExpression.get('typeAnnotation').node) ? propValuePathExpression.get('expression') : propValuePathExpression; if (bt.isObjectExpression(finalPropValuePathExpression.node)) { propPropertiesPath = finalPropValuePathExpression .get('properties') .filter(function (p) { return bt.isObjectProperty(p.node); }); // type and values describeTypeAndValuesFromPath(propValuePath, propDescriptor); // required describeRequired(propPropertiesPath, propDescriptor); // default describeDefault(propPropertiesPath, propDescriptor, (propDescriptor.type && propDescriptor.type.name) || ''); } else if (bt.isIdentifier(finalPropValuePathExpression.node)) { describeTypeAndValuesFromPath(propValuePath, propDescriptor); } } else { // in any other case, just display the code for the typing propDescriptor.type = { name: (0, recast_1.print)(prop.get('value')).code, func: true }; } _a.label = 4; case 4: return [2 /*return*/]; } }); }); }))]; case 1: _a.sent(); return [3 /*break*/, 3]; case 2: if (bt.isArrayExpression(propsValuePath.node)) { propsValuePath .get('elements') .filter(function (e) { return bt.isStringLiteral(e.node); }) .forEach(function (e) { var propDescriptor = documentation.getPropDescriptor(e.node.value); propDescriptor.type = { name: 'undefined' }; }); } _a.label = 3; case 3: return [2 /*return*/]; } }); }); } exports.describePropsFromValue = describePropsFromValue; /** * Deal with the description of the type * @param propPropertiesPath * @param propDescriptor * @returns the unaltered type member of the prop object */ function describeType(propPropertiesPath, propDescriptor) { var typeArray = propPropertiesPath.filter((0, getPropsFilter_1.default)('type')); if (propDescriptor.tags && propDescriptor.tags.type) { var _a = __read(propDescriptor.tags.type, 1), typeDesc = _a[0].type; if (typeDesc) { var typedAST = (0, getTemplateExpressionAST_1.default)("let a:".concat(typeDesc.name)); var typeValues_1; (0, recast_1.visit)(typedAST.program, { visitVariableDeclaration: function (path) { var typeAnnotation = path.get('declarations', 0, 'id', 'typeAnnotation').value.typeAnnotation; if (bt.isTSUnionType(typeAnnotation) && typeAnnotation.types.every(function (t) { return bt.isTSLiteralType(t); })) { typeValues_1 = typeAnnotation.types.map(function (t) { return 'literal' in t ? bt.isUnaryExpression(t.literal) ? t.literal.argument.toString() : bt.isTemplateLiteral(t.literal) ? t.literal.type : t.literal.value.toString() : t.type.toString(); }); } return false; } }); if (typeValues_1) { propDescriptor.values = typeValues_1; } else { propDescriptor.type = typeDesc; if (typeArray.length) { return getTypeFromTypePath(typeArray[0].get('value')).name; } } } } if (typeArray.length) { return describeTypeAndValuesFromPath(typeArray[0].get('value'), propDescriptor); } else { // deduce the type from default expression var defaultArray = propPropertiesPath.filter((0, getPropsFilter_1.default)('default')); if (defaultArray.length) { var typeNode = defaultArray[0].node; if (bt.isObjectProperty(typeNode)) { var func = bt.isArrowFunctionExpression(typeNode.value) || bt.isFunctionExpression(typeNode.value); var typeValueNode = defaultArray[0].get('value').node; var typeName = typeof typeValueNode.value; propDescriptor.type = { name: func ? 'func' : typeName }; } } } return undefined; } exports.describeType = describeType; var VALID_VUE_TYPES = [ 'string', 'number', 'boolean', 'array', 'object', 'date', 'function', 'symbol' ]; function resolveParenthesis(typeAnnotation) { var finalAnno = typeAnnotation; while (bt.isTSParenthesizedType(finalAnno)) { finalAnno = finalAnno.typeAnnotation; } return finalAnno; } function describeTypeAndValuesFromPath(propPropertiesPath, propDescriptor) { // values var values = getValuesFromTypePath(propPropertiesPath.node.typeAnnotation); // if it has an "as" annotation defining values if (values) { propDescriptor.values = values; propDescriptor.type = { name: 'string' }; } else { // Get natural type from its identifier // (classic way) // type: Object propDescriptor.type = getTypeFromTypePath(propPropertiesPath); } return propDescriptor.type.name; } function getTypeFromTypePath(typePath) { var typeNode = typePath.node; var typeAnnotation = typeNode.typeAnnotation; var typeName = !typeNode ? 'any' : bt.isTSTypeReference(typeAnnotation) && typeAnnotation.typeParameters ? (0, recast_1.print)(resolveParenthesis(typeAnnotation.typeParameters.params[0])).code : bt.isArrayExpression(typeNode) ? typePath .get('elements') .map(function (t) { return getTypeFromTypePath(t).name; }) .join('|') : bt.isIdentifier(typeNode) && VALID_VUE_TYPES.indexOf(typeNode.name.toLowerCase()) > -1 ? typeNode.name.toLowerCase() : bt.isObjectProperty(typeNode) && bt.isExpression(typeNode.value) && bt.isTSInstantiationExpression(typeNode.value) ? (0, recast_1.print)(typeNode.value.expression).code + (typeNode.value.typeParameters ? (0, recast_1.print)(typeNode.value.typeParameters).code : '') : (0, recast_1.print)(typeNode).code; return { name: typeName === 'function' ? 'func' : typeName }; } exports.getTypeFromTypePath = getTypeFromTypePath; /** * When a prop is type annotated with the "as" keyword, * It means that its possible values can be extracted from it * this extracts the values from the as * @param typeAnnotation the as annotation */ function getValuesFromTypePath(typeAnnotation) { if (bt.isTSTypeReference(typeAnnotation) && typeAnnotation.typeParameters) { var type = resolveParenthesis(typeAnnotation.typeParameters.params[0]); return getValuesFromTypeAnnotation(type); } return undefined; } function getValuesFromTypeAnnotation(type) { if (bt.isTSUnionType(type) && type.types.every(function (t) { return bt.isTSLiteralType(t); })) { return type.types.map(function (t) { return bt.isTSLiteralType(t) && !bt.isUnaryExpression(t.literal) ? bt.isTemplateLiteral(t.literal) ? t.literal.type : t.literal.value.toString() : ''; }); } return undefined; } exports.getValuesFromTypeAnnotation = getValuesFromTypeAnnotation; function describeRequired(propPropertiesPath, propDescriptor) { var requiredArray = propPropertiesPath.filter((0, getPropsFilter_1.default)('required')); var requiredNode = requiredArray.length ? requiredArray[0].get('value').node : undefined; var required = requiredNode && bt.isBooleanLiteral(requiredNode) ? requiredNode.value : undefined; if (required !== undefined) { propDescriptor.required = required; } } exports.describeRequired = describeRequired; function describeDefault(propPropertiesPath, propDescriptor, propType) { var _a; var defaultArray = propPropertiesPath.filter((0, getPropsFilter_1.default)('default')); if (defaultArray.length) { /** * This means the default value is formatted like so: `default: any` */ var defaultValueIsProp = bt.isObjectProperty(defaultArray[0].value); /** * This means the default value is formatted like so: `default () { return {} }` */ var defaultValueIsObjectMethod = bt.isObjectMethod(defaultArray[0].value); // objects and arrays should try to extract the body from functions if (propType === 'object' || propType === 'array') { if (defaultValueIsProp) { /* TODO: add correct type info here ↓ */ var defaultFunction = defaultArray[0].get('value'); var isArrowFunction = bt.isArrowFunctionExpression(defaultFunction.node); var isOldSchoolFunction = bt.isFunctionExpression(defaultFunction.node); // if default is undefined or null, literals are allowed if (bt.isNullLiteral(defaultFunction.node) || (bt.isIdentifier(defaultFunction.node) && defaultFunction.node.name === 'undefined')) { propDescriptor.defaultValue = { func: false, value: (0, recast_1.print)(defaultFunction.node).code }; return; } // check if the prop value is a function if (!isArrowFunction && !isOldSchoolFunction) { throw new Error('A default value needs to be a function when your type is an object or array'); } // retrieve the function "body" from the arrow function if (isArrowFunction) { var arrowFunctionBody = defaultFunction.get('body'); // arrow function looks like `() => { return {} }` if (bt.isBlockStatement(arrowFunctionBody.node)) { var rawValueParsed_1 = getRawValueParsedFromFunctionsBlockStatementNode(arrowFunctionBody.node); if (rawValueParsed_1) { propDescriptor.defaultValue = { func: false, value: rawValueParsed_1 }; return; } } if (bt.isArrayExpression(arrowFunctionBody.node) || bt.isObjectExpression(arrowFunctionBody.node)) { var rawCode = (0, recast_1.print)(arrowFunctionBody.node).code; var value = ((_a = arrowFunctionBody.node.extra) === null || _a === void 0 ? void 0 : _a.parenthesized) ? rawCode.slice(1, rawCode.length - 1) : rawCode; propDescriptor.defaultValue = { func: false, value: value }; return; } // arrow function looks like `() => ({})` propDescriptor.defaultValue = { func: true, value: (0, recast_1.print)(defaultFunction).code }; return; } } // defaultValue was either an ObjectMethod or an oldSchoolFunction // in either case we need to retrieve the blockStatement and work with that /* todo: add correct type info here ↓ */ var defaultBlockStatement = defaultValueIsObjectMethod ? defaultArray[0].get('body') : defaultArray[0].get('value').get('body'); var defaultBlockStatementNode = defaultBlockStatement.node; var rawValueParsed = getRawValueParsedFromFunctionsBlockStatementNode(defaultBlockStatementNode); if (rawValueParsed) { propDescriptor.defaultValue = { func: false, value: rawValueParsed }; return; } } // otherwise the rest should return whatever there is if (defaultValueIsProp) { // in this case, just return the rawValue var defaultPath = defaultArray[0].get('value'); if (bt.isTSAsExpression(defaultPath.value)) { defaultPath = defaultPath.get('expression'); } var rawValue = (0, recast_1.print)(defaultPath).code; propDescriptor.defaultValue = { func: bt.isFunction(defaultPath.node), value: rawValue }; return; } if (defaultValueIsObjectMethod) { // in this case, just the function needs to be reconstructed a bit var defaultObjectMethod = defaultArray[0].get('value'); var paramNodeArray = defaultObjectMethod.node.params; var params = paramNodeArray.map(function (p) { return p.name; }).join(', '); var defaultBlockStatement = defaultArray[0].get('body'); var rawValue = (0, recast_1.print)(defaultBlockStatement).code; // the function should be reconstructed as "old-school" function, because they have the same handling of "this", whereas arrow functions do not. var rawValueParsed = "function(".concat(params, ") ").concat(rawValue.trim()); propDescriptor.defaultValue = { func: true, value: rawValueParsed }; return; } throw new Error('Your default value was formatted incorrectly'); } } exports.describeDefault = describeDefault; function describeValues(propPropertiesPath, propDescriptor, ast, options) { return __awaiter(this, void 0, void 0, function () { var validatorArray, validatorNode, values; return __generator(this, function (_a) { switch (_a.label) { case 0: if (propDescriptor.values) { return [2 /*return*/]; } validatorArray = propPropertiesPath.filter((0, getPropsFilter_1.default)('validator')); if (!validatorArray.length) return [3 /*break*/, 2]; validatorNode = validatorArray[0].get('value').node; return [4 /*yield*/, (0, parseValidator_1.default)(validatorNode, ast, options)]; case 1: values = _a.sent(); if (values) { propDescriptor.values = values; } _a.label = 2; case 2: return [2 /*return*/]; } }); }); } function extractValuesFromTags(propDescriptor) { var _a; if (propDescriptor.tags && propDescriptor.tags.values) { var values = propDescriptor.tags.values.map(function (tag) { var description = tag.description; var choices = typeof description === 'string' ? description.split(',') : undefined; if (choices) { return choices.map(function (v) { return v.trim(); }); } return []; }); propDescriptor.values = (_a = []).concat.apply(_a, __spreadArray([], __read(values), false)); delete propDescriptor.tags.values; } } exports.extractValuesFromTags = extractValuesFromTags; /** * extract the property model.prop from the component object * @param path component NodePath * @returns name of the model prop, null if none */ function getModelPropName(path) { var modelPath = path .get('properties') .filter(function (p) { return bt.isObjectProperty(p.node) && (0, getPropsFilter_1.default)('model')(p); }); if (!modelPath.length) { return null; } var modelValue = modelPath.length && modelPath[0].get('value'); if (!bt.isObjectExpression(modelValue.node)) { return null; } var modelPropertyNamePath = modelValue .get('properties') .filter(function (p) { return bt.isObjectProperty(p.node) && (0, getPropsFilter_1.default)('prop')(p); }); if (!modelPropertyNamePath.length) { return null; } var valuePath = modelPropertyNamePath[0].get('value'); return bt.isStringLiteral(valuePath.node) ? valuePath.node.value : null; }