Add -i, -o flags to typecheck.ts

This commit is contained in:
Jerko Steiner 2019-08-14 14:16:41 +07:00
parent f8536b7446
commit cca888641a

View File

@ -1,6 +1,7 @@
import * as fs from 'fs' import * as fs from 'fs'
import * as ts from 'typescript' import * as ts from 'typescript'
import {argparse, arg} from '@rondo/argparse' import {argparse, arg} from '@rondo/argparse'
import {error, info} from '../log'
function isObjectType(type: ts.Type): type is ts.ObjectType { function isObjectType(type: ts.Type): type is ts.ObjectType {
return !!(type.flags & ts.TypeFlags.Object) return !!(type.flags & ts.TypeFlags.Object)
@ -52,19 +53,23 @@ interface IClassDefinition {
export function typecheck(...argv: string[]) { export function typecheck(...argv: string[]) {
const args = argparse({ const args = argparse({
file: arg('string', { input: arg('string', {alias: 'i', required: true}),
default: __dirname + '/' + 'intergen.sample.ts',
alias: 'f',
}),
debug: arg('boolean'), debug: arg('boolean'),
help: arg('boolean', {alias: 'h'}), help: arg('boolean', {alias: 'h'}),
output: arg('string', {default: '-'}),
}).parse(argv) }).parse(argv)
function debug(m: string, ...meta: any[]) {
if (args.debug) {
error(m, ...meta)
}
}
/** Generate interfaces for all exported classes in a set of .ts files */ /** Generate interfaces for all exported classes in a set of .ts files */
function generateInterfaces( function classesToInterfaces(
fileNames: string[], fileNames: string[],
options: ts.CompilerOptions, options: ts.CompilerOptions,
): void { ): string[] {
// Build a program using the set of root file names in fileNames // Build a program using the set of root file names in fileNames
const program = ts.createProgram(fileNames, options) const program = ts.createProgram(fileNames, options)
@ -82,45 +87,45 @@ export function typecheck(...argv: string[]) {
* of types. For example: types.filter(filterGlobalTypes) * of types. For example: types.filter(filterGlobalTypes)
*/ */
function filterGlobalTypes(type: ts.Type): boolean { function filterGlobalTypes(type: ts.Type): boolean {
// console.log('fgt', typeToString(type)) debug('filterGlobalTypes: %s', typeToString(type))
if (type.aliasSymbol) { if (type.aliasSymbol) {
// keep type aliases // keep type aliases
return true return true
} }
const symbol = type.getSymbol() const symbol = type.getSymbol()
if (!symbol) { if (!symbol) {
// console.log(' no symbol') debug(' no symbol')
// e.g. string or number types have no symbol // e.g. string or number types have no symbol
return false return false
} }
if (symbol && symbol.flags & ts.SymbolFlags.Transient) { if (symbol && symbol.flags & ts.SymbolFlags.Transient) {
console.log(' is transient') debug(' is transient')
// Array is transient. not sure if this is the best way to figure this // Array is transient. not sure if this is the best way to figure this
return false return false
} }
// if (symbol && !((symbol as any).parent)) { // if (symbol && !((symbol as any).parent)) {
// // console.log(' no parent', symbol) // // debug(' no parent', symbol)
// // e.g. Array symbol has no parent // // e.g. Array symbol has no parent
// return false // return false
// } // }
if (type.isLiteral()) { if (type.isLiteral()) {
// console.log(' is literal') debug(' is literal')
return false return false
} }
if (type.isUnionOrIntersection()) { if (type.isUnionOrIntersection()) {
// console.log(' is u or i') debug(' is u or i')
return false return false
} }
if (isObjectType(type) && isTypeReference(type)) { if (isObjectType(type) && isTypeReference(type)) {
// console.log(' is object type') debug(' is object type')
if (isObjectType(type.target) && if (isObjectType(type.target) &&
type.target.objectFlags & ts.ObjectFlags.Tuple) { type.target.objectFlags & ts.ObjectFlags.Tuple) {
// console.log(' is tuple') debug(' is tuple')
return false return false
} }
} }
// console.log(' keep!') debug(' keep!')
return true return true
} }
@ -267,7 +272,7 @@ export function typecheck(...argv: string[]) {
name: p.getName(), name: p.getName(),
type: propType, type: propType,
relevantTypes, relevantTypes,
typeString: typeToString(type), typeString: typeToString(propType),
optional, optional,
} }
}) })
@ -291,18 +296,6 @@ export function typecheck(...argv: string[]) {
properties: classProperties, properties: classProperties,
} }
console.log(`interface ${classDef.name} {`)
console.log(' ',
classDef.properties
.map(p => p.name + ': ' + typeToString(p.type) + ' {' +
p.relevantTypes.map(typeToString) + '}')
.join('\n '),
)
console.log('}')
console.log('\n allRelevantTypes:\n ',
classDef.allRelevantTypes.map(typeToString).join('\n '))
console.log('\n')
classDefs.push(classDef) classDefs.push(classDef)
typeDefinitions.set(type, classDef) typeDefinitions.set(type, classDef)
@ -313,18 +306,6 @@ export function typecheck(...argv: string[]) {
* Visit nodes finding exported classes * Visit nodes finding exported classes
*/ */
function visit(node: ts.Node) { function visit(node: ts.Node) {
// console.log('node.getText()', node.getText())
// console.log('node.kind', node.kind)
if (ts.isExportDeclaration(node)) {
if (node.exportClause) {
// console.log('export {...} from', node.exportClause.elements.length)
} else {
// console.log('export * from...')
// it is exporting *
}
}
// Only consider exported nodes // Only consider exported nodes
if (!isNodeExported(node)) { if (!isNodeExported(node)) {
return return
@ -343,32 +324,42 @@ export function typecheck(...argv: string[]) {
} }
} }
// const defs: Map<ts.Type, IClassDefinition> = new Map() function setTypeName(type: ts.Type, mappings: Map<ts.Type, string>) {
// classDefs.forEach(classDef => { const name = typeToString(type)
// defs.set(classDef.type, classDef) mappings.set(type, `I${name}`)
// }) }
// classDefs.forEach(classDef => { const nameMappings = new Map<ts.Type, string>()
// classDef.allRelevantTypes.forEach(type => { for (const classDef of classDefs) {
// if (!defs.has(type)) { setTypeName(classDef.type, nameMappings)
// handleType(type) for (const t of classDef.allRelevantTypes) {
// } setTypeName(classDef.type, nameMappings)
// }) }
// }) }
// const Vote = classDefs.find(c => c.name === 'Vote') function createInterface(classDef: IClassDefinition): string {
// if (Vote) { const name = nameMappings.get(classDef.type)!
// console.log('found vote') const start = `interface ${name} {`
// const U = Vote.allRelevantTypes.find(t => typeToString(t) === 'User') const properties = classDef.properties.map(p => {
// if (U) { return ` ${p.name}: ${nameMappings.get(p.type) || p.typeString}`
// console.log('found user') })
// handleType(U) .join('\n')
// } const end = '}'
// } return `${start}\n${properties}\n${end}`
}
return classDefs.map(createInterface)
} }
generateInterfaces([args.file], { const interfaces = classesToInterfaces([args.input], {
target: ts.ScriptTarget.ES5, target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS, module: ts.ModuleKind.CommonJS,
}) })
const value = interfaces.join('\n\n')
if (args.output === '-') {
info(value)
} else {
fs.writeFileSync(args.output, value)
}
} }