@travetto/transformer
Version:
Functionality for AST transformations, with transformer registration, and general utils
102 lines (89 loc) • 3.06 kB
text/typescript
import ts from 'typescript';
import { transformCast, type DeclDocumentation } from '../types/shared.ts';
import { CoreUtil } from './core.ts';
import { DeclarationUtil } from './declaration.ts';
/**
* Utilities for dealing with docs
*/
export class DocUtil {
/**
* See if node has js docs
*/
static hasJSDoc(node: ts.Node): node is (ts.Node & { jsDoc: ts.JSDoc[] }) {
return 'jsDoc' in node && node.jsDoc !== null && node.jsDoc !== undefined && Array.isArray(node.jsDoc) && node.jsDoc.length > 0;
}
/**
* Read doc comment for node
*/
static getDocComment(node: ts.JSDoc | ts.JSDocTag, def?: string): string | undefined {
return (typeof node.comment === 'string' ? node.comment : undefined) ?? def;
}
/**
* Read JS Docs from a `ts.Declaration`
*/
static describeDocs(node: ts.Declaration | ts.Type): DeclDocumentation {
let toDescribe = (node && !('getSourceFile' in node)) ?
DeclarationUtil.getOptionalPrimaryDeclarationNode(node) : node;
const out: DeclDocumentation = {
description: undefined,
return: undefined,
params: []
};
if (toDescribe) {
const tags = ts.getJSDocTags(toDescribe);
while (!this.hasJSDoc(toDescribe) && CoreUtil.hasOriginal(toDescribe)) {
toDescribe = transformCast<ts.Declaration>(toDescribe.original);
}
const docs = this.hasJSDoc(toDescribe) ? toDescribe.jsDoc : undefined;
if (docs) {
const top = docs.at(-1)!;
if (ts.isJSDoc(top)) {
out.description = this.getDocComment(top, out.description);
}
}
if (tags && tags.length) {
for (const tag of tags) {
if (ts.isJSDocReturnTag(tag)) {
out.return = this.getDocComment(tag, out.return);
} else if (ts.isJSDocParameterTag(tag)) {
out.params!.push({
name: tag.name && tag.name.getText(),
description: this.getDocComment(tag, '')!
});
}
}
}
}
return out;
}
/**
* Read JS Doc tags for a type
*/
static readDocTag(type: ts.Type | ts.Symbol, name: string): string[] {
const tags = CoreUtil.getSymbol(type)?.getJsDocTags() ?? [];
return tags
.filter(tag => tag.name === name && !!tag.text)
.map(tag => tag.text!.map(part => part.text).join('')); // Join all text
}
/**
* Has JS Doc tags for a type
*/
static hasDocTag(type: ts.Type | ts.Symbol, name: string): boolean {
const tags = CoreUtil.getSymbol(type)?.getJsDocTags() ?? [];
return tags.some(tag => tag.name === name);
}
/**
* Read augments information
* @param type
*/
static readAugments(type: ts.Type | ts.Symbol): string[] {
return this.readDocTag(type, 'augments').map(line => line.replace(/^.*?([^` ]+).*?$/, (_, b) => b));
}
/**
* Read example information
* @param type
*/
static readExample(type: ts.Type | ts.Symbol): string[] {
return this.readDocTag(type, 'example').map(line => line.replace(/^.*?([^` ]+).*?$/, (_, b) => b));
}
}