UNPKG

@irwinproject/storybook-addon-tsdoc

Version:
343 lines (341 loc) 15 kB
"use strict"; /* I just need to split up the logic into more consumable chunks for myself. This file will be responsible for providing resusable methods to parse common declaration types */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.enummember = exports.literal = exports.newExpression = exports.variableDeclaration = exports.getAccessor = exports.interfaceDeclaration = exports.classDeclaration = exports.parameter = exports.typeParameter = exports.identifier = exports.typeReference = exports.arrayLiteral = exports.literalType = exports.intersectionType = exports.unionType = exports.typeAliasDeclaration = exports.propertyAccessExpression = exports.namedTupleMember = exports.fnSignature = exports.propertyDecSig = exports.getArguments = exports.getTypeArguments = exports.getTypeParameters = exports.getGenerator = exports.getAsync = exports.spread = exports.opt = exports.getOperator = exports.getExtends = exports.getExtend = exports.getImplements = exports.getConstraint = exports.getModifiers = exports.objLiteral = exports.isGenerator = exports.genTypes = exports.fromReturn = exports.fromTypeNode = exports.getExpression = exports.fromName = exports.getSignatureFromType = exports.fromType = void 0; const ts_morph_1 = require("ts-morph"); const constants_1 = require("./constants"); const decorators_1 = require("./decorators"); const node_signature_1 = require("./node-signature"); const node_tools_1 = require("./node-tools"); const SyntaxKindDelegator_types_1 = __importDefault(require("./SyntaxKindDelegator.types")); const utils_1 = require("./utils"); const modHandler = (node, ...mods) => { return mods.reduce((o, v) => o + (typeof v === 'string' ? v : v(node)), ''); }; /** * Primitive types are simply converted to a string * @param t * @returns */ const isPrimitiveType = (t) => t.isAny() || t.isBigInt() || t.isNever() || t.isNull() || t.isNumber() || t.isString() || t.isBoolean() || t.isUndefined(); /** * Primitive literals are also converted to a string but wrapped in a different wrapper and escaped as they may contain illegal jsx characters. * @param t * @returns */ const isPrimitiveLiteral = (t) => t.isBigIntLiteral() || t.isNumberLiteral() || t.isStringLiteral() || t.isTemplateLiteral() || t.isBooleanLiteral(); /** * Diving down to the underlying type provides lower level access to the typing however ts-morph provides a wonderul interface via their Node class. Since it completely crawls the Source File it seems more suitable to get the dclaration that is being referenced * @param t * @returns */ const fromType = (t) => { var _a, _b; if (!t) return ''; if (isPrimitiveType(t)) return (0, decorators_1.$type)(t.getText()); if (isPrimitiveLiteral(t)) return (0, decorators_1.$literal)((0, utils_1.escape)(t.getText())); const args = t.getTypeArguments().map(exports.fromType).filter(n => !!n).join(', ').wrap('<', '>'); const symbol = (_a = t.getSymbol()) !== null && _a !== void 0 ? _a : t.getAliasSymbol(); const [dec] = (_b = symbol === null || symbol === void 0 ? void 0 : symbol.getDeclarations()) !== null && _b !== void 0 ? _b : []; if (dec) { return (ts_morph_1.Node.isExpression(dec) ? (ts_morph_1.Node.isNewExpression(dec) ? (0, decorators_1.$kd) `new` : '') + (0, decorators_1.$link)(dec.getParent()) : (0, decorators_1.$link)(dec)) + args; } return t.isUnion() ? t.getUnionTypes().map(exports.fromType).join(' | ') : t.isArray() ? (0, exports.fromType)(t.getArrayElementType()) + '[]' : t.isIntersection() ? t.getIntersectionTypes().map(exports.fromType).join(' & ') : t.isTuple() ? t.getTupleElements().map(exports.fromType).join(', ').wrap('[', ']') : ''; }; exports.fromType = fromType; /** * Attempts to get a type node from the types declaration. * @param node * @returns */ const getSignatureFromType = (node) => { if (!node) return ''; const t = node.getType(); const tp = (0, exports.fromType)(t); return tp; }; exports.getSignatureFromType = getSignatureFromType; /** * This mod handler simply attempts to parse the name node. This should ultimately end with an identifier but also allow for potential destructured objects to be traversed without change of logic. * @param node * @returns */ const fromName = (node) => ts_morph_1.Node.hasName(node) ? (0, node_signature_1.sig)(node.getNameNode()) : ''; exports.fromName = fromName; const getExpression = (node) => (0, node_signature_1.sig)(node.getExpression()); exports.getExpression = getExpression; /** * With typenodes not alway being provided this method acts as a point where I can change the logic if getting a typenode fails. * @param node * @returns */ const fromTypeNode = (node) => { var _a; const tn = (0, node_tools_1.getTypeNode)(node); return tn ? (0, node_signature_1.sig)(tn) : (_a = (0, exports.getSignatureFromType)(node)) !== null && _a !== void 0 ? _a : ''; }; exports.fromTypeNode = fromTypeNode; /** * Returns can be explicit or inferred. * * Similar to a typenode on how its handled. * @todo - prior to checking the type check the jsdocs for a returns or return tag * @todo - traverse the body and collect return expressions manually (this is purely to allow differenciation between class instances and class constructors being returned in the expression.) * @param node * @returns */ const fromReturn = (node) => { const tn = node.getReturnTypeNode(); const s = tn ? (0, node_signature_1.sig)(tn) : (0, exports.fromType)(node.getReturnType()); return s || (0, decorators_1.$literal)('void'); }; exports.fromReturn = fromReturn; /** * A convenience method to signature and wrap a statement * @param nodes * @param pre * @param post * @returns */ const genTypes = (nodes, pre = '<', post = '>') => nodes.map(node_signature_1.sig).join(', ').wrap(pre, post); exports.genTypes = genTypes; /** * Checks if a node is a generator. This is made generic as Arrow functions cant be generators but aside from that can do just about anything else a function can so it does not make sense to have two separate signators. * @param node * @returns */ const isGenerator = (node) => !('isGenerator' in node) || typeof node.isGenerator !== 'function' ? false : node.isGenerator(); exports.isGenerator = isGenerator; /** * Returns a literal representation of {...} * @returns */ const objLiteral = () => (0, decorators_1.$literal)(constants_1.typelit); exports.objLiteral = objLiteral; /** * Converts modifier tokens to plain text * @todo style this maybe. * @param node * @returns */ const getModifiers = (node) => { if (!ts_morph_1.Node.isModifierable(node)) return ''; return node.getModifiers().map(m => m.getText()).join(' ').wrap('', ' '); }; exports.getModifiers = getModifiers; /** * document a contraint * @param node * @returns */ const getConstraint = (node) => (0, node_signature_1.sig)(node.getConstraint()).wrap(' extends ', ''); exports.getConstraint = getConstraint; const getImplements = (node) => node.getImplements().map(node_signature_1.sig).join(', ').wrap(' implements ', ''); exports.getImplements = getImplements; const getExtend = (node) => (0, node_signature_1.sig)(node.getExtends()).wrap(' extends ', ''); exports.getExtend = getExtend; const getExtends = (node) => node.getExtends().map(node_signature_1.sig).join(', ').wrap(' extends ', ''); exports.getExtends = getExtends; /** * Returns the appropriate operator based on syntax kind. * * could probably just use getText. * @param node * @returns */ const getOperator = (node) => { switch (node.getOperator()) { case SyntaxKindDelegator_types_1.default.ReadonlyKeyword: return 'readonly'; case SyntaxKindDelegator_types_1.default.KeyOfKeyword: return 'keyof'; case SyntaxKindDelegator_types_1.default.UniqueKeyword: return 'unique'; } }; exports.getOperator = getOperator; /** * Checks if value has a question token and if so passes it to the signature. * @param node * @returns */ const opt = (node) => (ts_morph_1.Node.isQuestionTokenable(node) && node.hasQuestionToken()) ? '?' : ''; exports.opt = opt; /** * Checks if a spread symbol precedes the node and if so passes it to the signature. * @param node * @returns */ const spread = (node) => (ts_morph_1.Node.isDotDotDotTokenable(node) && node.getDotDotDotToken()) ? '...' : ''; exports.spread = spread; /** * documents a potential async modifier * @param node * @returns */ const getAsync = (node) => (0, node_tools_1.isAsync)(node) ? 'async ' : ''; exports.getAsync = getAsync; /** * documents a potential generator modifier. * @param node * @returns */ const getGenerator = (node) => (0, exports.isGenerator)(node) ? '*' : ''; exports.getGenerator = getGenerator; /** * documents type parameters. * @param node * @returns */ const getTypeParameters = (node) => (0, exports.genTypes)(ts_morph_1.Node.isTypeParametered(node) ? node.getTypeParameters() : []); exports.getTypeParameters = getTypeParameters; const getTypeArguments = (node) => (0, exports.genTypes)(ts_morph_1.Node.isTypeArgumented(node) ? node.getTypeArguments() : []); exports.getTypeArguments = getTypeArguments; /** * Documents a functions arguments (aka: parameters). * @param node * @returns */ const getArguments = (node) => `(${(ts_morph_1.Node.isParametered(node) ? node.getParameters().map(node_signature_1.sig) : []).join(', ')})`; exports.getArguments = getArguments; /** * These two declaration types share a common signature. no point in repeating myself. * Document a proprty type declaration. * @param node * @returns */ const propertyDecSig = (node) => modHandler(node, exports.getModifiers, exports.spread, exports.fromTypeNode, exports.opt); exports.propertyDecSig = propertyDecSig; /** * Document a function type declaration. * @param node * @returns */ const fnSignature = (node) => modHandler(node, exports.getAsync, exports.getTypeParameters, exports.getArguments, exports.getGenerator, ' =&gt; ', exports.fromReturn); exports.fnSignature = fnSignature; /** * document a named tuple member. * @param node * @returns */ const namedTupleMember = (node) => modHandler(node, exports.spread, exports.fromName, exports.opt, ': ', exports.fromTypeNode); exports.namedTupleMember = namedTupleMember; /** * document a property access expression. * @param node * @returns */ const propertyAccessExpression = (node) => modHandler(node, (0, node_signature_1.sig)(node.getExpression()), exports.opt); exports.propertyAccessExpression = propertyAccessExpression; /** * Get the type alias declaration * @param node * @returns */ const typeAliasDeclaration = (node) => (0, exports.fromTypeNode)(node); exports.typeAliasDeclaration = typeAliasDeclaration; /** * Document a union type. * @param node * @returns */ const unionType = (node) => node.getTypeNodes().map(node_signature_1.sig).join(' | '); exports.unionType = unionType; /** * Document an intersection type. * @param node * @returns */ const intersectionType = (node) => node.getTypeNodes().map(node_signature_1.sig).join(' & '); exports.intersectionType = intersectionType; /** * document a literal type * @param node * @returns */ const literalType = (node) => (0, decorators_1.$literal)((0, utils_1.escape)(node.getText())); exports.literalType = literalType; /** * document an array literal * @param node * @returns */ const arrayLiteral = (node) => node.getElements().map(node_signature_1.sig).join(', ').wrap('[', ']'); exports.arrayLiteral = arrayLiteral; /** * document a type reference. funny thing the type reference doesnt create a link. * @param node * @returns */ const typeReference = (node) => { const typeName = node.getTypeName(); const args = node.getTypeArguments(); if (typeName.getText() === "Array") return (0, node_signature_1.sig)(args[0]) + "[]"; return (0, node_signature_1.sig)(typeName) + args.map(node_signature_1.sig).join(', ').wrap('<', '>'); }; exports.typeReference = typeReference; /** * An identifier is a sort of reference that points to a definition. if theres a definition and the definition doesnt point back to said node then it becomes a link. * @todo test this with destructured nodes. * @param node * @returns */ const identifier = (node) => { const def = node.getDefinitionNodes()[0]; if (!def || (0, node_tools_1.getFullName)(def) === (0, node_tools_1.getFullName)(node)) return (0, decorators_1.$type)(node.getText()); const href = (0, node_tools_1.getDocPath)(def); return href ? (0, decorators_1.$href)(node.getText(), href) : (0, decorators_1.$type)(node.getText()); }; exports.identifier = identifier; /** * A type parameter * @param node * @returns */ const typeParameter = (node) => modHandler(node, exports.getModifiers, exports.fromName, exports.getConstraint); exports.typeParameter = typeParameter; /** * A parameter is a little different then a normal expression in typescript with a parameter typing should be expected or default to any and the initializer should not be used when a typeNode is not present. Also the typ should not be used for this same reason. * @param node */ const parameter = (node) => { const typeNode = (0, node_signature_1.sig)(node.getTypeNode()) || (0, decorators_1.$type)("any"); const initializer = (0, node_signature_1.sig)(node.getInitializer()); return `${node.isRestParameter() ? (0, decorators_1.$name)('...') : ''}${(0, node_signature_1.sig)(node.getNameNode())}: ${typeNode}${initializer.wrap(' = ', '')}`; }; exports.parameter = parameter; /** * A class declaration and class express differ at one point and thats the name. * @param node * @returns */ const classDeclaration = (node) => modHandler(node, ts_morph_1.Node.isClassExpression(node) ? (0, decorators_1.$kd) `class` : '', exports.getTypeParameters, exports.getExtend, exports.getImplements); exports.classDeclaration = classDeclaration; const interfaceDeclaration = (node) => modHandler(node, exports.getTypeParameters, exports.getExtends); exports.interfaceDeclaration = interfaceDeclaration; const getAccessor = (node) => modHandler(node, exports.getModifiers, exports.fromReturn); exports.getAccessor = getAccessor; const variableDeclaration = (node) => (0, exports.fromTypeNode)(node); exports.variableDeclaration = variableDeclaration; const newExpression = (node) => modHandler(node, (0, decorators_1.$kd) `new `, exports.getExpression, exports.getTypeArguments); exports.newExpression = newExpression; const literal = (node) => (0, decorators_1.$literal)((0, utils_1.escape)(node.getText())); exports.literal = literal; const enummember = (node) => (0, node_signature_1.sig)(node.getInitializer()).wrap(': ', ''); exports.enummember = enummember;