@tsonic/emitter 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.
Files changed (209) hide show
  1. package/package.json +34 -0
  2. package/scripts/update-golden-tests.ts +119 -0
  3. package/src/adapter-generator.ts +112 -0
  4. package/src/array.test.ts +301 -0
  5. package/src/constants.ts +32 -0
  6. package/src/core/exports.ts +36 -0
  7. package/src/core/imports.test.ts +83 -0
  8. package/src/core/imports.ts +243 -0
  9. package/src/core/index.ts +9 -0
  10. package/src/core/module-emitter/assembly.ts +83 -0
  11. package/src/core/module-emitter/header.ts +19 -0
  12. package/src/core/module-emitter/index.ts +17 -0
  13. package/src/core/module-emitter/namespace.ts +39 -0
  14. package/src/core/module-emitter/orchestrator.ts +98 -0
  15. package/src/core/module-emitter/separation.ts +41 -0
  16. package/src/core/module-emitter/static-container.ts +75 -0
  17. package/src/core/module-emitter.test.ts +154 -0
  18. package/src/core/module-emitter.ts +6 -0
  19. package/src/core/module-map.ts +218 -0
  20. package/src/core/options.ts +16 -0
  21. package/src/core/type-params.ts +34 -0
  22. package/src/emitter-types/context.ts +91 -0
  23. package/src/emitter-types/core.ts +138 -0
  24. package/src/emitter-types/csharp-types.ts +42 -0
  25. package/src/emitter-types/formatting.ts +13 -0
  26. package/src/emitter-types/fqn.ts +81 -0
  27. package/src/emitter-types/index.ts +31 -0
  28. package/src/emitter.ts +107 -0
  29. package/src/expression-emitter.ts +112 -0
  30. package/src/expressions/access.ts +104 -0
  31. package/src/expressions/calls.ts +264 -0
  32. package/src/expressions/collections.ts +354 -0
  33. package/src/expressions/functions.ts +71 -0
  34. package/src/expressions/identifiers.ts +125 -0
  35. package/src/expressions/index.test.ts +515 -0
  36. package/src/expressions/index.ts +38 -0
  37. package/src/expressions/literals.ts +138 -0
  38. package/src/expressions/operators.ts +211 -0
  39. package/src/expressions/other.ts +63 -0
  40. package/src/generator-exchange.ts +120 -0
  41. package/src/generator.test.ts +193 -0
  42. package/src/golden-tests/config-parser.ts +67 -0
  43. package/src/golden-tests/discovery.ts +61 -0
  44. package/src/golden-tests/index.ts +10 -0
  45. package/src/golden-tests/registration.ts +26 -0
  46. package/src/golden-tests/runner.ts +131 -0
  47. package/src/golden-tests/tree-builder.ts +43 -0
  48. package/src/golden-tests/types.ts +21 -0
  49. package/src/golden.test.ts +40 -0
  50. package/src/hierarchical-bindings.test.ts +258 -0
  51. package/src/index.ts +14 -0
  52. package/src/integration.test.ts +303 -0
  53. package/src/specialization/call-site-rewriting.test.ts +99 -0
  54. package/src/specialization/collection/expressions.ts +184 -0
  55. package/src/specialization/collection/index.ts +7 -0
  56. package/src/specialization/collection/orchestrator.ts +25 -0
  57. package/src/specialization/collection/statements.ts +91 -0
  58. package/src/specialization/collection.ts +10 -0
  59. package/src/specialization/generation.ts +189 -0
  60. package/src/specialization/generic-classes.test.ts +59 -0
  61. package/src/specialization/generic-functions.test.ts +292 -0
  62. package/src/specialization/helpers.ts +39 -0
  63. package/src/specialization/index.ts +28 -0
  64. package/src/specialization/interfaces.test.ts +151 -0
  65. package/src/specialization/naming.ts +34 -0
  66. package/src/specialization/substitution.ts +186 -0
  67. package/src/specialization/type-aliases.test.ts +134 -0
  68. package/src/specialization/types.ts +19 -0
  69. package/src/specialization-generator.ts +23 -0
  70. package/src/statement-emitter.ts +117 -0
  71. package/src/statements/blocks.ts +115 -0
  72. package/src/statements/classes/helpers.ts +9 -0
  73. package/src/statements/classes/index.ts +13 -0
  74. package/src/statements/classes/inline-types.ts +79 -0
  75. package/src/statements/classes/members/constructors.ts +123 -0
  76. package/src/statements/classes/members/index.ts +8 -0
  77. package/src/statements/classes/members/methods.ts +137 -0
  78. package/src/statements/classes/members/orchestrator.ts +33 -0
  79. package/src/statements/classes/members/properties.ts +62 -0
  80. package/src/statements/classes/members.ts +6 -0
  81. package/src/statements/classes/parameters.ts +69 -0
  82. package/src/statements/classes/properties.ts +95 -0
  83. package/src/statements/classes.ts +14 -0
  84. package/src/statements/control/conditionals.ts +134 -0
  85. package/src/statements/control/exceptions.ts +59 -0
  86. package/src/statements/control/index.ts +11 -0
  87. package/src/statements/control/loops.ts +250 -0
  88. package/src/statements/control.ts +14 -0
  89. package/src/statements/declarations/classes.ts +89 -0
  90. package/src/statements/declarations/enums.ts +32 -0
  91. package/src/statements/declarations/functions.ts +147 -0
  92. package/src/statements/declarations/index.ts +10 -0
  93. package/src/statements/declarations/interfaces.ts +116 -0
  94. package/src/statements/declarations/structs.test.ts +182 -0
  95. package/src/statements/declarations/type-aliases.ts +104 -0
  96. package/src/statements/declarations/variables.ts +159 -0
  97. package/src/statements/declarations.ts +13 -0
  98. package/src/statements/index.test.ts +258 -0
  99. package/src/statements/index.ts +43 -0
  100. package/src/type-assertion.test.ts +143 -0
  101. package/src/type-emitter.ts +18 -0
  102. package/src/types/arrays.ts +21 -0
  103. package/src/types/dictionaries.ts +52 -0
  104. package/src/types/emitter.ts +76 -0
  105. package/src/types/functions.ts +45 -0
  106. package/src/types/index.test.ts +116 -0
  107. package/src/types/index.ts +14 -0
  108. package/src/types/intersections.ts +19 -0
  109. package/src/types/literals.ts +26 -0
  110. package/src/types/objects.ts +27 -0
  111. package/src/types/parameters.test.ts +146 -0
  112. package/src/types/parameters.ts +95 -0
  113. package/src/types/primitives.ts +24 -0
  114. package/src/types/references.ts +187 -0
  115. package/src/types/unions.test.ts +397 -0
  116. package/src/types/unions.ts +62 -0
  117. package/src/types.ts +33 -0
  118. package/testcases/README.md +213 -0
  119. package/testcases/arrays/basic/ArrayLiteral.ts +4 -0
  120. package/testcases/arrays/basic/config.yaml +1 -0
  121. package/testcases/arrays/destructuring/ArrayDestructure.ts +4 -0
  122. package/testcases/arrays/destructuring/config.yaml +1 -0
  123. package/testcases/arrays/methods/ArrayMethods.ts +6 -0
  124. package/testcases/arrays/methods/config.yaml +1 -0
  125. package/testcases/arrays/multidimensional/MultiDimensional.ts +10 -0
  126. package/testcases/arrays/multidimensional/config.yaml +1 -0
  127. package/testcases/arrays/spread/ArraySpread.ts +3 -0
  128. package/testcases/arrays/spread/config.yaml +1 -0
  129. package/testcases/async/basic/AsyncFunction.ts +5 -0
  130. package/testcases/async/basic/config.yaml +1 -0
  131. package/testcases/classes/abstract/AbstractClasses.ts +53 -0
  132. package/testcases/classes/abstract/config.yaml +1 -0
  133. package/testcases/classes/basic/Person.ts +12 -0
  134. package/testcases/classes/basic/config.yaml +1 -0
  135. package/testcases/classes/constructor/User.ts +11 -0
  136. package/testcases/classes/constructor/config.yaml +1 -0
  137. package/testcases/classes/field-inference/Counter.ts +11 -0
  138. package/testcases/classes/field-inference/config.yaml +1 -0
  139. package/testcases/classes/inheritance/Inheritance.ts +24 -0
  140. package/testcases/classes/inheritance/config.yaml +1 -0
  141. package/testcases/classes/static-members/MathHelper.ts +12 -0
  142. package/testcases/classes/static-members/config.yaml +1 -0
  143. package/testcases/control-flow/error-handling/ErrorHandling.ts +13 -0
  144. package/testcases/control-flow/error-handling/config.yaml +1 -0
  145. package/testcases/control-flow/loops/Loops.ts +21 -0
  146. package/testcases/control-flow/loops/config.yaml +1 -0
  147. package/testcases/control-flow/switch/SwitchStatement.ts +15 -0
  148. package/testcases/control-flow/switch/config.yaml +1 -0
  149. package/testcases/edge-cases/complex-expressions/ComplexExpressions.ts +10 -0
  150. package/testcases/edge-cases/complex-expressions/config.yaml +1 -0
  151. package/testcases/edge-cases/nested-scopes/NestedScopes.ts +10 -0
  152. package/testcases/edge-cases/nested-scopes/config.yaml +1 -0
  153. package/testcases/edge-cases/shadowing/Shadowing.ts +16 -0
  154. package/testcases/edge-cases/shadowing/config.yaml +1 -0
  155. package/testcases/functions/arrow/ArrowFunction.ts +5 -0
  156. package/testcases/functions/arrow/config.yaml +1 -0
  157. package/testcases/functions/basic/Greet.ts +3 -0
  158. package/testcases/functions/basic/config.yaml +1 -0
  159. package/testcases/functions/closures/Closures.ts +11 -0
  160. package/testcases/functions/closures/config.yaml +1 -0
  161. package/testcases/functions/default-params/DefaultParams.ts +7 -0
  162. package/testcases/functions/default-params/config.yaml +1 -0
  163. package/testcases/functions/rest-params/RestParams.ts +7 -0
  164. package/testcases/functions/rest-params/config.yaml +1 -0
  165. package/testcases/functions/type-guards/TypeGuards.ts +52 -0
  166. package/testcases/functions/type-guards/config.yaml +1 -0
  167. package/testcases/operators/logical/LogicalOperators.ts +11 -0
  168. package/testcases/operators/logical/config.yaml +1 -0
  169. package/testcases/operators/nullish-coalescing/NullishCoalescing.ts +7 -0
  170. package/testcases/operators/nullish-coalescing/config.yaml +1 -0
  171. package/testcases/operators/optional-chaining/OptionalChaining.ts +15 -0
  172. package/testcases/operators/optional-chaining/config.yaml +1 -0
  173. package/testcases/real-world/advanced-generics/advanced-generics.ts +116 -0
  174. package/testcases/real-world/advanced-generics/config.yaml +1 -0
  175. package/testcases/real-world/async-ops/async-ops.ts +67 -0
  176. package/testcases/real-world/async-ops/config.yaml +1 -0
  177. package/testcases/real-world/business-logic/business-logic.ts +215 -0
  178. package/testcases/real-world/business-logic/config.yaml +1 -0
  179. package/testcases/real-world/calculator/calculator.ts +29 -0
  180. package/testcases/real-world/calculator/config.yaml +1 -0
  181. package/testcases/real-world/data-structures/config.yaml +1 -0
  182. package/testcases/real-world/data-structures/data-structures.ts +133 -0
  183. package/testcases/real-world/functional/config.yaml +1 -0
  184. package/testcases/real-world/functional/functional.ts +116 -0
  185. package/testcases/real-world/shapes/config.yaml +1 -0
  186. package/testcases/real-world/shapes/shapes.ts +87 -0
  187. package/testcases/real-world/string-utils/config.yaml +1 -0
  188. package/testcases/real-world/string-utils/string-utils.ts +47 -0
  189. package/testcases/real-world/todo-list/config.yaml +1 -0
  190. package/testcases/real-world/todo-list/todo-list.ts +52 -0
  191. package/testcases/real-world/type-guards/config.yaml +1 -0
  192. package/testcases/real-world/type-guards/type-guards.ts +71 -0
  193. package/testcases/structs/basic/Point.ts +9 -0
  194. package/testcases/structs/basic/config.yaml +1 -0
  195. package/testcases/types/conditional/ConditionalTypes.ts +35 -0
  196. package/testcases/types/conditional/config.yaml +1 -0
  197. package/testcases/types/constants/ModuleConstants.ts +6 -0
  198. package/testcases/types/constants/config.yaml +1 -0
  199. package/testcases/types/generics/Generics.ts +15 -0
  200. package/testcases/types/generics/config.yaml +1 -0
  201. package/testcases/types/interfaces/Interfaces.ts +14 -0
  202. package/testcases/types/interfaces/config.yaml +1 -0
  203. package/testcases/types/mapped/MappedTypes.ts +27 -0
  204. package/testcases/types/mapped/config.yaml +1 -0
  205. package/testcases/types/tuples-intersections/TuplesAndIntersections.ts +46 -0
  206. package/testcases/types/tuples-intersections/config.yaml +1 -0
  207. package/testcases/types/unions/UnionTypes.ts +11 -0
  208. package/testcases/types/unions/config.yaml +1 -0
  209. package/tsconfig.json +14 -0
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Tests for Generic Functions
3
+ * Covers spec/15-generics.md §3-5 - Generic Functions
4
+ */
5
+
6
+ import { describe, it } from "mocha";
7
+ import { expect } from "chai";
8
+ import { emitModule } from "../emitter.js";
9
+ import { IrModule } from "@tsonic/frontend";
10
+
11
+ describe("Generic Functions (spec/15 §3-5)", () => {
12
+ it("should emit generic function with single type parameter", () => {
13
+ const module: IrModule = {
14
+ kind: "module",
15
+ filePath: "/src/utils.ts",
16
+ namespace: "MyApp",
17
+ className: "utils",
18
+ isStaticContainer: true,
19
+ imports: [],
20
+ body: [
21
+ {
22
+ kind: "functionDeclaration",
23
+ name: "identity",
24
+ typeParameters: [
25
+ {
26
+ kind: "typeParameter",
27
+ name: "T",
28
+ constraint: undefined,
29
+ default: undefined,
30
+ variance: undefined,
31
+ isStructuralConstraint: false,
32
+ },
33
+ ],
34
+ parameters: [
35
+ {
36
+ kind: "parameter",
37
+ pattern: { kind: "identifierPattern", name: "value" },
38
+ type: { kind: "referenceType", name: "T", typeArguments: [] },
39
+ isOptional: false,
40
+ isRest: false,
41
+ passing: "value",
42
+ },
43
+ ],
44
+ returnType: { kind: "referenceType", name: "T", typeArguments: [] },
45
+ body: {
46
+ kind: "blockStatement",
47
+ statements: [
48
+ {
49
+ kind: "returnStatement",
50
+ expression: { kind: "identifier", name: "value" },
51
+ },
52
+ ],
53
+ },
54
+ isExported: true,
55
+ isAsync: false,
56
+ isGenerator: false,
57
+ },
58
+ ],
59
+ exports: [],
60
+ };
61
+
62
+ const result = emitModule(module);
63
+
64
+ expect(result).to.include("public static T identity<T>(T value)");
65
+ expect(result).to.include("return value");
66
+ });
67
+
68
+ it("should emit generic function with multiple type parameters", () => {
69
+ const module: IrModule = {
70
+ kind: "module",
71
+ filePath: "/src/utils.ts",
72
+ namespace: "MyApp",
73
+ className: "utils",
74
+ isStaticContainer: true,
75
+ imports: [],
76
+ body: [
77
+ {
78
+ kind: "functionDeclaration",
79
+ name: "pair",
80
+ typeParameters: [
81
+ {
82
+ kind: "typeParameter",
83
+ name: "T",
84
+ constraint: undefined,
85
+ default: undefined,
86
+ variance: undefined,
87
+ isStructuralConstraint: false,
88
+ },
89
+ {
90
+ kind: "typeParameter",
91
+ name: "U",
92
+ constraint: undefined,
93
+ default: undefined,
94
+ variance: undefined,
95
+ isStructuralConstraint: false,
96
+ },
97
+ ],
98
+ parameters: [
99
+ {
100
+ kind: "parameter",
101
+ pattern: { kind: "identifierPattern", name: "first" },
102
+ type: { kind: "referenceType", name: "T", typeArguments: [] },
103
+ isOptional: false,
104
+ isRest: false,
105
+ passing: "value",
106
+ },
107
+ {
108
+ kind: "parameter",
109
+ pattern: { kind: "identifierPattern", name: "second" },
110
+ type: { kind: "referenceType", name: "U", typeArguments: [] },
111
+ isOptional: false,
112
+ isRest: false,
113
+ passing: "value",
114
+ },
115
+ ],
116
+ returnType: {
117
+ kind: "arrayType",
118
+ elementType: {
119
+ kind: "unionType",
120
+ types: [
121
+ { kind: "referenceType", name: "T", typeArguments: [] },
122
+ { kind: "referenceType", name: "U", typeArguments: [] },
123
+ ],
124
+ },
125
+ },
126
+ body: {
127
+ kind: "blockStatement",
128
+ statements: [],
129
+ },
130
+ isExported: true,
131
+ isAsync: false,
132
+ isGenerator: false,
133
+ },
134
+ ],
135
+ exports: [],
136
+ };
137
+
138
+ const result = emitModule(module);
139
+
140
+ expect(result).to.include("<T, U>");
141
+ expect(result).to.include("pair");
142
+ expect(result).to.include("T first");
143
+ expect(result).to.include("U second");
144
+ });
145
+
146
+ it("should emit generic function with nominal constraint", () => {
147
+ const module: IrModule = {
148
+ kind: "module",
149
+ filePath: "/src/utils.ts",
150
+ namespace: "MyApp",
151
+ className: "utils",
152
+ isStaticContainer: true,
153
+ imports: [],
154
+ body: [
155
+ {
156
+ kind: "functionDeclaration",
157
+ name: "process",
158
+ typeParameters: [
159
+ {
160
+ kind: "typeParameter",
161
+ name: "T",
162
+ constraint: {
163
+ kind: "referenceType",
164
+ name: "Comparable",
165
+ typeArguments: [],
166
+ },
167
+ default: undefined,
168
+ variance: undefined,
169
+ isStructuralConstraint: false,
170
+ },
171
+ ],
172
+ parameters: [
173
+ {
174
+ kind: "parameter",
175
+ pattern: { kind: "identifierPattern", name: "item" },
176
+ type: { kind: "referenceType", name: "T", typeArguments: [] },
177
+ isOptional: false,
178
+ isRest: false,
179
+ passing: "value",
180
+ },
181
+ ],
182
+ returnType: { kind: "voidType" },
183
+ body: {
184
+ kind: "blockStatement",
185
+ statements: [],
186
+ },
187
+ isExported: true,
188
+ isAsync: false,
189
+ isGenerator: false,
190
+ },
191
+ ],
192
+ exports: [],
193
+ };
194
+
195
+ const result = emitModule(module);
196
+
197
+ expect(result).to.include("<T>");
198
+ expect(result).to.include("where T : Comparable");
199
+ });
200
+
201
+ it("should emit generic function with structural constraint and adapter", () => {
202
+ const module: IrModule = {
203
+ kind: "module",
204
+ filePath: "/src/utils.ts",
205
+ namespace: "MyApp",
206
+ className: "utils",
207
+ isStaticContainer: true,
208
+ imports: [],
209
+ body: [
210
+ {
211
+ kind: "functionDeclaration",
212
+ name: "getId",
213
+ typeParameters: [
214
+ {
215
+ kind: "typeParameter",
216
+ name: "T",
217
+ constraint: {
218
+ kind: "objectType",
219
+ members: [
220
+ {
221
+ kind: "propertySignature",
222
+ name: "id",
223
+ type: { kind: "primitiveType", name: "number" },
224
+ isOptional: false,
225
+ isReadonly: false,
226
+ },
227
+ ],
228
+ },
229
+ default: undefined,
230
+ variance: undefined,
231
+ isStructuralConstraint: true,
232
+ structuralMembers: [
233
+ {
234
+ kind: "propertySignature",
235
+ name: "id",
236
+ type: { kind: "primitiveType", name: "number" },
237
+ isOptional: false,
238
+ isReadonly: false,
239
+ },
240
+ ],
241
+ },
242
+ ],
243
+ parameters: [
244
+ {
245
+ kind: "parameter",
246
+ pattern: { kind: "identifierPattern", name: "obj" },
247
+ type: { kind: "referenceType", name: "T", typeArguments: [] },
248
+ isOptional: false,
249
+ isRest: false,
250
+ passing: "value",
251
+ },
252
+ ],
253
+ returnType: { kind: "primitiveType", name: "number" },
254
+ body: {
255
+ kind: "blockStatement",
256
+ statements: [
257
+ {
258
+ kind: "returnStatement",
259
+ expression: {
260
+ kind: "memberAccess",
261
+ object: { kind: "identifier", name: "obj" },
262
+ property: "id",
263
+ isComputed: false,
264
+ isOptional: false,
265
+ },
266
+ },
267
+ ],
268
+ },
269
+ isExported: true,
270
+ isAsync: false,
271
+ isGenerator: false,
272
+ },
273
+ ],
274
+ exports: [],
275
+ };
276
+
277
+ const result = emitModule(module);
278
+
279
+ // Should generate adapter interface
280
+ expect(result).to.include("public interface __Constraint_T");
281
+ expect(result).to.include("double id { get; }");
282
+
283
+ // Should generate adapter wrapper class
284
+ expect(result).to.include(
285
+ "public sealed class __Wrapper_T : __Constraint_T"
286
+ );
287
+ expect(result).to.include("public double id { get; set; }");
288
+
289
+ // Function should reference the constraint
290
+ expect(result).to.include("where T : __Constraint_T");
291
+ });
292
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Helper functions for specialization (key generation, serialization)
3
+ */
4
+
5
+ import { IrType } from "@tsonic/frontend";
6
+
7
+ /**
8
+ * Create a unique key for a specialization request
9
+ */
10
+ export const createSpecializationKey = (
11
+ name: string,
12
+ typeArgs: readonly IrType[]
13
+ ): string => {
14
+ // Simple serialization of type arguments for deduplication
15
+ const typeStrs = typeArgs.map((t) => serializeType(t));
16
+ return `${name}<${typeStrs.join(",")}>`;
17
+ };
18
+
19
+ /**
20
+ * Simple type serialization for deduplication
21
+ */
22
+ export const serializeType = (type: IrType): string => {
23
+ switch (type.kind) {
24
+ case "primitiveType":
25
+ return type.name;
26
+ case "referenceType":
27
+ if (type.typeArguments && type.typeArguments.length > 0) {
28
+ const args = type.typeArguments.map(serializeType).join(",");
29
+ return `${type.name}<${args}>`;
30
+ }
31
+ return type.name;
32
+ case "arrayType":
33
+ return `${serializeType(type.elementType)}[]`;
34
+ case "literalType":
35
+ return `literal:${type.value}`;
36
+ default:
37
+ return type.kind;
38
+ }
39
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Specialization system barrel exports
3
+ */
4
+
5
+ // Types
6
+ export type { SpecializationRequest } from "./types.js";
7
+
8
+ // Collection
9
+ export { collectSpecializations } from "./collection.js";
10
+
11
+ // Generation
12
+ export { generateSpecializations } from "./generation.js";
13
+
14
+ // Naming
15
+ export {
16
+ generateSpecializedFunctionName,
17
+ generateSpecializedClassName,
18
+ } from "./naming.js";
19
+
20
+ // Helpers
21
+ export { createSpecializationKey, serializeType } from "./helpers.js";
22
+
23
+ // Substitution
24
+ export {
25
+ substituteType,
26
+ substituteStatement,
27
+ substituteExpression,
28
+ } from "./substitution.js";
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Tests for Interfaces
3
+ * Covers spec/16-types-and-interfaces.md §2 - Interfaces
4
+ */
5
+
6
+ import { describe, it } from "mocha";
7
+ import { expect } from "chai";
8
+ import { emitModule } from "../emitter.js";
9
+ import { IrModule } from "@tsonic/frontend";
10
+
11
+ describe("Interfaces (spec/16 §2)", () => {
12
+ it("should emit interface as C# class with auto-properties", () => {
13
+ const module: IrModule = {
14
+ kind: "module",
15
+ filePath: "/src/User.ts",
16
+ namespace: "MyApp",
17
+ className: "User",
18
+ isStaticContainer: false,
19
+ imports: [],
20
+ body: [
21
+ {
22
+ kind: "interfaceDeclaration",
23
+ name: "User",
24
+ typeParameters: undefined,
25
+ extends: [],
26
+ isStruct: false,
27
+ members: [
28
+ {
29
+ kind: "propertySignature",
30
+ name: "id",
31
+ type: { kind: "primitiveType", name: "number" },
32
+ isOptional: false,
33
+ isReadonly: false,
34
+ },
35
+ {
36
+ kind: "propertySignature",
37
+ name: "name",
38
+ type: { kind: "primitiveType", name: "string" },
39
+ isOptional: false,
40
+ isReadonly: false,
41
+ },
42
+ {
43
+ kind: "propertySignature",
44
+ name: "active",
45
+ type: { kind: "primitiveType", name: "boolean" },
46
+ isOptional: true,
47
+ isReadonly: false,
48
+ },
49
+ ],
50
+ isExported: true,
51
+ },
52
+ ],
53
+ exports: [],
54
+ };
55
+
56
+ const result = emitModule(module);
57
+
58
+ // Should emit as C# class, not interface
59
+ expect(result).to.include("public class User");
60
+ expect(result).not.to.include("interface User");
61
+
62
+ // Should have auto-properties
63
+ expect(result).to.include("public double id { get; set; }");
64
+ expect(result).to.include("public string name { get; set; }");
65
+
66
+ // Optional property should be nullable
67
+ expect(result).to.include("public bool? active { get; set; }");
68
+ });
69
+
70
+ it("should emit interface with readonly members", () => {
71
+ const module: IrModule = {
72
+ kind: "module",
73
+ filePath: "/src/Config.ts",
74
+ namespace: "MyApp",
75
+ className: "Config",
76
+ isStaticContainer: false,
77
+ imports: [],
78
+ body: [
79
+ {
80
+ kind: "interfaceDeclaration",
81
+ name: "Config",
82
+ typeParameters: undefined,
83
+ extends: [],
84
+ isStruct: false,
85
+ members: [
86
+ {
87
+ kind: "propertySignature",
88
+ name: "apiUrl",
89
+ type: { kind: "primitiveType", name: "string" },
90
+ isOptional: false,
91
+ isReadonly: true,
92
+ },
93
+ ],
94
+ isExported: true,
95
+ },
96
+ ],
97
+ exports: [],
98
+ };
99
+
100
+ const result = emitModule(module);
101
+
102
+ // Readonly should use private set
103
+ expect(result).to.include("public string apiUrl { get; }");
104
+ });
105
+
106
+ it("should emit generic interface", () => {
107
+ const module: IrModule = {
108
+ kind: "module",
109
+ filePath: "/src/Result.ts",
110
+ namespace: "MyApp",
111
+ className: "Result",
112
+ isStaticContainer: true, // Changed to true to emit at top level
113
+ imports: [],
114
+ body: [
115
+ {
116
+ kind: "interfaceDeclaration",
117
+ name: "Result",
118
+ typeParameters: [
119
+ {
120
+ kind: "typeParameter",
121
+ name: "T",
122
+ constraint: undefined,
123
+ default: undefined,
124
+ variance: undefined,
125
+ isStructuralConstraint: false,
126
+ },
127
+ ],
128
+ extends: [],
129
+ isStruct: false,
130
+ members: [
131
+ {
132
+ kind: "propertySignature",
133
+ name: "data",
134
+ type: { kind: "referenceType", name: "T", typeArguments: [] },
135
+ isOptional: false,
136
+ isReadonly: false,
137
+ },
138
+ ],
139
+ isExported: true,
140
+ },
141
+ ],
142
+ exports: [],
143
+ };
144
+
145
+ const result = emitModule(module);
146
+
147
+ // Allow for whitespace variations
148
+ expect(result).to.match(/public\s+class\s+Result\s*<T>/);
149
+ expect(result).to.include("public T data { get; set; }");
150
+ });
151
+ });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Name generation for specialized declarations
3
+ */
4
+
5
+ import { IrType } from "@tsonic/frontend";
6
+ import { serializeType } from "./helpers.js";
7
+
8
+ /**
9
+ * Generate specialized function name
10
+ */
11
+ export const generateSpecializedFunctionName = (
12
+ baseName: string,
13
+ typeArgs: readonly IrType[]
14
+ ): string => {
15
+ const typeNames = typeArgs.map((t) => {
16
+ const serialized = serializeType(t);
17
+ return serialized.replace(/[<>?,\s]/g, "_").replace(/\./g, "_");
18
+ });
19
+ return `${baseName}__${typeNames.join("__")}`;
20
+ };
21
+
22
+ /**
23
+ * Generate specialized class name
24
+ */
25
+ export const generateSpecializedClassName = (
26
+ baseName: string,
27
+ typeArgs: readonly IrType[]
28
+ ): string => {
29
+ const typeNames = typeArgs.map((t) => {
30
+ const serialized = serializeType(t);
31
+ return serialized.replace(/[<>?,\s]/g, "_").replace(/\./g, "_");
32
+ });
33
+ return `${baseName}__${typeNames.join("__")}`;
34
+ };