import * as ts from 'typescript'; import fs = require('fs'); import { FileUtils } from '../../../utils/FileUtils'; const options: ts.CompilerOptions = { module: ts.ModuleKind.CommonJS }; export interface TempClassData{ baseNames: string[]; implementedNames: string[]; classNode: ClassNode; } export interface IDecorator{ className: string; propertyName: string; callName: string; arguments: any[]; } export class Props{ name: string = ''; type: string = ''; value: string = ''; decorators: IDecorator[]; } export class ClassNode{ props: any; methods: any; baseClass: ClassNode; className: string; implementeds: ClassNode[] = []; isInterface: boolean = false; } export class TsParser { tempClassMap = {}; private _services: ts.LanguageService; constructor() { } /** * 将ts语法文件字符串编译成js * @param source 编译源 */ transpileSource(source: string): ts.TranspileOutput { return ts.transpileModule(source, options); } /**将ts文件编译成js */ transpileFile(url: string): ts.TranspileOutput{ let source: string = fs.readFileSync(url, 'utf-8'); return source ? this.transpileSource(source) : null; } parse(url: string, projectPath: string): void { let datastr: string = fs.readFileSync(url, 'utf-8'); let data = JSON.parse(datastr); FileUtils.select(projectPath, ['ts'], (filePath: string) => { this.addFile(filePath); }, this); } addFile(filePath: string): void{ if (this.isTs(filePath)) { } } /** * 检测是否为ts文件 */ isTs(path: string): boolean{ return true; } /** * 文件改变 */ parseFiles(files: string[]): void { let servicesHost: ts.LanguageServiceHost = { getScriptFileNames: () => { return files.concat(); }, getScriptVersion: (fileName) => '0.0.0', getScriptSnapshot: (fileName: string) => { if (!fs.existsSync(fileName)) { return undefined; } return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); }, getCurrentDirectory: () => process.cwd(), getCompilationSettings: () => options, getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options) }; //创建语言服务 this._services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } getClassDataMap(): { [className: string]: TempClassData } { this.tempClassMap = {}; if (this._services) { let program = this._services.getProgram(); let sourceCodes: ts.SourceFile[] = program.getSourceFiles(); let checker = program.getTypeChecker(); for (let i: number = 0, l: number = sourceCodes.length; i < l; i++){ let sourceCode: ts.SourceFile = sourceCodes[i]; //if (sourceCode.fileName.indexOf('lib.d.ts') === -1) { this.delintNode(sourceCode, checker); //} } } return this.tempClassMap; } delintNode(node: ts.Node, checker: ts.TypeChecker): void { let nodeKind: ts.SyntaxKind = node.kind; if ((node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.InterfaceDeclaration) && this.isExport(node, checker)) { let nodeType = checker.getTypeAtLocation(node); let baseTypes = checker.getBaseTypes(nodeType); let implementedTypes = this.getImplementedInterfaces(nodeType, checker); let symbol = nodeType.getSymbol(); let props = symbol.members; let baseClassNames = this.getClassNames(baseTypes, checker); let implementedNames = this.getClassNames(implementedTypes, checker); let propHash: any = {}; let className = this.getQualifiedName(symbol, checker); let classNode: ClassNode = new ClassNode(); for (let name in props) { if (name.indexOf('$') === 0) continue; let prop: ts.Symbol = props[name]; //如果有多个declarations, 一般为getter与setter方法 let declaration = prop.declarations[0]; if (prop.flags & (ts.SymbolFlags.Property | ts.SymbolFlags.Accessor)) { let decoratorsDatas: IDecorator[] = this.getDecorators(declaration, className, name); //过滤掉私有属性 if ((declaration['modifierFlagsCache'] === ((ts.ModifierFlags.Private) | ts.ModifierFlags.HasComputedFlags))) { continue; } //过滤掉Proected属性 if ((declaration['modifierFlagsCache'] === ((ts.ModifierFlags.Protected) | ts.ModifierFlags.HasComputedFlags))) { continue; } //默认getter方法与setter方法显示 // if ((prop.flags & ts.SymbolFlags.Accessor) && prop.declarations.filter(d => d.kind === ts.SyntaxKind.SetAccessor).length === 0) { // continue; // } let jsdoc: any[] = declaration['jsDoc']; if (jsdoc) { if (jsdoc.some((value, index, array) => value.tags && value.tags.some((tagNode, tagIndex, tagArray) => tagNode.tagName.text.indexOf('private') >= 0))) { continue; } } // if (prop.getDocumentationComment().some(c => c.text.indexOf('@private') >= 0)) { // continue; // } let propType = checker.getTypeAtLocation(declaration); let initializer; if(prop.valueDeclaration) initializer = (prop.valueDeclaration).initializer; let flags: ts.TypeFlags = propType.getFlags(); let type = ''; let defaultValue:any; if (flags & ts.TypeFlags.Boolean) { type = 'boolean'; if (initializer) defaultValue = initializer.getText() === "true" ? true : false; else defaultValue = false; } else if (flags & ts.TypeFlags.String) { type = 'string'; if (initializer) defaultValue = initializer.getText(); else defaultValue = '\"\"'; } else if (flags & ts.TypeFlags.Number) { type = 'number'; if (initializer) defaultValue = parseFloat(initializer.getText()); else defaultValue = 0; } else { let symbol = propType.getSymbol(); if (symbol) type = this.getQualifiedName(symbol, checker); else type = 'any'; if (initializer) defaultValue = initializer.getText(); else defaultValue = null; } let propNode: Props = new Props(); propNode.name = name; propNode.type = type; propNode.value = defaultValue; propNode.decorators = decoratorsDatas; propHash[name] = propNode; } else if (prop.flags & ts.SymbolFlags.Method) { let declaration = prop.declarations[0]; let decoratorsDatas: IDecorator[] = this.getDecorators(declaration, className, name); let methods: any = {}; let methodNode = { name: name, decorators: decoratorsDatas }; if (methods[methodNode.name] === undefined) methods[methodNode.name] = methodNode; classNode.methods = methods; } } classNode.isInterface = (nodeKind === ts.SyntaxKind.InterfaceDeclaration); classNode.props = propHash; classNode.className = className; let tempClassNode = { baseNames: baseClassNames, implementedNames: implementedNames, classNode: classNode }; if (this.checkCanAdded(classNode)) { this.tempClassMap[className] = tempClassNode; } } ts.forEachChild(node, (node) => { this.delintNode(node, checker); }); } private checkCanAdded(node: ClassNode): boolean{ return true; } private isExport(node: ts.Node, checker: ts.TypeChecker): boolean{ let symbol: ts.Symbol = checker.getTypeAtLocation(node).getSymbol(); if (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile) { // if (!symbol['parent']) { // return true; // } return true; } else { if (symbol['parent']) { return true; } } return false; } getDecorators(declaration:ts.Declaration,className:string,propertyName:string):IDecorator[] { let decorators: ts.Decorator[] = declaration.decorators; let decoratorsDatas: IDecorator[] = []; if (decorators) { for (let i: number = 0, l: number = decorators.length; i < l; i++){ let decorator: ts.Decorator = decorators[i]; let expressionData: any = decorator.expression; let expressArguments: any[] = expressionData.arguments; let expression: any = expressionData.expression; let decorateCallHandler = expression.text; let decoratorData: IDecorator = { className: className, propertyName: propertyName, callName: expression.text, arguments: [] }; for (let j: number = 0, jl: number = expressArguments.length; j < jl; j++){ let argumentdata: any = expressArguments[j]; let argumentObject: any = {value: argumentdata.text }; decoratorData.arguments[j] = argumentObject; } decoratorsDatas[i] = decoratorData; } } return decoratorsDatas; } getQualifiedName(symbol: ts.Symbol, checker: ts.TypeChecker): string { let parent = symbol['parent']; let parentIsFile: boolean = false; if (parent && parent.declarations && parent.declarations.length === 1) { let fileObject = parent.declarations[0]; if (fileObject && fileObject['fileName']) { parentIsFile = true; } } return symbol['parent'] && !parentIsFile ? this.getQualifiedName(symbol['parent'], checker) + '.' + checker.symbolToString(symbol) : checker.symbolToString(symbol); } getClassExtendsHeritageClauseElement(node: ts.ClassLikeDeclaration) { let heritageClause = this.getHeritageClause(node.heritageClauses, ts.SyntaxKind.ExtendsKeyword); return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined; } getClassImplementsHeritageClauseElements(node: ts.ClassLikeDeclaration) { let heritageClause = this.getHeritageClause(node.heritageClauses, ts.SyntaxKind.ImplementsKeyword); return heritageClause ? heritageClause.types : undefined; } getInterfaceBaseTypeNodes(node: ts.InterfaceDeclaration) { let heritageClause = this.getHeritageClause(node.heritageClauses, ts.SyntaxKind.ExtendsKeyword); return heritageClause ? heritageClause.types : undefined; } getHeritageClause(clauses: ts.NodeArray, kind: ts.SyntaxKind) { if (clauses) { for (let clause of clauses) { if (clause.token === kind) { return clause; } } } return undefined; } getImplementedInterfaces(type: ts.Type, checker: ts.TypeChecker) { let superInterfaces: any[] = null; let result: ts.ObjectType[] = []; if (type.symbol.declarations) { type.symbol.declarations.forEach(node => { let interfaceType = checker.getTypeAtLocation(node); let isClass = interfaceType.flags & ts.TypeFlags.Object;//Class改为Object了 if (isClass) { superInterfaces = this.getClassImplementsHeritageClauseElements(node); } else { superInterfaces = this.getInterfaceBaseTypeNodes(node); } if (superInterfaces) { superInterfaces.forEach(sp => { interfaceType = checker.getTypeAtLocation(sp); if (interfaceType.flags & ts.TypeFlags['Interface']) { result.push(interfaceType); } }); } }); } return result; } getClassNames(types: ts.Type[], checker: ts.TypeChecker): string[] { let classNames: string[] = []; for (let i: number = 0, l: number = types.length; i < l; i++){ classNames[i] = this.getClassName(types[i], checker); } return classNames; } private getClassName(type: ts.Type, checker: ts.TypeChecker): string{ let symbol = type.getSymbol(); let className = this.getQualifiedName(symbol, checker); return className; } }