Use experimental Typescript API to process class defs
The goal is to generate reusable interfaces from entity definitions. While class definitions could be made to implement interfaces, this seems like too much work at this point.
This commit is contained in:
parent
19565563cc
commit
4f90e865fa
@ -1,2 +1,3 @@
|
||||
export * from './build'
|
||||
export * from './newlib'
|
||||
export * from './intergen'
|
||||
|
||||
38
packages/scripts/src/commands/intergen.sample.ts
Normal file
38
packages/scripts/src/commands/intergen.sample.ts
Normal file
@ -0,0 +1,38 @@
|
||||
export class Primitives {
|
||||
str!: string
|
||||
num!: number
|
||||
bool!: boolean
|
||||
|
||||
strArray!: string[]
|
||||
numArray!: number[]
|
||||
boolArray!: boolean[]
|
||||
}
|
||||
|
||||
export class Name {
|
||||
firstName!: string
|
||||
lastName!: string
|
||||
}
|
||||
|
||||
export interface IYear {
|
||||
year: number
|
||||
}
|
||||
|
||||
type AorB = 'A' | 'B'
|
||||
|
||||
export class Person {
|
||||
readonly name!: Name
|
||||
readonly aliases?: Name[]
|
||||
readonly aOrB!: AorB
|
||||
readonly inlineAOrB?: 'a' | 'b'
|
||||
readonly inlineInterface?: {
|
||||
a: number
|
||||
b: string
|
||||
}
|
||||
age?: number
|
||||
birthyear: IYear | null = null
|
||||
stringAndNumberTuple!: [string, number]
|
||||
}
|
||||
|
||||
export class Employee extends Person {
|
||||
duties: string[] = []
|
||||
}
|
||||
107
packages/scripts/src/commands/intergen.ts
Normal file
107
packages/scripts/src/commands/intergen.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import * as path from 'path'
|
||||
import * as ts from 'typescript'
|
||||
import {readFileSync} from 'fs'
|
||||
|
||||
function delint(sourceFile: ts.SourceFile) {
|
||||
delintNode(sourceFile)
|
||||
|
||||
function delintNode(node: ts.Node) {
|
||||
// TODO check which classes are exported
|
||||
// TODO check which interfaces are in use
|
||||
// TODO check which type references are in use in addition to the
|
||||
// primitives like string, string[], number, number[], boolean,
|
||||
// boolean[]
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
const cls = node as ts.ClassDeclaration
|
||||
if (!cls.name) {
|
||||
// TODO warn
|
||||
console.log('no class name', cls.pos)
|
||||
break
|
||||
}
|
||||
console.log(cls.name.escapedText)
|
||||
for (const m of cls.members) {
|
||||
if (m.kind !== ts.SyntaxKind.PropertyDeclaration) {
|
||||
console.log('not implemented support for node.kind:', m.kind)
|
||||
break
|
||||
}
|
||||
const member = m as ts.PropertyDeclaration
|
||||
const name = m.name as ts.Identifier
|
||||
console.log(' ', name.escapedText)
|
||||
if (m.modifiers) {
|
||||
for (const modifier of m.modifiers) {
|
||||
if (modifier.kind === ts.SyntaxKind.ReadonlyKeyword) {
|
||||
console.log(' ', 'readonly')
|
||||
} else {
|
||||
console.log(' modifier.kind:', modifier.kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!member.type) {
|
||||
console.log('no member type!')
|
||||
break
|
||||
}
|
||||
// console.log(' member:', JSON.stringify(member, null, ' '))
|
||||
console.log(' questionToken:', !!member.questionToken)
|
||||
switch (member.type.kind) {
|
||||
case ts.SyntaxKind.StringKeyword:
|
||||
console.log(' string')
|
||||
break
|
||||
case ts.SyntaxKind.BooleanKeyword:
|
||||
console.log(' boolean')
|
||||
break
|
||||
case ts.SyntaxKind.NumberKeyword:
|
||||
console.log(' number')
|
||||
break
|
||||
case ts.SyntaxKind.TypeReference:
|
||||
const typeRef = member.type as ts.TypeReferenceNode
|
||||
console.log(' ', (typeRef.typeName as any).escapedText)
|
||||
break
|
||||
case ts.SyntaxKind.TypeLiteral:
|
||||
console.log(' ', 'type literal...')
|
||||
break
|
||||
case ts.SyntaxKind.UnionType:
|
||||
console.log(' ', 'union type...')
|
||||
break
|
||||
case ts.SyntaxKind.TupleType:
|
||||
console.log(' ', 'tuple type...')
|
||||
break
|
||||
case ts.SyntaxKind.ArrayType:
|
||||
const arrayType = member.type as ts.ArrayTypeNode
|
||||
switch (arrayType.elementType.kind) {
|
||||
case ts.SyntaxKind.StringKeyword:
|
||||
console.log(' Array<string>')
|
||||
break
|
||||
case ts.SyntaxKind.BooleanKeyword:
|
||||
console.log(' Array<boolean>')
|
||||
break
|
||||
case ts.SyntaxKind.NumberKeyword:
|
||||
console.log(' Array<number>')
|
||||
break
|
||||
case ts.SyntaxKind.TypeReference:
|
||||
const typeRef = arrayType.elementType as ts.TypeReferenceNode
|
||||
console.log(` Array<${(typeRef.typeName as any).escapedText}>`)
|
||||
default:
|
||||
console.log('WARN unhandled array type:', arrayType.elementType.kind)
|
||||
}
|
||||
break
|
||||
default:
|
||||
console.log('unhandled type:', member.type.kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, delintNode)
|
||||
}
|
||||
}
|
||||
|
||||
export async function intergen(argv: string[]) {
|
||||
const filename = path.join(__dirname, 'intergen.sample.ts')
|
||||
const sourceFile = ts.createSourceFile(
|
||||
filename,
|
||||
readFileSync(filename).toString(),
|
||||
ts.ScriptTarget.ES2015,
|
||||
false,
|
||||
)
|
||||
|
||||
delint(sourceFile)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user