hono-takibi 0.6.2 → 0.6.4

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 CHANGED
@@ -33,37 +33,6 @@ If you have OpenAPI specifications, Hono Takibi automates the conversion process
33
33
  npx hono-takibi path/to/openapi.yaml -o path/to/output_hono.ts
34
34
  ```
35
35
 
36
- ## CLI
37
-
38
- ### Options
39
-
40
- basic
41
-
42
- ```bash
43
- Options:
44
- --export-schema exports all schema definitions
45
- --export-type exports all type definitions
46
- --naming-case-type PascalCase or camelCase
47
- --naming-case-schema PascalCase or camelCase
48
- ```
49
-
50
- template
51
-
52
- > **⚠️** When using the `-template` option, you must specify a valid directory path. Ensure the directory exists before executing the
53
-
54
- ```bash
55
- Options:
56
- -template generation of application and handler files
57
- -test automatic generation of test files
58
- --base-path base URL path for your API endpoints
59
- ```
60
-
61
- ### Example
62
-
63
- ```bash
64
- npx hono-takibi openapi.yaml -o project/routes.ts -template -test --basePath 'api'
65
- ```
66
-
67
36
  ## Demo
68
37
 
69
38
  ![](https://raw.githubusercontent.com/nakita628/hono-takibi/refs/heads/main/assets/demo/hono-takibi.gif)
@@ -75,7 +44,11 @@ openapi: 3.1.0
75
44
  info:
76
45
  title: Hono API
77
46
  version: v1
78
-
47
+ tags:
48
+ - name: Hono
49
+ description: Endpoints related to general Hono operations
50
+ - name: Post
51
+ description: Endpoints for creating, retrieving, updating, and deleting posts
79
52
  components:
80
53
  schemas:
81
54
  Error:
@@ -91,32 +64,21 @@ components:
91
64
  id:
92
65
  type: string
93
66
  format: uuid
94
- description: Unique identifier of the post
95
67
  post:
96
68
  type: string
97
- description: Content of the post
98
69
  minLength: 1
99
70
  maxLength: 140
100
71
  createdAt:
101
72
  type: string
102
73
  format: date-time
103
- description: Timestamp when the post was created
104
74
  updatedAt:
105
75
  type: string
106
76
  format: date-time
107
- description: Timestamp when the post was last updated
108
77
  required:
109
78
  - id
110
79
  - post
111
80
  - createdAt
112
81
  - updatedAt
113
-
114
- tags:
115
- - name: Hono
116
- description: Endpoints related to general Hono operations
117
- - name: Post
118
- description: Endpoints for creating, retrieving, updating, and deleting posts
119
-
120
82
  paths:
121
83
  /:
122
84
  get:
@@ -137,7 +99,6 @@ paths:
137
99
  example: Hono🔥
138
100
  required:
139
101
  - message
140
-
141
102
  /posts:
142
103
  post:
143
104
  tags:
@@ -153,63 +114,52 @@ paths:
153
114
  properties:
154
115
  post:
155
116
  type: string
156
- description: Content of the post
157
117
  minLength: 1
158
118
  maxLength: 140
159
119
  required:
160
120
  - post
161
- example:
162
- post: "This is my first post!"
163
121
  responses:
164
122
  '201':
165
123
  description: Post successfully created.
166
124
  content:
167
125
  application/json:
168
126
  schema:
169
- $ref: '#/components/schemas/Error'
170
- example:
171
- message: Created
127
+ type: object
128
+ properties:
129
+ message:
130
+ type: string
131
+ required:
132
+ - message
172
133
  '400':
173
134
  description: Invalid request due to bad input.
174
135
  content:
175
136
  application/json:
176
137
  schema:
177
138
  $ref: '#/components/schemas/Error'
178
- example:
179
- message: Post content is required and must be between 1 and 140 characters.
180
139
  '500':
181
140
  description: Internal server error.
182
141
  content:
183
142
  application/json:
184
143
  schema:
185
144
  $ref: '#/components/schemas/Error'
186
- example:
187
- message: An unexpected error occurred. Please try again later.
188
-
189
145
  get:
190
146
  tags:
191
147
  - Post
192
148
  summary: Retrieve a list of posts
193
- description: Retrieve a paginated list of posts. Specify the page number and the number of posts per page.
149
+ description: >-
150
+ Retrieve a paginated list of posts. Specify the page number and the
151
+ number of posts per page.
194
152
  parameters:
195
- - in: query
153
+ - schema:
154
+ type: string
155
+ required: true
196
156
  name: page
157
+ in: query
158
+ - schema:
159
+ type: string
197
160
  required: true
198
- schema:
199
- type: integer
200
- minimum: 0
201
- default: 1
202
- example: 1
203
- description: The page number to retrieve. Must be a positive integer. Defaults to 1.
204
- - in: query
205
161
  name: rows
206
- required: true
207
- schema:
208
- type: integer
209
- minimum: 0
210
- default: 10
211
- example: 10
212
- description: The number of posts per page. Must be a positive integer. Defaults to 10.
162
+ in: query
213
163
  responses:
214
164
  '200':
215
165
  description: Successfully retrieved a list of posts.
@@ -219,28 +169,18 @@ paths:
219
169
  type: array
220
170
  items:
221
171
  $ref: '#/components/schemas/Post'
222
- example:
223
- - id: "123e4567-e89b-12d3-a456-426614174000"
224
- post: "Hello world!"
225
- createdAt: "2024-12-01T12:34:56Z"
226
- updatedAt: "2024-12-02T14:20:00Z"
227
172
  '400':
228
173
  description: Invalid request due to bad input.
229
174
  content:
230
175
  application/json:
231
176
  schema:
232
177
  $ref: '#/components/schemas/Error'
233
- example:
234
- message: Invalid page or rows parameter. Both must be positive integers.
235
178
  '500':
236
179
  description: Internal server error.
237
180
  content:
238
181
  application/json:
239
182
  schema:
240
183
  $ref: '#/components/schemas/Error'
241
- example:
242
- message: An unexpected error occurred. Please try again later.
243
-
244
184
  /posts/{id}:
245
185
  put:
246
186
  tags:
@@ -248,13 +188,12 @@ paths:
248
188
  summary: Update an existing post
249
189
  description: Update the content of an existing post identified by its unique ID.
250
190
  parameters:
251
- - in: path
252
- name: id
253
- required: true
254
- schema:
191
+ - schema:
255
192
  type: string
256
193
  format: uuid
257
- description: Unique identifier of the post.
194
+ required: true
195
+ name: id
196
+ in: path
258
197
  requestBody:
259
198
  required: true
260
199
  content:
@@ -264,13 +203,10 @@ paths:
264
203
  properties:
265
204
  post:
266
205
  type: string
267
- description: Updated content for the post
268
206
  minLength: 1
269
207
  maxLength: 140
270
208
  required:
271
209
  - post
272
- example:
273
- post: "Updated post content."
274
210
  responses:
275
211
  '204':
276
212
  description: Post successfully updated.
@@ -280,31 +216,25 @@ paths:
280
216
  application/json:
281
217
  schema:
282
218
  $ref: '#/components/schemas/Error'
283
- example:
284
- message: Post content is required and must be between 1 and 140 characters.
285
219
  '500':
286
220
  description: Internal server error.
287
221
  content:
288
222
  application/json:
289
223
  schema:
290
224
  $ref: '#/components/schemas/Error'
291
- example:
292
- message: An unexpected error occurred. Please try again later.
293
-
294
225
  delete:
295
226
  tags:
296
227
  - Post
297
228
  summary: Delete a post
298
229
  description: Delete an existing post identified by its unique ID.
299
230
  parameters:
300
- - in: path
301
- name: id
302
- required: true
303
- schema:
231
+ - schema:
304
232
  type: string
305
233
  format: uuid
306
234
  example: 123e4567-e89b-12d3-a456-426614174000
307
- description: Unique identifier of the post.
235
+ required: true
236
+ name: id
237
+ in: path
308
238
  responses:
309
239
  '204':
310
240
  description: Post successfully deleted.
@@ -314,16 +244,12 @@ paths:
314
244
  application/json:
315
245
  schema:
316
246
  $ref: '#/components/schemas/Error'
317
- example:
318
- message: Invalid post ID.
319
247
  '500':
320
248
  description: Internal server error.
321
249
  content:
322
250
  application/json:
323
251
  schema:
324
252
  $ref: '#/components/schemas/Error'
325
- example:
326
- message: An unexpected error occurred. Please try again later.
327
253
  ```
328
254
 
329
255
  output:
@@ -331,9 +257,9 @@ output:
331
257
  ```ts
332
258
  import { createRoute, z } from '@hono/zod-openapi'
333
259
 
334
- const ErrorSchema = z.object({ message: z.string() }).openapi('Error')
260
+ const errorSchema = z.object({ message: z.string() }).openapi('Error')
335
261
 
336
- const PostSchema = z
262
+ const postSchema = z
337
263
  .object({
338
264
  id: z.string().uuid(),
339
265
  post: z.string().min(1).max(140),
@@ -375,15 +301,15 @@ export const postPostsRoute = createRoute({
375
301
  responses: {
376
302
  201: {
377
303
  description: 'Post successfully created.',
378
- content: { 'application/json': { schema: ErrorSchema } },
304
+ content: { 'application/json': { schema: z.object({ message: z.string() }) } },
379
305
  },
380
306
  400: {
381
307
  description: 'Invalid request due to bad input.',
382
- content: { 'application/json': { schema: ErrorSchema } },
308
+ content: { 'application/json': { schema: errorSchema } },
383
309
  },
384
310
  500: {
385
311
  description: 'Internal server error.',
386
- content: { 'application/json': { schema: ErrorSchema } },
312
+ content: { 'application/json': { schema: errorSchema } },
387
313
  },
388
314
  },
389
315
  })
@@ -395,24 +321,19 @@ export const getPostsRoute = createRoute({
395
321
  summary: 'Retrieve a list of posts',
396
322
  description:
397
323
  'Retrieve a paginated list of posts. Specify the page number and the number of posts per page.',
398
- request: {
399
- query: z.object({
400
- page: z.string().pipe(z.coerce.number().int().min(0).default(1).openapi({ example: 1 })),
401
- rows: z.string().pipe(z.coerce.number().int().min(0).default(10).openapi({ example: 10 })),
402
- }),
403
- },
324
+ request: { query: z.object({ page: z.string(), rows: z.string() }) },
404
325
  responses: {
405
326
  200: {
406
327
  description: 'Successfully retrieved a list of posts.',
407
- content: { 'application/json': { schema: z.array(PostSchema) } },
328
+ content: { 'application/json': { schema: z.array(postSchema) } },
408
329
  },
409
330
  400: {
410
331
  description: 'Invalid request due to bad input.',
411
- content: { 'application/json': { schema: ErrorSchema } },
332
+ content: { 'application/json': { schema: errorSchema } },
412
333
  },
413
334
  500: {
414
335
  description: 'Internal server error.',
415
- content: { 'application/json': { schema: ErrorSchema } },
336
+ content: { 'application/json': { schema: errorSchema } },
416
337
  },
417
338
  },
418
339
  })
@@ -434,11 +355,11 @@ export const putPostsIdRoute = createRoute({
434
355
  204: { description: 'Post successfully updated.' },
435
356
  400: {
436
357
  description: 'Invalid input.',
437
- content: { 'application/json': { schema: ErrorSchema } },
358
+ content: { 'application/json': { schema: errorSchema } },
438
359
  },
439
360
  500: {
440
361
  description: 'Internal server error.',
441
- content: { 'application/json': { schema: ErrorSchema } },
362
+ content: { 'application/json': { schema: errorSchema } },
442
363
  },
443
364
  },
444
365
  })
@@ -464,16 +385,47 @@ export const deletePostsIdRoute = createRoute({
464
385
  204: { description: 'Post successfully deleted.' },
465
386
  400: {
466
387
  description: 'Invalid input.',
467
- content: { 'application/json': { schema: ErrorSchema } },
388
+ content: { 'application/json': { schema: errorSchema } },
468
389
  },
469
390
  500: {
470
391
  description: 'Internal server error.',
471
- content: { 'application/json': { schema: ErrorSchema } },
392
+ content: { 'application/json': { schema: errorSchema } },
472
393
  },
473
394
  },
474
395
  })
475
396
  ```
476
397
 
398
+ ## CLI
399
+
400
+ ### Options
401
+
402
+ basic
403
+
404
+ ```bash
405
+ Options:
406
+ --export-schema exports all schema definitions
407
+ --export-type exports all type definitions
408
+ --naming-case-type PascalCase or camelCase
409
+ --naming-case-schema PascalCase or camelCase
410
+ ```
411
+
412
+ template
413
+
414
+ > **⚠️** When using the `-template` option, you must specify a valid directory path. Ensure the directory exists before executing the
415
+
416
+ ```bash
417
+ Options:
418
+ -template generation of application and handler files
419
+ -test automatic generation of test files
420
+ --base-path base URL path for your API endpoints
421
+ ```
422
+
423
+ ### Example
424
+
425
+ ```bash
426
+ npx hono-takibi openapi.yaml -o project/routes.ts -template -test --basePath 'api'
427
+ ```
428
+
477
429
  ## Configuration
478
430
 
479
431
  You can customize the code generation behavior by creating a `hono-takibi.json` file in your project root.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * getToSafeIdentifierHelper
3
+ * @param {string} str - The string to be converted to a safe identifier
4
+ * @returns {string} - A normalized, safe identifier
5
+ */
6
+ export declare function getToSafeIdentifierHelper(str: string): string;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getToSafeIdentifierHelper = getToSafeIdentifierHelper;
4
+ /**
5
+ * getToSafeIdentifierHelper
6
+ * @param {string} str - The string to be converted to a safe identifier
7
+ * @returns {string} - A normalized, safe identifier
8
+ */
9
+ function getToSafeIdentifierHelper(str) {
10
+ const trimmed = str.trim();
11
+ if (trimmed.includes('-')) {
12
+ return trimmed.replace(/\s*-\s*/g, '_');
13
+ }
14
+ return trimmed;
15
+ }
@@ -20,13 +20,48 @@ function generateZodEnum(schema) {
20
20
  if (schema.type === 'number' && schema.enum) {
21
21
  return `z.literal(${schema.enum})`;
22
22
  }
23
+ // integer
24
+ if (schema.type === 'integer' && schema.enum) {
25
+ if (schema.enum.length > 1) {
26
+ const literals = schema.enum.map((value) => `z.literal(${value})`);
27
+ return `z.union([${literals}])`;
28
+ }
29
+ return `z.literal(${schema.enum[0]})`;
30
+ }
23
31
  // bigint
24
32
  if (schema.type === 'bigint' && schema.enum) {
25
- return `z.literal(${schema.enum}n)`;
33
+ return `z.literal(${schema.enum})`;
34
+ }
35
+ // array
36
+ if (schema.type === 'array' && Array.isArray(schema.enum)) {
37
+ if (schema.enum.length === 1 && Array.isArray(schema.enum[0])) {
38
+ const tupleItems = schema.enum[0].map((item) => `z.literal(${item})`).join(', ');
39
+ return `z.tuple([${tupleItems}])`;
40
+ }
41
+ // union
42
+ const unionParts = schema.enum.map((value) => {
43
+ if (Array.isArray(value)) {
44
+ const tupleItems = value.map((item) => `z.literal(${item})`);
45
+ return `z.tuple([${tupleItems}])`;
46
+ }
47
+ return `z.literal(${value})`;
48
+ });
49
+ return `z.union([${unionParts}])`;
26
50
  }
27
51
  // boolean
28
52
  if (schema.type === 'boolean' && schema.enum) {
29
53
  return `z.literal(${schema.enum})`;
30
54
  }
31
- return `z.enum(${JSON.stringify(schema.enum)})`;
55
+ // enum.length > 1
56
+ if (schema.enum.length > 1) {
57
+ const allStrings = schema.enum.every((v) => typeof v === 'string');
58
+ if (allStrings) {
59
+ return `z.enum(${JSON.stringify(schema.enum)})`;
60
+ }
61
+ const unionLiterals = schema.enum.map((value) => value === null
62
+ ? 'z.null()'
63
+ : `z.literal(${typeof value === 'string' ? `'${value}'` : value})`);
64
+ return `z.union([${unionLiterals}])`;
65
+ }
66
+ return `z.literal(${typeof schema.enum[0] === 'string' ? `'${schema.enum[0]}'` : schema.enum[0]})`;
32
67
  }
@@ -51,11 +51,15 @@ function generateZodNumber(args) {
51
51
  }
52
52
  // nonpositive
53
53
  if (args.minimum === 0 && !args.exclusiveMinimum) {
54
- validations.push('.nonpositive()');
54
+ if (!args.default) {
55
+ validations.push('.nonpositive()');
56
+ }
55
57
  }
56
58
  // negative
57
59
  if (args.maximum === 0 && args.exclusiveMaximum) {
58
- validations.push('.negative()');
60
+ if (!args.default) {
61
+ validations.push('.negative()');
62
+ }
59
63
  }
60
64
  // gt
61
65
  if (args.minimum) {
@@ -1,19 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateZod = generateZod;
4
- const generate_zod_array_1 = require("./generate-zod-array");
5
- const generate_zod_string_1 = require("./generate-zod-string");
6
4
  const is_format_string_1 = require("../../core/validator/is-format-string");
7
- const generate_zod_number_1 = require("./generate-zod-number");
8
- const generate_zod_integer_schema_1 = require("./generate-zod-integer-schema");
9
5
  const get_variable_schema_name_helper_1 = require("../../core/helper/get-variable-schema-name-helper");
10
6
  const generate_zod_object_1 = require("./generate-zod-object");
11
7
  const generate_zod_enum_1 = require("./generate-zod-enum");
12
8
  const generate_zod_max_1 = require("./generate-zod-max");
13
9
  const generate_zod_min_1 = require("./generate-zod-min");
10
+ const generate_zod_number_1 = require("./generate-zod-number");
11
+ const generate_zod_integer_schema_1 = require("./generate-zod-integer-schema");
12
+ const generate_zod_length_1 = require("./generate-zod-length");
13
+ const generate_zod_array_1 = require("./generate-zod-array");
14
+ const generate_zod_string_1 = require("./generate-zod-string");
14
15
  const strip_min_if_gt_exist_helper_1 = require("./helper/strip-min-if-gt-exist-helper");
15
16
  const strip_max_if_lt_exist_helper_1 = require("./helper/strip-max-if-lt-exist-helper");
16
- const generate_zod_length_1 = require("./generate-zod-length");
17
17
  const strip_min_max_exist_helper_1 = require("./helper/strip-min-max-exist-helper");
18
18
  const generate_oneof_code_1 = require("../zod-openapi-hono/openapi/component/oneof/generate-oneof-code");
19
19
  const generate_anyof_code_1 = require("../zod-openapi-hono/openapi/component/anyof/generate-anyof-code");
@@ -23,13 +23,20 @@ const generate_zod_infer_1 = require("../generate-zod-infer");
23
23
  */
24
24
  function generateZodToOpenAPISchemaDefinition(schemaName, zodSchema, config) {
25
25
  const variableName = (0, get_variable_schema_name_helper_1.getVariableSchemaNameHelper)(schemaName, config);
26
+ // "-" → "_"
27
+ const safeVariableName = variableName.replace(/[^a-zA-Z0-9_$]/g, '_');
28
+ // "-" → "_"
29
+ const safeSchemaName = schemaName.replace(/[^a-zA-Z0-9_$]/g, '_');
26
30
  // schema code
27
31
  const schemaCode = config.schema.export
28
- ? `export const ${variableName} = ${zodSchema}.openapi('${schemaName}')`
29
- : `const ${variableName} = ${zodSchema}.openapi('${schemaName}')`;
32
+ ? `export const ${safeVariableName} = ${zodSchema}.openapi('${safeSchemaName}')`
33
+ : `const ${safeVariableName} = ${zodSchema}.openapi('${safeSchemaName}')`;
30
34
  // zod infer code
31
35
  const typeVariableName = (0, get_variable_name_helper_1.getVariableNameHelper)(schemaName, config);
32
- const zodInferCode = config.type.export ? (0, generate_zod_infer_1.generateZodInfer)(typeVariableName, variableName) : '';
36
+ const safeTypeVariableName = typeVariableName.replace(/[^a-zA-Z0-9_$]/g, '_');
37
+ const zodInferCode = config.type.export
38
+ ? (0, generate_zod_infer_1.generateZodInfer)(safeTypeVariableName, safeVariableName)
39
+ : '';
33
40
  // return code
34
41
  return `${schemaCode}\n\n${zodInferCode}`;
35
42
  }
@@ -4,6 +4,7 @@ exports.generateZodPropertiesSchema = generateZodPropertiesSchema;
4
4
  const is_all_optional_1 = require("../../../core/validator/is-all-optional");
5
5
  const generate_zod_partial_schema_1 = require("../generate-zod-partial-schema");
6
6
  const generate_zod_property_schema_1 = require("./generate-zod-property-schema");
7
+ const get_to_safe_identifier_helper_1 = require("../../../core/helper/get-to-safe-identifier-helper");
7
8
  /**
8
9
  * Generates a Zod object schema with properties and their requirements
9
10
  * @param { Record<string, Schema> } properties - Record of property names to their schema definitions
@@ -50,7 +51,9 @@ function generateZodPropertiesSchema(properties, required, config) {
50
51
  const objectProperties = Object.entries(properties).map(([key, schema]) => {
51
52
  const isRequired = required.includes(key);
52
53
  const propertySchema = (0, generate_zod_property_schema_1.generatePropertySchema)(schema, config);
53
- return `${key}:${propertySchema}${isRequired ? '' : '.optional()'}`;
54
+ const safePropertySchema = (0, get_to_safe_identifier_helper_1.getToSafeIdentifierHelper)(propertySchema);
55
+ const safeKey = (0, get_to_safe_identifier_helper_1.getToSafeIdentifierHelper)(key);
56
+ return `${safeKey}:${safePropertySchema}${isRequired ? '' : '.optional()'}`;
54
57
  });
55
58
  // Check if all properties are optional
56
59
  const allOptional = (0, is_all_optional_1.isAllOptional)(objectProperties);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateArrayReferenceSchema = generateArrayReferenceSchema;
4
+ const get_to_safe_identifier_helper_1 = require("../../../core/helper/get-to-safe-identifier-helper");
4
5
  const get_variable_schema_name_helper_1 = require("../../../core/helper/get-variable-schema-name-helper");
5
6
  const get_ref_name_1 = require("../../../core/schema/references/get-ref-name");
6
7
  const generate_zod_array_1 = require("../generate-zod-array");
@@ -19,5 +20,6 @@ function generateArrayReferenceSchema(schema, config) {
19
20
  return 'z.array(z.any())';
20
21
  }
21
22
  const variableName = (0, get_variable_schema_name_helper_1.getVariableSchemaNameHelper)(refName, config);
22
- return (0, generate_zod_array_1.generateZodArray)(variableName);
23
+ const safeVariableName = (0, get_to_safe_identifier_helper_1.getToSafeIdentifierHelper)(variableName);
24
+ return (0, generate_zod_array_1.generateZodArray)(safeVariableName);
23
25
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hono-takibi",
3
3
  "description": "Hono Takibi is a CLI tool that generates Hono routes from OpenAPI specifications.",
4
- "version": "0.6.2",
4
+ "version": "0.6.4",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "hono",