UNPKG

@irwinproject/storybook-addon-tsdoc

Version:
218 lines (215 loc) 13.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.render = exports.renderer = exports.build = exports.buildFromType = void 0; const ts_morph_1 = require("ts-morph"); const TS_1 = __importDefault(require("./TS")); const SyntaxKindDelegator_1 = require("./SyntaxKindDelegator"); const SyntaxKindDelegator_types_1 = __importDefault(require("./SyntaxKindDelegator.types")); const decorators_1 = require("./decorators"); const console_log_colors_1 = require("console-log-colors"); const node_tools_1 = require("./node-tools"); const node_signature_1 = require("./node-signature"); const constants_1 = require("./constants"); const signitors_1 = require("./signitors"); /** * @param node */ const shouldCrawl = (node) => { return ts_morph_1.Node.isTypeLiteral(node) || ts_morph_1.Node.isObjectBindingPattern(node) || ts_morph_1.Node.isObjectLiteralExpression(node) || ts_morph_1.Node.isFunctionExpression(node) || ts_morph_1.Node.isArrowFunction(node); }; /** * Standardizes the properties handled by different function like declarations and expressions. * @param typeParams * @param args * @param returnNode * @returns */ const renderFNDetails = (node) => { const tp = (0, exports.build)(...node.getTypeParameters()); const ag = (0, exports.build)(...node.getParameters()); const rt = (0, exports.build)(node.getReturnTypeNode()) || (0, exports.buildFromType)(node.getReturnType()) || (0, decorators_1.$literal)('void'); return [ ...tp ? [ (0, decorators_1.$section)(constants_1.SEP, (0, decorators_1.$h)(5, undefined, "Type Arguments:"), tp) ] : [], ...ag ? [ (0, decorators_1.$section)(constants_1.SEP, (0, decorators_1.$t)(5) `Arguments:`, ag) ] : [], ...rt ? [ (0, decorators_1.$section)(constants_1.SEP, (0, decorators_1.$h)(5, undefined, "Returns:"), rt) ] : [] ]; }; /** * Just a convenient way to combine strings as blocks of text within a file. * @param blocks * @returns */ const block = (...blocks) => blocks.filter(b => b.trim()).join('\n'); /** * Just a convenience to be used to generate a section from a node or nodes if any content is generated. * @param nodes * @returns */ const $sec = (...nodes) => (0, decorators_1.$section)((0, exports.build)(...nodes)); /** * This method will act as a central point to interface with JSDocs. * * *note:* Some tags will require more integrated support and at this time I am still deciding on the precedent JSDoc should get vs typescript declarations. * * But this is a place I can modify the code for all doc blocks in a centeral location * @param node * @returns */ const getDocs = (node) => block((0, node_tools_1.getComments)(node), (0, node_tools_1.getExample)(node)); /** * Tweaking node handling via syntax kind */ const RENDER_MAP = { [SyntaxKindDelegator_types_1.default.TypeAliasDeclaration]: node => block((0, decorators_1.$s)(2, 'type', node), getDocs(node), $sec(...node.getTypeParameters(), node.getTypeNode())), [SyntaxKindDelegator_types_1.default.TupleType]: node => (0, exports.build)(...node.getElements()), [SyntaxKindDelegator_types_1.default.NamedTupleMember]: node => block((0, decorators_1.$s)(4, 'tuple item', node), getDocs(node), (0, decorators_1.$section)((0, exports.build)(node.getTypeNode()))), [SyntaxKindDelegator_types_1.default.TypeLiteral]: node => { const members = (0, exports.build)(...node.getMembers()); return members ? (0, decorators_1.$section)((0, decorators_1.$t)(4) `Members: `, members) : ''; }, [SyntaxKindDelegator_types_1.default.PropertySignature]: node => block((0, decorators_1.$s)(4, 'property', node), getDocs(node), $sec((0, node_tools_1.getTypeNode)(node))), [SyntaxKindDelegator_types_1.default.PropertyDeclaration]: node => { const tn = (0, node_tools_1.getTypeNode)(node); return block((0, decorators_1.$s)(4, `${node.isStatic() ? 'static ' : ''}property`, node), getDocs(node), (tn && shouldCrawl(tn)) ? $sec((0, node_tools_1.getTypeNode)(node)) : ''); }, [SyntaxKindDelegator_types_1.default.MethodSignature]: node => { return block((0, decorators_1.$s)(4, `method`, node), getDocs(node), ...renderFNDetails(node)); }, //unlike the method signature [SyntaxKindDelegator_types_1.default.FunctionType]: node => block(...renderFNDetails(node)), [SyntaxKindDelegator_types_1.default.ArrowFunction]: node => block(...renderFNDetails(node)), [SyntaxKindDelegator_types_1.default.FunctionExpression]: node => block(...renderFNDetails(node)), [SyntaxKindDelegator_types_1.default.MethodDeclaration]: node => { return block((0, decorators_1.$s)(4, `${node.isStatic() ? 'static ' : ''}method`, node), getDocs(node), ...renderFNDetails(node)); }, [SyntaxKindDelegator_types_1.default.Parameter]: node => { return block((0, decorators_1.$s)(4, 'argument', node), getDocs(node)); }, [SyntaxKindDelegator_types_1.default.TypeParameter]: node => { const constraint = (0, exports.build)(node.getConstraint()); if (!constraint) return ''; return block((0, decorators_1.$s)(4, 'param', node), getDocs(node), (0, decorators_1.$section)(constraint)); }, [SyntaxKindDelegator_types_1.default.TypeReference]: (node) => (0, node_signature_1.sig)(node), //this should be referenced in a parent signature I am not sure it should be traversed. [SyntaxKindDelegator_types_1.default.UnionType]: node => { const tns = node.getTypeNodes().filter(n => ts_morph_1.Node.isObjectBindingPattern(node) || ts_morph_1.Node.isObjectLiteralExpression(node)); return tns.length ? (0, exports.build)(...tns) : ''; }, [SyntaxKindDelegator_types_1.default.IntersectionType]: node => (0, exports.build)(...node.getTypeNodes()), [SyntaxKindDelegator_types_1.default.ArrayType]: node => (0, exports.build)(node.getElementTypeNode()), [SyntaxKindDelegator_types_1.default.ClassDeclaration]: node => block((0, decorators_1.$s)(2, 'class', node), getDocs(node), (0, decorators_1.$section)((0, exports.build)(...node.getConstructors()), (0, exports.build)(...node.getStaticBlocks()), (0, exports.build)(...node.getStaticProperties()), (0, exports.build)(...node.getStaticMethods()), (0, exports.build)(...node.getInstanceProperties()), (0, exports.build)(...node.getInstanceMethods()))), [SyntaxKindDelegator_types_1.default.ClassStaticBlockDeclaration]: node => { const comments = (0, node_tools_1.getComments)(node).trim(); return comments ? block((0, decorators_1.$t)(4) `${(0, decorators_1.$kd) `static block`}`, comments, (0, node_tools_1.getExample)(node)) : ''; }, [SyntaxKindDelegator_types_1.default.Constructor]: node => block((0, decorators_1.$s)(4, 'constructor', node), getDocs(node)), [SyntaxKindDelegator_types_1.default.InterfaceDeclaration]: node => { const typeParams = (0, exports.build)(...node.getTypeParameters()); const extensions = (0, exports.build)(...node.getExtends()); return block((0, decorators_1.$s)(4, 'interface', node), ...(typeParams ? [ '---', (0, decorators_1.$t)(5) `Type Parameters`, (0, decorators_1.$section)(typeParams) ] : []), ...(extensions ? [ '---', (0, decorators_1.$t)(5) `Extends:`, (0, decorators_1.$section)(extensions) ] : []), '---', (0, decorators_1.$t)(5) `Members:`, $sec(...node.getMembers())); }, [SyntaxKindDelegator_types_1.default.ExpressionWithTypeArguments]: node => (0, exports.build)(...node.getTypeArguments()), [SyntaxKindDelegator_types_1.default.GetAccessor]: node => block((0, decorators_1.$s)(4, (node.isStatic() ? 'static ' : '') + 'get', node), (0, node_tools_1.getComments)(node), (0, node_tools_1.getExample)(node)), [SyntaxKindDelegator_types_1.default.SetAccessor]: node => block((0, decorators_1.$s)(4, (node.isStatic() ? 'static ' : '') + 'set', node), (0, node_tools_1.getComments)(node), (0, node_tools_1.getExample)(node)), [SyntaxKindDelegator_types_1.default.ConditionalType]: node => (0, exports.build)(node.getExtendsType(), node.getTrueType(), node.getFalseType()), [SyntaxKindDelegator_types_1.default.VariableStatement]: node => (0, exports.build)(...node.getDeclarations()), [SyntaxKindDelegator_types_1.default.VariableDeclaration]: node => { var _a; const statement = node.getVariableStatement(); if (!statement) return ''; return block((0, decorators_1.$s)(4, (0, decorators_1.$kind)(statement.getDeclarationKind()), node), (0, node_tools_1.getComments)(statement), (0, node_tools_1.getExample)(statement), (0, exports.build)((_a = node.getTypeNode()) !== null && _a !== void 0 ? _a : node.getInitializer())); }, [SyntaxKindDelegator_types_1.default.ObjectBindingPattern]: node => (0, exports.build)(...node.getElements()), [SyntaxKindDelegator_types_1.default.BindingElement]: node => (0, exports.build)(node.getPropertyNameNode()), [SyntaxKindDelegator_types_1.default.IndexedAccessType]: node => (0, exports.build)(node.getObjectTypeNode(), node.getIndexTypeNode()), [SyntaxKindDelegator_types_1.default.FunctionDeclaration]: node => block((0, decorators_1.$s)(4, 'function', node), getDocs(node), ...renderFNDetails(node)), [SyntaxKindDelegator_types_1.default.ExpressionStatement]: node => '', //expressions are blocks of logic. Currently I dont plan on handling these instances. [SyntaxKindDelegator_types_1.default.ClassExpression]: node => block(getDocs(node), (0, decorators_1.$section)((0, exports.build)(...node.getConstructors()), (0, exports.build)(...node.getStaticBlocks()), (0, exports.build)(...node.getStaticProperties()), (0, exports.build)(...node.getStaticMethods()), (0, exports.build)(...node.getInstanceProperties()), (0, exports.build)(...node.getInstanceMethods()))), [SyntaxKindDelegator_types_1.default.ArrayLiteralExpression]: node => (0, exports.build)(...node.getElements()), [SyntaxKindDelegator_types_1.default.ObjectLiteralExpression]: node => block((0, decorators_1.$section)((0, exports.build)(...node.getProperties()))), [SyntaxKindDelegator_types_1.default.PropertyAssignment]: node => block((0, decorators_1.$s)(4, 'property', node), getDocs(node), (0, exports.build)(node.getInitializer())), [SyntaxKindDelegator_types_1.default.EnumDeclaration]: node => block((0, decorators_1.$s)(2, 'enum', node), getDocs(node), (0, exports.build)(...node.getMembers())), [SyntaxKindDelegator_types_1.default.EnumMember]: node => block((0, decorators_1.$s)(4, 'enum item', node), getDocs(node)), [SyntaxKindDelegator_types_1.default.NewExpression]: () => ``, [SyntaxKindDelegator_types_1.default.ObjectKeyword]: () => '', [SyntaxKindDelegator_types_1.default.TypePredicate]: () => '', [SyntaxKindDelegator_types_1.default.CallExpression]: () => '', [SyntaxKindDelegator_types_1.default.BinaryExpression]: () => '', [SyntaxKindDelegator_types_1.default.Identifier]: () => ' ' }; //Again a way to ignore or not alert me of lacking support. it seems there should be an interface for this. const IGNOREKINDS = new Set([ SyntaxKindDelegator_types_1.default.ExportDeclaration, SyntaxKindDelegator_types_1.default.MultiLineCommentTrivia, SyntaxKindDelegator_types_1.default.SingleLineCommentTrivia ]); const buildFromType = (type) => { const node = (0, node_tools_1.declarationOfType)(type); //If no node is found not much else can be done if (!node) return `<h4 className="ts-doc-header">${(0, signitors_1.fromType)(type)}</h4>`; //if this is not directly referencing another node then I guess the documentation needs to continue traversing from the returning declaration. if (type.isAnonymous()) return (0, exports.build)(node); //Just show a link.... just like from the signature. return `<h4 className="ts-doc-header">${(0, signitors_1.fromType)(type)}</h4>`; }; exports.buildFromType = buildFromType; /** * Builds based on a list of nodes. * @param nodes * @returns */ const build = (...nodes) => nodes.map(node => { if (!TS_1.default.documentPrivate && (0, node_tools_1.isPrivate)(node)) return ''; const val = (0, SyntaxKindDelegator_1.bySyntax)(node, RENDER_MAP, n => { if (!n) return ''; //TS.err("No support", red(n.getKindName()), cyan(n.getKind()), n.getText(), getFullName(n)); return ' '; }); return val; }).filter(b => b).join('\n'); exports.build = build; const renderer = (node, df = n => { if (!n || (0, node_tools_1.isPrimitive)(n) || IGNOREKINDS.has(n.getKind())) return ''; TS_1.default.err("No support", (0, console_log_colors_1.red)(n.getKindName()), (0, console_log_colors_1.yellow)(n.getKind()), (0, node_tools_1.getFullName)(n)); return ''; }) => (0, SyntaxKindDelegator_1.bySyntax)(node, RENDER_MAP, df); exports.renderer = renderer; /** * A conveniece to render a source file. * @param node * @returns */ const render = (title, node) => { var _a; const data = (_a = node.getChildSyntaxList()) === null || _a === void 0 ? void 0 : _a.getChildren().map(c => (0, exports.renderer)(c)).filter(n => !!n.replace(/\s/g, '')).join('\n---\n'); if (!data) return; return `${constants_1.STORY_BOOK_BLOCK} <Meta title="${title}"/> ${data}`; }; exports.render = render;