@irwinproject/storybook-addon-tsdoc
Version:
Generate mdx documentation from your typescript!
204 lines (203 loc) • 9.59 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.documentDeclarations = exports.documentDeclaration = void 0;
const ts_morph_1 = require("ts-morph");
const decorators_1 = require("./decorators");
const TS_1 = __importDefault(require("./TS"));
const node_tools_1 = require("./node-tools");
const fs_1 = require("fs");
const node_signature_1 = require("./node-signature");
const traverse_1 = require("./traverse");
const signitors_1 = require("./signitors");
const getChildrenLinks = (node) => {
if (ts_morph_1.Node.isUnionTypeNode(node) || ts_morph_1.Node.isIntersectionTypeNode(node)) {
return node.getTypeNodes().flatMap(getChildrenLinks);
}
else if (ts_morph_1.Node.isTupleTypeNode(node) || ts_morph_1.Node.isArrayLiteralExpression(node) || ts_morph_1.Node.isArrayLiteralExpression(node) || ts_morph_1.Node.isObjectBindingPattern(node)) {
return node.getElements().flatMap(getChildrenLinks);
}
else if (ts_morph_1.Node.isObjectLiteralExpression(node) || ts_morph_1.Node.isTypeLiteral(node)) {
return node.getProperties().flatMap(getChildrenLinks);
}
else if (ts_morph_1.Node.isPropertyDeclaration(node) || ts_morph_1.Node.isTypeReference(node) || ts_morph_1.Node.isPropertySignature(node)) {
return [node];
}
return [];
};
/**
* @param node
* @returns
*/
const getProperties = (node) => {
const properties = [];
if ((0, node_tools_1.isPrivate)(node))
return [];
if (ts_morph_1.Node.isInitializerExpressionGetable(node) || ts_morph_1.Node.isInitializerExpressionable(node)) {
properties.push(...getProperties(node.getInitializer()));
}
if (ts_morph_1.Node.isTypeAliasDeclaration(node)) {
properties.push(...getProperties(node.getTypeNode()));
}
else if (ts_morph_1.Node.isTypeElementMembered(node)) {
properties.push(...node.getProperties().map(n => renderPropLink(n, 4)));
}
else if (ts_morph_1.Node.isUnionTypeNode(node) || ts_morph_1.Node.isIntersectionTypeNode(node)) {
properties.push(...node.getTypeNodes().flatMap(getProperties));
}
else if (ts_morph_1.Node.isArrayBindingPattern(node)) {
properties.push(...node.getElements().flatMap(getProperties));
}
return properties;
};
const getMembers = (node) => {
if (ts_morph_1.Node.isClassLikeDeclarationBase(node))
return node.getInstanceMembers().filter(n => !(0, node_tools_1.isPrivate)(n)).map(n => renderPropLink(n, 6));
if (ts_morph_1.Node.isVariableDeclaration(node)) {
const tn = node.getInitializer();
if (ts_morph_1.Node.isClassExpression(tn))
return tn.getInstanceMembers().filter(n => !(0, node_tools_1.isPrivate)(n)).map(n => renderPropLink(n, 6));
if (ts_morph_1.Node.isObjectLiteralExpression(tn))
return tn.getProperties().filter(n => !(0, node_tools_1.isPrivate)(n)).map(n => renderPropLink(n, 6));
}
if (ts_morph_1.Node.isInterfaceDeclaration(node))
return node.getMembers().filter(n => !(0, node_tools_1.isPrivate)(n)).map(n => renderPropLink(n, 6));
return [];
};
const getStaticMembers = (node) => {
if (ts_morph_1.Node.isClassLikeDeclarationBase(node))
return node.getStaticMembers().map(n => renderPropLink(n, 6));
return [];
};
const getKind = (node) => {
var _a, _b;
if (ts_morph_1.Node.isTypeAliasDeclaration(node))
return 'type';
if (ts_morph_1.Node.isVariableDeclaration(node))
return (_b = (_a = node.getVariableStatement()) === null || _a === void 0 ? void 0 : _a.getDeclarationKind()) !== null && _b !== void 0 ? _b : "const";
if (ts_morph_1.Node.isClassDeclaration(node))
return 'class';
if (ts_morph_1.Node.isPropertySignature(node) || ts_morph_1.Node.isPropertyAssignment(node))
return 'property'; //todo check if its a method
if (ts_morph_1.Node.isMethodSignature(node))
return 'method';
if (ts_morph_1.Node.isFunctionDeclaration(node))
return 'function';
if (ts_morph_1.Node.isParameterDeclaration(node))
return 'argument';
if (ts_morph_1.Node.isPropertyDeclaration(node))
return (node.isStatic() ? 'static ' : '') + 'property'; //todo check if its a method
if (ts_morph_1.Node.isMethodDeclaration(node))
return (node.isStatic() ? 'static ' : '') + 'method';
if (ts_morph_1.Node.isGetAccessorDeclaration(node))
return (node.isStatic() ? 'static ' : '') + 'get';
if (ts_morph_1.Node.isSetAccessorDeclaration(node))
return (node.isStatic() ? 'static ' : '') + 'set';
if (ts_morph_1.Node.isConstructorDeclaration(node))
return 'constructor';
if (ts_morph_1.Node.isInterfaceDeclaration(node))
return 'interface';
TS_1.default.err("No kind support", node.getKindName());
return 'no support';
};
const getTypeParameters = (node) => {
return ts_morph_1.Node.isTypeParametered(node) ? node.getTypeParameters().map(node_signature_1.getSignature).join('\n * ')
: '';
};
const getArguments = (node) => {
if (ts_morph_1.Node.isParametered(node))
return node.getParameters().map(n => render(n, 4));
//It makes sense that the initializer type node take precedence. Allows for later support to display default values.
if (ts_morph_1.Node.isInitializerExpressionGetable(node)) {
const tn = node.getInitializer();
if (ts_morph_1.Node.isParametered(tn)) {
return getArguments(tn);
}
}
if (ts_morph_1.Node.isTyped(node)) {
const tn = node.getTypeNode();
if (ts_morph_1.Node.isParametered(tn)) {
return getArguments(tn);
}
}
return [];
};
const getConstructor = (node) => {
if (ts_morph_1.Node.isVariableDeclaration(node))
return getConstructor(node.getInitializer());
if (!(ts_morph_1.Node.isClassDeclaration(node) || ts_morph_1.Node.isClassExpression(node)))
return [];
return node.getConstructors().map(n => render(n, 6));
};
const getReturns = (node) => {
if (ts_morph_1.Node.isReturnTyped(node))
return (0, signitors_1.fromReturn)(node);
if (ts_morph_1.Node.isTyped(node)) {
const tn = node.getTypeNode();
if (ts_morph_1.Node.isReturnTyped(tn))
return (0, signitors_1.fromReturn)(tn);
}
if (ts_morph_1.Node.isInitializerExpressionGetable(node)) {
const tn = node.getInitializer();
if (ts_morph_1.Node.isReturnTyped(tn))
return (0, signitors_1.fromReturn)(tn);
}
return '';
};
/**
* Instead of a full render method this generates a link title that can be used to go to a dedicated page.
* @param node
* @param h
*/
const renderPropLink = (node, h) => {
if (!node)
return '';
(0, exports.documentDeclaration)(node);
const path = (0, node_tools_1.getDocPath)(node);
if (!path)
return '';
return (0, decorators_1.$t)(h) `[${(0, decorators_1.$kind)(getKind(node))} ${(0, node_tools_1.getName)(node)}](${path})`;
};
const render = (node, h = 2) => {
if (!node)
return '';
return (0, decorators_1.$s)(h, getKind(node), node) + (0, decorators_1.$section)((0, node_tools_1.getComments)(node), (0, decorators_1.$section)(getConstructor(node).join('\n---\n')).wrap((0, decorators_1.$kd) `**Constructors**:` + '\n', '\n', false), getTypeParameters(node).wrap((0, decorators_1.$kd) `**Type Parameters**:` + '\n\n * ', '\n', false), (0, decorators_1.$section)(getProperties(node).join('\n---\n')).wrap((0, decorators_1.$kd) `**Type Properties**:` + '\n', '\n', false), (0, decorators_1.$section)(getArguments(node).join('\n---\n')).wrap((0, decorators_1.$kd) `**Arguments**:` + '\n', '\n', false), getReturns(node).wrap((0, decorators_1.$kd) `**Returns**: `, '\n', false), (0, node_tools_1.getExample)(node), (0, decorators_1.$section)(getMembers(node).join('\n---\n')).wrap((0, decorators_1.$kd) `**Properties**:` + '\n', '\n', false), (0, decorators_1.$section)(getStaticMembers(node).join('\n---\n')).wrap((0, decorators_1.$kd) `**Static Properties**:` + '\n', '\n', false));
};
const documentDeclaration = (node) => {
let title = TS_1.default.resolveUrl(node.getSourceFile().getFilePath()) + '/' + (0, node_tools_1.getFullName)(node, '/');
const t = title.toLowerCase();
const ds = new Set(Object.keys(TS_1.default.decs).map(d => d.toLowerCase()));
//Storybook titles are not case sensitive in document CSFs they appear to be in component CSF.
if (ds.has(t)) {
if (!TS_1.default.decs[title]) {
TS_1.default.decs[title] = title + '(1)';
title = TS_1.default.decs[title];
}
else {
let o = 2;
while (ds.has(t + o))
o++;
TS_1.default.decs[title] = title + `(${o})`;
title = TS_1.default.decs[title];
}
}
else {
TS_1.default.decs[title] = title;
}
const docPath = TS_1.default.resolvedDocFilePath(title);
const data = render(node);
if (!data) {
TS_1.default.err(node.getKindName());
return;
}
(0, fs_1.writeFileSync)(docPath, (0, decorators_1.$doc)(data, title));
};
exports.documentDeclaration = documentDeclaration;
const documentDeclarations = (source) => {
for (const node of (0, traverse_1.traverse)(source)) {
(0, exports.documentDeclaration)(node);
}
};
exports.documentDeclarations = documentDeclarations;