@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
package/src/ir/types.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intermediate Representation (IR) types for Tsonic compiler
|
|
3
|
+
* Main dispatcher - re-exports from specialized modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Re-export everything from types subdirectory for backward compatibility
|
|
7
|
+
export type {
|
|
8
|
+
// Module types
|
|
9
|
+
IrModule,
|
|
10
|
+
IrImport,
|
|
11
|
+
IrImportSpecifier,
|
|
12
|
+
IrExport,
|
|
13
|
+
// Statement types
|
|
14
|
+
IrStatement,
|
|
15
|
+
IrVariableDeclaration,
|
|
16
|
+
IrVariableDeclarator,
|
|
17
|
+
IrFunctionDeclaration,
|
|
18
|
+
IrClassDeclaration,
|
|
19
|
+
IrClassMember,
|
|
20
|
+
IrMethodDeclaration,
|
|
21
|
+
IrPropertyDeclaration,
|
|
22
|
+
IrConstructorDeclaration,
|
|
23
|
+
IrInterfaceDeclaration,
|
|
24
|
+
IrEnumDeclaration,
|
|
25
|
+
IrEnumMember,
|
|
26
|
+
IrTypeAliasDeclaration,
|
|
27
|
+
IrExpressionStatement,
|
|
28
|
+
IrReturnStatement,
|
|
29
|
+
IrIfStatement,
|
|
30
|
+
IrWhileStatement,
|
|
31
|
+
IrForStatement,
|
|
32
|
+
IrForOfStatement,
|
|
33
|
+
IrSwitchStatement,
|
|
34
|
+
IrSwitchCase,
|
|
35
|
+
IrThrowStatement,
|
|
36
|
+
IrTryStatement,
|
|
37
|
+
IrCatchClause,
|
|
38
|
+
IrBlockStatement,
|
|
39
|
+
IrBreakStatement,
|
|
40
|
+
IrContinueStatement,
|
|
41
|
+
IrEmptyStatement,
|
|
42
|
+
// Expression types
|
|
43
|
+
IrExpression,
|
|
44
|
+
IrLiteralExpression,
|
|
45
|
+
IrIdentifierExpression,
|
|
46
|
+
IrArrayExpression,
|
|
47
|
+
IrObjectExpression,
|
|
48
|
+
IrObjectProperty,
|
|
49
|
+
IrFunctionExpression,
|
|
50
|
+
IrArrowFunctionExpression,
|
|
51
|
+
IrMemberExpression,
|
|
52
|
+
IrCallExpression,
|
|
53
|
+
IrNewExpression,
|
|
54
|
+
IrThisExpression,
|
|
55
|
+
IrUpdateExpression,
|
|
56
|
+
IrUnaryExpression,
|
|
57
|
+
IrBinaryExpression,
|
|
58
|
+
IrLogicalExpression,
|
|
59
|
+
IrConditionalExpression,
|
|
60
|
+
IrAssignmentExpression,
|
|
61
|
+
IrTemplateLiteralExpression,
|
|
62
|
+
IrSpreadExpression,
|
|
63
|
+
IrAwaitExpression,
|
|
64
|
+
IrYieldExpression,
|
|
65
|
+
// Type system types
|
|
66
|
+
IrType,
|
|
67
|
+
IrPrimitiveType,
|
|
68
|
+
IrReferenceType,
|
|
69
|
+
IrArrayType,
|
|
70
|
+
IrFunctionType,
|
|
71
|
+
IrObjectType,
|
|
72
|
+
IrDictionaryType,
|
|
73
|
+
IrUnionType,
|
|
74
|
+
IrIntersectionType,
|
|
75
|
+
IrLiteralType,
|
|
76
|
+
IrAnyType,
|
|
77
|
+
IrUnknownType,
|
|
78
|
+
IrVoidType,
|
|
79
|
+
IrNeverType,
|
|
80
|
+
// Helper types
|
|
81
|
+
IrPattern,
|
|
82
|
+
IrIdentifierPattern,
|
|
83
|
+
IrArrayPattern,
|
|
84
|
+
IrObjectPattern,
|
|
85
|
+
IrObjectPatternProperty,
|
|
86
|
+
IrTypeParameter,
|
|
87
|
+
IrParameter,
|
|
88
|
+
IrInterfaceMember,
|
|
89
|
+
IrPropertySignature,
|
|
90
|
+
IrMethodSignature,
|
|
91
|
+
IrAccessibility,
|
|
92
|
+
IrBinaryOperator,
|
|
93
|
+
IrAssignmentOperator,
|
|
94
|
+
} from "./types/index.js";
|
|
95
|
+
|
|
96
|
+
// Re-export type guards
|
|
97
|
+
export { isStatement, isExpression } from "./types/guards.js";
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for bindings JSON loader
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it } from "mocha";
|
|
6
|
+
import { strict as assert } from "assert";
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import * as os from "os";
|
|
10
|
+
import {
|
|
11
|
+
loadBindingsFile,
|
|
12
|
+
buildBindingsRegistry,
|
|
13
|
+
lookupTypeBinding,
|
|
14
|
+
} from "./bindings-loader.js";
|
|
15
|
+
import type { BindingsFile } from "../types/bindings.js";
|
|
16
|
+
|
|
17
|
+
describe("Bindings Loader", () => {
|
|
18
|
+
describe("loadBindingsFile", () => {
|
|
19
|
+
it("should load valid bindings file", () => {
|
|
20
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-test-"));
|
|
21
|
+
const bindingsPath = path.join(tmpDir, "test.bindings.json");
|
|
22
|
+
|
|
23
|
+
const bindings: BindingsFile = {
|
|
24
|
+
namespace: "System",
|
|
25
|
+
types: [
|
|
26
|
+
{
|
|
27
|
+
clrName: "System.String",
|
|
28
|
+
tsEmitName: "String",
|
|
29
|
+
assemblyName: "System.Runtime",
|
|
30
|
+
metadataToken: 0x02000001,
|
|
31
|
+
methods: [],
|
|
32
|
+
exposedMethods: [],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
fs.writeFileSync(bindingsPath, JSON.stringify(bindings, null, 2));
|
|
38
|
+
|
|
39
|
+
const result = loadBindingsFile(bindingsPath);
|
|
40
|
+
|
|
41
|
+
// Cleanup
|
|
42
|
+
fs.unlinkSync(bindingsPath);
|
|
43
|
+
fs.rmdirSync(tmpDir);
|
|
44
|
+
|
|
45
|
+
assert.ok(result.ok);
|
|
46
|
+
if (result.ok) {
|
|
47
|
+
assert.equal(result.value.namespace, "System");
|
|
48
|
+
assert.equal(result.value.types.length, 1);
|
|
49
|
+
assert.equal(result.value.types[0]?.tsEmitName, "String");
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should return error for non-existent file", () => {
|
|
54
|
+
const result = loadBindingsFile("/nonexistent/file.bindings.json");
|
|
55
|
+
|
|
56
|
+
assert.ok(!result.ok);
|
|
57
|
+
if (!result.ok) {
|
|
58
|
+
assert.equal(result.error[0]?.code, "TSN9101");
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should return error for invalid JSON", () => {
|
|
63
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-test-"));
|
|
64
|
+
const bindingsPath = path.join(tmpDir, "invalid.bindings.json");
|
|
65
|
+
|
|
66
|
+
fs.writeFileSync(bindingsPath, "{ invalid json }");
|
|
67
|
+
|
|
68
|
+
const result = loadBindingsFile(bindingsPath);
|
|
69
|
+
|
|
70
|
+
// Cleanup
|
|
71
|
+
fs.unlinkSync(bindingsPath);
|
|
72
|
+
fs.rmdirSync(tmpDir);
|
|
73
|
+
|
|
74
|
+
assert.ok(!result.ok);
|
|
75
|
+
if (!result.ok) {
|
|
76
|
+
assert.equal(result.error[0]?.code, "TSN9103");
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("buildBindingsRegistry", () => {
|
|
82
|
+
it("should build registry from bindings files", () => {
|
|
83
|
+
const bindings1: BindingsFile = {
|
|
84
|
+
namespace: "System",
|
|
85
|
+
types: [
|
|
86
|
+
{
|
|
87
|
+
clrName: "System.String",
|
|
88
|
+
tsEmitName: "String",
|
|
89
|
+
assemblyName: "System.Runtime",
|
|
90
|
+
metadataToken: 0x02000001,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const bindings2: BindingsFile = {
|
|
96
|
+
namespace: "System.Collections",
|
|
97
|
+
types: [
|
|
98
|
+
{
|
|
99
|
+
clrName: "System.Collections.ArrayList",
|
|
100
|
+
tsEmitName: "ArrayList",
|
|
101
|
+
assemblyName: "System.Collections",
|
|
102
|
+
metadataToken: 0x02000002,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const registry = buildBindingsRegistry([bindings1, bindings2]);
|
|
108
|
+
|
|
109
|
+
assert.equal(registry.size, 2);
|
|
110
|
+
assert.ok(registry.has("System.String"));
|
|
111
|
+
assert.ok(registry.has("System.Collections.ArrayList"));
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("lookupTypeBinding", () => {
|
|
116
|
+
it("should find type binding in registry", () => {
|
|
117
|
+
const bindings: BindingsFile = {
|
|
118
|
+
namespace: "System",
|
|
119
|
+
types: [
|
|
120
|
+
{
|
|
121
|
+
clrName: "System.String",
|
|
122
|
+
tsEmitName: "String",
|
|
123
|
+
assemblyName: "System.Runtime",
|
|
124
|
+
metadataToken: 0x02000001,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const registry = buildBindingsRegistry([bindings]);
|
|
130
|
+
const binding = lookupTypeBinding(registry, "System.String");
|
|
131
|
+
|
|
132
|
+
assert.ok(binding);
|
|
133
|
+
assert.equal(binding?.tsEmitName, "String");
|
|
134
|
+
assert.equal(binding?.metadataToken, 0x02000001);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should return undefined for non-existent type", () => {
|
|
138
|
+
const registry = buildBindingsRegistry([]);
|
|
139
|
+
const binding = lookupTypeBinding(registry, "NonExistent.Type");
|
|
140
|
+
|
|
141
|
+
assert.equal(binding, undefined);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bindings JSON loader - Reads and validates .bindings.json files from tsbindgen.
|
|
3
|
+
*
|
|
4
|
+
* This module provides pure functions to load runtime binding files and validate
|
|
5
|
+
* their structure against the expected schema.
|
|
6
|
+
*
|
|
7
|
+
* @see spec/bindings.md for complete schema documentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
import type { Result } from "../types/result.ts";
|
|
13
|
+
import type { Diagnostic } from "../types/diagnostic.ts";
|
|
14
|
+
import type { BindingsFile, TypeBinding } from "../types/bindings.ts";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Load and parse a bindings.json file.
|
|
18
|
+
*
|
|
19
|
+
* @param filePath - Absolute path to the .bindings.json file
|
|
20
|
+
* @returns Result containing parsed bindings or diagnostics
|
|
21
|
+
*/
|
|
22
|
+
export const loadBindingsFile = (
|
|
23
|
+
filePath: string
|
|
24
|
+
): Result<BindingsFile, Diagnostic[]> => {
|
|
25
|
+
// Check file exists
|
|
26
|
+
if (!fs.existsSync(filePath)) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
error: [
|
|
30
|
+
{
|
|
31
|
+
code: "TSN9101",
|
|
32
|
+
message: `Bindings file not found: ${filePath}`,
|
|
33
|
+
severity: "error",
|
|
34
|
+
location: undefined,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Read file contents
|
|
41
|
+
let content: string;
|
|
42
|
+
try {
|
|
43
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
error: [
|
|
48
|
+
{
|
|
49
|
+
code: "TSN9102",
|
|
50
|
+
message: `Failed to read bindings file: ${error}`,
|
|
51
|
+
severity: "error",
|
|
52
|
+
location: undefined,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Parse JSON
|
|
59
|
+
let parsed: unknown;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(content);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
error: [
|
|
66
|
+
{
|
|
67
|
+
code: "TSN9103",
|
|
68
|
+
message: `Invalid JSON in bindings file: ${error}`,
|
|
69
|
+
severity: "error",
|
|
70
|
+
location: undefined,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Validate structure
|
|
77
|
+
const validation = validateBindingsFile(parsed, filePath);
|
|
78
|
+
if (!validation.ok) {
|
|
79
|
+
return validation;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { ok: true, value: validation.value };
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate that parsed JSON matches BindingsFile schema.
|
|
87
|
+
*
|
|
88
|
+
* @param data - Parsed JSON data
|
|
89
|
+
* @param filePath - File path for error messages
|
|
90
|
+
* @returns Result containing validated bindings or diagnostics
|
|
91
|
+
*/
|
|
92
|
+
const validateBindingsFile = (
|
|
93
|
+
data: unknown,
|
|
94
|
+
filePath: string
|
|
95
|
+
): Result<BindingsFile, Diagnostic[]> => {
|
|
96
|
+
const diagnostics: Diagnostic[] = [];
|
|
97
|
+
|
|
98
|
+
if (typeof data !== "object" || data === null) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
error: [
|
|
102
|
+
{
|
|
103
|
+
code: "TSN9104",
|
|
104
|
+
message: `Bindings file must be an object, got ${typeof data}`,
|
|
105
|
+
severity: "error",
|
|
106
|
+
location: undefined,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const obj = data as Record<string, unknown>;
|
|
113
|
+
|
|
114
|
+
// Validate namespace
|
|
115
|
+
if (typeof obj.namespace !== "string") {
|
|
116
|
+
diagnostics.push({
|
|
117
|
+
code: "TSN9105",
|
|
118
|
+
message: `Missing or invalid 'namespace' field in ${path.basename(filePath)}`,
|
|
119
|
+
severity: "error",
|
|
120
|
+
location: undefined,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Validate types array
|
|
125
|
+
if (!Array.isArray(obj.types)) {
|
|
126
|
+
diagnostics.push({
|
|
127
|
+
code: "TSN9106",
|
|
128
|
+
message: `Missing or invalid 'types' field in ${path.basename(filePath)}`,
|
|
129
|
+
severity: "error",
|
|
130
|
+
location: undefined,
|
|
131
|
+
});
|
|
132
|
+
} else {
|
|
133
|
+
// Validate each type binding
|
|
134
|
+
for (let i = 0; i < obj.types.length; i++) {
|
|
135
|
+
const typeValidation = validateTypeBinding(obj.types[i], filePath, i);
|
|
136
|
+
if (!typeValidation.ok) {
|
|
137
|
+
diagnostics.push(...typeValidation.error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (diagnostics.length > 0) {
|
|
143
|
+
return { ok: false, error: diagnostics };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { ok: true, value: obj as BindingsFile };
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Validate a single TypeBinding object.
|
|
151
|
+
*
|
|
152
|
+
* @param data - Type binding to validate
|
|
153
|
+
* @param filePath - File path for error messages
|
|
154
|
+
* @param index - Type index in array
|
|
155
|
+
* @returns Result indicating validation success or errors
|
|
156
|
+
*/
|
|
157
|
+
const validateTypeBinding = (
|
|
158
|
+
data: unknown,
|
|
159
|
+
filePath: string,
|
|
160
|
+
index: number
|
|
161
|
+
): Result<TypeBinding, Diagnostic[]> => {
|
|
162
|
+
const diagnostics: Diagnostic[] = [];
|
|
163
|
+
const context = `type binding ${index} in ${path.basename(filePath)}`;
|
|
164
|
+
|
|
165
|
+
if (typeof data !== "object" || data === null) {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
error: [
|
|
169
|
+
{
|
|
170
|
+
code: "TSN9107",
|
|
171
|
+
message: `Invalid ${context}: must be an object`,
|
|
172
|
+
severity: "error",
|
|
173
|
+
location: undefined,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const type = data as Record<string, unknown>;
|
|
180
|
+
|
|
181
|
+
// Validate required string fields
|
|
182
|
+
const requiredStringFields = ["clrName", "tsEmitName", "assemblyName"];
|
|
183
|
+
for (const field of requiredStringFields) {
|
|
184
|
+
if (typeof type[field] !== "string") {
|
|
185
|
+
diagnostics.push({
|
|
186
|
+
code: "TSN9108",
|
|
187
|
+
message: `Invalid ${context}: missing or invalid '${field}'`,
|
|
188
|
+
severity: "error",
|
|
189
|
+
location: undefined,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Validate metadataToken
|
|
195
|
+
if (typeof type.metadataToken !== "number") {
|
|
196
|
+
diagnostics.push({
|
|
197
|
+
code: "TSN9109",
|
|
198
|
+
message: `Invalid ${context}: 'metadataToken' must be a number`,
|
|
199
|
+
severity: "error",
|
|
200
|
+
location: undefined,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Validate optional array fields (V1 Definitions)
|
|
205
|
+
const v1ArrayFields = ["methods", "properties", "fields", "events"];
|
|
206
|
+
for (const field of v1ArrayFields) {
|
|
207
|
+
if (type[field] !== undefined && !Array.isArray(type[field])) {
|
|
208
|
+
diagnostics.push({
|
|
209
|
+
code: "TSN9110",
|
|
210
|
+
message: `Invalid ${context}: '${field}' must be an array if present`,
|
|
211
|
+
severity: "error",
|
|
212
|
+
location: undefined,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Validate optional array fields (V2 Exposures)
|
|
218
|
+
const v2ArrayFields = [
|
|
219
|
+
"exposedMethods",
|
|
220
|
+
"exposedProperties",
|
|
221
|
+
"exposedFields",
|
|
222
|
+
"exposedEvents",
|
|
223
|
+
];
|
|
224
|
+
for (const field of v2ArrayFields) {
|
|
225
|
+
if (type[field] !== undefined && !Array.isArray(type[field])) {
|
|
226
|
+
diagnostics.push({
|
|
227
|
+
code: "TSN9111",
|
|
228
|
+
message: `Invalid ${context}: '${field}' must be an array if present`,
|
|
229
|
+
severity: "error",
|
|
230
|
+
location: undefined,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Note: We're doing basic structural validation here
|
|
236
|
+
// More detailed validation of nested objects (method bindings, etc.)
|
|
237
|
+
// can be added in future iterations if needed
|
|
238
|
+
|
|
239
|
+
if (diagnostics.length > 0) {
|
|
240
|
+
return { ok: false, error: diagnostics };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { ok: true, value: type as TypeBinding };
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Load bindings from a directory containing .bindings.json files.
|
|
248
|
+
*
|
|
249
|
+
* @param directoryPath - Path to directory containing bindings files
|
|
250
|
+
* @returns Result containing array of loaded bindings files or diagnostics
|
|
251
|
+
*/
|
|
252
|
+
export const loadBindingsDirectory = (
|
|
253
|
+
directoryPath: string
|
|
254
|
+
): Result<BindingsFile[], Diagnostic[]> => {
|
|
255
|
+
if (!fs.existsSync(directoryPath)) {
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
error: [
|
|
259
|
+
{
|
|
260
|
+
code: "TSN9112",
|
|
261
|
+
message: `Bindings directory not found: ${directoryPath}`,
|
|
262
|
+
severity: "error",
|
|
263
|
+
location: undefined,
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const stats = fs.statSync(directoryPath);
|
|
270
|
+
if (!stats.isDirectory()) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
error: [
|
|
274
|
+
{
|
|
275
|
+
code: "TSN9113",
|
|
276
|
+
message: `Not a directory: ${directoryPath}`,
|
|
277
|
+
severity: "error",
|
|
278
|
+
location: undefined,
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Find all .bindings.json files
|
|
285
|
+
const files = fs
|
|
286
|
+
.readdirSync(directoryPath)
|
|
287
|
+
.filter((file) => file.endsWith(".bindings.json"))
|
|
288
|
+
.map((file) => path.join(directoryPath, file));
|
|
289
|
+
|
|
290
|
+
if (files.length === 0) {
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
error: [
|
|
294
|
+
{
|
|
295
|
+
code: "TSN9114",
|
|
296
|
+
message: `No .bindings.json files found in ${directoryPath}`,
|
|
297
|
+
severity: "warning",
|
|
298
|
+
location: undefined,
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Load each file
|
|
305
|
+
const bindingsFiles: BindingsFile[] = [];
|
|
306
|
+
const diagnostics: Diagnostic[] = [];
|
|
307
|
+
|
|
308
|
+
for (const file of files) {
|
|
309
|
+
const result = loadBindingsFile(file);
|
|
310
|
+
if (result.ok) {
|
|
311
|
+
bindingsFiles.push(result.value);
|
|
312
|
+
} else {
|
|
313
|
+
diagnostics.push(...result.error);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (diagnostics.length > 0) {
|
|
318
|
+
return { ok: false, error: diagnostics };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return { ok: true, value: bindingsFiles };
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Build a registry of type bindings for fast lookup by TypeScript emit name.
|
|
326
|
+
*
|
|
327
|
+
* @param bindingsFiles - Array of loaded bindings files
|
|
328
|
+
* @returns Map from tsEmitName to TypeBinding
|
|
329
|
+
*/
|
|
330
|
+
export const buildBindingsRegistry = (
|
|
331
|
+
bindingsFiles: readonly BindingsFile[]
|
|
332
|
+
): ReadonlyMap<string, TypeBinding> => {
|
|
333
|
+
const registry = new Map<string, TypeBinding>();
|
|
334
|
+
|
|
335
|
+
for (const file of bindingsFiles) {
|
|
336
|
+
for (const typeBinding of file.types) {
|
|
337
|
+
const key = `${file.namespace}.${typeBinding.tsEmitName}`;
|
|
338
|
+
registry.set(key, typeBinding);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return registry;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Look up a type binding by fully-qualified TypeScript emit name.
|
|
347
|
+
*
|
|
348
|
+
* @param registry - Bindings registry
|
|
349
|
+
* @param qualifiedTsName - Fully qualified TS name (e.g., "System.Collections.Generic.List_1")
|
|
350
|
+
* @returns TypeBinding if found, undefined otherwise
|
|
351
|
+
*/
|
|
352
|
+
export const lookupTypeBinding = (
|
|
353
|
+
registry: ReadonlyMap<string, TypeBinding>,
|
|
354
|
+
qualifiedTsName: string
|
|
355
|
+
): TypeBinding | undefined => {
|
|
356
|
+
return registry.get(qualifiedTsName);
|
|
357
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata and Bindings Loaders - Public API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { loadMetadataFile, loadMetadataDirectory } from "./loader.js";
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
loadBindingsFile,
|
|
9
|
+
loadBindingsDirectory,
|
|
10
|
+
buildBindingsRegistry,
|
|
11
|
+
lookupTypeBinding,
|
|
12
|
+
} from "./bindings-loader.js";
|
|
13
|
+
|
|
14
|
+
export { loadLibrary, loadLibraries } from "./library-loader.js";
|
|
15
|
+
export type { LibraryData } from "./library-loader.js";
|