-
-
Save goloveychuk/81ff1125d0b079a1faa87aa546bb9dd2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as ts from "typescript"; | |
import * as fs from "fs"; | |
import * as types from './lib'; | |
class PropertyInfo { | |
constructor(readonly name: string, readonly type: types.AllTypes) { | |
} | |
} | |
class TypeWrapper { | |
constructor(readonly name: string) {} | |
} | |
class ClsInfo { | |
constructor(readonly name: string) { | |
} | |
properties: PropertyInfo[] = [] | |
}; | |
export function unwrap<T>(typ: T | undefined): T | never { | |
if (typ === undefined) throw new Error(`type is undefined`); | |
return typ | |
} | |
class Visiter { | |
result: ClsInfo[] = [] | |
constructor(readonly checker: ts.TypeChecker, readonly sourceFile: ts.SourceFile) { | |
} | |
visitAllNodes() { | |
ts.forEachChild(this.sourceFile, this.visit); | |
} | |
serializePropType(type: ts.TypeNode): types.AllTypes { | |
if (type.kind == ts.SyntaxKind.TypeReference) { | |
const nodType = <ts.TypeReferenceNode>type; | |
const typeName = nodType.typeName.getText() | |
const typeArgs = nodType.typeArguments; | |
let allTypes: types.AllTypes[] = []; | |
if (typeArgs !== undefined) { | |
allTypes = typeArgs.map(t => this.serializePropType(t)) | |
} | |
return { kind: types.TypeKind.Reference, arguments: allTypes, type: new TypeWrapper(typeName) } | |
} else if (type.kind == ts.SyntaxKind.UnionType) { | |
const nodType = <ts.UnionTypeNode>type; | |
const nestedTypes = nodType.types.map(t => this.serializePropType(t)) | |
return { kind: types.TypeKind.Union, types: nestedTypes } | |
} else if (type.kind == ts.SyntaxKind.NumberKeyword) { | |
return { kind: types.TypeKind.Number } | |
} else if (type.kind == ts.SyntaxKind.BooleanKeyword) { | |
return { kind: types.TypeKind.Boolean } | |
} else if (type.kind == ts.SyntaxKind.StringKeyword) { | |
return { kind: types.TypeKind.String } | |
} else if (type.kind == ts.SyntaxKind.UndefinedKeyword) { | |
return { kind: types.TypeKind.Undefined } | |
} else if (type.kind == ts.SyntaxKind.NullKeyword) { | |
return { kind: types.TypeKind.Null } | |
} | |
throw new Error() | |
} | |
visitClsChild(clsInfo: ClsInfo, node: ts.Node) { | |
if (node.kind === ts.SyntaxKind.PropertyDeclaration) { | |
const nod = <ts.PropertyDeclaration>node | |
const propName = nod.name.getText(); | |
const nodeType = unwrap(nod.type) | |
const optional = nod.questionToken !== undefined | |
let serializedType = this.serializePropType(nodeType) | |
if (optional) { | |
serializedType = {kind: types.TypeKind.Union, types: [serializedType, {kind: types.TypeKind.Undefined}]} | |
} | |
const property = new PropertyInfo(propName, serializedType) | |
clsInfo.properties.push(property) | |
} | |
} | |
visit = (node: ts.Node) => { | |
if (node.kind === ts.SyntaxKind.ClassDeclaration) { | |
const nod = <ts.ClassDeclaration>node; | |
if (nod.name == undefined) { | |
throw new Error('cls name is undefined') | |
} | |
const clsInfo = new ClsInfo(nod.name.text) | |
this.result.push(clsInfo) | |
this.checker.getSymbolsInScope | |
ts.forEachChild(node, (n: ts.Node) => this.visitClsChild(clsInfo, n)); | |
} | |
else if (node.kind === ts.SyntaxKind.ModuleDeclaration) { | |
ts.forEachChild(node, this.visit); | |
} | |
} | |
} | |
function serializeType(obj: any): string { | |
if (obj instanceof TypeWrapper) { | |
return obj.name | |
} | |
if (Array.isArray(obj)) { | |
const arr = obj.map(o => serializeType(o)).join(', ') | |
return `[${arr}]` | |
} | |
if (obj instanceof Object) { | |
let res: string[] = [] | |
for (const k in obj) { | |
let s = JSON.stringify(k) | |
s+= ': ' | |
s+=serializeType(obj[k]) | |
res.push(s) | |
} | |
return `{ ${res.join(', ')} }` | |
} | |
return JSON.stringify(obj) | |
} | |
function makeDecorators(cls: ClsInfo[]) { | |
const res: string[] = [] | |
cls.forEach(cls => { | |
cls.properties.forEach(pr => { | |
const typ = serializeType(pr.type) | |
const dec = `Reflect.defineMetadata("tsreflect:type", ${typ}, ${cls.name}, "${pr.name}")` | |
res.push(dec) | |
}) | |
}) | |
return res.join("\n") | |
} | |
function Proceed() { | |
// console.log(text) | |
const filepath = process.argv[2]; | |
let text = fs.readFileSync(filepath).toString(); | |
const sourceFile = ts.createSourceFile(filepath, text, ts.ScriptTarget.ES2016, true) | |
let program = ts.createProgram([], { target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS }); | |
let checker = program.getTypeChecker(); | |
// const sourceFile = program.getSourceFile(filepath) | |
const visiter = new Visiter(checker, sourceFile) | |
visiter.visitAllNodes(); | |
const res = makeDecorators(visiter.result) | |
return text + "\n\n" + res | |
} | |
console.log(Proceed()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment