@typra/emitter 0.2.0

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 (82) hide show
  1. package/dist/src/cleanup/generated-file.d.ts +6 -0
  2. package/dist/src/cleanup/generated-file.js +61 -0
  3. package/dist/src/cli.d.ts +2 -0
  4. package/dist/src/cli.js +110 -0
  5. package/dist/src/decorators.d.ts +56 -0
  6. package/dist/src/decorators.js +177 -0
  7. package/dist/src/emitter.d.ts +13 -0
  8. package/dist/src/emitter.js +137 -0
  9. package/dist/src/generate.d.ts +86 -0
  10. package/dist/src/generate.js +104 -0
  11. package/dist/src/index.d.ts +4 -0
  12. package/dist/src/index.js +5 -0
  13. package/dist/src/ir/ast.d.ts +235 -0
  14. package/dist/src/ir/ast.js +589 -0
  15. package/dist/src/ir/declarations.d.ts +364 -0
  16. package/dist/src/ir/declarations.js +23 -0
  17. package/dist/src/ir/expansion.d.ts +140 -0
  18. package/dist/src/ir/expansion.js +407 -0
  19. package/dist/src/ir/lower.d.ts +53 -0
  20. package/dist/src/ir/lower.js +480 -0
  21. package/dist/src/ir/utilities.d.ts +12 -0
  22. package/dist/src/ir/utilities.js +39 -0
  23. package/dist/src/ir/visitor.d.ts +29 -0
  24. package/dist/src/ir/visitor.js +48 -0
  25. package/dist/src/languages/csharp/driver.d.ts +5 -0
  26. package/dist/src/languages/csharp/driver.js +315 -0
  27. package/dist/src/languages/csharp/emitter.d.ts +33 -0
  28. package/dist/src/languages/csharp/emitter.js +1140 -0
  29. package/dist/src/languages/csharp/scaffolding.d.ts +18 -0
  30. package/dist/src/languages/csharp/scaffolding.js +591 -0
  31. package/dist/src/languages/csharp/test-emitter.d.ts +43 -0
  32. package/dist/src/languages/csharp/test-emitter.js +274 -0
  33. package/dist/src/languages/csharp/visitor.d.ts +14 -0
  34. package/dist/src/languages/csharp/visitor.js +79 -0
  35. package/dist/src/languages/go/driver.d.ts +12 -0
  36. package/dist/src/languages/go/driver.js +128 -0
  37. package/dist/src/languages/go/emitter.d.ts +33 -0
  38. package/dist/src/languages/go/emitter.js +879 -0
  39. package/dist/src/languages/go/scaffolding.d.ts +18 -0
  40. package/dist/src/languages/go/scaffolding.js +53 -0
  41. package/dist/src/languages/go/test-emitter.d.ts +20 -0
  42. package/dist/src/languages/go/test-emitter.js +300 -0
  43. package/dist/src/languages/go/visitor.d.ts +14 -0
  44. package/dist/src/languages/go/visitor.js +78 -0
  45. package/dist/src/languages/markdown/driver.d.ts +19 -0
  46. package/dist/src/languages/markdown/driver.js +408 -0
  47. package/dist/src/languages/python/driver.d.ts +14 -0
  48. package/dist/src/languages/python/driver.js +372 -0
  49. package/dist/src/languages/python/emitter.d.ts +31 -0
  50. package/dist/src/languages/python/emitter.js +856 -0
  51. package/dist/src/languages/python/scaffolding.d.ts +33 -0
  52. package/dist/src/languages/python/scaffolding.js +279 -0
  53. package/dist/src/languages/python/test-emitter.d.ts +29 -0
  54. package/dist/src/languages/python/test-emitter.js +388 -0
  55. package/dist/src/languages/python/visitor.d.ts +14 -0
  56. package/dist/src/languages/python/visitor.js +65 -0
  57. package/dist/src/languages/rust/driver.d.ts +13 -0
  58. package/dist/src/languages/rust/driver.js +624 -0
  59. package/dist/src/languages/rust/emitter.d.ts +45 -0
  60. package/dist/src/languages/rust/emitter.js +1596 -0
  61. package/dist/src/languages/rust/visitor.d.ts +25 -0
  62. package/dist/src/languages/rust/visitor.js +153 -0
  63. package/dist/src/languages/typescript/driver.d.ts +8 -0
  64. package/dist/src/languages/typescript/driver.js +209 -0
  65. package/dist/src/languages/typescript/emitter.d.ts +42 -0
  66. package/dist/src/languages/typescript/emitter.js +904 -0
  67. package/dist/src/languages/typescript/scaffolding.d.ts +32 -0
  68. package/dist/src/languages/typescript/scaffolding.js +303 -0
  69. package/dist/src/languages/typescript/test-emitter.d.ts +23 -0
  70. package/dist/src/languages/typescript/test-emitter.js +204 -0
  71. package/dist/src/languages/typescript/visitor.d.ts +14 -0
  72. package/dist/src/languages/typescript/visitor.js +64 -0
  73. package/dist/src/lib.d.ts +33 -0
  74. package/dist/src/lib.js +101 -0
  75. package/dist/src/testing/index.d.ts +2 -0
  76. package/dist/src/testing/index.js +8 -0
  77. package/dist/src/testing/test-context.d.ts +63 -0
  78. package/dist/src/testing/test-context.js +355 -0
  79. package/fixtures/shapes/main.tsp +43 -0
  80. package/fixtures/tspconfig.yaml +13 -0
  81. package/package.json +76 -0
  82. package/src/lib/main.tsp +110 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Go scaffolding emitter — generates the LoadContext / SaveContext file.
3
+ *
4
+ * Replaces `context.go.njk` Nunjucks template with a typed TypeScript
5
+ * function that produces identical Go source output.
6
+ */
7
+ interface GoContextContext {
8
+ header: string;
9
+ packageName: string;
10
+ }
11
+ /**
12
+ * Emit the Go context file containing LoadContext and SaveContext structs.
13
+ *
14
+ * @param ctx - header string and package name
15
+ * @returns Complete Go source file as a string
16
+ */
17
+ export declare function emitGoContext(ctx: GoContextContext): string;
18
+ export {};
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Go scaffolding emitter — generates the LoadContext / SaveContext file.
3
+ *
4
+ * Replaces `context.go.njk` Nunjucks template with a typed TypeScript
5
+ * function that produces identical Go source output.
6
+ */
7
+ // ============================================================================
8
+ // Main entry point
9
+ // ============================================================================
10
+ /**
11
+ * Emit the Go context file containing LoadContext and SaveContext structs.
12
+ *
13
+ * @param ctx - header string and package name
14
+ * @returns Complete Go source file as a string
15
+ */
16
+ export function emitGoContext(ctx) {
17
+ const lines = [];
18
+ lines.push("// Code generated by Typra emitter; DO NOT EDIT.");
19
+ lines.push(`// ${ctx.header}`);
20
+ lines.push("");
21
+ lines.push(`package ${ctx.packageName}`);
22
+ lines.push("");
23
+ lines.push("// LoadContext provides context for loading operations");
24
+ lines.push("type LoadContext struct {");
25
+ lines.push("\t// Add any context fields needed for loading");
26
+ lines.push("\t// e.g., file paths, base directories, etc.");
27
+ lines.push("}");
28
+ lines.push("");
29
+ lines.push("// NewLoadContext creates a new LoadContext");
30
+ lines.push("func NewLoadContext() *LoadContext {");
31
+ lines.push("\treturn &LoadContext{}");
32
+ lines.push("}");
33
+ lines.push("");
34
+ lines.push("// SaveContext provides context for saving operations");
35
+ lines.push("type SaveContext struct {");
36
+ lines.push("\t// Add any context fields needed for saving");
37
+ lines.push("\t// e.g., output directories, formatting options, etc.");
38
+ lines.push("}");
39
+ lines.push("");
40
+ lines.push("// NewSaveContext creates a new SaveContext");
41
+ lines.push("func NewSaveContext() *SaveContext {");
42
+ lines.push("\treturn &SaveContext{}");
43
+ lines.push("}");
44
+ lines.push("");
45
+ lines.push("// ptrOf returns a pointer to the given value. Used by factory functions");
46
+ lines.push("// to set optional (pointer) fields in struct literals.");
47
+ lines.push("func ptrOf[T any](v T) *T {");
48
+ lines.push("\treturn &v");
49
+ lines.push("}");
50
+ lines.push("");
51
+ return lines.join("\n");
52
+ }
53
+ //# sourceMappingURL=scaffolding.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Go test emitter — BaseTestContext → Go test source code.
3
+ *
4
+ * Replaces `test.go.njk` and `_macros.njk` Nunjucks templates with
5
+ * typed TypeScript functions that produce identical Go test output.
6
+ *
7
+ * Each test file covers one type and contains:
8
+ * - Per-example: LoadJSON, LoadYAML, Roundtrip, ToJSON, ToYAML tests
9
+ * - Per-coercion: From<Title> tests for scalar-to-object expansion
10
+ */
11
+ import { BaseTestContext } from "../../ir/ast.js";
12
+ /**
13
+ * Emit a complete Go test file for a single type.
14
+ *
15
+ * @param ctx - test context built by `buildBaseTestContext()`
16
+ * @returns Complete Go test source file as a string
17
+ */
18
+ export declare function emitGoTest(ctx: BaseTestContext & {
19
+ importPath: string;
20
+ }): string;
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Go test emitter — BaseTestContext → Go test source code.
3
+ *
4
+ * Replaces `test.go.njk` and `_macros.njk` Nunjucks templates with
5
+ * typed TypeScript functions that produce identical Go test output.
6
+ *
7
+ * Each test file covers one type and contains:
8
+ * - Per-example: LoadJSON, LoadYAML, Roundtrip, ToJSON, ToYAML tests
9
+ * - Per-coercion: From<Title> tests for scalar-to-object expansion
10
+ */
11
+ // ============================================================================
12
+ // Helpers
13
+ // ============================================================================
14
+ /** Nunjucks `capitalize` filter: upper-case first char, lower-case rest. */
15
+ function capitalize(s) {
16
+ if (!s)
17
+ return s;
18
+ return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
19
+ }
20
+ /**
21
+ * Emit a validation assertion for a property.
22
+ *
23
+ * @param varName - "instance" or "reloaded"
24
+ * @param v - property validation descriptor
25
+ */
26
+ function emitValidation(lines, varName, v) {
27
+ // Determine the display value for the error message — includes quotes if delimiter is "
28
+ const displayQuote = v.delimiter === '"' ? '"' : '';
29
+ const display = `${displayQuote}${v.value}${displayQuote}`;
30
+ if (v.isOptional) {
31
+ lines.push(`if ${varName}.${v.key} == nil || *${varName}.${v.key} != ${v.delimiter}${v.value}${v.delimiter} {`);
32
+ lines.push(`t.Errorf(\`Expected ${v.key} to be ${display}, got %v\`, ${varName}.${v.key})`);
33
+ lines.push(`}`);
34
+ }
35
+ else {
36
+ lines.push(`if ${varName}.${v.key} != ${v.delimiter}${v.value}${v.delimiter} {`);
37
+ lines.push(`t.Errorf(\`Expected ${v.key} to be ${display}, got %v\`, ${varName}.${v.key})`);
38
+ lines.push(`}`);
39
+ }
40
+ }
41
+ // ============================================================================
42
+ // Main entry point
43
+ // ============================================================================
44
+ /**
45
+ * Emit a complete Go test file for a single type.
46
+ *
47
+ * @param ctx - test context built by `buildBaseTestContext()`
48
+ * @returns Complete Go test source file as a string
49
+ */
50
+ export function emitGoTest(ctx) {
51
+ const lines = [];
52
+ const typeName = ctx.node.typeName.name;
53
+ const pkg = ctx.package ?? "";
54
+ const isAbstract = ctx.isAbstract;
55
+ // File header (template lines 14-16)
56
+ lines.push("// Code generated by Typra emitter; DO NOT EDIT.");
57
+ lines.push("");
58
+ lines.push(`package ${pkg}_test`);
59
+ // Import block (template lines 17-26)
60
+ if (ctx.examples.length > 0 || ctx.coercions.length > 0) {
61
+ lines.push(""); // {% if true %}\n — blank line between package and import
62
+ lines.push(`import (`);
63
+ lines.push(`"encoding/json"`);
64
+ lines.push(`"testing"`);
65
+ lines.push(``);
66
+ lines.push(`"gopkg.in/yaml.v3"`);
67
+ lines.push(``);
68
+ lines.push(`"${ctx.importPath}"`);
69
+ lines.push(`)`);
70
+ }
71
+ lines.push(""); // {% endif %}\n (line 26)
72
+ lines.push(""); // blank line 27
73
+ // Per-example test functions (template lines 28-234)
74
+ for (let i = 0; i < ctx.examples.length; i++) {
75
+ const sample = ctx.examples[i];
76
+ const isFirst = i === 0;
77
+ const suffix = isFirst ? "" : String(i);
78
+ lines.push(""); // {% for %}\n — loop body starts with \n
79
+ emitLoadJSONTest(lines, typeName, pkg, suffix, sample, isAbstract);
80
+ lines.push("");
81
+ emitLoadYAMLTest(lines, typeName, pkg, suffix, sample, isAbstract);
82
+ lines.push("");
83
+ emitRoundtripTest(lines, typeName, pkg, suffix, sample, isAbstract);
84
+ lines.push("");
85
+ emitToJSONTest(lines, typeName, pkg, suffix, sample, isAbstract);
86
+ lines.push("");
87
+ emitToYAMLTest(lines, typeName, pkg, suffix, sample, isAbstract);
88
+ lines.push(""); // blank line 233
89
+ }
90
+ lines.push(""); // {% endfor %}\n (line 234)
91
+ // Coercion test functions (template lines 235-271)
92
+ if (ctx.coercions.length > 0) {
93
+ lines.push(""); // {% if true %}\n — if body starts with \n
94
+ for (let i = 0; i < ctx.coercions.length; i++) {
95
+ const alt = ctx.coercions[i];
96
+ const isFirst = i === 0;
97
+ const suffix = isFirst ? "" : String(i + 1);
98
+ lines.push(""); // {% for %}\n — loop body starts with \n
99
+ emitCoercionTest(lines, typeName, pkg, suffix, alt, isAbstract);
100
+ lines.push(""); // blank line 269
101
+ }
102
+ lines.push(""); // {% endfor %}\n (line 270)
103
+ lines.push(""); // {% endif %}\n (line 271)
104
+ }
105
+ else {
106
+ lines.push(""); // {% if false %}...{% endif %}\n — 1 \n
107
+ }
108
+ return lines.join("\n") + "\n";
109
+ }
110
+ // ============================================================================
111
+ // Per-example test emitters
112
+ // ============================================================================
113
+ function emitJsonDataBlock(lines, sample) {
114
+ lines.push("jsonData := `");
115
+ for (const line of sample.json) {
116
+ lines.push(line);
117
+ }
118
+ lines.push("`");
119
+ }
120
+ function emitYamlDataBlock(lines, sample) {
121
+ lines.push("yamlData := `");
122
+ for (const line of sample.yaml) {
123
+ lines.push(line);
124
+ }
125
+ lines.push("`");
126
+ }
127
+ function emitJsonUnmarshal(lines, varName = "data") {
128
+ lines.push(`var ${varName} map[string]interface{}`);
129
+ lines.push(`if err := json.Unmarshal([]byte(jsonData), &${varName}); err != nil {`);
130
+ lines.push(`t.Fatalf("Failed to parse JSON: %v", err)`);
131
+ lines.push(`}`);
132
+ }
133
+ function emitYamlUnmarshal(lines, varName = "data") {
134
+ lines.push(`var ${varName} map[string]interface{}`);
135
+ lines.push(`if err := yaml.Unmarshal([]byte(yamlData), &${varName}); err != nil {`);
136
+ lines.push(`t.Fatalf("Failed to parse YAML: %v", err)`);
137
+ lines.push(`}`);
138
+ }
139
+ function emitLoadCall(lines, typeName, pkg, ctxVar, dataVar, instanceVar) {
140
+ lines.push(`${ctxVar} := ${pkg}.NewLoadContext()`);
141
+ lines.push(`${instanceVar}, err := ${pkg}.Load${typeName}(${dataVar}, ${ctxVar})`);
142
+ lines.push(`if err != nil {`);
143
+ lines.push(`t.Fatalf("Failed to load ${typeName}: %v", err)`);
144
+ lines.push(`}`);
145
+ }
146
+ function emitAbstractExampleValidations(lines, hasValidations) {
147
+ lines.push("// Polymorphic types return interface{}, extract common fields via reflection or type-specific access");
148
+ lines.push("_ = instance // Load succeeded, exact type depends on discriminator");
149
+ if (hasValidations) {
150
+ lines.push("// Note: Validation skipped for polymorphic base types - test child types directly");
151
+ }
152
+ }
153
+ function emitConcreteExampleValidations(lines, varName, validations) {
154
+ if (validations.length > 0) {
155
+ for (const v of validations) {
156
+ emitValidation(lines, varName, v);
157
+ }
158
+ }
159
+ else {
160
+ lines.push(`_ = ${varName} // No scalar properties to validate`);
161
+ }
162
+ }
163
+ // ---- LoadJSON ----
164
+ function emitLoadJSONTest(lines, typeName, pkg, suffix, sample, isAbstract) {
165
+ lines.push(`// Test${typeName}LoadJSON${suffix} tests loading ${typeName} from JSON`);
166
+ lines.push(`func Test${typeName}LoadJSON${suffix}(t *testing.T) {`);
167
+ emitJsonDataBlock(lines, sample);
168
+ emitJsonUnmarshal(lines);
169
+ lines.push("");
170
+ emitLoadCall(lines, typeName, pkg, "ctx", "data", "instance");
171
+ if (isAbstract) {
172
+ emitAbstractExampleValidations(lines, sample.validations.length > 0);
173
+ }
174
+ else {
175
+ emitConcreteExampleValidations(lines, "instance", sample.validations);
176
+ }
177
+ lines.push("}");
178
+ }
179
+ // ---- LoadYAML ----
180
+ function emitLoadYAMLTest(lines, typeName, pkg, suffix, sample, isAbstract) {
181
+ lines.push(`// Test${typeName}LoadYAML${suffix} tests loading ${typeName} from YAML`);
182
+ lines.push(`func Test${typeName}LoadYAML${suffix}(t *testing.T) {`);
183
+ emitYamlDataBlock(lines, sample);
184
+ emitYamlUnmarshal(lines);
185
+ lines.push("");
186
+ emitLoadCall(lines, typeName, pkg, "ctx", "data", "instance");
187
+ if (isAbstract) {
188
+ emitAbstractExampleValidations(lines, sample.validations.length > 0);
189
+ }
190
+ else {
191
+ emitConcreteExampleValidations(lines, "instance", sample.validations);
192
+ }
193
+ lines.push("}");
194
+ }
195
+ // ---- Roundtrip ----
196
+ function emitRoundtripTest(lines, typeName, pkg, suffix, sample, isAbstract) {
197
+ lines.push(`// Test${typeName}Roundtrip${suffix} tests load -> save -> load produces equivalent data`);
198
+ lines.push(`func Test${typeName}Roundtrip${suffix}(t *testing.T) {`);
199
+ emitJsonDataBlock(lines, sample);
200
+ emitJsonUnmarshal(lines);
201
+ lines.push("");
202
+ lines.push(`loadCtx := ${pkg}.NewLoadContext()`);
203
+ lines.push(`instance, err := ${pkg}.Load${typeName}(data, loadCtx)`);
204
+ lines.push(`if err != nil {`);
205
+ lines.push(`t.Fatalf("Failed to load ${typeName}: %v", err)`);
206
+ lines.push(`}`);
207
+ if (isAbstract) {
208
+ lines.push("// Polymorphic roundtrip testing requires type-specific handling");
209
+ lines.push("_ = instance // Load succeeded, exact type depends on discriminator");
210
+ lines.push("// Note: Roundtrip test skipped for polymorphic base types - test child types directly");
211
+ }
212
+ else {
213
+ lines.push(`saveCtx := ${pkg}.NewSaveContext()`);
214
+ lines.push(`savedData := instance.Save(saveCtx)`);
215
+ lines.push("");
216
+ lines.push(`reloaded, err := ${pkg}.Load${typeName}(savedData, loadCtx)`);
217
+ lines.push(`if err != nil {`);
218
+ lines.push(`t.Fatalf("Failed to reload ${typeName}: %v", err)`);
219
+ lines.push(`}`);
220
+ emitConcreteExampleValidations(lines, "reloaded", sample.validations);
221
+ }
222
+ lines.push("}");
223
+ }
224
+ // ---- ToJSON ----
225
+ function emitToJSONTest(lines, typeName, pkg, suffix, sample, isAbstract) {
226
+ lines.push(`// Test${typeName}ToJSON${suffix} tests that ToJSON produces valid JSON`);
227
+ lines.push(`func Test${typeName}ToJSON${suffix}(t *testing.T) {`);
228
+ emitJsonDataBlock(lines, sample);
229
+ emitJsonUnmarshal(lines);
230
+ lines.push("");
231
+ emitLoadCall(lines, typeName, pkg, "ctx", "data", "instance");
232
+ if (isAbstract) {
233
+ lines.push("// Polymorphic ToJSON requires type-specific handling");
234
+ lines.push("_ = instance // Load succeeded, exact type depends on discriminator");
235
+ lines.push("// Note: ToJSON test skipped for polymorphic base types - test child types directly");
236
+ }
237
+ else {
238
+ lines.push("jsonOutput, err := instance.ToJSON()");
239
+ lines.push("if err != nil {");
240
+ lines.push(`t.Fatalf("Failed to convert to JSON: %v", err)`);
241
+ lines.push("}");
242
+ lines.push("");
243
+ lines.push("var parsed map[string]interface{}");
244
+ lines.push("if err := json.Unmarshal([]byte(jsonOutput), &parsed); err != nil {");
245
+ lines.push(`t.Fatalf("Failed to parse generated JSON: %v", err)`);
246
+ lines.push("}");
247
+ }
248
+ lines.push("}");
249
+ }
250
+ // ---- ToYAML ----
251
+ function emitToYAMLTest(lines, typeName, pkg, suffix, sample, isAbstract) {
252
+ lines.push(`// Test${typeName}ToYAML${suffix} tests that ToYAML produces valid YAML`);
253
+ lines.push(`func Test${typeName}ToYAML${suffix}(t *testing.T) {`);
254
+ emitJsonDataBlock(lines, sample);
255
+ emitJsonUnmarshal(lines);
256
+ lines.push("");
257
+ emitLoadCall(lines, typeName, pkg, "ctx", "data", "instance");
258
+ if (isAbstract) {
259
+ lines.push("// Polymorphic ToYAML requires type-specific handling");
260
+ lines.push("_ = instance // Load succeeded, exact type depends on discriminator");
261
+ lines.push("// Note: ToYAML test skipped for polymorphic base types - test child types directly");
262
+ }
263
+ else {
264
+ lines.push("yamlOutput, err := instance.ToYAML()");
265
+ lines.push("if err != nil {");
266
+ lines.push(`t.Fatalf("Failed to convert to YAML: %v", err)`);
267
+ lines.push("}");
268
+ lines.push("");
269
+ lines.push("var parsed map[string]interface{}");
270
+ lines.push("if err := yaml.Unmarshal([]byte(yamlOutput), &parsed); err != nil {");
271
+ lines.push(`t.Fatalf("Failed to parse generated YAML: %v", err)`);
272
+ lines.push("}");
273
+ }
274
+ lines.push("}");
275
+ }
276
+ // ============================================================================
277
+ // Coercion test emitter
278
+ // ============================================================================
279
+ function emitCoercionTest(lines, typeName, pkg, suffix, alt, isAbstract) {
280
+ const title = capitalize(alt.title);
281
+ lines.push(`// Test${typeName}From${title}${suffix} tests loading ${typeName} from ${alt.scalarType}`);
282
+ lines.push(`func Test${typeName}From${title}${suffix}(t *testing.T) {`);
283
+ lines.push(`ctx := ${pkg}.NewLoadContext()`);
284
+ lines.push(`instance, err := ${pkg}.Load${typeName}(${alt.value}, ctx)`);
285
+ lines.push(`if err != nil {`);
286
+ lines.push(`t.Fatalf("Failed to load ${typeName} from ${alt.scalarType}: %v", err)`);
287
+ lines.push(`}`);
288
+ if (isAbstract) {
289
+ lines.push("// Polymorphic alternate loading requires type-specific handling");
290
+ lines.push("_ = instance // Load succeeded, exact type depends on discriminator");
291
+ if (alt.validations.length > 0) {
292
+ lines.push("// Note: Validation skipped for polymorphic base types - test child types directly");
293
+ }
294
+ }
295
+ else {
296
+ emitConcreteExampleValidations(lines, "instance", alt.validations);
297
+ }
298
+ lines.push("}");
299
+ }
300
+ //# sourceMappingURL=test-emitter.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Go expression visitor — Expr IR → Go source fragments.
3
+ */
4
+ import { Expr, TypeRegistry } from "../../ir/expansion.js";
5
+ import { ExprVisitor } from "../../ir/visitor.js";
6
+ export declare class GoExprVisitor implements ExprVisitor {
7
+ registry?: TypeRegistry;
8
+ constructor(registry?: TypeRegistry);
9
+ visitExpr(expr: Expr): string;
10
+ private visitConstruct;
11
+ private visitVariant;
12
+ private visitArray;
13
+ private escapeString;
14
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Go expression visitor — Expr IR → Go source fragments.
3
+ */
4
+ import { toPascalCase, assertNever } from "../../ir/visitor.js";
5
+ export class GoExprVisitor {
6
+ registry;
7
+ constructor(registry) {
8
+ this.registry = registry;
9
+ }
10
+ visitExpr(expr) {
11
+ switch (expr.kind) {
12
+ case "string":
13
+ return `"${this.escapeString(expr.value)}"`;
14
+ case "number":
15
+ return String(expr.value);
16
+ case "boolean":
17
+ return expr.value ? "true" : "false";
18
+ case "null":
19
+ return "nil";
20
+ case "param":
21
+ return expr.name;
22
+ case "construct":
23
+ return this.visitConstruct(expr);
24
+ case "variant":
25
+ return this.visitVariant(expr);
26
+ case "array":
27
+ return this.visitArray(expr);
28
+ case "dict":
29
+ return `map[string]interface{}{${expr.entries.map(e => `"${e.key}": ${this.visitExpr(e.value)}`).join(", ")}}`;
30
+ case "field_read":
31
+ return `${expr.objectName}.${toPascalCase(expr.fieldName)}`;
32
+ default:
33
+ return assertNever(expr);
34
+ }
35
+ }
36
+ visitConstruct(expr) {
37
+ const typeName = expr.typeName.name;
38
+ if (expr.fields.length === 0) {
39
+ return `${typeName}{}`;
40
+ }
41
+ // Look up the target type to check which fields are optional (pointer types in Go)
42
+ const typeNode = this.registry?.get(typeName);
43
+ const fields = expr.fields.map(f => {
44
+ const val = this.visitExpr(f.value);
45
+ const prop = typeNode?.properties.find(p => p.name === f.propertyName);
46
+ // Optional fields are pointers in Go — wrap scalar values with a helper
47
+ const needsAddr = prop?.isOptional && !prop.isCollection && !prop.isDict;
48
+ return `${toPascalCase(f.propertyName)}: ${needsAddr ? `ptrOf(${val})` : val}`;
49
+ }).join(", ");
50
+ return `${typeName}{ ${fields} }`;
51
+ }
52
+ visitVariant(expr) {
53
+ // Go child types are full structs with an explicit discriminator field.
54
+ const variantName = expr.variantTypeName.name;
55
+ // Include the discriminator (e.g., Kind: "text") since Go has no default field values.
56
+ const discField = `${toPascalCase(expr.discriminator)}: "${expr.discriminatorValue}"`;
57
+ const dataFields = expr.fields.map(f => `${toPascalCase(f.propertyName)}: ${this.visitExpr(f.value)}`);
58
+ const allFields = [discField, ...dataFields].join(", ");
59
+ return `${variantName}{ ${allFields} }`;
60
+ }
61
+ visitArray(expr) {
62
+ const elementType = expr.elementTypeName.name;
63
+ // Polymorphic types are stored as []interface{} in Go (no inheritance).
64
+ // A type is polymorphic if it has child types in the registry.
65
+ const typeNode = this.registry?.get(elementType);
66
+ const isPolymorphic = typeNode !== undefined && typeNode.childTypes.length > 0;
67
+ const goElementType = isPolymorphic ? "interface{}" : elementType;
68
+ if (expr.items.length === 0) {
69
+ return `[]${goElementType}{}`;
70
+ }
71
+ const items = expr.items.map(i => this.visitExpr(i)).join(", ");
72
+ return `[]${goElementType}{${items}}`;
73
+ }
74
+ escapeString(s) {
75
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
76
+ }
77
+ }
78
+ //# sourceMappingURL=visitor.js.map
@@ -0,0 +1,19 @@
1
+ import { EmitContext } from "@typespec/compiler";
2
+ import { EmitTarget, TypraEmitterOptions } from "../../lib.js";
3
+ import { PropertyNode, TypeNode } from "../../ir/ast.js";
4
+ import { GeneratorOptions } from "../../emitter.js";
5
+ export declare const generateMarkdown: (context: EmitContext<TypraEmitterOptions>, node: TypeNode, emitTarget: EmitTarget, options?: GeneratorOptions) => Promise<void>;
6
+ export declare const renderType: (prop: PropertyNode) => string;
7
+ export declare const renderChildTypes: (node: PropertyNode) => string;
8
+ export declare const getChildTypes: (node: TypeNode) => {
9
+ source: string;
10
+ target: string;
11
+ }[];
12
+ export declare const getCompositionTypes: (node: TypeNode) => TypeNode[];
13
+ export declare const generateCoercions: (node: TypeNode) => {
14
+ title: string;
15
+ description: string;
16
+ scalar: string;
17
+ simple: string;
18
+ expanded: string;
19
+ }[];