I've been utilizing the TypeScript compiler API to analyze source code and extract class references.
Within the Node
object that represents the class definition, there is an array of decorators
, but I'm struggling to retrieve the name of each decorator individually.
For guidance, I referred to this particular example on the TypeScript wiki.
///<reference path="typings/node/node.d.ts" />
import * as ts from "typescript";
import * as fs from "fs";
interface DocEntry {
name?: string,
fileName?: string,
documentation?: string,
type?: string,
constructors?: DocEntry[],
parameters?: DocEntry[],
returnType?: string
};
/** Function to generate documentation for all classes within a set of .ts files */
function generateDocumentation(fileNames: string[], options: ts.CompilerOptions): void {
// Creating a program using the root file names provided in fileNames
let program = ts.createProgram(fileNames, options);
// Obtaining the checker to delve deeper into classes
let checker = program.getTypeChecker();
let output: DocEntry[] = [];
// Iterating through each sourceFile in the program
for (const sourceFile of program.getSourceFiles()) {
// Traversing the tree in search of classes
ts.forEachChild(sourceFile, visit);
}
// Saving the documentation to a file
fs.writeFileSync("classes.json", JSON.stringify(output, undefined, 4));
return;
/** Function to visit nodes and locate exported classes */
function visit(node: ts.Node) {
// Only considering exported nodes
if (!isNodeExported(node)) {
return;
}
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
// Identifying top level classes and obtaining their symbols
let symbol = checker.getSymbolAtLocation((<ts.ClassDeclaration>node).name);
output.push(serializeClass(symbol));
// No need to go further as inner declarations cannot be exported
}
else if (node.kind === ts.SyntaxKind.ModuleDeclaration) {
// Handling namespaces by visiting their children
ts.forEachChild(node, visit);
}
}
/** Converting a symbol into a json object */
function serializeSymbol(symbol: ts.Symbol): DocEntry {
return {
name: symbol.getName(),
documentation: ts.displayPartsToString(symbol.getDocumentationComment()),
type: checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration))
};
}
/** Converting class symbol information into json format */
function serializeClass(symbol: ts.Symbol) {
let details = serializeSymbol(symbol);
// Retrieving construct signatures
let constructorType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration);
details.constructors = constructorType.getConstructSignatures().map(serializeSignature);
return details;
}
/** Converting a signature (call or construct) into json */
function serializeSignature(signature: ts.Signature) {
return {
parameters: signature.parameters.map(serializeSymbol),
returnType: checker.typeToString(signature.getReturnType()),
documentation: ts.displayPartsToString(signature.getDocumentationComment())
};
}
/** Checking if the node is exported */
function isNodeExported(node: ts.Node): boolean {
return (node.flags & ts.NodeFlags.Export) !== 0 || (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
}
}
generateDocumentation(process.argv.slice(2), {
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
});