Skip to content

Commit 14ee152

Browse files
Simplifications for typemap generation and test fixtures
1 parent 1678d07 commit 14ee152

File tree

5 files changed

+178
-172
lines changed

5 files changed

+178
-172
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@joernio/astgen",
3-
"version": "3.31.0",
3+
"version": "3.32.0",
44
"description": "Generate JS/TS AST in json format with Babel",
55
"exports": "./index.js",
66
"keywords": [

src/Defaults.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,9 @@ export const DEFAULT_TSC_OPTIONS: tsc.CompilerOptions = {
9191
noPropertyAccessFromIndexSignature: false,
9292
removeComments: true
9393
}
94+
95+
export const DEFAULT_TSC_TYPE_OPTIONS: number = tsc.TypeFormatFlags.NoTruncation | tsc.TypeFormatFlags.InTypeAlias
96+
97+
export const ANY: string = "any"
98+
99+
export const DEFAULT_IGNORED_TYPES: string[] = ["any", "unknown", "any[]", "unknown[]"]

src/TscUtils.ts

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,67 @@
11
import * as Defaults from "./Defaults";
2+
import {DEFAULT_IGNORED_TYPES} from "./Defaults";
23

34
import tsc from "typescript";
45

5-
export interface TscResult {
6-
program: tsc.Program,
7-
typeChecker: tsc.TypeChecker,
8-
addType: (node: tsc.Node) => (void),
9-
seenTypes: Map<number, string>
10-
}
6+
export type TypeMap = Map<string, string>;
117

12-
export function tscForFile(file: string): TscResult {
13-
const program = tsc.createProgram([file], Defaults.DEFAULT_TSC_OPTIONS);
14-
const typeChecker = program.getTypeChecker();
15-
const seenTypes = new Map<number, string>();
8+
function forEachNode(ast: tsc.Node, callback: (node: tsc.Node) => void): void {
9+
function visit(node: tsc.Node) {
10+
tsc.forEachChild(node, visit);
11+
callback(node);
12+
}
1613

17-
function safeTypeToString(node: tsc.Type): string {
18-
try {
19-
return typeChecker.typeToString(node, undefined, tsc.TypeFormatFlags.NoTruncation | tsc.TypeFormatFlags.InTypeAlias);
20-
} catch (err) {
21-
return "any";
14+
visit(ast);
15+
}
16+
17+
function safeTypeToString(node: tsc.Type, typeChecker: tsc.TypeChecker): string {
18+
try {
19+
const tpe: string = typeChecker.typeToString(node, undefined, Defaults.DEFAULT_TSC_TYPE_OPTIONS);
20+
if (/^["'`].*["'`]$/.test(tpe)) {
21+
return "string";
2222
}
23+
return tpe;
24+
} catch (err) {
25+
return Defaults.ANY;
2326
}
27+
}
2428

25-
function addType(node: tsc.Node) {
29+
function isSignatureDeclaration(node: tsc.Node): node is tsc.SignatureDeclaration {
30+
return tsc.isSetAccessor(node) || tsc.isGetAccessor(node) ||
31+
tsc.isConstructSignatureDeclaration(node) || tsc.isMethodDeclaration(node) ||
32+
tsc.isFunctionDeclaration(node) || tsc.isConstructorDeclaration(node)
33+
}
34+
35+
export function typeMapForFile(file: string): TypeMap {
36+
function addType(node: tsc.Node): void {
37+
if (tsc.isSourceFile(node)) return;
2638
let typeStr;
27-
if (tsc.isSetAccessor(node) ||
28-
tsc.isGetAccessor(node) ||
29-
tsc.isConstructSignatureDeclaration(node) ||
30-
tsc.isMethodDeclaration(node) ||
31-
tsc.isFunctionDeclaration(node) ||
32-
tsc.isConstructorDeclaration(node)) {
39+
if (isSignatureDeclaration(node)) {
3340
const signature: tsc.Signature = typeChecker.getSignatureFromDeclaration(node)!;
34-
const returnType = typeChecker.getReturnTypeOfSignature(signature);
35-
typeStr = safeTypeToString(returnType);
41+
const returnType: tsc.Type = typeChecker.getReturnTypeOfSignature(signature);
42+
typeStr = safeTypeToString(returnType, typeChecker);
3643
} else if (tsc.isFunctionLike(node)) {
37-
const funcType = typeChecker.getTypeAtLocation(node);
38-
const funcSignature = typeChecker.getSignaturesOfType(funcType, tsc.SignatureKind.Call)[0];
44+
const funcType: tsc.Type = typeChecker.getTypeAtLocation(node);
45+
const funcSignature: tsc.Signature = typeChecker.getSignaturesOfType(funcType, tsc.SignatureKind.Call)[0];
3946
if (funcSignature) {
40-
typeStr = safeTypeToString(funcSignature.getReturnType());
47+
typeStr = safeTypeToString(funcSignature.getReturnType(), typeChecker);
4148
} else {
42-
typeStr = safeTypeToString(typeChecker.getTypeAtLocation(node));
49+
typeStr = safeTypeToString(typeChecker.getTypeAtLocation(node), typeChecker);
4350
}
4451
} else {
45-
typeStr = safeTypeToString(typeChecker.getTypeAtLocation(node));
52+
typeStr = safeTypeToString(typeChecker.getTypeAtLocation(node), typeChecker);
53+
}
54+
if (!DEFAULT_IGNORED_TYPES.includes(typeStr)) {
55+
const pos = `${node.getStart()}:${node.getEnd()}`;
56+
seenTypes.set(pos, typeStr);
4657
}
47-
if (!["any", "unknown", "any[]", "unknown[]"].includes(typeStr)) seenTypes.set(node.getStart(), typeStr);
48-
tsc.forEachChild(node, addType);
4958
}
5059

51-
return {
52-
program: program,
53-
typeChecker: typeChecker,
54-
addType: addType,
55-
seenTypes: seenTypes
56-
};
60+
const program: tsc.Program = tsc.createProgram([file], Defaults.DEFAULT_TSC_OPTIONS);
61+
const typeChecker: tsc.TypeChecker = program.getTypeChecker();
62+
const seenTypes = new Map<string, string>();
63+
64+
forEachNode(program.getSourceFile(file)!, addType)
65+
return seenTypes
5766
}
67+

src/index.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import * as FileUtils from "./FileUtils"
44
import * as JsonUtils from "./JsonUtils"
55
import * as VueCodeCleaner from "./VueCodeCleaner"
66
import * as TscUtils from "./TscUtils"
7+
import {TypeMap} from "./TscUtils"
78

89
import * as babelParser from "@babel/parser"
9-
import tsc, {SourceFile} from "typescript"
1010
import * as path from "node:path"
1111
import * as fs from "node:fs"
1212

@@ -41,12 +41,12 @@ function toVueAst(file: string): babelParser.ParseResult {
4141
return codeToJsAst(cleanedCode);
4242
}
4343

44-
function createTsc(file: string): TscUtils.TscResult | undefined {
44+
function typeMapForFile(file: string): TypeMap | undefined {
4545
try {
46-
return TscUtils.tscForFile(file)
46+
return TscUtils.typeMapForFile(file)
4747
} catch (err) {
4848
if (err instanceof Error) {
49-
console.warn("Retrieving types", err.message);
49+
console.warn("Retrieving types", file, ":", err.message);
5050
}
5151
return undefined;
5252
}
@@ -60,15 +60,12 @@ async function createJSAst(options: Options) {
6060
const srcFiles: string[] = await FileUtils.filesWithExtensions(options, Defaults.JS_EXTENSIONS);
6161
for (const file of srcFiles) {
6262
try {
63-
const ast = fileToJsAst(file);
63+
const ast: babelParser.ParseResult = fileToJsAst(file);
6464
writeAstFile(file, ast, options);
6565
try {
66-
let ts = options.tsTypes ? createTsc(file) : undefined;
67-
if (ts) {
68-
const tsAst: SourceFile = ts.program.getSourceFile(file)!;
69-
tsc.forEachChild(tsAst, ts.addType);
70-
writeTypesFile(file, ts.seenTypes, options);
71-
ts.seenTypes.clear();
66+
let ts: TypeMap | undefined = options.tsTypes ? typeMapForFile(file) : undefined;
67+
if (ts && ts.size !== 0) {
68+
writeTypesFile(file, ts, options);
7269
}
7370
} catch (err) {
7471
if (err instanceof Error) {
@@ -93,7 +90,7 @@ async function createVueAst(options: Options) {
9390
const srcFiles: string[] = await FileUtils.filesWithExtensions(options, [".vue"]);
9491
for (const file of srcFiles) {
9592
try {
96-
const ast = toVueAst(file);
93+
const ast: babelParser.ParseResult = toVueAst(file);
9794
if (ast) {
9895
writeAstFile(file, ast, options);
9996
}
@@ -109,8 +106,8 @@ async function createVueAst(options: Options) {
109106
* Write AST data to a json file
110107
*/
111108
function writeAstFile(file: string, ast: babelParser.ParseResult, options: Options) {
112-
const relativePath = path.relative(options.src, file)
113-
const outAstFile = path.join(options.output, relativePath + ".json");
109+
const relativePath: string = path.relative(options.src, file)
110+
const outAstFile: string = path.join(options.output, relativePath + ".json");
114111
const data = {
115112
fullName: file,
116113
relativeName: relativePath,
@@ -124,16 +121,16 @@ function writeAstFile(file: string, ast: babelParser.ParseResult, options: Optio
124121
/**
125122
* Write tsc type data to a json file
126123
*/
127-
function writeTypesFile(file: string, seenTypes: Map<number, string>, options: Options) {
128-
const relativePath = path.relative(options.src, file)
129-
const outTypeFile = path.join(options.output, relativePath + ".typemap");
124+
function writeTypesFile(file: string, seenTypes: TypeMap, options: Options) {
125+
const relativePath: string = path.relative(options.src, file)
126+
const outTypeFile: string = path.join(options.output, relativePath + ".typemap");
130127
fs.mkdirSync(path.dirname(outTypeFile), {recursive: true});
131128
fs.writeFileSync(outTypeFile, JsonUtils.stringify(Object.fromEntries(seenTypes)));
132129
console.log("Converted types for", relativePath, "to", outTypeFile);
133130
}
134131

135132
async function createXAst(options: Options) {
136-
const srcDir = options.src;
133+
const srcDir: string = options.src;
137134
try {
138135
fs.accessSync(srcDir, fs.constants.R_OK);
139136
} catch (err) {

0 commit comments

Comments
 (0)