tova 0.1.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.
@@ -0,0 +1,148 @@
1
+ // Main code generator — orchestrates shared/server/client codegen
2
+ // Supports named multi-blocks: server "api" { }, server "ws" { }
3
+ // Blocks with the same name are merged; different names produce separate output files.
4
+
5
+ import { SharedCodegen } from './shared-codegen.js';
6
+ import { ServerCodegen } from './server-codegen.js';
7
+ import { ClientCodegen } from './client-codegen.js';
8
+
9
+ export class CodeGenerator {
10
+ constructor(ast, filename = '<stdin>') {
11
+ this.ast = ast;
12
+ this.filename = filename;
13
+ }
14
+
15
+ // Group blocks by name (null name = "default")
16
+ _groupByName(blocks) {
17
+ const groups = new Map();
18
+ for (const block of blocks) {
19
+ const key = block.name || null;
20
+ if (!groups.has(key)) groups.set(key, []);
21
+ groups.get(key).push(block);
22
+ }
23
+ return groups;
24
+ }
25
+
26
+ generate() {
27
+ const sharedBlocks = [];
28
+ const serverBlocks = [];
29
+ const clientBlocks = [];
30
+ const topLevel = [];
31
+
32
+ const testBlocks = [];
33
+
34
+ for (const node of this.ast.body) {
35
+ switch (node.type) {
36
+ case 'SharedBlock': sharedBlocks.push(node); break;
37
+ case 'ServerBlock': serverBlocks.push(node); break;
38
+ case 'ClientBlock': clientBlocks.push(node); break;
39
+ case 'TestBlock': testBlocks.push(node); break;
40
+ default: topLevel.push(node); break;
41
+ }
42
+ }
43
+
44
+ const sharedGen = new SharedCodegen();
45
+
46
+ // All shared blocks (regardless of name) are merged into one shared output
47
+ const sharedCode = sharedBlocks.map(b => sharedGen.generate(b)).join('\n');
48
+ const topLevelCode = topLevel.map(s => sharedGen.generateStatement(s)).join('\n');
49
+ const helpers = sharedGen.generateHelpers();
50
+ const combinedShared = [helpers, sharedCode, topLevelCode].filter(s => s.trim()).join('\n').trim();
51
+
52
+ // Group server and client blocks by name
53
+ const serverGroups = this._groupByName(serverBlocks);
54
+ const clientGroups = this._groupByName(clientBlocks);
55
+
56
+ // Collect function names per named server block for inter-server RPC
57
+ const serverFunctionMap = new Map(); // blockName -> [fnName, ...]
58
+ const collectFns = (stmts) => {
59
+ const fns = [];
60
+ for (const stmt of stmts) {
61
+ if (stmt.type === 'FunctionDeclaration') {
62
+ fns.push(stmt.name);
63
+ } else if (stmt.type === 'RouteGroupDeclaration') {
64
+ fns.push(...collectFns(stmt.body));
65
+ }
66
+ }
67
+ return fns;
68
+ };
69
+ for (const [name, blocks] of serverGroups) {
70
+ if (name) {
71
+ const fns = [];
72
+ for (const block of blocks) {
73
+ fns.push(...collectFns(block.body));
74
+ }
75
+ serverFunctionMap.set(name, fns);
76
+ }
77
+ }
78
+
79
+ // Generate server outputs (one per named group)
80
+ const servers = {};
81
+ for (const [name, blocks] of serverGroups) {
82
+ const gen = new ServerCodegen();
83
+ const key = name || 'default';
84
+ // Build peer blocks map (all named blocks except self)
85
+ let peerBlocks = null;
86
+ if (name && serverFunctionMap.size > 1) {
87
+ peerBlocks = new Map();
88
+ for (const [peerName, peerFns] of serverFunctionMap) {
89
+ if (peerName !== name) {
90
+ peerBlocks.set(peerName, peerFns);
91
+ }
92
+ }
93
+ }
94
+ servers[key] = gen.generate(blocks, combinedShared, name, peerBlocks, sharedBlocks);
95
+ }
96
+
97
+ // Generate client outputs (one per named group)
98
+ const clients = {};
99
+ for (const [name, blocks] of clientGroups) {
100
+ const gen = new ClientCodegen();
101
+ const key = name || 'default';
102
+ clients[key] = gen.generate(blocks, combinedShared);
103
+ }
104
+
105
+ // Generate tests if test blocks exist
106
+ let testCode = '';
107
+ if (testBlocks.length > 0) {
108
+ const testGen = new ServerCodegen();
109
+ testCode = testGen.generateTests(testBlocks);
110
+
111
+ // Add __handleRequest export to server code
112
+ const defaultServer = servers['default'] || '';
113
+ if (defaultServer) {
114
+ servers['default'] = defaultServer + '\nexport { __handleRequest };\n';
115
+ }
116
+ }
117
+
118
+ // Backward-compatible: if only unnamed blocks, return flat structure
119
+ const hasNamedBlocks = [...serverGroups.keys(), ...clientGroups.keys()].some(k => k !== null);
120
+
121
+ // Collect source mappings from all codegens
122
+ const sourceMappings = sharedGen.getSourceMappings();
123
+
124
+ if (!hasNamedBlocks) {
125
+ const result = {
126
+ shared: combinedShared,
127
+ server: servers['default'] || '',
128
+ client: clients['default'] || '',
129
+ sourceMappings,
130
+ };
131
+ if (testCode) result.test = testCode;
132
+ return result;
133
+ }
134
+
135
+ // Multi-block output: separate files per named block
136
+ const result = {
137
+ shared: combinedShared,
138
+ server: servers['default'] || '',
139
+ client: clients['default'] || '',
140
+ servers, // { "api": code, "ws": code, ... }
141
+ clients, // { "admin": code, "dashboard": code, ... }
142
+ multiBlock: true,
143
+ sourceMappings,
144
+ };
145
+ if (testCode) result.test = testCode;
146
+ return result;
147
+ }
148
+ }