Add typecheck.ts

This uses TypeScript's TypeChecker to determine the types and the code
is a little cleaner, but still needs a lot of work:

- Determine how to figure out class type parameters
This commit is contained in:
Jerko Steiner 2019-08-11 21:59:38 +07:00
parent 7a26d5c0f1
commit d7b2ad7a38
2 changed files with 114 additions and 0 deletions

View File

@ -1,3 +1,4 @@
export * from './build' export * from './build'
export * from './newlib' export * from './newlib'
export * from './intergen' export * from './intergen'
export * from './typecheck'

View File

@ -0,0 +1,113 @@
import * as ts from 'typescript'
import * as fs from 'fs'
export function typecheck() {
interface DocEntry {
name?: string
fileName?: string
documentation?: string
type?: string
constructors?: DocEntry[]
parameters?: DocEntry[]
returnType?: string
}
/** Generate documentation for all classes in a set of .ts files */
function generateDocumentation(
fileNames: string[],
options: ts.CompilerOptions,
): void {
// Build a program using the set of root file names in fileNames
const program = ts.createProgram(fileNames, options)
// Get the checker, we will use it to find more about classes
const checker = program.getTypeChecker()
// Visit every sourceFile in the program
for (const sourceFile of program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) {
// Walk the tree to search for classes
ts.forEachChild(sourceFile, visit)
}
}
return
/** visit nodes finding exported classes */
function visit(node: ts.Node) {
// Only consider exported nodes
if (!isNodeExported(node)) {
return
}
if (ts.isClassDeclaration(node) && node.name) {
// This is a top level class, get its symbol
const symbol = checker.getSymbolAtLocation(node.name)
if (symbol) {
console.log('class', symbol.getName())
// console.log('symbolToString', checker.symbolToString(symbol))
// const tpd = checker.symbolToTypeParameterDeclarations(symbol)
// if (tpd) {
// console.log(' type params: %o', tpd.map(t => {
// ts.getCombinedModifierFlags(t.symbol.valueDeclaration)
// return {
// name: t.name.text,
// constraint: !!t.constraint,
// default: !!t.default,
// // constraint: !!t.constraint
// // ? checker.getTypeFromTypeNode(t.constraint)
// // : undefined,
// }
// }))
// }
const type = checker.getDeclaredTypeOfSymbol(symbol)
// const properties = checker.getPropertiesOfType(type)
const properties = type.getApparentProperties()
console.log(' %o', properties
.filter(p => {
const flags = ts.getCombinedModifierFlags(p.valueDeclaration)
// return s.parent && s.parent.flags & 32 /* Class */
// ? flags
// : flags & ~28 /* AccessibilityModifier */;
return !(flags & ts.ModifierFlags.NonPublicAccessibilityModifier)
})
.map(p => {
const propType = checker
.getTypeOfSymbolAtLocation(p, p.valueDeclaration!)
return {
name: p.getName(),
type: checker.typeToString(propType),
classOrIface: propType.isClassOrInterface(),
}
}))
// output.push(serializeClass(symbol))
}
// No need to walk any further, class expressions/inner declarations
// cannot be exported
} else if (ts.isModuleDeclaration(node)) {
// This is a namespace, visit its children
ts.forEachChild(node, visit)
}
}
/** True if this is visible outside this file, false otherwise */
function isNodeExported(node: ts.Node): boolean {
return (
(ts.getCombinedModifierFlags(node as any) &
ts.ModifierFlags.Export) !== 0 ||
(!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile)
)
}
}
const path = __dirname + '/' + 'intergen.sample.ts'
generateDocumentation([path], {
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS,
})
}