@tsonic/frontend 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +53 -0
- package/src/dependency-graph.ts +18 -0
- package/src/dotnet-metadata.ts +121 -0
- package/src/graph/builder.ts +81 -0
- package/src/graph/circular.ts +58 -0
- package/src/graph/extraction/exports.ts +55 -0
- package/src/graph/extraction/imports.ts +81 -0
- package/src/graph/extraction/index.ts +7 -0
- package/src/graph/extraction/orchestrator.ts +99 -0
- package/src/graph/extraction.ts +10 -0
- package/src/graph/helpers.ts +51 -0
- package/src/graph/index.ts +17 -0
- package/src/graph/types.ts +13 -0
- package/src/index.ts +80 -0
- package/src/ir/binding-resolution.test.ts +585 -0
- package/src/ir/builder/exports.ts +78 -0
- package/src/ir/builder/helpers.ts +27 -0
- package/src/ir/builder/imports.ts +153 -0
- package/src/ir/builder/index.ts +10 -0
- package/src/ir/builder/orchestrator.ts +178 -0
- package/src/ir/builder/statements.ts +55 -0
- package/src/ir/builder/types.ts +8 -0
- package/src/ir/builder/validation.ts +129 -0
- package/src/ir/builder.test.ts +581 -0
- package/src/ir/builder.ts +14 -0
- package/src/ir/converters/expressions/access.ts +99 -0
- package/src/ir/converters/expressions/calls.ts +137 -0
- package/src/ir/converters/expressions/collections.ts +84 -0
- package/src/ir/converters/expressions/functions.ts +62 -0
- package/src/ir/converters/expressions/helpers.ts +264 -0
- package/src/ir/converters/expressions/index.ts +43 -0
- package/src/ir/converters/expressions/literals.ts +22 -0
- package/src/ir/converters/expressions/operators.ts +147 -0
- package/src/ir/converters/expressions/other.ts +60 -0
- package/src/ir/converters/statements/control/blocks.ts +22 -0
- package/src/ir/converters/statements/control/conditionals.ts +67 -0
- package/src/ir/converters/statements/control/exceptions.ts +43 -0
- package/src/ir/converters/statements/control/index.ts +17 -0
- package/src/ir/converters/statements/control/loops.ts +99 -0
- package/src/ir/converters/statements/control.ts +17 -0
- package/src/ir/converters/statements/declarations/classes/constructors.ts +120 -0
- package/src/ir/converters/statements/declarations/classes/index.ts +12 -0
- package/src/ir/converters/statements/declarations/classes/methods.ts +61 -0
- package/src/ir/converters/statements/declarations/classes/orchestrator.ts +166 -0
- package/src/ir/converters/statements/declarations/classes/override-detection.ts +116 -0
- package/src/ir/converters/statements/declarations/classes/properties.ts +63 -0
- package/src/ir/converters/statements/declarations/classes.ts +6 -0
- package/src/ir/converters/statements/declarations/enums.ts +29 -0
- package/src/ir/converters/statements/declarations/functions.ts +39 -0
- package/src/ir/converters/statements/declarations/index.ts +14 -0
- package/src/ir/converters/statements/declarations/interfaces.ts +131 -0
- package/src/ir/converters/statements/declarations/registry.ts +45 -0
- package/src/ir/converters/statements/declarations/type-aliases.ts +25 -0
- package/src/ir/converters/statements/declarations/variables.ts +60 -0
- package/src/ir/converters/statements/declarations.ts +16 -0
- package/src/ir/converters/statements/helpers.ts +174 -0
- package/src/ir/converters/statements/index.ts +40 -0
- package/src/ir/expression-converter.ts +207 -0
- package/src/ir/generic-validator.ts +100 -0
- package/src/ir/hierarchical-bindings-e2e.test.ts +163 -0
- package/src/ir/index.ts +6 -0
- package/src/ir/statement-converter.ts +128 -0
- package/src/ir/type-converter/arrays.ts +20 -0
- package/src/ir/type-converter/converter.ts +10 -0
- package/src/ir/type-converter/functions.ts +22 -0
- package/src/ir/type-converter/index.ts +11 -0
- package/src/ir/type-converter/inference.ts +122 -0
- package/src/ir/type-converter/literals.ts +40 -0
- package/src/ir/type-converter/objects.ts +107 -0
- package/src/ir/type-converter/orchestrator.ts +85 -0
- package/src/ir/type-converter/patterns.ts +73 -0
- package/src/ir/type-converter/primitives.ts +57 -0
- package/src/ir/type-converter/references.ts +64 -0
- package/src/ir/type-converter/unions-intersections.ts +34 -0
- package/src/ir/type-converter.ts +13 -0
- package/src/ir/types/expressions.ts +215 -0
- package/src/ir/types/guards.ts +39 -0
- package/src/ir/types/helpers.ts +135 -0
- package/src/ir/types/index.ts +108 -0
- package/src/ir/types/ir-types.ts +96 -0
- package/src/ir/types/module.ts +57 -0
- package/src/ir/types/statements.ts +238 -0
- package/src/ir/types.ts +97 -0
- package/src/metadata/bindings-loader.test.ts +144 -0
- package/src/metadata/bindings-loader.ts +357 -0
- package/src/metadata/index.ts +15 -0
- package/src/metadata/library-loader.ts +153 -0
- package/src/metadata/loader.test.ts +156 -0
- package/src/metadata/loader.ts +382 -0
- package/src/program/bindings.test.ts +512 -0
- package/src/program/bindings.ts +253 -0
- package/src/program/config.ts +30 -0
- package/src/program/creation.ts +249 -0
- package/src/program/dependency-graph.ts +245 -0
- package/src/program/diagnostics.ts +103 -0
- package/src/program/index.ts +19 -0
- package/src/program/metadata.ts +68 -0
- package/src/program/queries.ts +18 -0
- package/src/program/types.ts +38 -0
- package/src/program.ts +13 -0
- package/src/resolver/dotnet-import-resolver.ts +226 -0
- package/src/resolver/import-resolution.ts +177 -0
- package/src/resolver/index.ts +18 -0
- package/src/resolver/namespace.test.ts +86 -0
- package/src/resolver/namespace.ts +42 -0
- package/src/resolver/naming.ts +38 -0
- package/src/resolver/path-resolution.ts +22 -0
- package/src/resolver/types.ts +15 -0
- package/src/resolver.test.ts +155 -0
- package/src/resolver.ts +14 -0
- package/src/symbol-table/builder.ts +114 -0
- package/src/symbol-table/creation.ts +42 -0
- package/src/symbol-table/helpers.ts +18 -0
- package/src/symbol-table/index.ts +13 -0
- package/src/symbol-table/queries.ts +42 -0
- package/src/symbol-table/types.ts +28 -0
- package/src/symbol-table.ts +14 -0
- package/src/types/bindings.ts +172 -0
- package/src/types/diagnostic.test.ts +164 -0
- package/src/types/diagnostic.ts +153 -0
- package/src/types/explicit-views.test.ts +113 -0
- package/src/types/explicit-views.ts +218 -0
- package/src/types/metadata.ts +229 -0
- package/src/types/module.ts +99 -0
- package/src/types/nested-types.test.ts +194 -0
- package/src/types/nested-types.ts +215 -0
- package/src/types/parameter-modifiers.ts +173 -0
- package/src/types/ref-parameters.test.ts +192 -0
- package/src/types/ref-parameters.ts +268 -0
- package/src/types/result.test.ts +157 -0
- package/src/types/result.ts +48 -0
- package/src/types/support-types.test.ts +81 -0
- package/src/types/support-types.ts +288 -0
- package/src/types/test-harness.ts +180 -0
- package/src/validation/exports.ts +98 -0
- package/src/validation/features.ts +89 -0
- package/src/validation/generics.ts +40 -0
- package/src/validation/helpers.ts +31 -0
- package/src/validation/imports.ts +97 -0
- package/src/validation/index.ts +11 -0
- package/src/validation/orchestrator.ts +51 -0
- package/src/validation/static-safety.ts +267 -0
- package/src/validator.test.ts +468 -0
- package/src/validator.ts +15 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end test for hierarchical bindings
|
|
3
|
+
* Verifies the full pipeline: TypeScript -> IR -> C# with hierarchical bindings
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it } from "mocha";
|
|
7
|
+
import { expect } from "chai";
|
|
8
|
+
import * as ts from "typescript";
|
|
9
|
+
import { buildIrModule } from "./builder.js";
|
|
10
|
+
import { DotnetMetadataRegistry } from "../dotnet-metadata.js";
|
|
11
|
+
import { BindingRegistry } from "../program/bindings.js";
|
|
12
|
+
import { createDotNetImportResolver } from "../resolver/dotnet-import-resolver.js";
|
|
13
|
+
|
|
14
|
+
describe("Hierarchical Bindings End-to-End", () => {
|
|
15
|
+
it("should resolve hierarchical bindings in IR for member access chain", () => {
|
|
16
|
+
// TypeScript source with systemLinq.enumerable.selectMany
|
|
17
|
+
const source = `
|
|
18
|
+
export function processData() {
|
|
19
|
+
const arr = [1, 2, 3];
|
|
20
|
+
// systemLinq.enumerable.selectMany should resolve to System.Linq.Enumerable.SelectMany
|
|
21
|
+
const result = systemLinq.enumerable.selectMany(arr, x => [x, x * 2]);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
// Create hierarchical binding manifest
|
|
27
|
+
const bindings = new BindingRegistry();
|
|
28
|
+
bindings.addBindings("/test/system-linq.json", {
|
|
29
|
+
assembly: "System.Linq",
|
|
30
|
+
namespaces: [
|
|
31
|
+
{
|
|
32
|
+
name: "System.Linq",
|
|
33
|
+
alias: "systemLinq",
|
|
34
|
+
types: [
|
|
35
|
+
{
|
|
36
|
+
name: "Enumerable",
|
|
37
|
+
alias: "enumerable",
|
|
38
|
+
kind: "class",
|
|
39
|
+
members: [
|
|
40
|
+
{
|
|
41
|
+
kind: "method",
|
|
42
|
+
name: "SelectMany",
|
|
43
|
+
alias: "selectMany",
|
|
44
|
+
binding: {
|
|
45
|
+
assembly: "System.Linq",
|
|
46
|
+
type: "System.Linq.Enumerable",
|
|
47
|
+
member: "SelectMany",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Create TypeScript program
|
|
58
|
+
const fileName = "/test/sample.ts";
|
|
59
|
+
const sourceFile = ts.createSourceFile(
|
|
60
|
+
fileName,
|
|
61
|
+
source,
|
|
62
|
+
ts.ScriptTarget.ES2022,
|
|
63
|
+
true,
|
|
64
|
+
ts.ScriptKind.TS
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const program = ts.createProgram(
|
|
68
|
+
[fileName],
|
|
69
|
+
{
|
|
70
|
+
target: ts.ScriptTarget.ES2022,
|
|
71
|
+
module: ts.ModuleKind.ES2022,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
getSourceFile: (name) => (name === fileName ? sourceFile : undefined),
|
|
75
|
+
writeFile: () => {},
|
|
76
|
+
getCurrentDirectory: () => "/test",
|
|
77
|
+
getDirectories: () => [],
|
|
78
|
+
fileExists: () => true,
|
|
79
|
+
readFile: () => source,
|
|
80
|
+
getCanonicalFileName: (f) => f,
|
|
81
|
+
useCaseSensitiveFileNames: () => true,
|
|
82
|
+
getNewLine: () => "\n",
|
|
83
|
+
getDefaultLibFileName: (_options) => "lib.d.ts",
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const testProgram = {
|
|
88
|
+
program,
|
|
89
|
+
checker: program.getTypeChecker(),
|
|
90
|
+
options: { sourceRoot: "/test", rootNamespace: "TestApp", strict: true },
|
|
91
|
+
sourceFiles: [sourceFile],
|
|
92
|
+
metadata: new DotnetMetadataRegistry(),
|
|
93
|
+
bindings,
|
|
94
|
+
dotnetResolver: createDotNetImportResolver("/test"),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Build IR
|
|
98
|
+
const irResult = buildIrModule(sourceFile, testProgram, {
|
|
99
|
+
sourceRoot: "/test",
|
|
100
|
+
rootNamespace: "TestApp",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// MUST succeed - this is a strict test
|
|
104
|
+
if (!irResult.ok) {
|
|
105
|
+
console.error("IR build failed:", irResult.error);
|
|
106
|
+
throw new Error(
|
|
107
|
+
`IR build MUST succeed for e2e test, got error: ${JSON.stringify(irResult.error)}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const irModule = irResult.value;
|
|
112
|
+
|
|
113
|
+
// Navigate to the selectMany call in IR
|
|
114
|
+
const funcDecl = irModule.body[0];
|
|
115
|
+
expect(funcDecl?.kind).to.equal(
|
|
116
|
+
"functionDeclaration",
|
|
117
|
+
"First body item should be function declaration"
|
|
118
|
+
);
|
|
119
|
+
if (funcDecl?.kind !== "functionDeclaration") return;
|
|
120
|
+
|
|
121
|
+
// First statement: const arr = [1, 2, 3];
|
|
122
|
+
// Second statement: const result = systemLinq.enumerable.selectMany(...)
|
|
123
|
+
const resultDecl = funcDecl.body.statements[1];
|
|
124
|
+
expect(resultDecl?.kind).to.equal(
|
|
125
|
+
"variableDeclaration",
|
|
126
|
+
"Second statement should be variable declaration for result"
|
|
127
|
+
);
|
|
128
|
+
if (resultDecl?.kind !== "variableDeclaration") return;
|
|
129
|
+
|
|
130
|
+
const declarator = resultDecl.declarations[0];
|
|
131
|
+
if (!declarator?.initializer) {
|
|
132
|
+
throw new Error("Expected initializer for result variable");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// The initializer is the systemLinq.enumerable.selectMany(...) call
|
|
136
|
+
const callExpr = declarator.initializer;
|
|
137
|
+
expect(callExpr.kind).to.equal(
|
|
138
|
+
"call",
|
|
139
|
+
"Initializer should be call expression"
|
|
140
|
+
);
|
|
141
|
+
if (callExpr.kind !== "call") return;
|
|
142
|
+
|
|
143
|
+
// The callee is systemLinq.enumerable.selectMany (member access)
|
|
144
|
+
const memberExpr = callExpr.callee;
|
|
145
|
+
expect(memberExpr.kind).to.equal(
|
|
146
|
+
"memberAccess",
|
|
147
|
+
"Callee should be member access"
|
|
148
|
+
);
|
|
149
|
+
if (memberExpr.kind !== "memberAccess") return;
|
|
150
|
+
|
|
151
|
+
// CRITICAL: Verify memberBinding was resolved by the hierarchical binding system
|
|
152
|
+
expect(
|
|
153
|
+
memberExpr.memberBinding,
|
|
154
|
+
"Member binding MUST be resolved for systemLinq.enumerable.selectMany"
|
|
155
|
+
).to.not.equal(undefined);
|
|
156
|
+
|
|
157
|
+
expect(memberExpr.memberBinding?.assembly).to.equal("System.Linq");
|
|
158
|
+
expect(memberExpr.memberBinding?.type).to.equal("System.Linq.Enumerable");
|
|
159
|
+
expect(memberExpr.memberBinding?.member).to.equal("SelectMany");
|
|
160
|
+
|
|
161
|
+
console.log("✅ End-to-end test passed: Hierarchical bindings work!");
|
|
162
|
+
});
|
|
163
|
+
});
|
package/src/ir/index.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statement converter - TypeScript AST to IR statements
|
|
3
|
+
* Main dispatcher - delegates to specialized modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as ts from "typescript";
|
|
7
|
+
import { IrStatement } from "./types.js";
|
|
8
|
+
import { convertExpression } from "./expression-converter.js";
|
|
9
|
+
|
|
10
|
+
// Import converters from specialized modules
|
|
11
|
+
import {
|
|
12
|
+
convertVariableStatement,
|
|
13
|
+
convertFunctionDeclaration,
|
|
14
|
+
convertClassDeclaration,
|
|
15
|
+
convertInterfaceDeclaration,
|
|
16
|
+
convertEnumDeclaration,
|
|
17
|
+
convertTypeAliasDeclaration,
|
|
18
|
+
} from "./converters/statements/declarations.js";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
convertIfStatement,
|
|
22
|
+
convertWhileStatement,
|
|
23
|
+
convertForStatement,
|
|
24
|
+
convertForOfStatement,
|
|
25
|
+
convertForInStatement,
|
|
26
|
+
convertSwitchStatement,
|
|
27
|
+
convertTryStatement,
|
|
28
|
+
convertBlockStatement,
|
|
29
|
+
} from "./converters/statements/control.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Main statement converter dispatcher
|
|
33
|
+
*/
|
|
34
|
+
export const convertStatement = (
|
|
35
|
+
node: ts.Node,
|
|
36
|
+
checker: ts.TypeChecker
|
|
37
|
+
): IrStatement | null => {
|
|
38
|
+
if (ts.isVariableStatement(node)) {
|
|
39
|
+
return convertVariableStatement(node, checker);
|
|
40
|
+
}
|
|
41
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
42
|
+
return convertFunctionDeclaration(node, checker);
|
|
43
|
+
}
|
|
44
|
+
if (ts.isClassDeclaration(node)) {
|
|
45
|
+
return convertClassDeclaration(node, checker);
|
|
46
|
+
}
|
|
47
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
48
|
+
return convertInterfaceDeclaration(node, checker);
|
|
49
|
+
}
|
|
50
|
+
if (ts.isEnumDeclaration(node)) {
|
|
51
|
+
return convertEnumDeclaration(node, checker);
|
|
52
|
+
}
|
|
53
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
54
|
+
return convertTypeAliasDeclaration(node, checker);
|
|
55
|
+
}
|
|
56
|
+
if (ts.isExpressionStatement(node)) {
|
|
57
|
+
return {
|
|
58
|
+
kind: "expressionStatement",
|
|
59
|
+
expression: convertExpression(node.expression, checker),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (ts.isReturnStatement(node)) {
|
|
63
|
+
return {
|
|
64
|
+
kind: "returnStatement",
|
|
65
|
+
expression: node.expression
|
|
66
|
+
? convertExpression(node.expression, checker)
|
|
67
|
+
: undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (ts.isIfStatement(node)) {
|
|
71
|
+
return convertIfStatement(node, checker);
|
|
72
|
+
}
|
|
73
|
+
if (ts.isWhileStatement(node)) {
|
|
74
|
+
return convertWhileStatement(node, checker);
|
|
75
|
+
}
|
|
76
|
+
if (ts.isForStatement(node)) {
|
|
77
|
+
return convertForStatement(node, checker);
|
|
78
|
+
}
|
|
79
|
+
if (ts.isForOfStatement(node)) {
|
|
80
|
+
return convertForOfStatement(node, checker);
|
|
81
|
+
}
|
|
82
|
+
if (ts.isForInStatement(node)) {
|
|
83
|
+
return convertForInStatement(node, checker);
|
|
84
|
+
}
|
|
85
|
+
if (ts.isSwitchStatement(node)) {
|
|
86
|
+
return convertSwitchStatement(node, checker);
|
|
87
|
+
}
|
|
88
|
+
if (ts.isThrowStatement(node)) {
|
|
89
|
+
if (!node.expression) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
kind: "throwStatement",
|
|
94
|
+
expression: convertExpression(node.expression, checker),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (ts.isTryStatement(node)) {
|
|
98
|
+
return convertTryStatement(node, checker);
|
|
99
|
+
}
|
|
100
|
+
if (ts.isBlock(node)) {
|
|
101
|
+
return convertBlockStatement(node, checker);
|
|
102
|
+
}
|
|
103
|
+
if (ts.isBreakStatement(node)) {
|
|
104
|
+
return {
|
|
105
|
+
kind: "breakStatement",
|
|
106
|
+
label: node.label?.text,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (ts.isContinueStatement(node)) {
|
|
110
|
+
return {
|
|
111
|
+
kind: "continueStatement",
|
|
112
|
+
label: node.label?.text,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (ts.isEmptyStatement(node)) {
|
|
116
|
+
return { kind: "emptyStatement" };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return null;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Re-export commonly used functions for backward compatibility
|
|
123
|
+
export { convertBlockStatement } from "./converters/statements/control.js";
|
|
124
|
+
export { convertParameters } from "./converters/statements/helpers.js";
|
|
125
|
+
export {
|
|
126
|
+
setMetadataRegistry,
|
|
127
|
+
setBindingRegistry,
|
|
128
|
+
} from "./converters/statements/declarations.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array type conversion
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as ts from "typescript";
|
|
6
|
+
import { IrType } from "../types.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Convert TypeScript array type to IR array type
|
|
10
|
+
*/
|
|
11
|
+
export const convertArrayType = (
|
|
12
|
+
node: ts.ArrayTypeNode,
|
|
13
|
+
checker: ts.TypeChecker,
|
|
14
|
+
convertType: (node: ts.TypeNode, checker: ts.TypeChecker) => IrType
|
|
15
|
+
): IrType => {
|
|
16
|
+
return {
|
|
17
|
+
kind: "arrayType",
|
|
18
|
+
elementType: convertType(node.elementType, checker),
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function type conversion
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as ts from "typescript";
|
|
6
|
+
import { IrType, IrFunctionType } from "../types.js";
|
|
7
|
+
import { convertParameters as convertParametersFromStatement } from "../statement-converter.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Convert TypeScript function type to IR function type
|
|
11
|
+
*/
|
|
12
|
+
export const convertFunctionType = (
|
|
13
|
+
node: ts.FunctionTypeNode,
|
|
14
|
+
checker: ts.TypeChecker,
|
|
15
|
+
convertType: (node: ts.TypeNode, checker: ts.TypeChecker) => IrType
|
|
16
|
+
): IrFunctionType => {
|
|
17
|
+
return {
|
|
18
|
+
kind: "functionType",
|
|
19
|
+
parameters: convertParametersFromStatement(node.parameters, checker),
|
|
20
|
+
returnType: convertType(node.type, checker),
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type converter - Public API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
convertType,
|
|
7
|
+
convertFunctionType,
|
|
8
|
+
convertObjectType,
|
|
9
|
+
} from "./converter.js";
|
|
10
|
+
export { convertBindingName } from "./patterns.js";
|
|
11
|
+
export { inferType, convertTsTypeToIr } from "./inference.js";
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type inference - Converts TypeScript inferred types to IR types
|
|
3
|
+
*
|
|
4
|
+
* This uses the TypeChecker to get the inferred type and converts it to IR.
|
|
5
|
+
* Used for declarations without explicit type annotations where the type
|
|
6
|
+
* must be inferred from the initializer.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as ts from "typescript";
|
|
10
|
+
import type { IrType } from "../types.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Infer IR type from a declaration node using the TypeChecker.
|
|
14
|
+
* Returns undefined for complex types that cannot be easily represented.
|
|
15
|
+
*/
|
|
16
|
+
export const inferType = (
|
|
17
|
+
node: ts.VariableDeclaration | ts.PropertyDeclaration,
|
|
18
|
+
checker: ts.TypeChecker
|
|
19
|
+
): IrType | undefined => {
|
|
20
|
+
const type = checker.getTypeAtLocation(node);
|
|
21
|
+
return convertTsTypeToIr(type, checker);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Convert a TypeScript Type (from checker) to IR type.
|
|
26
|
+
* This is different from convertType which takes a TypeNode (syntax).
|
|
27
|
+
*/
|
|
28
|
+
export const convertTsTypeToIr = (
|
|
29
|
+
type: ts.Type,
|
|
30
|
+
checker: ts.TypeChecker
|
|
31
|
+
): IrType | undefined => {
|
|
32
|
+
const flags = type.flags;
|
|
33
|
+
|
|
34
|
+
// Primitives
|
|
35
|
+
if (flags & ts.TypeFlags.Number || flags & ts.TypeFlags.NumberLiteral) {
|
|
36
|
+
return { kind: "primitiveType", name: "number" };
|
|
37
|
+
}
|
|
38
|
+
if (flags & ts.TypeFlags.String || flags & ts.TypeFlags.StringLiteral) {
|
|
39
|
+
return { kind: "primitiveType", name: "string" };
|
|
40
|
+
}
|
|
41
|
+
if (flags & ts.TypeFlags.Boolean || flags & ts.TypeFlags.BooleanLiteral) {
|
|
42
|
+
return { kind: "primitiveType", name: "boolean" };
|
|
43
|
+
}
|
|
44
|
+
if (flags & ts.TypeFlags.Void) {
|
|
45
|
+
return { kind: "voidType" };
|
|
46
|
+
}
|
|
47
|
+
if (flags & ts.TypeFlags.Null) {
|
|
48
|
+
return { kind: "primitiveType", name: "null" };
|
|
49
|
+
}
|
|
50
|
+
if (flags & ts.TypeFlags.Undefined) {
|
|
51
|
+
return { kind: "primitiveType", name: "undefined" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Object type - check if it's an array
|
|
55
|
+
if (flags & ts.TypeFlags.Object) {
|
|
56
|
+
// Check for array type
|
|
57
|
+
if (checker.isArrayType(type)) {
|
|
58
|
+
const typeArgs = checker.getTypeArguments(type as ts.TypeReference);
|
|
59
|
+
if (typeArgs.length > 0) {
|
|
60
|
+
const elementType = convertTsTypeToIr(typeArgs[0]!, checker);
|
|
61
|
+
if (elementType) {
|
|
62
|
+
return { kind: "arrayType", elementType };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { kind: "arrayType", elementType: { kind: "anyType" } };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check for callable signatures (function types)
|
|
69
|
+
const callSignatures = type.getCallSignatures();
|
|
70
|
+
if (callSignatures.length > 0) {
|
|
71
|
+
// Function types need complex handling - return undefined for now
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check for symbol with name (class, interface, etc.)
|
|
76
|
+
const objectType = type as ts.ObjectType;
|
|
77
|
+
if (objectType.symbol) {
|
|
78
|
+
const name = objectType.symbol.name;
|
|
79
|
+
// Skip internal TypeScript symbol names
|
|
80
|
+
if (name.startsWith("__")) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
// For named types, return as reference type with type arguments if generic
|
|
84
|
+
if (name && name !== "Object" && name !== "Array") {
|
|
85
|
+
// Extract type arguments for generic types
|
|
86
|
+
const typeRef = type as ts.TypeReference;
|
|
87
|
+
const typeArgs = checker.getTypeArguments(typeRef);
|
|
88
|
+
if (typeArgs && typeArgs.length > 0) {
|
|
89
|
+
const irTypeArgs = typeArgs
|
|
90
|
+
.map((arg) => convertTsTypeToIr(arg, checker))
|
|
91
|
+
.filter((t): t is IrType => t !== undefined);
|
|
92
|
+
if (irTypeArgs.length === typeArgs.length) {
|
|
93
|
+
return { kind: "referenceType", name, typeArguments: irTypeArgs };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { kind: "referenceType", name };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Anonymous object type
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Type parameters (e.g., T in Container<T>)
|
|
105
|
+
if (flags & ts.TypeFlags.TypeParameter) {
|
|
106
|
+
const typeParam = type as ts.TypeParameter;
|
|
107
|
+
const name = typeParam.symbol?.name ?? "T";
|
|
108
|
+
return { kind: "referenceType", name };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Any and unknown
|
|
112
|
+
if (flags & ts.TypeFlags.Any || flags & ts.TypeFlags.Unknown) {
|
|
113
|
+
return { kind: "anyType" };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Union and intersection types - too complex for simple inference
|
|
117
|
+
if (flags & ts.TypeFlags.Union || flags & ts.TypeFlags.Intersection) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return undefined;
|
|
122
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal type conversion
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as ts from "typescript";
|
|
6
|
+
import { IrType } from "../types.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Convert TypeScript literal type to IR literal type
|
|
10
|
+
*/
|
|
11
|
+
export const convertLiteralType = (node: ts.LiteralTypeNode): IrType => {
|
|
12
|
+
const literal = node.literal;
|
|
13
|
+
|
|
14
|
+
if (ts.isStringLiteral(literal)) {
|
|
15
|
+
return { kind: "literalType", value: literal.text };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (ts.isNumericLiteral(literal)) {
|
|
19
|
+
return { kind: "literalType", value: Number(literal.text) };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (literal.kind === ts.SyntaxKind.TrueKeyword) {
|
|
23
|
+
return { kind: "literalType", value: true };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (literal.kind === ts.SyntaxKind.FalseKeyword) {
|
|
27
|
+
return { kind: "literalType", value: false };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (literal.kind === ts.SyntaxKind.NullKeyword) {
|
|
31
|
+
return { kind: "primitiveType", name: "null" };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (literal.kind === ts.SyntaxKind.UndefinedKeyword) {
|
|
35
|
+
return { kind: "primitiveType", name: "undefined" };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fallback
|
|
39
|
+
return { kind: "anyType" };
|
|
40
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object/interface type conversion
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as ts from "typescript";
|
|
6
|
+
import {
|
|
7
|
+
IrType,
|
|
8
|
+
IrObjectType,
|
|
9
|
+
IrDictionaryType,
|
|
10
|
+
IrInterfaceMember,
|
|
11
|
+
IrPropertySignature,
|
|
12
|
+
IrMethodSignature,
|
|
13
|
+
} from "../types.js";
|
|
14
|
+
import { convertParameters as convertParametersFromStatement } from "../statement-converter.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Convert TypeScript object literal type to IR type.
|
|
18
|
+
*
|
|
19
|
+
* Returns IrDictionaryType for pure index signature types like:
|
|
20
|
+
* - `{ [k: string]: T }`
|
|
21
|
+
* - `{ [k: number]: T }`
|
|
22
|
+
*
|
|
23
|
+
* Returns IrObjectType for regular object types with named members.
|
|
24
|
+
*/
|
|
25
|
+
export const convertObjectType = (
|
|
26
|
+
node: ts.TypeLiteralNode,
|
|
27
|
+
checker: ts.TypeChecker,
|
|
28
|
+
convertType: (node: ts.TypeNode, checker: ts.TypeChecker) => IrType
|
|
29
|
+
): IrObjectType | IrDictionaryType => {
|
|
30
|
+
// Check for pure index signature type (no other members)
|
|
31
|
+
const indexSignatures = node.members.filter(ts.isIndexSignatureDeclaration);
|
|
32
|
+
const otherMembers = node.members.filter(
|
|
33
|
+
(m) => !ts.isIndexSignatureDeclaration(m)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// If ONLY index signature(s) exist, convert to dictionary type
|
|
37
|
+
if (indexSignatures.length > 0 && otherMembers.length === 0) {
|
|
38
|
+
// Use the first index signature (TypeScript allows multiple, but we take first)
|
|
39
|
+
const indexSig = indexSignatures[0]!;
|
|
40
|
+
const keyParam = indexSig.parameters[0];
|
|
41
|
+
|
|
42
|
+
// Determine key type from parameter type
|
|
43
|
+
const keyType: IrType = keyParam?.type
|
|
44
|
+
? convertKeyType(keyParam.type)
|
|
45
|
+
: { kind: "primitiveType", name: "string" };
|
|
46
|
+
|
|
47
|
+
// Determine value type
|
|
48
|
+
const valueType: IrType = indexSig.type
|
|
49
|
+
? convertType(indexSig.type, checker)
|
|
50
|
+
: { kind: "anyType" };
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
kind: "dictionaryType",
|
|
54
|
+
keyType,
|
|
55
|
+
valueType,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Regular object type with named members
|
|
60
|
+
const members: IrInterfaceMember[] = [];
|
|
61
|
+
|
|
62
|
+
node.members.forEach((member) => {
|
|
63
|
+
if (ts.isPropertySignature(member) && member.type) {
|
|
64
|
+
const propSig: IrPropertySignature = {
|
|
65
|
+
kind: "propertySignature",
|
|
66
|
+
name:
|
|
67
|
+
member.name && ts.isIdentifier(member.name)
|
|
68
|
+
? member.name.text
|
|
69
|
+
: "[computed]",
|
|
70
|
+
type: convertType(member.type, checker),
|
|
71
|
+
isOptional: !!member.questionToken,
|
|
72
|
+
isReadonly: !!member.modifiers?.some(
|
|
73
|
+
(m) => m.kind === ts.SyntaxKind.ReadonlyKeyword
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
members.push(propSig);
|
|
77
|
+
} else if (ts.isMethodSignature(member)) {
|
|
78
|
+
const methSig: IrMethodSignature = {
|
|
79
|
+
kind: "methodSignature",
|
|
80
|
+
name:
|
|
81
|
+
member.name && ts.isIdentifier(member.name)
|
|
82
|
+
? member.name.text
|
|
83
|
+
: "[computed]",
|
|
84
|
+
parameters: convertParametersFromStatement(member.parameters, checker),
|
|
85
|
+
returnType: member.type ? convertType(member.type, checker) : undefined,
|
|
86
|
+
};
|
|
87
|
+
members.push(methSig);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return { kind: "objectType", members };
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Convert index signature key type to IR type.
|
|
96
|
+
* Only string and number are valid as index signature keys.
|
|
97
|
+
*/
|
|
98
|
+
const convertKeyType = (typeNode: ts.TypeNode): IrType => {
|
|
99
|
+
if (typeNode.kind === ts.SyntaxKind.StringKeyword) {
|
|
100
|
+
return { kind: "primitiveType", name: "string" };
|
|
101
|
+
}
|
|
102
|
+
if (typeNode.kind === ts.SyntaxKind.NumberKeyword) {
|
|
103
|
+
return { kind: "primitiveType", name: "number" };
|
|
104
|
+
}
|
|
105
|
+
// Fallback to string for other cases
|
|
106
|
+
return { kind: "primitiveType", name: "string" };
|
|
107
|
+
};
|