vite-bolt/plugin/ts2json.ts
2023-02-01 17:52:53 +08:00

214 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ts from "typescript";
export interface DocsType {
name?: string;
type?: string;
description?: string;
default?: string;
required?: boolean;
version?: number | string;
}
export interface Interfaces {
name?: {
description?: string;
/** 是否不渲染 markdown */
ignore?: string;
props?: DocsType[];
};
}
/**
* Ts Interface 类型定义转换为 json
*/
export default class TsToJson {
checker: any;
interfaces: any;
fileNames: any;
types: any;
_createProgram(fileNames: string[], options: ts.CompilerOptions) {
this.interfaces = {};
this.types = {};
// 使用filename中的根文件名构建一个程序;
const program = ts.createProgram(fileNames, options);
// 获取检查器,我们将使用它来查找更多关于 ast 的信息
this.checker = program.getTypeChecker();
this.fileNames = fileNames;
// 访问程序中的每个sourceFile
for (const sourceFile of program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) {
// 遍历树以搜索 interface 和 type
ts.forEachChild(sourceFile, (node) => this._visitAst(node));
}
}
}
/**
* 访问查找导出的节点
* @param node
* @returns
*/
_visitAst(node: ts.Node) {
// 只考虑导出的节点
if (!this._isNodeExported(node)) {
return;
}
switch (ts.SyntaxKind[node.kind]) {
case "InterfaceDeclaration":
this.generateInterfaceDeclaration(node);
break;
case "TypeAliasDeclaration":
this.generateTypeAliasDeclaration(node);
break;
default:
break;
}
}
/**
* 获取 Interface 类型 json 数据
* @param node
*/
generateInterfaceDeclaration(node: ts.Node) {
if (
// @ts-ignore
node.parent?.fileName.replace(/\\/g, "/") !==
this.fileNames[0].replace(/\\/g, "/")
)
return;
const newNode = node as ts.InterfaceDeclaration;
const symbol = this.checker.getSymbolAtLocation(newNode.name);
const { escapedName } = symbol;
const { name = escapedName as string, ...rest } = this._getDocs(symbol);
this.interfaces[name] = {
...rest,
props: [],
};
if (newNode.heritageClauses) {
// 是否有extends
const firstHeritageClause = newNode.heritageClauses[0];
const firstHeritageClauseType = firstHeritageClause.types![0];
const extendsType = this.checker.getTypeAtLocation(
firstHeritageClauseType.expression
);
if (extendsType?.symbol?.members && extendsType.symbol.members.size > 0) {
// 接口 extends 接口Interface
extendsType.symbol.members.forEach((member: any) => {
this.interfaces[name].props.push(this._serializeSymbol(member));
});
} else if (firstHeritageClauseType) {
console.log("暂时不支持接口继承Type类型");
// 接口 extends 类型Type
// const type = this.checker.typeToTypeNode(
// this.checker.getTypeAtLocation(firstHeritageClauseType),
// firstHeritageClauseType,
// ts.NodeBuilderFlags.InTypeAlias
// // ts.TypeFormatFlags.InTypeAlias
// );
// console.log(firstHeritageClauseType.getFullText());
}
}
symbol.members.forEach((member: any) => {
this.interfaces[name].props.push(this._serializeSymbol(member));
});
}
/**
* 获取 Type 类型 json 数据
* @param node
*/
generateTypeAliasDeclaration(node: ts.Node) {
const type = this.checker.typeToString(
this.checker.getTypeAtLocation(node),
node,
ts.TypeFormatFlags.InTypeAlias
);
const name = (node as any).name.escapedText;
this.types[name] = type;
}
/**
* 将symbol序列化为json对象
* @param symbol
* @returns
*/
_serializeSymbol(symbol: ts.Symbol): DocsType | undefined {
if (!symbol) return;
const docs = this._getDocs(symbol);
const questionToken = (symbol as any).valueDeclaration?.questionToken;
const type = this.checker.typeToString(
this.checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration)
);
let newType = type;
if (type.indexOf("|") > 0) {
newType = type
.split("|")
.map((item: any) => {
const hasType = this.types[item.trim()];
if (hasType) {
return ` ${hasType} `;
}
return item;
})
.join("|");
} else if (this.types[type]) {
newType = this.types[type];
}
return {
name: symbol.getName(),
required: !questionToken,
type: newType,
...docs,
};
}
/**
* 获取jsDoc信息
* @param symbol
* @returns
*/
_getDocs(symbol: ts.Symbol): DocsType {
const docs: DocsType = {};
if (
symbol.getJsDocTags &&
Array.isArray(symbol.getJsDocTags(this.checker))
) {
symbol.getJsDocTags(this.checker).forEach((tag) => {
(docs as any)[tag.name] = tag.text && tag.text[0].text;
});
}
return docs;
}
/**
* 如果在文件外部可见则为True否则为false
* @param node
* @returns boolean
*/
_isNodeExported(node: ts.Node): boolean {
return (
(ts.getCombinedModifierFlags(node as ts.Declaration) &
ts.ModifierFlags.Export) !==
0 ||
(!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile)
);
}
parse(
fileNames: string,
options: ts.CompilerOptions = {
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS,
}
) {
this._createProgram([fileNames], options);
return this.interfaces;
}
}