hono-takibi 0.9.71 → 0.9.73
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/README.md +0 -2
- package/dist/cli/index.d.ts +0 -36
- package/dist/cli/index.js +317 -3
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +62 -1
- package/dist/core/components/examples.js +42 -6
- package/dist/core/components/headers.js +2 -2
- package/dist/core/components/parameters.js +2 -2
- package/dist/core/components/requestBodies.js +2 -2
- package/dist/core/components/responses.js +2 -2
- package/dist/core/components/schemas.js +2 -2
- package/dist/core/route/index.js +237 -2
- package/dist/core/rpc/index.js +1173 -240
- package/dist/core/takibi/index.d.ts +1 -1
- package/dist/core/takibi/index.js +501 -2
- package/dist/core/type/index.d.ts +0 -28
- package/dist/core/type/index.js +550 -119
- package/dist/format/index.js +18 -0
- package/dist/fsp/index.js +87 -0
- package/dist/generator/zod-openapi-hono/app/index.js +116 -0
- package/dist/generator/zod-openapi-hono/openapi/components/callbacks.js +61 -0
- package/dist/generator/zod-openapi-hono/openapi/components/examples.d.ts +137 -0
- package/dist/generator/zod-openapi-hono/openapi/components/examples.js +405 -2
- package/dist/generator/zod-openapi-hono/openapi/components/headers.js +46 -0
- package/dist/generator/zod-openapi-hono/openapi/components/index.js +100 -0
- package/dist/generator/zod-openapi-hono/openapi/components/links.js +34 -0
- package/dist/generator/zod-openapi-hono/openapi/components/parameters.js +53 -0
- package/dist/generator/zod-openapi-hono/openapi/components/request-bodies.js +49 -0
- package/dist/generator/zod-openapi-hono/openapi/components/responses.js +43 -0
- package/dist/generator/zod-openapi-hono/openapi/components/schemas.js +72 -0
- package/dist/generator/zod-openapi-hono/openapi/components/securitySchemes.js +36 -0
- package/dist/generator/zod-openapi-hono/openapi/index.js +460 -0
- package/dist/generator/zod-openapi-hono/openapi/routes/create-route.js +36 -0
- package/dist/generator/zod-openapi-hono/openapi/routes/index.js +44 -0
- package/dist/generator/zod-to-openapi/index.js +781 -26
- package/dist/generator/zod-to-openapi/z/enum.js +27 -0
- package/dist/generator/zod-to-openapi/z/index.js +27 -0
- package/dist/generator/zod-to-openapi/z/integer.js +139 -0
- package/dist/generator/zod-to-openapi/z/number.js +45 -0
- package/dist/generator/zod-to-openapi/z/object.d.ts +6 -0
- package/dist/generator/zod-to-openapi/z/object.js +101 -55
- package/dist/generator/zod-to-openapi/z/string.js +38 -0
- package/dist/helper/ast.d.ts +15 -31
- package/dist/helper/ast.js +314 -7
- package/dist/helper/barell.js +32 -0
- package/dist/helper/code.js +36 -1
- package/dist/helper/core.js +18 -0
- package/dist/helper/exports.d.ts +22 -0
- package/dist/helper/exports.js +104 -3
- package/dist/helper/handler.js +2 -2
- package/dist/helper/index.d.ts +0 -1
- package/dist/helper/index.js +14 -0
- package/dist/helper/openapi.d.ts +40 -3
- package/dist/helper/openapi.js +901 -9
- package/dist/helper/schema.d.ts +4 -4
- package/dist/helper/schema.js +210 -1
- package/dist/helper/type.js +232 -0
- package/dist/helper/wrap.js +279 -1
- package/dist/index.js +2178 -10
- package/dist/openapi/index.d.ts +83 -4
- package/dist/openapi/index.js +253 -0
- package/dist/utils/index.d.ts +1 -78
- package/dist/utils/index.js +271 -106
- package/dist/vite-plugin/index.d.ts +0 -41
- package/dist/vite-plugin/index.js +362 -141
- package/package.json +1 -5
package/README.md
CHANGED
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,39 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Main CLI entry point for hono-takibi.
|
|
3
|
-
*
|
|
4
|
-
* Processes command-line arguments or config file to generate TypeScript
|
|
5
|
-
* code from OpenAPI specifications. Supports both CLI mode and config file mode.
|
|
6
|
-
*
|
|
7
|
-
* ```mermaid
|
|
8
|
-
* flowchart TD
|
|
9
|
-
* A["Start"] --> B{"Args: --help/-h?"}
|
|
10
|
-
* B -->|Yes| C["Return help text"]
|
|
11
|
-
* B -->|No| D{"Config file exists?"}
|
|
12
|
-
* D -->|No| E["CLI Mode"]
|
|
13
|
-
* D -->|Yes| F["Config Mode"]
|
|
14
|
-
* E --> G["parseCli(args)"]
|
|
15
|
-
* G --> H["parseOpenAPI(input)"]
|
|
16
|
-
* H --> I["takibi(openAPI, ...)"]
|
|
17
|
-
* F --> J["config()"]
|
|
18
|
-
* J --> K["parseOpenAPI(config.input)"]
|
|
19
|
-
* K --> L["Generate all components"]
|
|
20
|
-
* L --> M["Return results"]
|
|
21
|
-
* I --> M
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* @returns Promise resolving to success with output message or error
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* // CLI usage
|
|
29
|
-
* const result = await honoTakibi()
|
|
30
|
-
* if (result.ok) {
|
|
31
|
-
* console.log(result.value) // "Generated code written to routes.ts"
|
|
32
|
-
* } else {
|
|
33
|
-
* console.error(result.error)
|
|
34
|
-
* }
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
1
|
export declare function honoTakibi(): Promise<{
|
|
38
2
|
readonly ok: true;
|
|
39
3
|
readonly value: string;
|
package/dist/cli/index.js
CHANGED
|
@@ -21,10 +21,9 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { existsSync } from 'node:fs';
|
|
23
23
|
import { resolve } from 'node:path';
|
|
24
|
-
import {
|
|
24
|
+
import { readConfig } from '../config/index.js';
|
|
25
25
|
import { callbacks, examples, headers, links, parameters, requestBodies, responses, route, rpc, schemas, securitySchemes, takibi, type, } from '../core/index.js';
|
|
26
26
|
import { parseOpenAPI } from '../openapi/index.js';
|
|
27
|
-
import { parseCli } from '../utils/index.js';
|
|
28
27
|
const HELP_TEXT = `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
|
|
29
28
|
|
|
30
29
|
Options:
|
|
@@ -80,6 +79,71 @@ Options:
|
|
|
80
79
|
* }
|
|
81
80
|
* ```
|
|
82
81
|
*/
|
|
82
|
+
/**
|
|
83
|
+
* Parse raw CLI arguments into structured options.
|
|
84
|
+
*
|
|
85
|
+
* - Validates `<input>` ends with `.yaml`/`.json`/`.tsp`
|
|
86
|
+
* - Requires `-o <output.ts>`
|
|
87
|
+
* - Extracts boolean flags for component exports and templates/tests
|
|
88
|
+
* - Extracts optional `--base-path <path>`
|
|
89
|
+
*
|
|
90
|
+
* ```mermaid
|
|
91
|
+
* flowchart TD
|
|
92
|
+
* A["parseCli(args)"] --> B["Extract input & output (-o)"]
|
|
93
|
+
* B --> C{"input endsWith .yaml/.json/.tsp AND output endsWith .ts?"}
|
|
94
|
+
* C -->|No| D["return { ok:false, error:'Usage: hono-takibi ...' }"]
|
|
95
|
+
* C -->|Yes| E["Read flags (--export-schemas-types, --export-schemas, ..., --template, --test)"]
|
|
96
|
+
* E --> F["Read optional --base-path value"]
|
|
97
|
+
* F --> G["return { ok:true, value:{ input, output, flags... } }"]
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* @param args - Raw CLI arguments (e.g., `process.argv.slice(2)`).
|
|
101
|
+
* @returns `{ ok:true, value }` on success; `{ ok:false, error }` on invalid usage.
|
|
102
|
+
*/
|
|
103
|
+
function parseCli(args) {
|
|
104
|
+
const input = args[0];
|
|
105
|
+
const oIdx = args.indexOf('-o');
|
|
106
|
+
const output = oIdx !== -1 ? args[oIdx + 1] : undefined;
|
|
107
|
+
/** yaml or json or tsp */
|
|
108
|
+
const isYamlOrJsonOrTsp = (i) => i.endsWith('.yaml') || i.endsWith('.json') || i.endsWith('.tsp');
|
|
109
|
+
const isTs = (o) => o.endsWith('.ts');
|
|
110
|
+
const getFlagValue = (args, flag) => {
|
|
111
|
+
const idx = args.indexOf(flag);
|
|
112
|
+
if (idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith('-'))
|
|
113
|
+
return args[idx + 1];
|
|
114
|
+
return undefined;
|
|
115
|
+
};
|
|
116
|
+
if (!(input && output && isYamlOrJsonOrTsp(input) && isTs(output))) {
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
error: HELP_TEXT,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
ok: true,
|
|
124
|
+
value: {
|
|
125
|
+
input,
|
|
126
|
+
output,
|
|
127
|
+
template: args.includes('--template'),
|
|
128
|
+
test: args.includes('--test'),
|
|
129
|
+
basePath: getFlagValue(args, '--base-path') ?? '/', // default: /
|
|
130
|
+
componentsOptions: {
|
|
131
|
+
exportSchemas: args.includes('--export-schemas') ?? false,
|
|
132
|
+
exportSchemasTypes: args.includes('--export-schemas-types') ?? false,
|
|
133
|
+
exportParameters: args.includes('--export-parameters') ?? false,
|
|
134
|
+
exportParametersTypes: args.includes('--export-parameters-types') ?? false,
|
|
135
|
+
exportSecuritySchemes: args.includes('--export-security-schemes') ?? false,
|
|
136
|
+
exportRequestBodies: args.includes('--export-request-bodies') ?? false,
|
|
137
|
+
exportResponses: args.includes('--export-responses') ?? false,
|
|
138
|
+
exportHeaders: args.includes('--export-headers') ?? false,
|
|
139
|
+
exportHeadersTypes: args.includes('--export-headers-types') ?? false,
|
|
140
|
+
exportExamples: args.includes('--export-examples') ?? false,
|
|
141
|
+
exportLinks: args.includes('--export-links') ?? false,
|
|
142
|
+
exportCallbacks: args.includes('--export-callbacks') ?? false,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
83
147
|
export async function honoTakibi() {
|
|
84
148
|
const args = process.argv.slice(2);
|
|
85
149
|
if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
|
|
@@ -101,7 +165,7 @@ export async function honoTakibi() {
|
|
|
101
165
|
return { ok: false, error: takibiResult.error };
|
|
102
166
|
return { ok: true, value: takibiResult.value };
|
|
103
167
|
}
|
|
104
|
-
const loadConfigResult = await
|
|
168
|
+
const loadConfigResult = await readConfig();
|
|
105
169
|
if (!loadConfigResult.ok)
|
|
106
170
|
return { ok: false, error: loadConfigResult.error };
|
|
107
171
|
const config = loadConfigResult.value;
|
|
@@ -204,3 +268,253 @@ export async function honoTakibi() {
|
|
|
204
268
|
].filter((v) => v !== undefined);
|
|
205
269
|
return { ok: true, value: results.join('\n') };
|
|
206
270
|
}
|
|
271
|
+
// Test run
|
|
272
|
+
// pnpm vitest run ./packages/hono-takibi/src/cli/index.ts
|
|
273
|
+
if (import.meta.vitest) {
|
|
274
|
+
const { describe, it, expect, beforeAll, afterAll } = import.meta.vitest;
|
|
275
|
+
const fs = await import('node:fs');
|
|
276
|
+
const openapi = {
|
|
277
|
+
openapi: '3.1.0',
|
|
278
|
+
info: {
|
|
279
|
+
title: 'HonoTakibi',
|
|
280
|
+
version: 'v1',
|
|
281
|
+
},
|
|
282
|
+
tags: [{ name: 'Hono' }, { name: 'HonoX' }, { name: 'ZodOpenAPIHono' }],
|
|
283
|
+
paths: {
|
|
284
|
+
'/hono': {
|
|
285
|
+
get: {
|
|
286
|
+
tags: ['Hono'],
|
|
287
|
+
summary: 'Hono',
|
|
288
|
+
description: 'Hono',
|
|
289
|
+
responses: {
|
|
290
|
+
'200': {
|
|
291
|
+
description: 'OK',
|
|
292
|
+
content: {
|
|
293
|
+
'application/json': {
|
|
294
|
+
schema: {
|
|
295
|
+
type: 'object',
|
|
296
|
+
properties: {
|
|
297
|
+
message: {
|
|
298
|
+
type: 'string',
|
|
299
|
+
example: 'Hono',
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
required: ['message'],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
'/hono-x': {
|
|
311
|
+
get: {
|
|
312
|
+
tags: ['HonoX'],
|
|
313
|
+
summary: 'HonoX',
|
|
314
|
+
description: 'HonoX',
|
|
315
|
+
responses: {
|
|
316
|
+
'200': {
|
|
317
|
+
description: 'OK',
|
|
318
|
+
content: {
|
|
319
|
+
'application/json': {
|
|
320
|
+
schema: {
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: {
|
|
323
|
+
message: {
|
|
324
|
+
type: 'string',
|
|
325
|
+
example: 'HonoX',
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
required: ['message'],
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
'/zod-openapi-hono': {
|
|
337
|
+
get: {
|
|
338
|
+
tags: ['ZodOpenAPIHono'],
|
|
339
|
+
summary: 'ZodOpenAPIHono',
|
|
340
|
+
description: 'ZodOpenAPIHono',
|
|
341
|
+
responses: {
|
|
342
|
+
'200': {
|
|
343
|
+
description: 'OK',
|
|
344
|
+
content: {
|
|
345
|
+
'application/json': {
|
|
346
|
+
schema: {
|
|
347
|
+
type: 'object',
|
|
348
|
+
properties: {
|
|
349
|
+
message: {
|
|
350
|
+
type: 'string',
|
|
351
|
+
example: 'ZodOpenAPIHono',
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
required: ['message'],
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
describe('honoTakibi', () => {
|
|
365
|
+
beforeAll(() => {
|
|
366
|
+
process.argv = [
|
|
367
|
+
'*/*/bin/node',
|
|
368
|
+
'*/dist/index.js',
|
|
369
|
+
'openapi.json',
|
|
370
|
+
'-o',
|
|
371
|
+
'zod-openapi-hono.ts',
|
|
372
|
+
];
|
|
373
|
+
fs.writeFileSync('openapi.json', JSON.stringify(openapi));
|
|
374
|
+
});
|
|
375
|
+
afterAll(() => {
|
|
376
|
+
fs.rmSync('openapi.json', { force: true });
|
|
377
|
+
fs.rmSync('zod-openapi-hono.ts', { force: true });
|
|
378
|
+
});
|
|
379
|
+
it('honoTakibi generated', async () => {
|
|
380
|
+
const result = await honoTakibi();
|
|
381
|
+
expect(result).toEqual({
|
|
382
|
+
ok: true,
|
|
383
|
+
value: 'Generated code written to zod-openapi-hono.ts',
|
|
384
|
+
});
|
|
385
|
+
const generatedCode = fs.readFileSync('zod-openapi-hono.ts', 'utf-8');
|
|
386
|
+
const expectedCode = `import { createRoute, z } from '@hono/zod-openapi'
|
|
387
|
+
|
|
388
|
+
export const getHonoRoute = createRoute({
|
|
389
|
+
method: 'get',
|
|
390
|
+
path: '/hono',
|
|
391
|
+
tags: ['Hono'],
|
|
392
|
+
summary: 'Hono',
|
|
393
|
+
description: 'Hono',
|
|
394
|
+
responses: {
|
|
395
|
+
200: {
|
|
396
|
+
description: 'OK',
|
|
397
|
+
content: {
|
|
398
|
+
'application/json': {
|
|
399
|
+
schema: z
|
|
400
|
+
.object({ message: z.string().openapi({ example: 'Hono' }) })
|
|
401
|
+
.openapi({ required: ['message'] }),
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
export const getHonoXRoute = createRoute({
|
|
409
|
+
method: 'get',
|
|
410
|
+
path: '/hono-x',
|
|
411
|
+
tags: ['HonoX'],
|
|
412
|
+
summary: 'HonoX',
|
|
413
|
+
description: 'HonoX',
|
|
414
|
+
responses: {
|
|
415
|
+
200: {
|
|
416
|
+
description: 'OK',
|
|
417
|
+
content: {
|
|
418
|
+
'application/json': {
|
|
419
|
+
schema: z
|
|
420
|
+
.object({ message: z.string().openapi({ example: 'HonoX' }) })
|
|
421
|
+
.openapi({ required: ['message'] }),
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
export const getZodOpenapiHonoRoute = createRoute({
|
|
429
|
+
method: 'get',
|
|
430
|
+
path: '/zod-openapi-hono',
|
|
431
|
+
tags: ['ZodOpenAPIHono'],
|
|
432
|
+
summary: 'ZodOpenAPIHono',
|
|
433
|
+
description: 'ZodOpenAPIHono',
|
|
434
|
+
responses: {
|
|
435
|
+
200: {
|
|
436
|
+
description: 'OK',
|
|
437
|
+
content: {
|
|
438
|
+
'application/json': {
|
|
439
|
+
schema: z
|
|
440
|
+
.object({ message: z.string().openapi({ example: 'ZodOpenAPIHono' }) })
|
|
441
|
+
.openapi({ required: ['message'] }),
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
})
|
|
447
|
+
`;
|
|
448
|
+
expect(generatedCode).toBe(expectedCode);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
describe('honoTakibi --help', () => {
|
|
452
|
+
beforeAll(() => {
|
|
453
|
+
process.argv = ['*/*/bin/node', '*/dist/index.js', '--help'];
|
|
454
|
+
});
|
|
455
|
+
it('honoTakibi help requested --help', async () => {
|
|
456
|
+
const result = await honoTakibi();
|
|
457
|
+
expect(result).toStrictEqual({
|
|
458
|
+
ok: true,
|
|
459
|
+
value: `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
|
|
460
|
+
|
|
461
|
+
Options:
|
|
462
|
+
--export-schemas-types export schemas types
|
|
463
|
+
--export-schemas export schemas
|
|
464
|
+
--export-parameters-types export parameters types
|
|
465
|
+
--export-parameters export parameters
|
|
466
|
+
--export-security-schemes export securitySchemes
|
|
467
|
+
--export-request-bodies export requestBodies
|
|
468
|
+
--export-responses export responses
|
|
469
|
+
--export-headers-types export headers types
|
|
470
|
+
--export-headers export headers
|
|
471
|
+
--export-examples export examples
|
|
472
|
+
--export-links export links
|
|
473
|
+
--export-callbacks export callbacks
|
|
474
|
+
--template generate app file and handler stubs
|
|
475
|
+
--test generate empty *.test.ts files
|
|
476
|
+
--base-path <path> api prefix (default: /)
|
|
477
|
+
-h, --help display help for command`,
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
describe('honoTakibi -h', () => {
|
|
482
|
+
beforeAll(() => {
|
|
483
|
+
process.argv = ['*/*/bin/node', '*/dist/index.js', '-h'];
|
|
484
|
+
});
|
|
485
|
+
it('honoTakibi help requested -h', async () => {
|
|
486
|
+
const result = await honoTakibi();
|
|
487
|
+
expect(result).toStrictEqual({
|
|
488
|
+
ok: true,
|
|
489
|
+
value: `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
|
|
490
|
+
|
|
491
|
+
Options:
|
|
492
|
+
--export-schemas-types export schemas types
|
|
493
|
+
--export-schemas export schemas
|
|
494
|
+
--export-parameters-types export parameters types
|
|
495
|
+
--export-parameters export parameters
|
|
496
|
+
--export-security-schemes export securitySchemes
|
|
497
|
+
--export-request-bodies export requestBodies
|
|
498
|
+
--export-responses export responses
|
|
499
|
+
--export-headers-types export headers types
|
|
500
|
+
--export-headers export headers
|
|
501
|
+
--export-examples export examples
|
|
502
|
+
--export-links export links
|
|
503
|
+
--export-callbacks export callbacks
|
|
504
|
+
--template generate app file and handler stubs
|
|
505
|
+
--test generate empty *.test.ts files
|
|
506
|
+
--base-path <path> api prefix (default: /)
|
|
507
|
+
-h, --help display help for command`,
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
describe('honoTakibi missing output', () => {
|
|
511
|
+
beforeAll(() => {
|
|
512
|
+
process.argv = ['node', 'dist/index.js', 'openapi.yaml'];
|
|
513
|
+
});
|
|
514
|
+
it('should fail if output is not specified', async () => {
|
|
515
|
+
const result = await honoTakibi();
|
|
516
|
+
expect(result.ok).toBe(false);
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
}
|
package/dist/config/index.d.ts
CHANGED
|
@@ -85,7 +85,7 @@ export declare function parseConfig(config: Config): {
|
|
|
85
85
|
readonly ok: false;
|
|
86
86
|
readonly error: string;
|
|
87
87
|
};
|
|
88
|
-
export declare function
|
|
88
|
+
export declare function readConfig(): Promise<{
|
|
89
89
|
readonly ok: true;
|
|
90
90
|
readonly value: Config;
|
|
91
91
|
} | {
|
package/dist/config/index.js
CHANGED
|
@@ -225,7 +225,7 @@ export function parseConfig(config) {
|
|
|
225
225
|
}
|
|
226
226
|
return { ok: true, value: config };
|
|
227
227
|
}
|
|
228
|
-
export async function
|
|
228
|
+
export async function readConfig() {
|
|
229
229
|
const abs = resolve(process.cwd(), 'hono-takibi.config.ts');
|
|
230
230
|
if (!existsSync(abs))
|
|
231
231
|
return { ok: false, error: `Config not found: ${abs}` };
|
|
@@ -252,3 +252,64 @@ export async function loadConfig() {
|
|
|
252
252
|
export function defineConfig(config) {
|
|
253
253
|
return config;
|
|
254
254
|
}
|
|
255
|
+
// Test run
|
|
256
|
+
// pnpm vitest run ./packages/hono-takibi/src/config/index.ts
|
|
257
|
+
if (import.meta.vitest) {
|
|
258
|
+
const { describe, it, expect, beforeEach, afterEach, vi } = import.meta.vitest;
|
|
259
|
+
const fs = await import('node:fs');
|
|
260
|
+
const os = await import('node:os');
|
|
261
|
+
const path = await import('node:path');
|
|
262
|
+
describe('loadConfig()', () => {
|
|
263
|
+
const origCwd = process.cwd();
|
|
264
|
+
beforeEach(() => {
|
|
265
|
+
vi.resetModules();
|
|
266
|
+
const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'hono-takibi-config-ts-'));
|
|
267
|
+
process.chdir(tmpdir);
|
|
268
|
+
});
|
|
269
|
+
afterEach(() => {
|
|
270
|
+
const cwd = process.cwd();
|
|
271
|
+
process.chdir(origCwd);
|
|
272
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
273
|
+
});
|
|
274
|
+
it('passes: legacy top-level output mode', async () => {
|
|
275
|
+
const p = path.join(process.cwd(), 'hono-takibi.config.ts');
|
|
276
|
+
const c = `
|
|
277
|
+
export default {
|
|
278
|
+
input: 'openapi.yaml',
|
|
279
|
+
'zod-openapi': {
|
|
280
|
+
output: 'routes/index.ts',
|
|
281
|
+
exportSchemasTypes: true,
|
|
282
|
+
exportSchemas: true
|
|
283
|
+
},
|
|
284
|
+
rpc: {
|
|
285
|
+
output: 'rpc/index.ts',
|
|
286
|
+
import: '../client'
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
`;
|
|
290
|
+
fs.writeFileSync(p, c, 'utf-8');
|
|
291
|
+
await expect(readConfig()).resolves.toStrictEqual({
|
|
292
|
+
ok: true,
|
|
293
|
+
value: {
|
|
294
|
+
input: 'openapi.yaml',
|
|
295
|
+
'zod-openapi': {
|
|
296
|
+
output: 'routes/index.ts',
|
|
297
|
+
exportSchemasTypes: true,
|
|
298
|
+
exportSchemas: true,
|
|
299
|
+
},
|
|
300
|
+
rpc: {
|
|
301
|
+
output: 'rpc/index.ts',
|
|
302
|
+
import: '../client',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
it('fails: config file missing', async () => {
|
|
308
|
+
const result = await readConfig();
|
|
309
|
+
expect(result.ok).toBe(false);
|
|
310
|
+
if (!result.ok) {
|
|
311
|
+
expect(result.error).toMatch(/Config not found:/);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
}
|
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
* Example component generation module.
|
|
3
3
|
*
|
|
4
4
|
* Handles generation of JSON exports from OpenAPI example components
|
|
5
|
-
* with support for split mode.
|
|
5
|
+
* with support for split mode and $ref resolution.
|
|
6
6
|
*
|
|
7
7
|
* @module core/components/examples
|
|
8
8
|
*/
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { makeExportConst } from '../../helper/code.js';
|
|
11
|
-
import { core,
|
|
11
|
+
import { core, makeRef } from '../../helper/index.js';
|
|
12
|
+
import { ensureSuffix, lowerFirst, toIdentifierPascalCase } from '../../utils/index.js';
|
|
13
|
+
/**
|
|
14
|
+
* Type guard for $ref property.
|
|
15
|
+
*
|
|
16
|
+
* @param val - Value to check
|
|
17
|
+
* @returns True if value has $ref property
|
|
18
|
+
*/
|
|
19
|
+
const hasRef = (val) => typeof val === 'object' && val !== null && '$ref' in val && typeof val.$ref === 'string';
|
|
12
20
|
/**
|
|
13
21
|
* Generates example component files.
|
|
14
22
|
*
|
|
@@ -42,10 +50,38 @@ export async function examples(examples, output, split) {
|
|
|
42
50
|
if (keys.length === 0)
|
|
43
51
|
return { ok: true, value: 'No examples found' };
|
|
44
52
|
if (split) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
const outDir = output.replace(/\.ts$/, '');
|
|
54
|
+
// Generate index.ts with sorted exports
|
|
55
|
+
const indexCode = `${keys
|
|
56
|
+
.sort()
|
|
57
|
+
.map((v) => `export * from './${lowerFirst(v)}.ts'`)
|
|
58
|
+
.join('\n')}\n`;
|
|
59
|
+
const results = await Promise.all([
|
|
60
|
+
...keys.map((key) => {
|
|
61
|
+
const v = examples[key];
|
|
62
|
+
const name = toIdentifierPascalCase(ensureSuffix(key, 'Example'));
|
|
63
|
+
const filePath = path.join(outDir, `${lowerFirst(key)}.ts`);
|
|
64
|
+
// Handle $ref references: generate import + re-export
|
|
65
|
+
if (hasRef(v)) {
|
|
66
|
+
const refName = makeRef(v.$ref);
|
|
67
|
+
const refKey = v.$ref.split('/').at(-1) ?? '';
|
|
68
|
+
const importPath = `./${lowerFirst(refKey)}.ts`;
|
|
69
|
+
const body = `import { ${refName} } from '${importPath}'\n\nexport const ${name} = ${refName}\n`;
|
|
70
|
+
return core(body, path.dirname(filePath), filePath);
|
|
71
|
+
}
|
|
72
|
+
// Inline value: generate JSON export
|
|
73
|
+
const body = `export const ${name} = ${JSON.stringify(v)}\n`;
|
|
74
|
+
return core(body, path.dirname(filePath), filePath);
|
|
75
|
+
}),
|
|
76
|
+
core(indexCode, path.dirname(path.join(outDir, 'index.ts')), path.join(outDir, 'index.ts')),
|
|
77
|
+
]);
|
|
78
|
+
const firstError = results.find((r) => !r.ok);
|
|
79
|
+
if (firstError)
|
|
80
|
+
return firstError;
|
|
81
|
+
return {
|
|
82
|
+
ok: true,
|
|
83
|
+
value: `Generated Example code written to ${outDir}/*.ts (index.ts included)`,
|
|
84
|
+
};
|
|
49
85
|
}
|
|
50
86
|
const code = makeExportConst(examples, 'Example');
|
|
51
87
|
const coreResult = await core(code, path.dirname(output), output);
|
|
@@ -59,8 +59,8 @@ export async function headers(headers, output, split, exportType, components) {
|
|
|
59
59
|
core(makeBarell(headers), outDir, path.join(outDir, 'index.ts')),
|
|
60
60
|
]);
|
|
61
61
|
const firstError = allResults.find((r) => !r.ok);
|
|
62
|
-
if (firstError
|
|
63
|
-
return
|
|
62
|
+
if (firstError)
|
|
63
|
+
return firstError;
|
|
64
64
|
return {
|
|
65
65
|
ok: true,
|
|
66
66
|
value: `Generated headers code written to ${outDir}/*.ts (index.ts included)`,
|
|
@@ -59,8 +59,8 @@ export async function parameters(parameters, output, split, exportType, componen
|
|
|
59
59
|
core(makeBarell(parameters), outDir, path.join(outDir, 'index.ts')),
|
|
60
60
|
]);
|
|
61
61
|
const firstError = allResults.find((r) => !r.ok);
|
|
62
|
-
if (firstError
|
|
63
|
-
return
|
|
62
|
+
if (firstError)
|
|
63
|
+
return firstError;
|
|
64
64
|
return {
|
|
65
65
|
ok: true,
|
|
66
66
|
value: `Generated parameters code written to ${outDir}/*.ts (index.ts included)`,
|
|
@@ -56,8 +56,8 @@ export async function requestBodies(requestBodies, output, split, components) {
|
|
|
56
56
|
core(makeBarell(requestBodies), outDir, path.join(outDir, 'index.ts')),
|
|
57
57
|
]);
|
|
58
58
|
const firstError = allResults.find((r) => !r.ok);
|
|
59
|
-
if (firstError
|
|
60
|
-
return
|
|
59
|
+
if (firstError)
|
|
60
|
+
return firstError;
|
|
61
61
|
return {
|
|
62
62
|
ok: true,
|
|
63
63
|
value: `Generated requestBodies code written to ${outDir}/*.ts (index.ts included)`,
|
|
@@ -56,8 +56,8 @@ export async function responses(responses, output, split, components) {
|
|
|
56
56
|
core(makeBarell(responses), outDir, path.join(outDir, 'index.ts')),
|
|
57
57
|
]);
|
|
58
58
|
const firstError = allResults.find((r) => !r.ok);
|
|
59
|
-
if (firstError
|
|
60
|
-
return
|
|
59
|
+
if (firstError)
|
|
60
|
+
return firstError;
|
|
61
61
|
return {
|
|
62
62
|
ok: true,
|
|
63
63
|
value: `Generated responses code written to ${outDir}/*.ts (index.ts included)`,
|
|
@@ -57,8 +57,8 @@ export async function schemas(schemas, output, split, exportType) {
|
|
|
57
57
|
core(makeBarell(schemas), path.dirname(`${outDir}/index.ts`), `${outDir}/index.ts`),
|
|
58
58
|
]);
|
|
59
59
|
const firstError = allResults.find((r) => !r.ok);
|
|
60
|
-
if (firstError
|
|
61
|
-
return
|
|
60
|
+
if (firstError)
|
|
61
|
+
return firstError;
|
|
62
62
|
return {
|
|
63
63
|
ok: true,
|
|
64
64
|
value: `Generated schema code written to ${outDir}/*.ts (index.ts included)`,
|