@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,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for validator - now only checks truly unsupported features
|
|
3
|
+
*
|
|
4
|
+
* Most generic constructs are now handled via:
|
|
5
|
+
* - Monomorphisation
|
|
6
|
+
* - CRTP pattern
|
|
7
|
+
* - Tuple specialisations
|
|
8
|
+
* - Structural adapters
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it } from "mocha";
|
|
12
|
+
import { expect } from "chai";
|
|
13
|
+
import * as ts from "typescript";
|
|
14
|
+
import { TsonicProgram } from "./program.js";
|
|
15
|
+
import { validateProgram } from "./validator.js";
|
|
16
|
+
import { DotnetMetadataRegistry } from "./dotnet-metadata.js";
|
|
17
|
+
import { BindingRegistry } from "./program/bindings.js";
|
|
18
|
+
import { createDotNetImportResolver } from "./resolver/dotnet-import-resolver.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Helper to create a test program from source code
|
|
22
|
+
*/
|
|
23
|
+
const createTestProgram = (
|
|
24
|
+
source: string,
|
|
25
|
+
fileName = "test.ts"
|
|
26
|
+
): TsonicProgram => {
|
|
27
|
+
const sourceFile = ts.createSourceFile(
|
|
28
|
+
fileName,
|
|
29
|
+
source,
|
|
30
|
+
ts.ScriptTarget.Latest,
|
|
31
|
+
true,
|
|
32
|
+
ts.ScriptKind.TS
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const compilerOptions: ts.CompilerOptions = {
|
|
36
|
+
target: ts.ScriptTarget.ES2022,
|
|
37
|
+
module: ts.ModuleKind.NodeNext,
|
|
38
|
+
strict: true,
|
|
39
|
+
noEmit: true,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const host = ts.createCompilerHost(compilerOptions);
|
|
43
|
+
const originalGetSourceFile = host.getSourceFile;
|
|
44
|
+
host.getSourceFile = (
|
|
45
|
+
name: string,
|
|
46
|
+
languageVersionOrOptions: ts.ScriptTarget | ts.CreateSourceFileOptions,
|
|
47
|
+
onError?: (message: string) => void,
|
|
48
|
+
shouldCreateNewSourceFile?: boolean
|
|
49
|
+
) => {
|
|
50
|
+
if (name === fileName) {
|
|
51
|
+
return sourceFile;
|
|
52
|
+
}
|
|
53
|
+
return originalGetSourceFile.call(
|
|
54
|
+
host,
|
|
55
|
+
name,
|
|
56
|
+
languageVersionOrOptions,
|
|
57
|
+
onError,
|
|
58
|
+
shouldCreateNewSourceFile
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const program = ts.createProgram([fileName], compilerOptions, host);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
program,
|
|
66
|
+
checker: program.getTypeChecker(),
|
|
67
|
+
options: {
|
|
68
|
+
sourceRoot: "/test",
|
|
69
|
+
rootNamespace: "Test",
|
|
70
|
+
},
|
|
71
|
+
sourceFiles: [sourceFile],
|
|
72
|
+
metadata: new DotnetMetadataRegistry(),
|
|
73
|
+
bindings: new BindingRegistry(),
|
|
74
|
+
dotnetResolver: createDotNetImportResolver("/test"),
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
describe("Generic Validation", () => {
|
|
79
|
+
describe("TSN7203 - Symbol Index Signatures (still blocked)", () => {
|
|
80
|
+
it("should detect symbol index signatures", () => {
|
|
81
|
+
const source = `
|
|
82
|
+
export interface WithSymbolIndex {
|
|
83
|
+
[key: symbol]: string;
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
const program = createTestProgram(source);
|
|
88
|
+
const diagnostics = validateProgram(program);
|
|
89
|
+
|
|
90
|
+
const symbolDiag = diagnostics.diagnostics.find(
|
|
91
|
+
(d) => d.code === "TSN7203"
|
|
92
|
+
);
|
|
93
|
+
expect(symbolDiag).not.to.equal(undefined);
|
|
94
|
+
expect(symbolDiag?.message).to.include(
|
|
95
|
+
"Symbol keys are not supported in C#"
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should not flag string index signatures", () => {
|
|
100
|
+
const source = `
|
|
101
|
+
interface WithStringIndex {
|
|
102
|
+
[key: string]: number;
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const program = createTestProgram(source);
|
|
107
|
+
const diagnostics = validateProgram(program);
|
|
108
|
+
|
|
109
|
+
const symbolDiag = diagnostics.diagnostics.find(
|
|
110
|
+
(d) => d.code === "TSN7203"
|
|
111
|
+
);
|
|
112
|
+
expect(symbolDiag).to.equal(undefined);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should not flag number index signatures", () => {
|
|
116
|
+
const source = `
|
|
117
|
+
interface WithNumberIndex {
|
|
118
|
+
[key: number]: string;
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
|
|
122
|
+
const program = createTestProgram(source);
|
|
123
|
+
const diagnostics = validateProgram(program);
|
|
124
|
+
|
|
125
|
+
const symbolDiag = diagnostics.diagnostics.find(
|
|
126
|
+
(d) => d.code === "TSN7203"
|
|
127
|
+
);
|
|
128
|
+
expect(symbolDiag).to.equal(undefined);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("Previously-blocked constructs (now ALLOWED)", () => {
|
|
133
|
+
it("should allow recursive mapped types (handled via monomorphisation)", () => {
|
|
134
|
+
const source = `
|
|
135
|
+
type RecursiveMapped<T> = {
|
|
136
|
+
[K in keyof T]: RecursiveMapped<T[K]>
|
|
137
|
+
};
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
const program = createTestProgram(source);
|
|
141
|
+
const diagnostics = validateProgram(program);
|
|
142
|
+
|
|
143
|
+
// Should NOT have TSN7101 error anymore
|
|
144
|
+
const recursiveDiag = diagnostics.diagnostics.find(
|
|
145
|
+
(d) => d.code === "TSN7101"
|
|
146
|
+
);
|
|
147
|
+
expect(recursiveDiag).to.equal(undefined);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should allow conditional types with infer (handled via monomorphisation)", () => {
|
|
151
|
+
const source = `
|
|
152
|
+
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
const program = createTestProgram(source);
|
|
156
|
+
const diagnostics = validateProgram(program);
|
|
157
|
+
|
|
158
|
+
// Should NOT have TSN7102 error anymore
|
|
159
|
+
const inferDiag = diagnostics.diagnostics.find(
|
|
160
|
+
(d) => d.code === "TSN7102"
|
|
161
|
+
);
|
|
162
|
+
expect(inferDiag).to.equal(undefined);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should allow this typing (handled via CRTP pattern)", () => {
|
|
166
|
+
const source = `
|
|
167
|
+
interface Chainable {
|
|
168
|
+
add(value: number): this;
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
const program = createTestProgram(source);
|
|
173
|
+
const diagnostics = validateProgram(program);
|
|
174
|
+
|
|
175
|
+
// Should NOT have TSN7103 error anymore
|
|
176
|
+
const thisDiag = diagnostics.diagnostics.find(
|
|
177
|
+
(d) => d.code === "TSN7103"
|
|
178
|
+
);
|
|
179
|
+
expect(thisDiag).to.equal(undefined);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should allow variadic type parameters (handled via tuple specialisations)", () => {
|
|
183
|
+
const source = `
|
|
184
|
+
type VariadicFunction<T extends unknown[]> = (...args: T) => void;
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
const program = createTestProgram(source);
|
|
188
|
+
const diagnostics = validateProgram(program);
|
|
189
|
+
|
|
190
|
+
// Should NOT have TSN7104 error anymore
|
|
191
|
+
const variadicDiag = diagnostics.diagnostics.find(
|
|
192
|
+
(d) => d.code === "TSN7104"
|
|
193
|
+
);
|
|
194
|
+
expect(variadicDiag).to.equal(undefined);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should allow recursive structural aliases (emit as C# classes)", () => {
|
|
198
|
+
const source = `
|
|
199
|
+
type Node = {
|
|
200
|
+
name: string;
|
|
201
|
+
children: Node[];
|
|
202
|
+
};
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
const program = createTestProgram(source);
|
|
206
|
+
const diagnostics = validateProgram(program);
|
|
207
|
+
|
|
208
|
+
// Should NOT have TSN7201 error anymore
|
|
209
|
+
const recursiveDiag = diagnostics.diagnostics.find(
|
|
210
|
+
(d) => d.code === "TSN7201"
|
|
211
|
+
);
|
|
212
|
+
expect(recursiveDiag).to.equal(undefined);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should allow complex generic code without errors", () => {
|
|
216
|
+
const source = `
|
|
217
|
+
// Conditional type with infer
|
|
218
|
+
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
|
|
219
|
+
|
|
220
|
+
// This typing
|
|
221
|
+
interface Builder {
|
|
222
|
+
set(key: string, value: any): this;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Variadic parameters
|
|
226
|
+
function concat<T extends any[]>(...arrays: T): T {
|
|
227
|
+
return arrays;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Recursive structural alias
|
|
231
|
+
type Tree = {
|
|
232
|
+
value: number;
|
|
233
|
+
left?: Tree;
|
|
234
|
+
right?: Tree;
|
|
235
|
+
};
|
|
236
|
+
`;
|
|
237
|
+
|
|
238
|
+
const program = createTestProgram(source);
|
|
239
|
+
const diagnostics = validateProgram(program);
|
|
240
|
+
|
|
241
|
+
// Should have NO generic-specific diagnostics (TSN71xx, TSN72xx)
|
|
242
|
+
// Note: TSN74xx (static safety) may fire due to 'any' in test code, but that's expected
|
|
243
|
+
const genericDiags = diagnostics.diagnostics.filter(
|
|
244
|
+
(d) => d.code.startsWith("TSN71") || d.code.startsWith("TSN72")
|
|
245
|
+
);
|
|
246
|
+
expect(genericDiags).to.have.lengthOf(0);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe("Static Safety Validation", () => {
|
|
252
|
+
describe("TSN7401 - 'any' type banned", () => {
|
|
253
|
+
it("should reject explicit any type annotation", () => {
|
|
254
|
+
const source = `
|
|
255
|
+
export const x: any = 1;
|
|
256
|
+
`;
|
|
257
|
+
|
|
258
|
+
const program = createTestProgram(source);
|
|
259
|
+
const diagnostics = validateProgram(program);
|
|
260
|
+
|
|
261
|
+
const anyDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7401");
|
|
262
|
+
expect(anyDiag).not.to.equal(undefined);
|
|
263
|
+
expect(anyDiag?.message).to.include("'any' type is not supported");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should reject 'as any' type assertion", () => {
|
|
267
|
+
const source = `
|
|
268
|
+
export const x = (123 as any);
|
|
269
|
+
`;
|
|
270
|
+
|
|
271
|
+
const program = createTestProgram(source);
|
|
272
|
+
const diagnostics = validateProgram(program);
|
|
273
|
+
|
|
274
|
+
const anyDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7401");
|
|
275
|
+
expect(anyDiag).not.to.equal(undefined);
|
|
276
|
+
expect(anyDiag?.message).to.include("'as any'");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("should allow unknown type", () => {
|
|
280
|
+
const source = `
|
|
281
|
+
export function process(data: unknown): void {
|
|
282
|
+
console.log(data);
|
|
283
|
+
}
|
|
284
|
+
`;
|
|
285
|
+
|
|
286
|
+
const program = createTestProgram(source);
|
|
287
|
+
const diagnostics = validateProgram(program);
|
|
288
|
+
|
|
289
|
+
const anyDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7401");
|
|
290
|
+
expect(anyDiag).to.equal(undefined);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe("TSN7403 - Object literal requires nominal type", () => {
|
|
295
|
+
it("should reject object literal without contextual type", () => {
|
|
296
|
+
const source = `
|
|
297
|
+
const a = { x: 1 };
|
|
298
|
+
`;
|
|
299
|
+
|
|
300
|
+
const program = createTestProgram(source);
|
|
301
|
+
const diagnostics = validateProgram(program);
|
|
302
|
+
|
|
303
|
+
const objDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7403");
|
|
304
|
+
expect(objDiag).not.to.equal(undefined);
|
|
305
|
+
expect(objDiag?.message).to.include("contextual nominal type");
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("should allow object literal with interface type", () => {
|
|
309
|
+
const source = `
|
|
310
|
+
interface Point { x: number; y: number }
|
|
311
|
+
const p: Point = { x: 1, y: 2 };
|
|
312
|
+
`;
|
|
313
|
+
|
|
314
|
+
const program = createTestProgram(source);
|
|
315
|
+
const diagnostics = validateProgram(program);
|
|
316
|
+
|
|
317
|
+
const objDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7403");
|
|
318
|
+
expect(objDiag).to.equal(undefined);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("should allow object literal with Record type", () => {
|
|
322
|
+
const source = `
|
|
323
|
+
const d: Record<string, number> = { a: 1, b: 2 };
|
|
324
|
+
`;
|
|
325
|
+
|
|
326
|
+
const program = createTestProgram(source);
|
|
327
|
+
const diagnostics = validateProgram(program);
|
|
328
|
+
|
|
329
|
+
const objDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7403");
|
|
330
|
+
expect(objDiag).to.equal(undefined);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe("TSN7405 - Untyped function parameters", () => {
|
|
335
|
+
it("should reject untyped function parameter", () => {
|
|
336
|
+
const source = `
|
|
337
|
+
export function greet(name): void {
|
|
338
|
+
console.log(name);
|
|
339
|
+
}
|
|
340
|
+
`;
|
|
341
|
+
|
|
342
|
+
const program = createTestProgram(source);
|
|
343
|
+
const diagnostics = validateProgram(program);
|
|
344
|
+
|
|
345
|
+
const paramDiag = diagnostics.diagnostics.find(
|
|
346
|
+
(d) => d.code === "TSN7405"
|
|
347
|
+
);
|
|
348
|
+
expect(paramDiag).not.to.equal(undefined);
|
|
349
|
+
expect(paramDiag?.message).to.include("explicit type annotation");
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("should reject untyped arrow function parameter", () => {
|
|
353
|
+
const source = `
|
|
354
|
+
const fn = (x) => x + 1;
|
|
355
|
+
`;
|
|
356
|
+
|
|
357
|
+
const program = createTestProgram(source);
|
|
358
|
+
const diagnostics = validateProgram(program);
|
|
359
|
+
|
|
360
|
+
const paramDiag = diagnostics.diagnostics.find(
|
|
361
|
+
(d) => d.code === "TSN7405"
|
|
362
|
+
);
|
|
363
|
+
expect(paramDiag).not.to.equal(undefined);
|
|
364
|
+
expect(paramDiag?.message).to.include("explicit type annotation");
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("should reject untyped function expression parameter", () => {
|
|
368
|
+
const source = `
|
|
369
|
+
const fn = function(x) { return x + 1; };
|
|
370
|
+
`;
|
|
371
|
+
|
|
372
|
+
const program = createTestProgram(source);
|
|
373
|
+
const diagnostics = validateProgram(program);
|
|
374
|
+
|
|
375
|
+
const paramDiag = diagnostics.diagnostics.find(
|
|
376
|
+
(d) => d.code === "TSN7405"
|
|
377
|
+
);
|
|
378
|
+
expect(paramDiag).not.to.equal(undefined);
|
|
379
|
+
expect(paramDiag?.message).to.include("explicit type annotation");
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it("should allow typed function parameter", () => {
|
|
383
|
+
const source = `
|
|
384
|
+
export function greet(name: string): void {
|
|
385
|
+
console.log(name);
|
|
386
|
+
}
|
|
387
|
+
`;
|
|
388
|
+
|
|
389
|
+
const program = createTestProgram(source);
|
|
390
|
+
const diagnostics = validateProgram(program);
|
|
391
|
+
|
|
392
|
+
const paramDiag = diagnostics.diagnostics.find(
|
|
393
|
+
(d) => d.code === "TSN7405"
|
|
394
|
+
);
|
|
395
|
+
expect(paramDiag).to.equal(undefined);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it("should allow typed arrow function parameter", () => {
|
|
399
|
+
const source = `
|
|
400
|
+
const fn = (x: number): number => x + 1;
|
|
401
|
+
`;
|
|
402
|
+
|
|
403
|
+
const program = createTestProgram(source);
|
|
404
|
+
const diagnostics = validateProgram(program);
|
|
405
|
+
|
|
406
|
+
const paramDiag = diagnostics.diagnostics.find(
|
|
407
|
+
(d) => d.code === "TSN7405"
|
|
408
|
+
);
|
|
409
|
+
expect(paramDiag).to.equal(undefined);
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
describe("TSN7413 - Dictionary key must be string", () => {
|
|
414
|
+
it("should reject Record with number key", () => {
|
|
415
|
+
const source = `
|
|
416
|
+
const d: Record<number, string> = {};
|
|
417
|
+
`;
|
|
418
|
+
|
|
419
|
+
const program = createTestProgram(source);
|
|
420
|
+
const diagnostics = validateProgram(program);
|
|
421
|
+
|
|
422
|
+
const keyDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7413");
|
|
423
|
+
expect(keyDiag).not.to.equal(undefined);
|
|
424
|
+
expect(keyDiag?.message).to.include("string");
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("should reject index signature with number key", () => {
|
|
428
|
+
const source = `
|
|
429
|
+
interface NumIndexed {
|
|
430
|
+
[key: number]: string;
|
|
431
|
+
}
|
|
432
|
+
`;
|
|
433
|
+
|
|
434
|
+
const program = createTestProgram(source);
|
|
435
|
+
const diagnostics = validateProgram(program);
|
|
436
|
+
|
|
437
|
+
const keyDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7413");
|
|
438
|
+
expect(keyDiag).not.to.equal(undefined);
|
|
439
|
+
expect(keyDiag?.message).to.include("string");
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it("should allow Record with string key", () => {
|
|
443
|
+
const source = `
|
|
444
|
+
const d: Record<string, number> = {};
|
|
445
|
+
`;
|
|
446
|
+
|
|
447
|
+
const program = createTestProgram(source);
|
|
448
|
+
const diagnostics = validateProgram(program);
|
|
449
|
+
|
|
450
|
+
const keyDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7413");
|
|
451
|
+
expect(keyDiag).to.equal(undefined);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should allow index signature with string key", () => {
|
|
455
|
+
const source = `
|
|
456
|
+
interface StringIndexed {
|
|
457
|
+
[key: string]: number;
|
|
458
|
+
}
|
|
459
|
+
`;
|
|
460
|
+
|
|
461
|
+
const program = createTestProgram(source);
|
|
462
|
+
const diagnostics = validateProgram(program);
|
|
463
|
+
|
|
464
|
+
const keyDiag = diagnostics.diagnostics.find((d) => d.code === "TSN7413");
|
|
465
|
+
expect(keyDiag).to.equal(undefined);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
});
|
package/src/validator.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESM and TypeScript validation rules
|
|
3
|
+
* Main dispatcher - re-exports from validation/ subdirectory
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
validateProgram,
|
|
8
|
+
validateSourceFile,
|
|
9
|
+
validateImports,
|
|
10
|
+
validateImportDeclaration,
|
|
11
|
+
validateExports,
|
|
12
|
+
validateUnsupportedFeatures,
|
|
13
|
+
validateGenerics,
|
|
14
|
+
getNodeLocation,
|
|
15
|
+
} from "./validation/index.js";
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"composite": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"tsBuildInfoFile": "./dist/.tsbuildinfo"
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*.ts"],
|
|
12
|
+
"exclude": ["node_modules", "dist"]
|
|
13
|
+
}
|