naider 1.9.0 → 1.10.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.
- package/bin/naide.js +38 -5
- package/lsp/server.js +140 -0
- package/package.json +1 -1
- package/src/generator-bun.js +513 -0
- package/src/generator-python.js +1449 -0
- package/src/index.js +55 -1
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { Generator } from './generator.js';
|
|
2
|
+
|
|
3
|
+
export class BunGenerator extends Generator {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
super(options);
|
|
6
|
+
this.usesBunServe = false;
|
|
7
|
+
this.routes = [];
|
|
8
|
+
this.corsOrigin = null;
|
|
9
|
+
this.serverName = 'app';
|
|
10
|
+
this.bunAuthSecret = null;
|
|
11
|
+
this.bunAuthPaths = [];
|
|
12
|
+
this.staticDir = null;
|
|
13
|
+
this.wsHandlers = [];
|
|
14
|
+
this.crudEntries = [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
generate(ast) {
|
|
18
|
+
this.visitProgram(ast);
|
|
19
|
+
|
|
20
|
+
const preamble = [];
|
|
21
|
+
|
|
22
|
+
if (this.needsEventBus) {
|
|
23
|
+
this.runtimeImports.add('createEventBus');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (this.runtimeImports.size > 0) {
|
|
27
|
+
const imports = [...this.runtimeImports].join(', ');
|
|
28
|
+
preamble.push(`import { ${imports} } from '${this.runtimePath}';`);
|
|
29
|
+
preamble.push('');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (this.hasTests) {
|
|
33
|
+
preamble.push("import { test, expect } from 'bun:test';");
|
|
34
|
+
preamble.push('');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (this.needsEventBus) {
|
|
38
|
+
preamble.push('const __eventBus = createEventBus();');
|
|
39
|
+
preamble.push('');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (preamble.length > 0) {
|
|
43
|
+
this.output.unshift(...preamble);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return this.output.join('\n');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
visitServer(node) {
|
|
50
|
+
this.usesBunServe = true;
|
|
51
|
+
this.serverName = node.name;
|
|
52
|
+
|
|
53
|
+
const errorHandlers = [];
|
|
54
|
+
|
|
55
|
+
for (const mid of node.middleware) {
|
|
56
|
+
this.visitStatement(mid);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const child of node.routes) {
|
|
60
|
+
if (child.type === 'Route') {
|
|
61
|
+
this.collectRoute(child);
|
|
62
|
+
} else if (child.type === 'CorsDecl') {
|
|
63
|
+
this.visitBunCors(child);
|
|
64
|
+
} else if (child.type === 'AuthDecl') {
|
|
65
|
+
this.visitBunAuth(child);
|
|
66
|
+
} else if (child.type === 'CrudDecl') {
|
|
67
|
+
this.visitBunCrud(child);
|
|
68
|
+
} else if (child.type === 'StaticDecl') {
|
|
69
|
+
this.visitBunStatic(child);
|
|
70
|
+
} else if (child.type === 'WsDecl') {
|
|
71
|
+
this.wsHandlers.push(child);
|
|
72
|
+
} else if (child.type === 'LimitDecl') {
|
|
73
|
+
// rate limiting handled at app level for Bun
|
|
74
|
+
} else if (child.type === 'ErrorHandler') {
|
|
75
|
+
errorHandlers.push(child);
|
|
76
|
+
} else if (child.type === 'GroupDecl') {
|
|
77
|
+
this.visitBunGroup(child);
|
|
78
|
+
} else if (child.type === 'SchemaDecl') {
|
|
79
|
+
this.visitSchema(child);
|
|
80
|
+
} else {
|
|
81
|
+
this.visitStatement(child);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.emitBunServe(node, errorHandlers);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
collectRoute(route) {
|
|
89
|
+
this.routes.push({ ...route, prefix: '' });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
visitBunCors(node) {
|
|
93
|
+
this.corsOrigin = this.stringValue(node.origins);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
visitBunGroup(node) {
|
|
97
|
+
const prefix = this.rawString(node.prefix);
|
|
98
|
+
for (const child of node.routes) {
|
|
99
|
+
if (child.type === 'Route') {
|
|
100
|
+
this.routes.push({ ...child, prefix });
|
|
101
|
+
} else {
|
|
102
|
+
this.visitStatement(child);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
rawString(strData) {
|
|
108
|
+
if (!strData) return '';
|
|
109
|
+
if (strData.raw !== null && strData.raw !== undefined) return strData.raw;
|
|
110
|
+
if (strData.parts) return strData.parts.map(p => p.value).join('');
|
|
111
|
+
return '';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
visitBunAuth(node) {
|
|
115
|
+
this.bunAuthSecret = this.expr(node.secret);
|
|
116
|
+
if (node.protectedPaths.length > 0) {
|
|
117
|
+
this.bunAuthPaths = node.protectedPaths.map(p => this.rawString(p));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
visitBunCrud(node) {
|
|
122
|
+
const path = this.rawString(node.path);
|
|
123
|
+
this.crudEntries.push({ path, schema: node.schemaName });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
visitBunStatic(node) {
|
|
127
|
+
let raw = node.path.raw || node.path.parts?.map(p => p.value).join('') || 'public';
|
|
128
|
+
if (raw.startsWith('/')) raw = raw.slice(1);
|
|
129
|
+
this.staticDir = raw;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
emitBunServe(node, errorHandlers) {
|
|
133
|
+
const port = node.port ? this.expr(node.port) : '3000';
|
|
134
|
+
const ch = this.corsOrigin ? ', ...corsHeaders' : '';
|
|
135
|
+
|
|
136
|
+
if (this.bunAuthSecret) {
|
|
137
|
+
this.emitRaw('');
|
|
138
|
+
this.emit(`const __authSecret = ${this.bunAuthSecret};`);
|
|
139
|
+
this.emit(`function __verifyJwt(req) {`);
|
|
140
|
+
this.indent++;
|
|
141
|
+
this.emit(`const auth = req.headers.get('Authorization') || '';`);
|
|
142
|
+
this.emit(`const token = auth.replace('Bearer ', '');`);
|
|
143
|
+
this.emit(`if (!token) return null;`);
|
|
144
|
+
this.emit(`try {`);
|
|
145
|
+
this.indent++;
|
|
146
|
+
this.emit(`const [,payload] = token.split('.');`);
|
|
147
|
+
this.emit(`return JSON.parse(atob(payload));`);
|
|
148
|
+
this.indent--;
|
|
149
|
+
this.emit(`} catch { return null; }`);
|
|
150
|
+
this.indent--;
|
|
151
|
+
this.emit(`}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this.crudEntries.length > 0) {
|
|
155
|
+
this.emitRaw('');
|
|
156
|
+
for (const crud of this.crudEntries) {
|
|
157
|
+
this.emit(`const __${crud.schema.toLowerCase()}Store = [];`);
|
|
158
|
+
this.emit(`let __${crud.schema.toLowerCase()}Id = 1;`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.emitRaw('');
|
|
163
|
+
this.emit(`const server = Bun.serve({`);
|
|
164
|
+
this.indent++;
|
|
165
|
+
this.emit(`port: ${port},`);
|
|
166
|
+
|
|
167
|
+
if (this.wsHandlers.length > 0) {
|
|
168
|
+
this.emit(`websocket: {`);
|
|
169
|
+
this.indent++;
|
|
170
|
+
for (const ws of this.wsHandlers) {
|
|
171
|
+
this.emitBunWsHandlers(ws);
|
|
172
|
+
}
|
|
173
|
+
this.indent--;
|
|
174
|
+
this.emit(`},`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.emit(`async fetch(req${this.wsHandlers.length > 0 ? ', server' : ''}) {`);
|
|
178
|
+
this.indent++;
|
|
179
|
+
this.emit(`const url = new URL(req.url);`);
|
|
180
|
+
this.emit(`const path = url.pathname;`);
|
|
181
|
+
this.emit(`const method = req.method;`);
|
|
182
|
+
this.emitRaw('');
|
|
183
|
+
|
|
184
|
+
if (this.corsOrigin) {
|
|
185
|
+
this.emit(`const corsHeaders = {`);
|
|
186
|
+
this.indent++;
|
|
187
|
+
this.emit(`'Access-Control-Allow-Origin': ${this.corsOrigin},`);
|
|
188
|
+
this.emit(`'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',`);
|
|
189
|
+
this.emit(`'Access-Control-Allow-Headers': 'Content-Type, Authorization',`);
|
|
190
|
+
this.indent--;
|
|
191
|
+
this.emit(`};`);
|
|
192
|
+
this.emitRaw('');
|
|
193
|
+
this.emit(`if (method === 'OPTIONS') {`);
|
|
194
|
+
this.indent++;
|
|
195
|
+
this.emit(`return new Response(null, { status: 204, headers: corsHeaders });`);
|
|
196
|
+
this.indent--;
|
|
197
|
+
this.emit(`}`);
|
|
198
|
+
this.emitRaw('');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (this.bunAuthSecret && this.bunAuthPaths.length > 0) {
|
|
202
|
+
for (const p of this.bunAuthPaths) {
|
|
203
|
+
const pattern = p.replace(/\*/g, '');
|
|
204
|
+
this.emit(`if (path.startsWith(${JSON.stringify(pattern)})) {`);
|
|
205
|
+
this.indent++;
|
|
206
|
+
this.emit(`const user = __verifyJwt(req);`);
|
|
207
|
+
this.emit(`if (!user) return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json'${ch} } });`);
|
|
208
|
+
this.indent--;
|
|
209
|
+
this.emit(`}`);
|
|
210
|
+
this.emitRaw('');
|
|
211
|
+
}
|
|
212
|
+
} else if (this.bunAuthSecret) {
|
|
213
|
+
this.emit(`const user = __verifyJwt(req);`);
|
|
214
|
+
this.emit(`if (!user) return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json'${ch} } });`);
|
|
215
|
+
this.emitRaw('');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (this.wsHandlers.length > 0) {
|
|
219
|
+
for (const ws of this.wsHandlers) {
|
|
220
|
+
const wsPath = this.rawString(ws.path);
|
|
221
|
+
this.emit(`if (path === ${JSON.stringify(wsPath)} && server.upgrade(req)) return;`);
|
|
222
|
+
}
|
|
223
|
+
this.emitRaw('');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (this.staticDir) {
|
|
227
|
+
this.emit(`const file = Bun.file(${JSON.stringify(this.staticDir)} + path);`);
|
|
228
|
+
this.emit(`if (await file.exists()) return new Response(file);`);
|
|
229
|
+
this.emitRaw('');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const crud of this.crudEntries) {
|
|
233
|
+
this.emitBunCrud(crud);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (const route of this.routes) {
|
|
237
|
+
this.emitBunRoute(route);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.emit(`return new Response(JSON.stringify({ error: 'Not Found' }), {`);
|
|
241
|
+
this.indent++;
|
|
242
|
+
this.emit(`status: 404,`);
|
|
243
|
+
this.emit(`headers: { 'Content-Type': 'application/json'${ch} },`);
|
|
244
|
+
this.indent--;
|
|
245
|
+
this.emit(`});`);
|
|
246
|
+
|
|
247
|
+
this.indent--;
|
|
248
|
+
this.emit(`},`);
|
|
249
|
+
|
|
250
|
+
if (errorHandlers.length > 0) {
|
|
251
|
+
this.emit(`error(err) {`);
|
|
252
|
+
this.indent++;
|
|
253
|
+
this.emit(`return new Response(JSON.stringify({ error: err.message }), {`);
|
|
254
|
+
this.indent++;
|
|
255
|
+
this.emit(`status: 500,`);
|
|
256
|
+
this.emit(`headers: { 'Content-Type': 'application/json' },`);
|
|
257
|
+
this.indent--;
|
|
258
|
+
this.emit(`});`);
|
|
259
|
+
this.indent--;
|
|
260
|
+
this.emit(`},`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.indent--;
|
|
264
|
+
this.emit(`});`);
|
|
265
|
+
this.emitRaw('');
|
|
266
|
+
this.emit(`console.log(\`Server running on port \${server.port}\`);`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
emitBunCrud(crud) {
|
|
270
|
+
const store = `__${crud.schema.toLowerCase()}Store`;
|
|
271
|
+
const idVar = `__${crud.schema.toLowerCase()}Id`;
|
|
272
|
+
const ch = this.corsOrigin ? ', ...corsHeaders' : '';
|
|
273
|
+
const jsonH = `{ 'Content-Type': 'application/json'${ch} }`;
|
|
274
|
+
|
|
275
|
+
this.emit(`if (method === 'GET' && path === ${JSON.stringify(crud.path)}) {`);
|
|
276
|
+
this.indent++;
|
|
277
|
+
this.emit(`return new Response(JSON.stringify(${store}), { headers: ${jsonH} });`);
|
|
278
|
+
this.indent--;
|
|
279
|
+
this.emit(`}`);
|
|
280
|
+
|
|
281
|
+
const idMatch = `path.match(/^${crud.path.replace(/\//g, '\\/')}\\/(\\w+)$/)`;
|
|
282
|
+
this.emit(`if (method === 'GET' && ${idMatch}) {`);
|
|
283
|
+
this.indent++;
|
|
284
|
+
this.emit(`const id = ${idMatch}[1];`);
|
|
285
|
+
this.emit(`const item = ${store}.find(i => String(i.id) === id);`);
|
|
286
|
+
this.emit(`if (!item) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404, headers: ${jsonH} });`);
|
|
287
|
+
this.emit(`return new Response(JSON.stringify(item), { headers: ${jsonH} });`);
|
|
288
|
+
this.indent--;
|
|
289
|
+
this.emit(`}`);
|
|
290
|
+
|
|
291
|
+
this.emit(`if (method === 'POST' && path === ${JSON.stringify(crud.path)}) {`);
|
|
292
|
+
this.indent++;
|
|
293
|
+
this.emit(`const body = await req.json();`);
|
|
294
|
+
this.emit(`const item = { id: ${idVar}++, ...body };`);
|
|
295
|
+
this.emit(`${store}.push(item);`);
|
|
296
|
+
this.emit(`return new Response(JSON.stringify(item), { status: 201, headers: ${jsonH} });`);
|
|
297
|
+
this.indent--;
|
|
298
|
+
this.emit(`}`);
|
|
299
|
+
|
|
300
|
+
this.emit(`if (method === 'PUT' && ${idMatch}) {`);
|
|
301
|
+
this.indent++;
|
|
302
|
+
this.emit(`const id = ${idMatch}[1];`);
|
|
303
|
+
this.emit(`const idx = ${store}.findIndex(i => String(i.id) === id);`);
|
|
304
|
+
this.emit(`if (idx === -1) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404, headers: ${jsonH} });`);
|
|
305
|
+
this.emit(`const body = await req.json();`);
|
|
306
|
+
this.emit(`${store}[idx] = { ...${store}[idx], ...body };`);
|
|
307
|
+
this.emit(`return new Response(JSON.stringify(${store}[idx]), { headers: ${jsonH} });`);
|
|
308
|
+
this.indent--;
|
|
309
|
+
this.emit(`}`);
|
|
310
|
+
|
|
311
|
+
this.emit(`if (method === 'DELETE' && ${idMatch}) {`);
|
|
312
|
+
this.indent++;
|
|
313
|
+
this.emit(`const id = ${idMatch}[1];`);
|
|
314
|
+
this.emit(`const idx = ${store}.findIndex(i => String(i.id) === id);`);
|
|
315
|
+
this.emit(`if (idx === -1) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404, headers: ${jsonH} });`);
|
|
316
|
+
this.emit(`${store}.splice(idx, 1);`);
|
|
317
|
+
this.emit(`return new Response(JSON.stringify({ deleted: true }), { headers: ${jsonH} });`);
|
|
318
|
+
this.indent--;
|
|
319
|
+
this.emit(`}`);
|
|
320
|
+
this.emitRaw('');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
emitBunWsHandlers(ws) {
|
|
324
|
+
const events = ws.events || [];
|
|
325
|
+
|
|
326
|
+
const messageEvt = events.find(e => {
|
|
327
|
+
const name = e.name.raw || e.name.parts?.map(p => p.value).join('');
|
|
328
|
+
return name === 'message';
|
|
329
|
+
});
|
|
330
|
+
const openEvt = events.find(e => {
|
|
331
|
+
const name = e.name.raw || e.name.parts?.map(p => p.value).join('');
|
|
332
|
+
return name === 'connect' || name === 'open';
|
|
333
|
+
});
|
|
334
|
+
const closeEvt = events.find(e => {
|
|
335
|
+
const name = e.name.raw || e.name.parts?.map(p => p.value).join('');
|
|
336
|
+
return name === 'close';
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (messageEvt) {
|
|
340
|
+
this.emit(`message(ws, message) {`);
|
|
341
|
+
this.indent++;
|
|
342
|
+
const dataParam = messageEvt.params[0] || 'data';
|
|
343
|
+
this.emit(`const ${dataParam} = JSON.parse(message);`);
|
|
344
|
+
this.emit(`const send = (d) => ws.send(typeof d === 'string' ? d : JSON.stringify(d));`);
|
|
345
|
+
for (const stmt of messageEvt.body) this.visitStatement(stmt);
|
|
346
|
+
this.indent--;
|
|
347
|
+
this.emit(`},`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (openEvt) {
|
|
351
|
+
this.emit(`open(ws) {`);
|
|
352
|
+
this.indent++;
|
|
353
|
+
for (const stmt of openEvt.body) this.visitStatement(stmt);
|
|
354
|
+
this.indent--;
|
|
355
|
+
this.emit(`},`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (closeEvt) {
|
|
359
|
+
this.emit(`close(ws) {`);
|
|
360
|
+
this.indent++;
|
|
361
|
+
for (const stmt of closeEvt.body) this.visitStatement(stmt);
|
|
362
|
+
this.indent--;
|
|
363
|
+
this.emit(`},`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
emitBunRoute(route) {
|
|
368
|
+
const method = route.method === 'del' ? 'DELETE' : route.method.toUpperCase();
|
|
369
|
+
const rawPath = this.rawString(route.path);
|
|
370
|
+
const fullPath = route.prefix ? route.prefix + rawPath : rawPath;
|
|
371
|
+
|
|
372
|
+
const hasParams = /:(\w+)/.test(fullPath);
|
|
373
|
+
const hasTryCatch = route.body.some(s => s.type === 'Try');
|
|
374
|
+
const needsBody = ['POST', 'PUT', 'PATCH'].includes(method);
|
|
375
|
+
const routeIdx = this.routes.indexOf(route);
|
|
376
|
+
|
|
377
|
+
if (hasParams) {
|
|
378
|
+
const paramNames = [];
|
|
379
|
+
const regexPath = fullPath.replace(/:(\w+)/g, (_, name) => {
|
|
380
|
+
paramNames.push(name);
|
|
381
|
+
return '([^/]+)';
|
|
382
|
+
});
|
|
383
|
+
this.emit(`const __match${routeIdx} = path.match(/^${regexPath.replace(/\//g, '\\/')}$/);`);
|
|
384
|
+
this.emit(`if (method === '${method}' && __match${routeIdx}) {`);
|
|
385
|
+
this.indent++;
|
|
386
|
+
|
|
387
|
+
if (!hasTryCatch) {
|
|
388
|
+
this.emit('try {');
|
|
389
|
+
this.indent++;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
this.emit(`const params = { ${paramNames.map((n, i) => `${n}: __match${routeIdx}[${i + 1}]`).join(', ')} };`);
|
|
393
|
+
if (needsBody) {
|
|
394
|
+
this.emit(`const body = await req.json().catch(() => null);`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
this.emitBunRouteBody(route.body);
|
|
398
|
+
|
|
399
|
+
if (!hasTryCatch) {
|
|
400
|
+
this.indent--;
|
|
401
|
+
this.emit(`} catch (__err) {`);
|
|
402
|
+
this.indent++;
|
|
403
|
+
this.emit(`return new Response(JSON.stringify({ error: __err.message }), {`);
|
|
404
|
+
this.indent++;
|
|
405
|
+
this.emit(`status: 500,`);
|
|
406
|
+
this.emit(`headers: { 'Content-Type': 'application/json'${this.corsOrigin ? ', ...corsHeaders' : ''} },`);
|
|
407
|
+
this.indent--;
|
|
408
|
+
this.emit(`});`);
|
|
409
|
+
this.indent--;
|
|
410
|
+
this.emit(`}`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
this.indent--;
|
|
414
|
+
this.emit(`}`);
|
|
415
|
+
this.emitRaw('');
|
|
416
|
+
} else {
|
|
417
|
+
this.emit(`if (method === '${method}' && path === ${JSON.stringify(fullPath)}) {`);
|
|
418
|
+
this.indent++;
|
|
419
|
+
|
|
420
|
+
if (!hasTryCatch) {
|
|
421
|
+
this.emit('try {');
|
|
422
|
+
this.indent++;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (needsBody) {
|
|
426
|
+
this.emit(`const body = await req.json().catch(() => null);`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
this.emitBunRouteBody(route.body);
|
|
430
|
+
|
|
431
|
+
if (!hasTryCatch) {
|
|
432
|
+
this.indent--;
|
|
433
|
+
this.emit(`} catch (__err) {`);
|
|
434
|
+
this.indent++;
|
|
435
|
+
this.emit(`return new Response(JSON.stringify({ error: __err.message }), {`);
|
|
436
|
+
this.indent++;
|
|
437
|
+
this.emit(`status: 500,`);
|
|
438
|
+
this.emit(`headers: { 'Content-Type': 'application/json'${this.corsOrigin ? ', ...corsHeaders' : ''} },`);
|
|
439
|
+
this.indent--;
|
|
440
|
+
this.emit(`});`);
|
|
441
|
+
this.indent--;
|
|
442
|
+
this.emit(`}`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
this.indent--;
|
|
446
|
+
this.emit(`}`);
|
|
447
|
+
this.emitRaw('');
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
emitBunRouteBody(body) {
|
|
452
|
+
for (const stmt of body) {
|
|
453
|
+
if (stmt.type === 'Return' && stmt.value !== null) {
|
|
454
|
+
const val = this.expr(stmt.value);
|
|
455
|
+
this.emit(`return new Response(JSON.stringify(${val}), {`);
|
|
456
|
+
this.indent++;
|
|
457
|
+
this.emit(`headers: { 'Content-Type': 'application/json'${this.corsOrigin ? ', ...corsHeaders' : ''} },`);
|
|
458
|
+
this.indent--;
|
|
459
|
+
this.emit(`});`);
|
|
460
|
+
} else if (stmt.type === 'ReturnStatus') {
|
|
461
|
+
const val = this.expr(stmt.body);
|
|
462
|
+
const status = this.expr(stmt.statusCode);
|
|
463
|
+
this.emit(`return new Response(JSON.stringify(${val}), {`);
|
|
464
|
+
this.indent++;
|
|
465
|
+
this.emit(`status: ${status},`);
|
|
466
|
+
this.emit(`headers: { 'Content-Type': 'application/json'${this.corsOrigin ? ', ...corsHeaders' : ''} },`);
|
|
467
|
+
this.indent--;
|
|
468
|
+
this.emit(`});`);
|
|
469
|
+
} else if (stmt.type === 'ReturnMethod') {
|
|
470
|
+
const val = this.expr(stmt.value);
|
|
471
|
+
switch (stmt.method) {
|
|
472
|
+
case 'html':
|
|
473
|
+
this.emit(`return new Response(${val}, {`);
|
|
474
|
+
this.indent++;
|
|
475
|
+
this.emit(`headers: { 'Content-Type': 'text/html'${this.corsOrigin ? ', ...corsHeaders' : ''} },`);
|
|
476
|
+
this.indent--;
|
|
477
|
+
this.emit(`});`);
|
|
478
|
+
break;
|
|
479
|
+
case 'text':
|
|
480
|
+
this.emit(`return new Response(${val}, {`);
|
|
481
|
+
this.indent++;
|
|
482
|
+
this.emit(`headers: { 'Content-Type': 'text/plain'${this.corsOrigin ? ', ...corsHeaders' : ''} },`);
|
|
483
|
+
this.indent--;
|
|
484
|
+
this.emit(`});`);
|
|
485
|
+
break;
|
|
486
|
+
case 'redirect':
|
|
487
|
+
this.emit(`return Response.redirect(${val}, 302);`);
|
|
488
|
+
break;
|
|
489
|
+
case 'file':
|
|
490
|
+
this.emit(`return new Response(Bun.file(${val}));`);
|
|
491
|
+
break;
|
|
492
|
+
default:
|
|
493
|
+
this.emit(`return new Response(${val});`);
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
this.visitStatement(stmt);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
visitTest(node) {
|
|
502
|
+
this.hasTests = true;
|
|
503
|
+
const name = this.stringValue(node.name);
|
|
504
|
+
this.emit(`test(${name}, () => {`);
|
|
505
|
+
this.indent++;
|
|
506
|
+
for (const stmt of node.body) {
|
|
507
|
+
this.visitStatement(stmt);
|
|
508
|
+
}
|
|
509
|
+
this.indent--;
|
|
510
|
+
this.emit('});');
|
|
511
|
+
this.emitRaw('');
|
|
512
|
+
}
|
|
513
|
+
}
|