@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,25 @@
1
+ /**
2
+ * Rust expression visitor — Expr IR → Rust source fragments.
3
+ */
4
+ import { Expr, TypeRegistry } from "../../ir/expansion.js";
5
+ import { ExprVisitor } from "../../ir/visitor.js";
6
+ export declare class RustExprVisitor implements ExprVisitor {
7
+ registry?: TypeRegistry;
8
+ constructor(registry?: TypeRegistry);
9
+ visitExpr(expr: Expr): string;
10
+ private visitConstruct;
11
+ /**
12
+ * Handle Construct on a polymorphic type — the discriminator field becomes an enum variant.
13
+ * E.g., Property { kind: "boolean", example: v } → Property { kind: PropertyKind::Custom { kind_name: "boolean".to_string() }, example: Some(v.into()), ..Default::default() }
14
+ */
15
+ private visitPolymorphicConstruct;
16
+ private visitVariant;
17
+ private visitArray;
18
+ /**
19
+ * Wrap field values appropriately for Rust — Option<T> fields need Some(),
20
+ * string fields need .into(), etc.
21
+ */
22
+ private wrapFieldValue;
23
+ private escapeString;
24
+ private visitFieldRead;
25
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Rust expression visitor — Expr IR → Rust source fragments.
3
+ */
4
+ import { assertNever } from "../../ir/visitor.js";
5
+ import { toSnakeCase } from "../../ir/utilities.js";
6
+ const RUST_KEYWORDS = new Set([
7
+ "as", "break", "const", "continue", "crate", "else", "enum", "extern",
8
+ "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod",
9
+ "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct",
10
+ "super", "trait", "true", "type", "unsafe", "use", "where", "while",
11
+ "async", "await", "dyn",
12
+ ]);
13
+ function rustFieldName(name) {
14
+ const snake = toSnakeCase(name);
15
+ return RUST_KEYWORDS.has(snake) ? `r#${snake}` : snake;
16
+ }
17
+ export class RustExprVisitor {
18
+ registry;
19
+ constructor(registry) {
20
+ this.registry = registry;
21
+ }
22
+ visitExpr(expr) {
23
+ switch (expr.kind) {
24
+ case "string":
25
+ return `"${this.escapeString(expr.value)}".to_string()`;
26
+ case "number":
27
+ return String(expr.value);
28
+ case "boolean":
29
+ return expr.value ? "true" : "false";
30
+ case "null":
31
+ return "None";
32
+ case "param":
33
+ // Always use .into() in Rust — handles String→String, bool→Value, i64→Value, etc.
34
+ return `${toSnakeCase(expr.name)}.into()`;
35
+ case "construct":
36
+ return this.visitConstruct(expr);
37
+ case "variant":
38
+ return this.visitVariant(expr);
39
+ case "array":
40
+ return this.visitArray(expr);
41
+ case "dict":
42
+ return `serde_json::json!({${expr.entries.map(e => `"${e.key}": ${this.visitExpr(e.value)}`).join(", ")}})`;
43
+ case "field_read":
44
+ return this.visitFieldRead(expr);
45
+ default:
46
+ return assertNever(expr);
47
+ }
48
+ }
49
+ visitConstruct(expr) {
50
+ const typeName = expr.typeName.name;
51
+ // Check if this is a polymorphic type — discriminator fields need enum variant wrapping
52
+ const typeNode = this.registry?.get(typeName);
53
+ if (typeNode?.discriminator && typeNode.childTypes.length > 0) {
54
+ return this.visitPolymorphicConstruct(expr, typeNode);
55
+ }
56
+ if (expr.fields.length === 0) {
57
+ return `${typeName} { ..Default::default() }`;
58
+ }
59
+ const fields = expr.fields.map(f => {
60
+ let val = this.wrapFieldValue(f);
61
+ // For enum fields, convert string literals to EnumName::VariantName
62
+ if (f.value.kind === "string" && typeNode) {
63
+ const prop = typeNode.properties.find(p => p.name === f.propertyName);
64
+ if (prop?.enumName) {
65
+ const variantName = f.value.value.charAt(0).toUpperCase() + f.value.value.slice(1);
66
+ const enumVal = `${prop.enumName}::${variantName}`;
67
+ val = f.isOptional ? `Some(${enumVal})` : enumVal;
68
+ }
69
+ }
70
+ return `${toSnakeCase(f.propertyName)}: ${val}`;
71
+ }).join(", ");
72
+ return `${typeName} { ${fields}, ..Default::default() }`;
73
+ }
74
+ /**
75
+ * Handle Construct on a polymorphic type — the discriminator field becomes an enum variant.
76
+ * E.g., Property { kind: "boolean", example: v } → Property { kind: PropertyKind::Custom { kind_name: "boolean".to_string() }, example: Some(v.into()), ..Default::default() }
77
+ */
78
+ visitPolymorphicConstruct(expr, typeNode) {
79
+ const typeName = expr.typeName.name;
80
+ const enumName = `${typeName}Kind`;
81
+ const discFieldName = typeNode.discriminator;
82
+ // Find the discriminator field assignment
83
+ const discField = expr.fields.find(f => f.propertyName === discFieldName);
84
+ const discValue = discField?.value;
85
+ // If no discriminator field or not a string literal, fall back to normal construction
86
+ if (!discField || discValue?.kind !== "string") {
87
+ const fields = expr.fields.map(f => `${toSnakeCase(f.propertyName)}: ${this.wrapFieldValue(f)}`).join(", ");
88
+ return fields.length > 0
89
+ ? `${typeName} { ${fields}, ..Default::default() }`
90
+ : `${typeName} { ..Default::default() }`;
91
+ }
92
+ const discValueStr = discValue.value;
93
+ // Find matching named child type
94
+ const childType = typeNode.childTypes.find(child => {
95
+ const dp = child.properties.find((p) => p.name === discFieldName);
96
+ return dp?.defaultValue === discValueStr;
97
+ });
98
+ let kindValue;
99
+ if (childType) {
100
+ // Named variant (e.g., PropertyKind::Array)
101
+ const variantName = childType.typeName.name.replace(typeName, '') || childType.typeName.name;
102
+ kindValue = `${enumName}::${variantName}`;
103
+ }
104
+ else {
105
+ // Wildcard/Custom variant — carries kind_name field
106
+ kindValue = `${enumName}::Custom { kind_name: "${discValueStr}".to_string() }`;
107
+ }
108
+ // Non-discriminator fields
109
+ const baseFields = expr.fields
110
+ .filter(f => f.propertyName !== discFieldName)
111
+ .map(f => `${toSnakeCase(f.propertyName)}: ${this.wrapFieldValue(f)}`);
112
+ const allFields = [
113
+ `${toSnakeCase(discFieldName)}: ${kindValue}`,
114
+ ...baseFields,
115
+ ];
116
+ return `${typeName} { ${allFields.join(", ")}, ..Default::default() }`;
117
+ }
118
+ visitVariant(expr) {
119
+ const baseName = expr.baseTypeName.name;
120
+ const variantName = expr.variantTypeName.name;
121
+ const enumName = `${baseName}Kind`;
122
+ const fields = expr.fields.map(f => `${toSnakeCase(f.propertyName)}: ${this.wrapFieldValue(f)}`).join(", ");
123
+ const kindValue = fields.length > 0
124
+ ? `${enumName}::${variantName} { ${fields} }`
125
+ : `${enumName}::${variantName}`;
126
+ return `${baseName} { ${toSnakeCase(expr.discriminator)}: ${kindValue}, ..Default::default() }`;
127
+ }
128
+ visitArray(expr) {
129
+ if (expr.items.length === 0) {
130
+ return "vec![]";
131
+ }
132
+ const items = expr.items.map(i => this.visitExpr(i)).join(", ");
133
+ return `vec![${items}]`;
134
+ }
135
+ /**
136
+ * Wrap field values appropriately for Rust — Option<T> fields need Some(),
137
+ * string fields need .into(), etc.
138
+ */
139
+ wrapFieldValue(field) {
140
+ const inner = this.visitExpr(field.value);
141
+ if (field.isOptional) {
142
+ return `Some(${inner})`;
143
+ }
144
+ return inner;
145
+ }
146
+ escapeString(s) {
147
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
148
+ }
149
+ visitFieldRead(expr) {
150
+ return `${toSnakeCase(expr.objectName)}.${rustFieldName(expr.fieldName)}`;
151
+ }
152
+ }
153
+ //# sourceMappingURL=visitor.js.map
@@ -0,0 +1,8 @@
1
+ import { EmitContext } from "@typespec/compiler";
2
+ import { EmitTarget, TypraEmitterOptions } from "../../lib.js";
3
+ import { TypeNode } from "../../ir/ast.js";
4
+ import { GeneratorOptions } from "../../emitter.js";
5
+ /**
6
+ * Generate TypeScript code from TypeSpec models.
7
+ */
8
+ export declare const generateTypeScript: (context: EmitContext<TypraEmitterOptions>, node: TypeNode, emitTarget: EmitTarget, options?: GeneratorOptions) => Promise<void>;
@@ -0,0 +1,209 @@
1
+ import { resolvePath } from "@typespec/compiler";
2
+ import { enumerateTypes } from "../../ir/ast.js";
3
+ import { filterNodes } from "../../emitter.js";
4
+ import { TypeRegistry } from "../../ir/expansion.js";
5
+ import { TypeScriptExprVisitor } from "./visitor.js";
6
+ import { emitTypeScriptFile as emitTypeScriptFileDecl } from "./emitter.js";
7
+ import { emitTypeScriptContext, emitTypeScriptIndex, emitTypeScriptGroupIndex, emitEslintConfig } from "./scaffolding.js";
8
+ import { emitTypeScriptTest } from "./test-emitter.js";
9
+ import { lowerFile, collectPolymorphicTypeNames } from "../../ir/lower.js";
10
+ import { buildBaseTestContext, typescriptTestOptions } from "../../testing/test-context.js";
11
+ import { toKebabCase } from "../../ir/utilities.js";
12
+ import { resolve, dirname } from "path";
13
+ import { execFileSync } from "child_process";
14
+ import { existsSync } from "fs";
15
+ import { emitGeneratedFile } from "../../cleanup/generated-file.js";
16
+ /**
17
+ * Stale-file deletion is intentionally disabled until manifest cleanup is enabled.
18
+ */
19
+ function cleanupFlatTypeFiles(relDir, isTypeFile) {
20
+ void relDir;
21
+ void isTypeFile;
22
+ return;
23
+ }
24
+ /**
25
+ * Generate TypeScript code from TypeSpec models.
26
+ */
27
+ export const generateTypeScript = async (context, node, emitTarget, options) => {
28
+ const allTypes = Array.from(enumerateTypes(node));
29
+ const nodes = filterNodes(allTypes, options);
30
+ // Build the expression IR infrastructure
31
+ const registry = TypeRegistry.fromTypeGraph(allTypes);
32
+ const visitor = new TypeScriptExprVisitor(registry);
33
+ // Determine namespace: use override or default
34
+ const originalNamespace = node.typeName.namespace;
35
+ const tsNamespace = emitTarget.namespace ?? originalNamespace.replace(/\.Core$/, "");
36
+ // Stale flat-file cleanup is disabled in this slice.
37
+ cleanupFlatTypeFiles(emitTarget["output-dir"], name => name.endsWith(".ts") && name !== "context.ts" && name !== "index.ts" && name !== "eslint.config.js");
38
+ cleanupFlatTypeFiles(emitTarget["test-dir"], name => name.endsWith(".ts") && name !== "context.test.ts");
39
+ // Emit context classes (LoadContext, SaveContext)
40
+ const contextCode = emitTypeScriptContext();
41
+ await emitTypeScriptFile(context, "context.ts", contextCode, emitTarget["output-dir"]);
42
+ // Collect polymorphic type names once for the full type graph
43
+ const polymorphicTypeNames = new Set();
44
+ for (const n of allTypes) {
45
+ for (const name of collectPolymorphicTypeNames(n, registry)) {
46
+ polymorphicTypeNames.add(name);
47
+ }
48
+ }
49
+ // Group root nodes by their semantic group folder
50
+ const groupMap = new Map();
51
+ for (const n of nodes) {
52
+ if (!n.base) {
53
+ const g = n.group || "";
54
+ if (!groupMap.has(g))
55
+ groupMap.set(g, []);
56
+ groupMap.get(g).push(n);
57
+ }
58
+ }
59
+ // Emit each base type file (includes children in the same file)
60
+ for (const n of nodes) {
61
+ // Skip child types - they're rendered with their parent
62
+ if (n.base) {
63
+ continue;
64
+ }
65
+ const group = n.group || "";
66
+ const fileDecl = lowerFile(n, registry, polymorphicTypeNames);
67
+ const code = emitTypeScriptFileDecl(fileDecl, visitor, tsNamespace, group);
68
+ const outDir = group ? `${emitTarget["output-dir"]}/${group}` : emitTarget["output-dir"];
69
+ await emitTypeScriptFile(context, `${toKebabCase(n.typeName.name)}.ts`, code, outDir);
70
+ }
71
+ // Emit group index.ts files
72
+ for (const [group, groupNodes] of groupMap) {
73
+ if (!group)
74
+ continue;
75
+ const groupIndexCode = emitTypeScriptGroupIndex(group, groupNodes);
76
+ await emitTypeScriptFile(context, "index.ts", groupIndexCode, `${emitTarget["output-dir"]}/${group}`);
77
+ }
78
+ // Emit test files for all types (skip protocols — they have no data to test)
79
+ if (emitTarget["test-dir"]) {
80
+ const importPath = emitTarget["import-path"] || "../src/index";
81
+ for (const n of nodes) {
82
+ if (n.isProtocol)
83
+ continue;
84
+ const group = n.group || "";
85
+ const testDir = group ? `${emitTarget["test-dir"]}/${group}` : emitTarget["test-dir"];
86
+ const groupDepth = group ? group.split("/").filter(Boolean).length : 0;
87
+ const testImportPath = groupDepth > 0 ? `${"../".repeat(groupDepth)}${importPath}` : importPath;
88
+ const testContext = buildTestContext(n);
89
+ const testCode = emitTypeScriptTest({
90
+ ...testContext,
91
+ importPath: testImportPath,
92
+ namespace: tsNamespace,
93
+ });
94
+ await emitTypeScriptFile(context, `${toKebabCase(n.typeName.name)}.test.ts`, testCode, testDir);
95
+ }
96
+ }
97
+ // Emit root index.ts file — re-exports from group sub-indexes
98
+ const indexContext = buildIndexContext(nodes);
99
+ const indexCode = emitTypeScriptIndex(indexContext.baseTypes, indexContext.types);
100
+ await emitTypeScriptFile(context, "index.ts", indexCode, emitTarget["output-dir"]);
101
+ // Emit eslint.config.js to project root (parent of output-dir)
102
+ if (emitTarget["output-dir"]) {
103
+ const projectRoot = resolve(process.cwd(), emitTarget["output-dir"], "..");
104
+ const eslintConfigCode = emitEslintConfig();
105
+ await emitTypeScriptFile(context, "eslint.config.js", eslintConfigCode, projectRoot);
106
+ }
107
+ // Format emitted files if format option is enabled (default: true)
108
+ if (emitTarget.format !== false) {
109
+ const outputDir = emitTarget["output-dir"]
110
+ ? resolve(process.cwd(), emitTarget["output-dir"])
111
+ : context.emitterOutputDir;
112
+ const testDir = emitTarget["test-dir"]
113
+ ? resolve(process.cwd(), emitTarget["test-dir"])
114
+ : undefined;
115
+ formatTypeScriptFiles(outputDir, testDir);
116
+ }
117
+ };
118
+ /**
119
+ * Format TypeScript files using prettier.
120
+ */
121
+ function formatTypeScriptFiles(outputDir, testDir) {
122
+ const projectRoot = findTypeScriptProjectRoot(outputDir);
123
+ if (!projectRoot) {
124
+ console.warn(`Warning: Could not find package.json. Skipping formatting.`);
125
+ return;
126
+ }
127
+ const dirs = [outputDir, ...(testDir ? [testDir] : [])];
128
+ const prettierBin = findNodeModuleFile(projectRoot, ["prettier", "bin", "prettier.cjs"]);
129
+ for (const dir of dirs) {
130
+ const globPattern = `${dir}/**/*.ts`;
131
+ if (prettierBin) {
132
+ try {
133
+ execFileSync(process.execPath, [prettierBin, "--write", globPattern], {
134
+ cwd: projectRoot,
135
+ stdio: "pipe",
136
+ encoding: "utf-8",
137
+ });
138
+ }
139
+ catch (error) {
140
+ console.warn(`Warning: prettier formatting failed for ${dir}.`);
141
+ }
142
+ }
143
+ else {
144
+ console.warn(`Warning: prettier not found for ${dir}. Run npm install in the TypeScript workspace.`);
145
+ }
146
+ // Run eslint fix
147
+ try {
148
+ execFileSync("npx", ["eslint", "--fix", globPattern], {
149
+ cwd: projectRoot,
150
+ stdio: "pipe",
151
+ encoding: "utf-8",
152
+ });
153
+ }
154
+ catch (error) {
155
+ // ESLint errors are common, don't warn about them
156
+ }
157
+ }
158
+ }
159
+ function findNodeModuleFile(startDir, segments) {
160
+ let currentDir = resolve(startDir);
161
+ const root = resolve("/");
162
+ while (currentDir !== root && currentDir !== dirname(currentDir)) {
163
+ const candidate = resolve(currentDir, "node_modules", ...segments);
164
+ if (existsSync(candidate)) {
165
+ return candidate;
166
+ }
167
+ currentDir = dirname(currentDir);
168
+ }
169
+ return undefined;
170
+ }
171
+ /**
172
+ * Find the TypeScript project root by looking for package.json.
173
+ */
174
+ function findTypeScriptProjectRoot(startDir) {
175
+ let currentDir = resolve(startDir);
176
+ const root = resolve("/");
177
+ while (currentDir !== root && currentDir !== dirname(currentDir)) {
178
+ const packageJsonPath = resolve(currentDir, "package.json");
179
+ if (existsSync(packageJsonPath)) {
180
+ return currentDir;
181
+ }
182
+ currentDir = dirname(currentDir);
183
+ }
184
+ return undefined;
185
+ }
186
+ /**
187
+ * Build context for rendering the index.ts file.
188
+ */
189
+ function buildIndexContext(nodes) {
190
+ return {
191
+ baseTypes: nodes.filter((n) => !n.base),
192
+ types: nodes,
193
+ };
194
+ }
195
+ /**
196
+ * Build context for rendering a test file.
197
+ */
198
+ function buildTestContext(node) {
199
+ return buildBaseTestContext(node, undefined, typescriptTestOptions);
200
+ }
201
+ /**
202
+ * Write generated TypeScript content to file.
203
+ */
204
+ async function emitTypeScriptFile(context, filename, content, outputDir) {
205
+ outputDir = outputDir || `${context.emitterOutputDir}/typescript`;
206
+ const filePath = resolvePath(outputDir, filename);
207
+ await emitGeneratedFile(context, filePath, content);
208
+ }
209
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * TypeScript code emitter — Declaration IR → TypeScript source code.
3
+ *
4
+ * Replaces `file.ts.njk` (~414 lines of Nunjucks templates)
5
+ * with a typed TypeScript function that walks the FileDecl tree.
6
+ *
7
+ * The emitter produces correctly-formatted TypeScript code using modern
8
+ * class patterns with `Partial<T>` constructors. Output is post-processed
9
+ * by prettier + eslint, so exact whitespace is not critical.
10
+ *
11
+ * Structural blocks emitted (in order):
12
+ * 1. Header comment (auto-generated warning)
13
+ * 2. Imports (LoadContext/SaveContext, referenced types)
14
+ * 3. For each type in the file:
15
+ * a. [abstract] class definition with extends
16
+ * b. shorthandProperty static ClassVar
17
+ * c. Field declarations with type annotations + defaults
18
+ * d. Constructor with Partial<T>
19
+ * e. //#region Load Methods
20
+ * - load() static method
21
+ * - loadKind() for polymorphic dispatch (private static)
22
+ * - Collection helpers (loadX / saveX)
23
+ * f. //#endregion
24
+ * g. //#region Save Methods
25
+ * - save() instance method
26
+ * - toYaml() / toJson()
27
+ * - fromJson() / fromYaml() static methods
28
+ * h. //#endregion
29
+ * i. Factory static methods
30
+ * j. Method stubs (as comments)
31
+ */
32
+ import { FileDecl } from "../../ir/declarations.js";
33
+ import { ExprVisitor } from "../../ir/visitor.js";
34
+ /**
35
+ * Emit a complete TypeScript file from a FileDecl.
36
+ *
37
+ * @param decl - The file declaration to emit
38
+ * @param visitor - Expression visitor for rendering expressions
39
+ * @param namespace - Optional namespace override
40
+ * @param group - Semantic group folder this file lives in (e.g. "connection"). Empty string = root.
41
+ */
42
+ export declare function emitTypeScriptFile(decl: FileDecl, visitor: ExprVisitor, namespace?: string, group?: string): string;