hono-takibi 0.9.1 → 0.9.5
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 +133 -82
- package/dist/cli/index.d.ts +14 -15
- package/dist/cli/index.js +42 -17
- package/dist/config/index.d.ts +29 -14
- package/dist/config/index.js +19 -9
- package/dist/core/route.d.ts +7 -0
- package/dist/core/route.js +99 -0
- package/dist/core/rpc.d.ts +11 -5
- package/dist/core/rpc.js +339 -15
- package/dist/core/schema.d.ts +27 -0
- package/dist/core/schema.js +125 -0
- package/dist/core/takibi.d.ts +4 -4
- package/dist/core/takibi.js +91 -49
- package/dist/format/index.d.ts +4 -6
- package/dist/fsp/index.d.ts +11 -11
- package/dist/generator/zod-openapi-hono/app/index.js +53 -20
- package/dist/generator/zod-openapi-hono/openapi/index.d.ts +1 -1
- package/dist/generator/zod-openapi-hono/openapi/index.js +1 -1
- package/dist/generator/zod-openapi-hono/openapi/route/index.d.ts +0 -12
- package/dist/generator/zod-openapi-hono/openapi/route/index.js +37 -29
- package/dist/generator/zod-openapi-hono/openapi/route/params/params-object.d.ts +1 -1
- package/dist/generator/zod-openapi-hono/openapi/route/params/request-parameter.d.ts +1 -1
- package/dist/generator/zod-openapi-hono/openapi/route/params/request-parameter.js +1 -0
- package/dist/generator/zod-openapi-hono/openapi/route/response/index.js +66 -33
- package/dist/generator/zod-openapi-hono/openapi/route/route.js +2 -2
- package/dist/generator/zod-to-openapi/z/object.js +21 -1
- package/dist/helper/index.d.ts +0 -1
- package/dist/helper/index.js +0 -1
- package/dist/helper/properties-schema.d.ts +1 -1
- package/dist/openapi/index.d.ts +7 -7
- package/dist/openapi/index.js +4 -4
- package/dist/typespec/index.d.ts +4 -4
- package/dist/utils/index.d.ts +95 -184
- package/dist/utils/index.js +188 -228
- package/dist/vite-plugin/index.d.ts +1 -41
- package/dist/vite-plugin/index.js +458 -70
- package/package.json +4 -4
- package/dist/generator/rpc/index.d.ts +0 -2
- package/dist/generator/rpc/index.js +0 -338
- package/dist/helper/get-route-maps.d.ts +0 -12
- package/dist/helper/get-route-maps.js +0 -20
package/README.md
CHANGED
|
@@ -33,31 +33,6 @@ If you have OpenAPI specifications, Hono Takibi automates the conversion process
|
|
|
33
33
|
npx hono-takibi path/to/input.{yaml,json,tsp} -o path/to/output.ts
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
## CLI
|
|
37
|
-
|
|
38
|
-
### Options
|
|
39
|
-
|
|
40
|
-
basic
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
Options:
|
|
44
|
-
--export-type export TypeScript type aliases
|
|
45
|
-
--export-schema export Zod schema objects
|
|
46
|
-
--template generate app file and handler stubs
|
|
47
|
-
--test generate empty *.test.ts files
|
|
48
|
-
--base-path <path> api prefix (default: /)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
template
|
|
52
|
-
|
|
53
|
-
> **⚠️** When using the `--template` option, you must specify a valid directory path. Ensure the directory exists before executing the
|
|
54
|
-
|
|
55
|
-
### Example
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
npx hono-takibi path/to/input.{yaml,json,tsp} -o path/to/output.ts --export-type --export-schema --template --base-path '/api/v1'
|
|
59
|
-
```
|
|
60
|
-
|
|
61
36
|
input:
|
|
62
37
|
|
|
63
38
|
```yaml
|
|
@@ -112,101 +87,177 @@ export const getRoute = createRoute({
|
|
|
112
87
|
|
|
113
88
|

|
|
114
89
|
|
|
115
|
-
##
|
|
90
|
+
## CLI
|
|
116
91
|
|
|
117
|
-
###
|
|
92
|
+
### Options
|
|
118
93
|
|
|
119
|
-
|
|
94
|
+
basic
|
|
120
95
|
|
|
121
|
-
|
|
96
|
+
```bash
|
|
97
|
+
Options:
|
|
98
|
+
--export-type export TypeScript type aliases
|
|
99
|
+
--export-schema export Zod schema objects
|
|
100
|
+
--template generate app file and handler stubs
|
|
101
|
+
--test generate empty *.test.ts files
|
|
102
|
+
--base-path <path> api prefix (default: /)
|
|
103
|
+
```
|
|
122
104
|
|
|
123
|
-
|
|
124
|
-
- It must be fully compliant with **OpenAPI 3.0 or later (e.g., 3.0 or 3.1)**.
|
|
125
|
-
- Do **not** include `paths`, `tags`, or any other OpenAPI sections.
|
|
105
|
+
template
|
|
126
106
|
|
|
127
|
-
|
|
107
|
+
> **⚠️** When using the `--template` option, you must specify a valid directory path. Ensure the directory exists before executing the
|
|
128
108
|
|
|
129
|
-
|
|
109
|
+
### Example
|
|
130
110
|
|
|
131
|
-
|
|
132
|
-
-
|
|
133
|
-
|
|
111
|
+
```bash
|
|
112
|
+
npx hono-takibi path/to/input.{yaml,json,tsp} -o path/to/output.ts --export-type --export-schema --template --base-path '/api/v1'
|
|
113
|
+
```
|
|
134
114
|
|
|
135
|
-
|
|
115
|
+
## Configuration File (`hono-takibi.config.ts`)
|
|
136
116
|
|
|
137
|
-
|
|
117
|
+
Config used by both the CLI and the Vite plugin.
|
|
138
118
|
|
|
139
|
-
|
|
140
|
-
- @typespec/rest
|
|
141
|
-
- ...other
|
|
119
|
+
## Essentials
|
|
142
120
|
|
|
143
|
-
|
|
121
|
+
* Put **`hono-takibi.config.ts`** at repo root.
|
|
122
|
+
* Default‑export with `defineConfig(...)`.
|
|
123
|
+
* `input`: **`openapi.yaml`** (recommended), or `*.json` / `*.tsp`.
|
|
144
124
|
|
|
145
|
-
`
|
|
125
|
+
> **About `split`**
|
|
126
|
+
>
|
|
127
|
+
> * `split: true` → `output` is a **directory**; many files + `index.ts`.
|
|
128
|
+
> * `split` **omitted** or `false` → `output` is a **single `*.ts` file** (one file only).
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Single‑file
|
|
133
|
+
|
|
134
|
+
One file. Set top‑level `output` (don’t define `schema`/`route`).
|
|
146
135
|
|
|
147
136
|
```ts
|
|
148
|
-
import { defineConfig } from '
|
|
149
|
-
import HonoTakibiVite from 'hono-takibi/vite-plugin'
|
|
137
|
+
import { defineConfig } from 'hono-takibi/config'
|
|
150
138
|
|
|
151
139
|
export default defineConfig({
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}),
|
|
159
|
-
],
|
|
140
|
+
input: 'openapi.yaml',
|
|
141
|
+
'zod-openapi': {
|
|
142
|
+
output: './src/index.ts',
|
|
143
|
+
exportSchema: true,
|
|
144
|
+
exportType: true,
|
|
145
|
+
},
|
|
160
146
|
})
|
|
161
147
|
```
|
|
162
148
|
|
|
163
|
-
|
|
149
|
+
---
|
|
164
150
|
|
|
165
|
-
|
|
151
|
+
## Schemas & Routes
|
|
152
|
+
|
|
153
|
+
Define **both** `schema` and `route` (don’t set top‑level `output`).
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { defineConfig } from 'hono-takibi/config'
|
|
157
|
+
|
|
158
|
+
export default defineConfig({
|
|
159
|
+
input: 'openapi.yaml',
|
|
160
|
+
'zod-openapi': {
|
|
161
|
+
// split ON → outputs are directories
|
|
162
|
+
schema: { output: './src/schemas', split: true },
|
|
163
|
+
route: { output: './src/routes', import: '../schemas', split: true },
|
|
164
|
+
|
|
165
|
+
// split OFF example (one file each):
|
|
166
|
+
// schema: { output: './src/schemas/index.ts' },
|
|
167
|
+
// route: { output: './src/routes/index.ts', import: '../schemas' },
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## RPC (optional)
|
|
175
|
+
|
|
176
|
+
Works with either pattern.
|
|
177
|
+
|
|
178
|
+
* `split: true` → `output` is a **directory**; many files + `index.ts`.
|
|
179
|
+
* `split` **omitted** or `false` → `output` is **one `*.ts` file**.
|
|
180
|
+
|
|
181
|
+
**Example (split: true)**
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { defineConfig } from 'hono-takibi/config'
|
|
166
185
|
|
|
186
|
+
export default defineConfig({
|
|
187
|
+
input: 'openapi.yaml',
|
|
188
|
+
'zod-openapi': { output: './src/index.ts', exportSchema: true, exportType: true },
|
|
189
|
+
rpc: { output: './src/rpc', import: '../client', split: true },
|
|
190
|
+
})
|
|
191
|
+
```
|
|
167
192
|
|
|
168
|
-
|
|
193
|
+
**Example (single file; split omitted/false)**
|
|
169
194
|
|
|
170
|
-
|
|
195
|
+
```ts
|
|
196
|
+
import { defineConfig } from 'hono-takibi/config'
|
|
171
197
|
|
|
172
|
-
|
|
198
|
+
export default defineConfig({
|
|
199
|
+
input: 'openapi.yaml',
|
|
200
|
+
'zod-openapi': { output: './src/index.ts', exportSchema: true, exportType: true },
|
|
201
|
+
rpc: { output: './src/rpc/index.ts', import: '../client' },
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Vite Plugin (`honoTakibiVite`)
|
|
173
208
|
|
|
174
|
-
|
|
209
|
+
Auto‑regenerates on changes and reloads dev server.
|
|
175
210
|
|
|
176
|
-
```
|
|
177
|
-
|
|
211
|
+
```ts
|
|
212
|
+
// vite.config.ts
|
|
213
|
+
import { honoTakibiVite } from 'hono-takibi/vite-plugin'
|
|
214
|
+
import { defineConfig } from 'vite'
|
|
215
|
+
|
|
216
|
+
export default defineConfig({
|
|
217
|
+
plugins: [honoTakibiVite()],
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**What it does**
|
|
222
|
+
|
|
223
|
+
* **Watches**: the config, your `input`, and nearby `**/*.{yaml,json,tsp}`.
|
|
224
|
+
* **Generates** outputs per your config (single‑file or split, plus `rpc`).
|
|
225
|
+
* **Cleans** old generated files safely when paths or `split` change.
|
|
226
|
+
|
|
227
|
+
That’s it — set `input`, choose one of the two patterns, and (optionally) add `rpc`. ✅
|
|
228
|
+
|
|
229
|
+
### Demo (Vite + HMR)
|
|
230
|
+
|
|
231
|
+

|
|
178
232
|
|
|
179
|
-
## 1. Version
|
|
180
|
-
- Accept files that start with `openapi: "3.0.0"` or newer.
|
|
181
|
-
- Otherwise reply with: `Unsupported OpenAPI version (must be 3.0+).`
|
|
182
233
|
|
|
183
|
-
##
|
|
184
|
-
- Look **only** inside `#/components/schemas/`. Ignore everything else.
|
|
185
|
-
- `$ref` must also point inside that section.
|
|
234
|
+
## AI Prompt Example
|
|
186
235
|
|
|
187
|
-
|
|
188
|
-
|
|
236
|
+
```sh
|
|
237
|
+
Generate one **OpenAPI 3.x+** YAML (prefer **3.1.0**).
|
|
189
238
|
|
|
190
|
-
|
|
191
|
-
-
|
|
192
|
-
-
|
|
239
|
+
Rules:
|
|
240
|
+
- Use only `components.schemas` (no other `components`).
|
|
241
|
+
- Never include `parameters:`.
|
|
242
|
+
- No path params; all inputs in `requestBody` (`application/json`) with `$ref: '#/components/schemas/*'`.
|
|
243
|
+
- All responses use `application/json` with `$ref: '#/components/schemas/*'`.
|
|
244
|
+
- POST-only action routes: `/resource/create|get|search|update|delete`.
|
|
245
|
+
- No inline schemas in `paths`.
|
|
193
246
|
|
|
194
|
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
-
|
|
247
|
+
Fill, then generate:
|
|
248
|
+
- title / version / tags
|
|
249
|
+
- resources & fields
|
|
250
|
+
- ops per resource: create / get / search / update / delete
|
|
198
251
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
3. Otherwise, output the exact error message above—nothing more.
|
|
252
|
+
**Output format (strict):**
|
|
253
|
+
- Return a **single fenced code block** labeled `yaml` that contains **only** the YAML.
|
|
254
|
+
- No text before or after the code block.
|
|
203
255
|
```
|
|
204
256
|
|
|
205
257
|
### ⚠️ WARNING: Potential Breaking Changes Without Notice
|
|
206
258
|
|
|
207
259
|
**This package is in active development and may introduce breaking changes without prior notice.**
|
|
208
260
|
Specifically:
|
|
209
|
-
- Query parameter coercion behavior may change
|
|
210
261
|
- Schema generation logic might be updated
|
|
211
262
|
- Output code structure could be modified
|
|
212
263
|
- Example value handling might be altered
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -5,17 +5,16 @@
|
|
|
5
5
|
*
|
|
6
6
|
* ```mermaid
|
|
7
7
|
* flowchart TD
|
|
8
|
-
* A["Start honoTakibi
|
|
9
|
-
* B --> C{"
|
|
10
|
-
* C
|
|
11
|
-
* C
|
|
12
|
-
* E --> F{"cliResult.ok
|
|
13
|
-
* F
|
|
14
|
-
* F
|
|
15
|
-
* H --> I
|
|
16
|
-
* I --> J
|
|
17
|
-
*
|
|
18
|
-
* J -->|Yes| L["return { ok:true, value: takibiResult.value }"]
|
|
8
|
+
* A["Start honoTakibi"] --> B["Parse args"]
|
|
9
|
+
* B --> C{"Help requested?"}
|
|
10
|
+
* C -- Yes --> D["Return help text"]
|
|
11
|
+
* C -- No --> E["parseCli(args)"]
|
|
12
|
+
* E --> F{"cliResult.ok?"}
|
|
13
|
+
* F -- No --> G["Return parse error"]
|
|
14
|
+
* F -- Yes --> H["Run takibi"]
|
|
15
|
+
* H --> I{"takibi.ok?"}
|
|
16
|
+
* I -- No --> J["Return takibi error"]
|
|
17
|
+
* I -- Yes --> K["Return success message"]
|
|
19
18
|
* ```
|
|
20
19
|
*
|
|
21
20
|
* **Options**
|
|
@@ -31,9 +30,9 @@
|
|
|
31
30
|
* - `{ ok: false, error: string }` on validation or generation errors
|
|
32
31
|
*/
|
|
33
32
|
export declare function honoTakibi(): Promise<{
|
|
34
|
-
ok: true;
|
|
35
|
-
value: string;
|
|
33
|
+
readonly ok: true;
|
|
34
|
+
readonly value: string;
|
|
36
35
|
} | {
|
|
37
|
-
ok: false;
|
|
38
|
-
error: string;
|
|
36
|
+
readonly ok: false;
|
|
37
|
+
readonly error: string;
|
|
39
38
|
}>;
|
package/dist/cli/index.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { config } from '../config/index.js';
|
|
4
|
+
import { route } from '../core/route.js';
|
|
4
5
|
import { rpc } from '../core/rpc.js';
|
|
6
|
+
import { schema } from '../core/schema.js';
|
|
5
7
|
import { takibi } from '../core/takibi.js';
|
|
8
|
+
// import { honoRpcWithSWR } from '../generator/swr/index.js'
|
|
6
9
|
import { parseCli } from '../utils/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* CLI usage help text shown when `-h`/`--help` is provided.
|
|
12
|
+
* Kept as a single template for easy updates and snapshot stability.
|
|
13
|
+
*/
|
|
7
14
|
const HELP_TEXT = `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
|
|
8
15
|
|
|
9
16
|
Options:
|
|
@@ -20,17 +27,16 @@ Options:
|
|
|
20
27
|
*
|
|
21
28
|
* ```mermaid
|
|
22
29
|
* flowchart TD
|
|
23
|
-
* A["Start honoTakibi
|
|
24
|
-
* B --> C{"
|
|
25
|
-
* C
|
|
26
|
-
* C
|
|
27
|
-
* E --> F{"cliResult.ok
|
|
28
|
-
* F
|
|
29
|
-
* F
|
|
30
|
-
* H --> I
|
|
31
|
-
* I --> J
|
|
32
|
-
*
|
|
33
|
-
* J -->|Yes| L["return { ok:true, value: takibiResult.value }"]
|
|
30
|
+
* A["Start honoTakibi"] --> B["Parse args"]
|
|
31
|
+
* B --> C{"Help requested?"}
|
|
32
|
+
* C -- Yes --> D["Return help text"]
|
|
33
|
+
* C -- No --> E["parseCli(args)"]
|
|
34
|
+
* E --> F{"cliResult.ok?"}
|
|
35
|
+
* F -- No --> G["Return parse error"]
|
|
36
|
+
* F -- Yes --> H["Run takibi"]
|
|
37
|
+
* H --> I{"takibi.ok?"}
|
|
38
|
+
* I -- No --> J["Return takibi error"]
|
|
39
|
+
* I -- Yes --> K["Return success message"]
|
|
34
40
|
* ```
|
|
35
41
|
*
|
|
36
42
|
* **Options**
|
|
@@ -46,7 +52,7 @@ Options:
|
|
|
46
52
|
* - `{ ok: false, error: string }` on validation or generation errors
|
|
47
53
|
*/
|
|
48
54
|
export async function honoTakibi() {
|
|
49
|
-
|
|
55
|
+
/** Slice the arguments to remove the first two (node and script path) */
|
|
50
56
|
const args = process.argv.slice(2);
|
|
51
57
|
const isHelpRequested = (args) => {
|
|
52
58
|
return args.length === 1 && (args[0] === '--help' || args[0] === '-h');
|
|
@@ -59,11 +65,11 @@ export async function honoTakibi() {
|
|
|
59
65
|
};
|
|
60
66
|
}
|
|
61
67
|
const abs = resolve(process.cwd(), 'hono-takibi.config.ts');
|
|
68
|
+
/** If config file does not exist, parse CLI arguments */
|
|
62
69
|
if (!existsSync(abs)) {
|
|
63
70
|
const cliResult = parseCli(args);
|
|
64
|
-
if (!cliResult.ok)
|
|
71
|
+
if (!cliResult.ok)
|
|
65
72
|
return { ok: false, error: cliResult.error };
|
|
66
|
-
}
|
|
67
73
|
const cli = cliResult.value;
|
|
68
74
|
const takibiResult = await takibi(cli.input, cli.output, cli.exportSchema ?? false, cli.exportType ?? false, cli.template ?? false, cli.test ?? false, cli.basePath);
|
|
69
75
|
if (!takibiResult.ok) {
|
|
@@ -74,19 +80,38 @@ export async function honoTakibi() {
|
|
|
74
80
|
value: takibiResult.value,
|
|
75
81
|
};
|
|
76
82
|
}
|
|
83
|
+
/** If config file exists, parse config file */
|
|
77
84
|
const configResult = await config();
|
|
78
85
|
if (!configResult.ok) {
|
|
79
86
|
return { ok: false, error: configResult.error };
|
|
80
87
|
}
|
|
81
88
|
const c = configResult.value;
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
/** takibi */
|
|
90
|
+
const takibiResult = c['zod-openapi']?.output
|
|
91
|
+
? await takibi(c.input, c['zod-openapi']?.output, c['zod-openapi']?.exportSchema ?? false, c['zod-openapi']?.exportType ?? false, false, // template
|
|
84
92
|
false)
|
|
85
93
|
: undefined;
|
|
86
94
|
if (takibiResult && !takibiResult.ok) {
|
|
87
95
|
return { ok: false, error: takibiResult.error };
|
|
88
96
|
}
|
|
89
|
-
|
|
97
|
+
/** schema */
|
|
98
|
+
const schemaResult = c['zod-openapi']?.schema
|
|
99
|
+
? await schema(c.input, c['zod-openapi'].schema.output, c['zod-openapi'].schema.exportType ?? false, c['zod-openapi']?.schema.split ?? false)
|
|
100
|
+
: undefined;
|
|
101
|
+
if (schemaResult && !schemaResult.ok) {
|
|
102
|
+
return { ok: false, error: schemaResult.error };
|
|
103
|
+
}
|
|
104
|
+
/** route */
|
|
105
|
+
const routeResult = c['zod-openapi']?.route
|
|
106
|
+
? await route(c.input, c['zod-openapi'].route.output, c['zod-openapi'].route.import, c['zod-openapi'].route.split ?? false)
|
|
107
|
+
: undefined;
|
|
108
|
+
if (routeResult && !routeResult.ok) {
|
|
109
|
+
return { ok: false, error: routeResult.error };
|
|
110
|
+
}
|
|
111
|
+
/** rpc */
|
|
112
|
+
const rpcResult = c.rpc
|
|
113
|
+
? await rpc(c.input, c.rpc.output, c.rpc.import, c.rpc.split ?? false)
|
|
114
|
+
: undefined;
|
|
90
115
|
if (rpcResult && !rpcResult.ok) {
|
|
91
116
|
return { ok: false, error: rpcResult.error };
|
|
92
117
|
}
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
type Config = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
output
|
|
5
|
-
exportType?: boolean;
|
|
6
|
-
exportSchema?: boolean;
|
|
2
|
+
readonly input: `${string}.yaml` | `${string}.json` | `${string}.tsp`;
|
|
3
|
+
readonly 'zod-openapi'?: {
|
|
4
|
+
readonly output?: `${string}.ts`;
|
|
5
|
+
readonly exportType?: boolean;
|
|
6
|
+
readonly exportSchema?: boolean;
|
|
7
|
+
readonly schema?: {
|
|
8
|
+
readonly output: string | `${string}.ts`;
|
|
9
|
+
readonly exportType?: boolean;
|
|
10
|
+
readonly split?: boolean;
|
|
11
|
+
};
|
|
12
|
+
readonly route?: {
|
|
13
|
+
readonly output: string | `${string}.ts`;
|
|
14
|
+
readonly import: string;
|
|
15
|
+
readonly split?: boolean;
|
|
16
|
+
};
|
|
7
17
|
};
|
|
8
|
-
rpc?: {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
readonly rpc?: {
|
|
19
|
+
readonly output: string | `${string}.ts`;
|
|
20
|
+
readonly import: string;
|
|
21
|
+
readonly split?: boolean;
|
|
12
22
|
};
|
|
13
23
|
};
|
|
14
24
|
export declare function config(): Promise<{
|
|
15
|
-
ok: true;
|
|
16
|
-
value: Config;
|
|
25
|
+
readonly ok: true;
|
|
26
|
+
readonly value: Config;
|
|
17
27
|
} | {
|
|
18
|
-
ok: false;
|
|
19
|
-
error: string;
|
|
28
|
+
readonly ok: false;
|
|
29
|
+
readonly error: string;
|
|
20
30
|
}>;
|
|
21
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Helper to define a config with full type completion.
|
|
33
|
+
*
|
|
34
|
+
* @see config
|
|
35
|
+
*/
|
|
36
|
+
export declare function defineConfig(c: Config): Config;
|
|
22
37
|
export {};
|
package/dist/config/index.js
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
1
2
|
import { resolve } from 'node:path';
|
|
2
3
|
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { register } from 'tsx/esm/api';
|
|
5
|
+
import { parseConfig } from '../utils/index.js';
|
|
4
6
|
export async function config() {
|
|
5
7
|
const abs = resolve(process.cwd(), 'hono-takibi.config.ts');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// }
|
|
9
|
-
register();
|
|
8
|
+
if (!existsSync(abs))
|
|
9
|
+
return { ok: false, error: `Config not found: ${abs}` };
|
|
10
10
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
register();
|
|
12
|
+
const url = pathToFileURL(abs).href;
|
|
13
|
+
const mod = await import(/* @vite-ignore */ url);
|
|
14
|
+
if (!('default' in mod) || mod.default === undefined) {
|
|
13
15
|
return { ok: false, error: 'Config must export default object' };
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
const result = parseConfig(mod.default);
|
|
18
|
+
if (!result.ok)
|
|
19
|
+
return { ok: false, error: result.error };
|
|
20
|
+
return { ok: true, value: result.value };
|
|
16
21
|
}
|
|
17
22
|
catch (e) {
|
|
18
23
|
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Helper to define a config with full type completion.
|
|
28
|
+
*
|
|
29
|
+
* @see config
|
|
30
|
+
*/
|
|
31
|
+
export function defineConfig(c) {
|
|
32
|
+
return c;
|
|
23
33
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function route(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: string | `${string}.ts`, importPath: string, split?: boolean): Promise<{
|
|
2
|
+
readonly ok: true;
|
|
3
|
+
readonly value: string;
|
|
4
|
+
} | {
|
|
5
|
+
readonly ok: false;
|
|
6
|
+
readonly error: string;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fmt } from '../format/index.js';
|
|
3
|
+
import { mkdir, writeFile } from '../fsp/index.js';
|
|
4
|
+
import { routeCode } from '../generator/zod-openapi-hono/openapi/route/index.js';
|
|
5
|
+
import { parseOpenAPI } from '../openapi/index.js';
|
|
6
|
+
const findSchemaTokens = (code) => Array.from(new Set(Array.from(code.matchAll(/\b([A-Za-z_$][A-Za-z0-9_$]*Schema)\b/g))
|
|
7
|
+
.map((m) => m[1] ?? '')
|
|
8
|
+
.filter(Boolean)));
|
|
9
|
+
const extractRouteBlocks = (src) => {
|
|
10
|
+
const re = /export\s+const\s+([A-Za-z_$][A-Za-z0-9_$]*)Route\s*=/g;
|
|
11
|
+
const hits = [];
|
|
12
|
+
for (const m of src.matchAll(re)) {
|
|
13
|
+
const name = (m[1] ?? '').trim();
|
|
14
|
+
const start = m.index ?? 0;
|
|
15
|
+
if (name)
|
|
16
|
+
hits.push({ name, start });
|
|
17
|
+
}
|
|
18
|
+
return hits.map((h, i) => {
|
|
19
|
+
const start = h.start;
|
|
20
|
+
const end = i + 1 < hits.length ? (hits[i + 1]?.start ?? src.length) : src.length;
|
|
21
|
+
return { name: h.name, block: src.slice(start, end).trim() };
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const lowerFirst = (s) => (s ? s.charAt(0).toLowerCase() + s.slice(1) : s);
|
|
25
|
+
export async function route(input, output, importPath, split) {
|
|
26
|
+
const openAPIResult = await parseOpenAPI(input);
|
|
27
|
+
if (!openAPIResult.ok) {
|
|
28
|
+
return { ok: false, error: openAPIResult.error };
|
|
29
|
+
}
|
|
30
|
+
const openAPI = openAPIResult.value;
|
|
31
|
+
const routesSrc = routeCode(openAPI.paths);
|
|
32
|
+
if (!split) {
|
|
33
|
+
const includeZ = routesSrc.includes('z.');
|
|
34
|
+
const schemaTokens = findSchemaTokens(routesSrc);
|
|
35
|
+
const importHono = `import { createRoute${includeZ ? ', z' : ''} } from '@hono/zod-openapi'`;
|
|
36
|
+
const importSchemas = schemaTokens.length > 0 ? `import { ${schemaTokens.join(',')} } from '${importPath}'` : '';
|
|
37
|
+
const finalSrc = [importHono, importSchemas, '\n', routesSrc].filter(Boolean).join('\n');
|
|
38
|
+
const fmtResult = await fmt(finalSrc);
|
|
39
|
+
if (!fmtResult.ok)
|
|
40
|
+
return { ok: false, error: fmtResult.error };
|
|
41
|
+
const mkdirResult = await mkdir(path.dirname(output));
|
|
42
|
+
if (!mkdirResult.ok)
|
|
43
|
+
return { ok: false, error: mkdirResult.error };
|
|
44
|
+
const writeResult = await writeFile(output, fmtResult.value);
|
|
45
|
+
return writeResult.ok
|
|
46
|
+
? { ok: true, value: `Generated route code written to ${output}` }
|
|
47
|
+
: { ok: false, error: writeResult.error };
|
|
48
|
+
}
|
|
49
|
+
const outDir = output.replace(/\.ts$/, '');
|
|
50
|
+
const blocks = extractRouteBlocks(routesSrc);
|
|
51
|
+
if (blocks.length === 0) {
|
|
52
|
+
const includeZ = routesSrc.includes('z.');
|
|
53
|
+
const schemaTokens = findSchemaTokens(routesSrc);
|
|
54
|
+
const importHono = `import { createRoute${includeZ ? ', z' : ''} } from '@hono/zod-openapi'`;
|
|
55
|
+
const importSchemas = schemaTokens.length > 0 ? `import { ${schemaTokens.join(',')} } from '${importPath}'` : '';
|
|
56
|
+
const finalSrc = [importHono, importSchemas, '\n', routesSrc].filter(Boolean).join('\n');
|
|
57
|
+
const fmtResult = await fmt(finalSrc);
|
|
58
|
+
if (!fmtResult.ok)
|
|
59
|
+
return { ok: false, error: fmtResult.error };
|
|
60
|
+
const mkdirResult = await mkdir(path.dirname(output));
|
|
61
|
+
if (!mkdirResult.ok)
|
|
62
|
+
return { ok: false, error: mkdirResult.error };
|
|
63
|
+
const writeResult = await writeFile(output, fmtResult.value);
|
|
64
|
+
return writeResult.ok
|
|
65
|
+
? { ok: true, value: `Generated route code written to ${output}` }
|
|
66
|
+
: { ok: false, error: writeResult.error };
|
|
67
|
+
}
|
|
68
|
+
for (const { name, block } of blocks) {
|
|
69
|
+
const includeZ = block.includes('z.');
|
|
70
|
+
const schemaTokens = findSchemaTokens(block);
|
|
71
|
+
const importHono = `import { createRoute${includeZ ? ', z' : ''} } from '@hono/zod-openapi'`;
|
|
72
|
+
const importSchemas = schemaTokens.length > 0 ? `import { ${schemaTokens.join(',')} } from '${importPath}'` : '';
|
|
73
|
+
const fileSrc = [importHono, importSchemas, '\n', block, ''].filter(Boolean).join('\n');
|
|
74
|
+
const fmtResult = await fmt(fileSrc);
|
|
75
|
+
if (!fmtResult.ok)
|
|
76
|
+
return { ok: false, error: fmtResult.error };
|
|
77
|
+
const filePath = `${outDir}/${lowerFirst(name)}.ts`;
|
|
78
|
+
const mkdirResult = await mkdir(path.dirname(filePath));
|
|
79
|
+
if (!mkdirResult.ok)
|
|
80
|
+
return { ok: false, error: mkdirResult.error };
|
|
81
|
+
const writeResult = await writeFile(filePath, fmtResult.value);
|
|
82
|
+
if (!writeResult.ok)
|
|
83
|
+
return { ok: false, error: writeResult.error };
|
|
84
|
+
}
|
|
85
|
+
const indexBody = `${blocks
|
|
86
|
+
.sort()
|
|
87
|
+
.map(({ name }) => `export * from './${lowerFirst(name)}'`)
|
|
88
|
+
.join('\n')}\n`;
|
|
89
|
+
const fmtResult = await fmt(indexBody);
|
|
90
|
+
if (!fmtResult.ok)
|
|
91
|
+
return { ok: false, error: fmtResult.error };
|
|
92
|
+
const mkdirResult = await mkdir(path.dirname(`${outDir}/index.ts`));
|
|
93
|
+
if (!mkdirResult.ok)
|
|
94
|
+
return { ok: false, error: mkdirResult.error };
|
|
95
|
+
const writeResult = await writeFile(`${outDir}/index.ts`, fmtResult.value);
|
|
96
|
+
if (!writeResult.ok)
|
|
97
|
+
return { ok: false, error: writeResult.error };
|
|
98
|
+
return { ok: true, value: `Generated route code written to ${outDir}/*.ts (index.ts included)` };
|
|
99
|
+
}
|
package/dist/core/rpc.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Generate RPC client wrappers from an OpenAPI/TypeSpec source.
|
|
3
|
+
*
|
|
4
|
+
* - When `split=true`, writes one file per RPC function under `output` (directory) and an `index.ts` barrel.
|
|
5
|
+
* - Otherwise, emits a single `.ts` file at `output`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function rpc(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: string | `${string}.ts`, importPath: string, split?: boolean): Promise<{
|
|
8
|
+
readonly ok: true;
|
|
9
|
+
readonly value: string;
|
|
4
10
|
} | {
|
|
5
|
-
ok: false;
|
|
6
|
-
error: string;
|
|
11
|
+
readonly ok: false;
|
|
12
|
+
readonly error: string;
|
|
7
13
|
}>;
|