zod-codegen 1.6.3 → 1.7.1
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/.github/workflows/ci.yml +50 -48
- package/.github/workflows/release.yml +13 -3
- package/.husky/commit-msg +1 -1
- package/.husky/pre-commit +1 -1
- package/.lintstagedrc.json +5 -1
- package/.nvmrc +1 -1
- package/.prettierrc.json +12 -5
- package/CHANGELOG.md +17 -0
- package/CONTRIBUTING.md +12 -12
- package/EXAMPLES.md +135 -57
- package/PERFORMANCE.md +4 -4
- package/README.md +87 -64
- package/SECURITY.md +1 -1
- package/dist/src/cli.js +11 -18
- package/dist/src/generator.d.ts +2 -2
- package/dist/src/generator.d.ts.map +1 -1
- package/dist/src/generator.js +5 -3
- package/dist/src/interfaces/code-generator.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.d.ts +3 -1
- package/dist/src/services/code-generator.service.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.js +236 -219
- package/dist/src/services/file-reader.service.d.ts +2 -0
- package/dist/src/services/file-reader.service.d.ts.map +1 -1
- package/dist/src/services/file-reader.service.js +25 -11
- package/dist/src/services/file-writer.service.d.ts.map +1 -1
- package/dist/src/services/file-writer.service.js +2 -2
- package/dist/src/services/import-builder.service.d.ts.map +1 -1
- package/dist/src/services/import-builder.service.js +3 -3
- package/dist/src/services/type-builder.service.d.ts.map +1 -1
- package/dist/src/types/generator-options.d.ts.map +1 -1
- package/dist/src/types/openapi.d.ts.map +1 -1
- package/dist/src/types/openapi.js +20 -20
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.js +6 -3
- package/dist/src/utils/signal-handler.d.ts.map +1 -1
- package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
- package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
- package/dist/tests/integration/cli-comprehensive.test.js +123 -0
- package/dist/tests/integration/cli.test.d.ts +2 -0
- package/dist/tests/integration/cli.test.d.ts.map +1 -0
- package/dist/tests/integration/cli.test.js +25 -0
- package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
- package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
- package/dist/tests/integration/error-scenarios.test.js +169 -0
- package/dist/tests/integration/snapshots.test.d.ts +2 -0
- package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
- package/dist/tests/integration/snapshots.test.js +100 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
- package/dist/tests/unit/code-generator.test.d.ts +2 -0
- package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator.test.js +1364 -0
- package/dist/tests/unit/file-reader.test.d.ts +2 -0
- package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
- package/dist/tests/unit/file-reader.test.js +153 -0
- package/dist/tests/unit/generator.test.d.ts +2 -0
- package/dist/tests/unit/generator.test.d.ts.map +1 -0
- package/dist/tests/unit/generator.test.js +119 -0
- package/dist/tests/unit/naming-convention.test.d.ts +2 -0
- package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
- package/dist/tests/unit/naming-convention.test.js +256 -0
- package/dist/tests/unit/reporter.test.d.ts +2 -0
- package/dist/tests/unit/reporter.test.d.ts.map +1 -0
- package/dist/tests/unit/reporter.test.js +44 -0
- package/dist/tests/unit/type-builder.test.d.ts +2 -0
- package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/type-builder.test.js +108 -0
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +10 -20
- package/eslint.config.mjs +38 -28
- package/examples/.gitkeep +1 -1
- package/examples/README.md +4 -2
- package/examples/petstore/README.md +18 -17
- package/examples/petstore/{type.ts → api.ts} +158 -74
- package/examples/petstore/authenticated-usage.ts +6 -4
- package/examples/petstore/basic-usage.ts +4 -3
- package/examples/petstore/error-handling-usage.ts +84 -0
- package/examples/petstore/retry-handler-usage.ts +11 -18
- package/examples/petstore/server-variables-usage.ts +10 -10
- package/examples/pokeapi/README.md +8 -8
- package/examples/pokeapi/api.ts +218 -0
- package/examples/pokeapi/basic-usage.ts +3 -2
- package/examples/pokeapi/custom-client.ts +5 -4
- package/package.json +17 -21
- package/src/cli.ts +20 -25
- package/src/generator.ts +13 -11
- package/src/interfaces/code-generator.ts +1 -1
- package/src/services/code-generator.service.ts +799 -1120
- package/src/services/file-reader.service.ts +35 -15
- package/src/services/file-writer.service.ts +7 -7
- package/src/services/import-builder.service.ts +9 -13
- package/src/services/type-builder.service.ts +8 -19
- package/src/types/generator-options.ts +1 -1
- package/src/types/openapi.ts +22 -22
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/naming-convention.ts +13 -10
- package/src/utils/reporter.ts +2 -2
- package/src/utils/signal-handler.ts +7 -8
- package/tests/integration/cli-comprehensive.test.ts +53 -31
- package/tests/integration/cli.test.ts +5 -5
- package/tests/integration/error-scenarios.test.ts +20 -26
- package/tests/integration/snapshots.test.ts +19 -23
- package/tests/unit/code-generator-edge-cases.test.ts +133 -133
- package/tests/unit/code-generator.test.ts +431 -330
- package/tests/unit/file-reader.test.ts +58 -18
- package/tests/unit/generator.test.ts +30 -18
- package/tests/unit/naming-convention.test.ts +27 -27
- package/tests/unit/type-builder.test.ts +2 -2
- package/tsconfig.json +5 -3
- package/vitest.config.ts +11 -21
- package/dist/scripts/update-manifest.d.ts +0 -14
- package/dist/scripts/update-manifest.d.ts.map +0 -1
- package/dist/scripts/update-manifest.js +0 -33
- package/dist/src/assets/manifest.json +0 -5
- package/examples/pokeapi/type.ts +0 -109
- package/generated/type.ts +0 -371
- package/scripts/update-manifest.ts +0 -49
- package/src/assets/manifest.json +0 -5
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ A powerful TypeScript code generator that creates **Zod schemas** and **type-saf
|
|
|
20
20
|
- **🛡️ Runtime Validation**: Built-in Zod validation for request/response data
|
|
21
21
|
- **🌍 Form Support**: Supports both JSON and form-urlencoded request bodies
|
|
22
22
|
- **🔐 Extensible**: Override `getBaseRequestOptions()` to add authentication, custom headers, CORS, and other fetch options
|
|
23
|
-
- **🔄 Response Handling**: Override `handleResponse()` to implement custom retry logic, logging, and
|
|
23
|
+
- **🔄 Response Handling**: Override `handleResponse()` to implement custom retry logic, logging, error handling, and catch 4xx/5xx with custom errors
|
|
24
24
|
- **🌐 Server Configuration**: Full support for OpenAPI server variables and templating (e.g., `{environment}.example.com`)
|
|
25
25
|
- **⚙️ Flexible Client Options**: Options-based constructor supporting server selection, variable overrides, and custom base URLs
|
|
26
26
|
|
|
@@ -29,13 +29,13 @@ A powerful TypeScript code generator that creates **Zod schemas** and **type-saf
|
|
|
29
29
|
### Global Installation (CLI)
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
|
|
32
|
+
npm install -g zod-codegen
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
### Project Installation
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
|
|
38
|
+
npm install --save-dev zod-codegen
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
## 🔧 Usage
|
|
@@ -55,13 +55,13 @@ zod-codegen -i ./swagger.yaml -o ./src/generated
|
|
|
55
55
|
|
|
56
56
|
#### CLI Options
|
|
57
57
|
|
|
58
|
-
| Option | Alias | Description
|
|
59
|
-
| --------------------- | ----- |
|
|
60
|
-
| `--input` | `-i` | Path or URL to OpenAPI file
|
|
61
|
-
| `--output` | `-o` |
|
|
62
|
-
| `--naming-convention` | `-n` | Naming convention for operation IDs
|
|
63
|
-
| `--help` | `-h` | Show help
|
|
64
|
-
| `--version` | `-v` | Show version
|
|
58
|
+
| Option | Alias | Description | Default |
|
|
59
|
+
| --------------------- | ----- | -------------------------------------------------------------- | ----------- |
|
|
60
|
+
| `--input` | `-i` | Path or URL to OpenAPI file | Required |
|
|
61
|
+
| `--output` | `-o` | Output directory (writes api.ts) or path to the generated file | `generated` |
|
|
62
|
+
| `--naming-convention` | `-n` | Naming convention for operation IDs | (none) |
|
|
63
|
+
| `--help` | `-h` | Show help | |
|
|
64
|
+
| `--version` | `-v` | Show version | |
|
|
65
65
|
|
|
66
66
|
#### Naming Conventions
|
|
67
67
|
|
|
@@ -89,13 +89,13 @@ This is particularly useful when OpenAPI specs have inconsistent or poorly named
|
|
|
89
89
|
### Programmatic Usage
|
|
90
90
|
|
|
91
91
|
```typescript
|
|
92
|
-
import {Generator} from 'zod-codegen';
|
|
93
|
-
import type {GeneratorOptions} from 'zod-codegen';
|
|
92
|
+
import { Generator } from 'zod-codegen';
|
|
93
|
+
import type { GeneratorOptions } from 'zod-codegen';
|
|
94
94
|
|
|
95
95
|
// Create a simple reporter object
|
|
96
96
|
const reporter = {
|
|
97
97
|
log: (...args: unknown[]) => console.log(...args),
|
|
98
|
-
error: (...args: unknown[]) => console.error(...args)
|
|
98
|
+
error: (...args: unknown[]) => console.error(...args)
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
// Create generator instance with naming convention
|
|
@@ -106,8 +106,8 @@ const generator = new Generator(
|
|
|
106
106
|
'./openapi.json', // Input path or URL
|
|
107
107
|
'./generated', // Output directory
|
|
108
108
|
{
|
|
109
|
-
namingConvention: 'camelCase'
|
|
110
|
-
}
|
|
109
|
+
namingConvention: 'camelCase' // Transform operation IDs to camelCase
|
|
110
|
+
}
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
// Run the generator
|
|
@@ -119,12 +119,12 @@ const exitCode = await generator.run();
|
|
|
119
119
|
For more advanced use cases, you can provide a custom transformer function that receives full operation details:
|
|
120
120
|
|
|
121
121
|
```typescript
|
|
122
|
-
import {Generator} from 'zod-codegen';
|
|
123
|
-
import type {GeneratorOptions, OperationDetails} from 'zod-codegen';
|
|
122
|
+
import { Generator } from 'zod-codegen';
|
|
123
|
+
import type { GeneratorOptions, OperationDetails } from 'zod-codegen';
|
|
124
124
|
|
|
125
125
|
const customTransformer: GeneratorOptions['operationNameTransformer'] = (details: OperationDetails) => {
|
|
126
126
|
// details includes: operationId, method, path, tags, summary, description
|
|
127
|
-
const {operationId, method, tags} = details;
|
|
127
|
+
const { operationId, method, tags } = details;
|
|
128
128
|
|
|
129
129
|
// Example: Prefix with HTTP method and tag
|
|
130
130
|
const tag = tags?.[0] || 'default';
|
|
@@ -132,7 +132,7 @@ const customTransformer: GeneratorOptions['operationNameTransformer'] = (details
|
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
const generator = new Generator('my-app', '1.0.0', reporter, './openapi.json', './generated', {
|
|
135
|
-
operationNameTransformer: customTransformer
|
|
135
|
+
operationNameTransformer: customTransformer
|
|
136
136
|
});
|
|
137
137
|
```
|
|
138
138
|
|
|
@@ -140,10 +140,11 @@ const generator = new Generator('my-app', '1.0.0', reporter, './openapi.json', '
|
|
|
140
140
|
|
|
141
141
|
## 📁 Generated Output
|
|
142
142
|
|
|
143
|
-
The generator creates a single TypeScript file (`
|
|
143
|
+
The generator creates a single TypeScript file (`api.ts`) containing:
|
|
144
144
|
|
|
145
145
|
- **Zod Schemas**: Exported Zod validation schemas for all component schemas defined in your OpenAPI spec
|
|
146
146
|
- **API Client Class**: A type-safe client class with methods for each endpoint operation
|
|
147
|
+
- **ResponseValidationError**: A generic error class thrown when response data fails Zod schema validation, carrying the original response and error details
|
|
147
148
|
- **Server Configuration**: `serverConfigurations` array and `defaultBaseUrl` constant extracted from OpenAPI servers
|
|
148
149
|
- **Client Options Type**: `ClientOptions` type for flexible server selection and variable overrides
|
|
149
150
|
- **Protected Extension Points**:
|
|
@@ -202,7 +203,7 @@ export const defaultBaseUrl: string; // First server with default variables
|
|
|
202
203
|
|
|
203
204
|
```
|
|
204
205
|
generated/
|
|
205
|
-
└──
|
|
206
|
+
└── api.ts # All schemas and client in one file
|
|
206
207
|
```
|
|
207
208
|
|
|
208
209
|
## 🎯 Example
|
|
@@ -247,23 +248,23 @@ components:
|
|
|
247
248
|
required: [id, name, email]
|
|
248
249
|
```
|
|
249
250
|
|
|
250
|
-
**Generated Output** (`generated/
|
|
251
|
+
**Generated Output** (`generated/api.ts`):
|
|
251
252
|
|
|
252
253
|
```typescript
|
|
253
|
-
import {z} from 'zod';
|
|
254
|
+
import { z } from 'zod';
|
|
254
255
|
|
|
255
256
|
// Components schemas
|
|
256
257
|
export const User = z.object({
|
|
257
258
|
id: z.number().int(),
|
|
258
259
|
name: z.string(),
|
|
259
|
-
email: z.string().email()
|
|
260
|
+
email: z.string().email()
|
|
260
261
|
});
|
|
261
262
|
|
|
262
263
|
// Server configuration (when servers are defined in OpenAPI spec)
|
|
263
264
|
export const serverConfigurations = [
|
|
264
265
|
{
|
|
265
|
-
url: 'https://api.example.com'
|
|
266
|
-
}
|
|
266
|
+
url: 'https://api.example.com'
|
|
267
|
+
}
|
|
267
268
|
];
|
|
268
269
|
export const defaultBaseUrl = 'https://api.example.com';
|
|
269
270
|
export type ClientOptions = {
|
|
@@ -272,6 +273,21 @@ export type ClientOptions = {
|
|
|
272
273
|
serverVariables?: Record<string, string>;
|
|
273
274
|
};
|
|
274
275
|
|
|
276
|
+
// ResponseValidationError class
|
|
277
|
+
export class ResponseValidationError<T> extends Error {
|
|
278
|
+
readonly response: Response;
|
|
279
|
+
readonly error: z.ZodError<T>;
|
|
280
|
+
constructor(message: string, response: Response, error: z.ZodError<T>) {
|
|
281
|
+
super(message);
|
|
282
|
+
this.name = 'ResponseValidationError' as const;
|
|
283
|
+
this.response = response;
|
|
284
|
+
this.error = error;
|
|
285
|
+
}
|
|
286
|
+
get data(): T {
|
|
287
|
+
return this.response.json() as T;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
275
291
|
// Client class
|
|
276
292
|
export class UserAPI {
|
|
277
293
|
readonly #baseUrl: string;
|
|
@@ -284,18 +300,23 @@ export class UserAPI {
|
|
|
284
300
|
return {};
|
|
285
301
|
}
|
|
286
302
|
|
|
287
|
-
async getUserById(id: number): Promise<
|
|
288
|
-
|
|
303
|
+
async getUserById(id: number): Promise<User> {
|
|
304
|
+
const response = await this.makeRequest('GET', `/users/${id}`, {});
|
|
305
|
+
const parsedUser = User.safeParse(response);
|
|
306
|
+
if (!parsedUser.success) {
|
|
307
|
+
throw new ResponseValidationError<User>(`Invalid user: ...`, response, parsedUser.error);
|
|
308
|
+
}
|
|
309
|
+
return parsedUser.data;
|
|
289
310
|
}
|
|
290
311
|
|
|
291
|
-
// ...
|
|
312
|
+
// ... protected makeRequest method
|
|
292
313
|
}
|
|
293
314
|
```
|
|
294
315
|
|
|
295
316
|
**Usage:**
|
|
296
317
|
|
|
297
318
|
```typescript
|
|
298
|
-
import
|
|
319
|
+
import UserAPI from './generated/api';
|
|
299
320
|
|
|
300
321
|
// Use default server from OpenAPI spec
|
|
301
322
|
const client = new UserAPI({});
|
|
@@ -316,7 +337,8 @@ The generated client includes a protected `getBaseRequestOptions()` method that
|
|
|
316
337
|
#### Basic Authentication Example
|
|
317
338
|
|
|
318
339
|
```typescript
|
|
319
|
-
import
|
|
340
|
+
import UserAPI from './generated/api';
|
|
341
|
+
import type { ClientOptions } from './generated/api';
|
|
320
342
|
|
|
321
343
|
class AuthenticatedUserAPI extends UserAPI {
|
|
322
344
|
private accessToken: string | null = null;
|
|
@@ -331,8 +353,8 @@ class AuthenticatedUserAPI extends UserAPI {
|
|
|
331
353
|
...options,
|
|
332
354
|
headers: {
|
|
333
355
|
...((options.headers as Record<string, string>) || {}),
|
|
334
|
-
...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {})
|
|
335
|
-
}
|
|
356
|
+
...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
|
|
357
|
+
}
|
|
336
358
|
};
|
|
337
359
|
}
|
|
338
360
|
|
|
@@ -350,7 +372,8 @@ const user = await client.getUserById(123); // Includes Authorization header
|
|
|
350
372
|
#### Complete Configuration Example
|
|
351
373
|
|
|
352
374
|
```typescript
|
|
353
|
-
import
|
|
375
|
+
import UserAPI from './generated/api';
|
|
376
|
+
import type { ClientOptions } from './generated/api';
|
|
354
377
|
|
|
355
378
|
class FullyConfiguredAPI extends UserAPI {
|
|
356
379
|
private accessToken: string | null = null;
|
|
@@ -366,11 +389,11 @@ class FullyConfiguredAPI extends UserAPI {
|
|
|
366
389
|
headers: {
|
|
367
390
|
...((options.headers as Record<string, string>) || {}),
|
|
368
391
|
'User-Agent': 'MyApp/1.0.0',
|
|
369
|
-
...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {})
|
|
392
|
+
...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
|
|
370
393
|
},
|
|
371
394
|
mode: 'cors',
|
|
372
395
|
credentials: 'include',
|
|
373
|
-
cache: 'no-cache'
|
|
396
|
+
cache: 'no-cache'
|
|
374
397
|
};
|
|
375
398
|
}
|
|
376
399
|
|
|
@@ -409,7 +432,7 @@ See [EXAMPLES.md](EXAMPLES.md) for comprehensive examples including:
|
|
|
409
432
|
- CORS configuration
|
|
410
433
|
- Request cancellation with AbortController
|
|
411
434
|
- Environment-specific configurations
|
|
412
|
-
- Response handling with custom retry logic and error
|
|
435
|
+
- Response handling with custom retry logic, [4xx/5xx error handling](EXAMPLES.md#handling-4xx5xx-in-handleresponse), and error transformation
|
|
413
436
|
|
|
414
437
|
## 📖 Examples
|
|
415
438
|
|
|
@@ -436,8 +459,8 @@ Each example includes:
|
|
|
436
459
|
|
|
437
460
|
### Prerequisites
|
|
438
461
|
|
|
439
|
-
- Node.js ≥
|
|
440
|
-
-
|
|
462
|
+
- Node.js ≥ 18.0.0 (see [.nvmrc](.nvmrc) for recommended version)
|
|
463
|
+
- npm
|
|
441
464
|
|
|
442
465
|
### Setup
|
|
443
466
|
|
|
@@ -447,56 +470,56 @@ git clone https://github.com/julienandreu/zod-codegen.git
|
|
|
447
470
|
cd zod-codegen
|
|
448
471
|
|
|
449
472
|
# Install dependencies
|
|
450
|
-
|
|
473
|
+
npm install
|
|
451
474
|
|
|
452
475
|
# Build the project
|
|
453
|
-
|
|
476
|
+
npm run build
|
|
454
477
|
|
|
455
478
|
# Run tests
|
|
456
|
-
|
|
479
|
+
npm test
|
|
457
480
|
|
|
458
481
|
# Run linting
|
|
459
|
-
|
|
482
|
+
npm run lint
|
|
460
483
|
|
|
461
484
|
# Format code
|
|
462
|
-
|
|
485
|
+
npm run format
|
|
463
486
|
```
|
|
464
487
|
|
|
465
488
|
### Testing
|
|
466
489
|
|
|
467
490
|
```bash
|
|
468
491
|
# Run all tests
|
|
469
|
-
|
|
492
|
+
npm test
|
|
470
493
|
|
|
471
494
|
# Run tests in watch mode
|
|
472
|
-
|
|
495
|
+
npm run test:watch
|
|
473
496
|
|
|
474
497
|
# Run tests with coverage
|
|
475
|
-
|
|
498
|
+
npm run test:coverage
|
|
476
499
|
|
|
477
500
|
# Run tests with UI
|
|
478
|
-
|
|
501
|
+
npm run test:ui
|
|
479
502
|
```
|
|
480
503
|
|
|
481
504
|
### Available Scripts
|
|
482
505
|
|
|
483
|
-
| Script
|
|
484
|
-
|
|
|
485
|
-
| `
|
|
486
|
-
| `
|
|
487
|
-
| `
|
|
488
|
-
| `
|
|
489
|
-
| `
|
|
490
|
-
| `
|
|
491
|
-
| `
|
|
492
|
-
| `
|
|
493
|
-
| `
|
|
494
|
-
| `
|
|
495
|
-
| `
|
|
506
|
+
| Script | Description |
|
|
507
|
+
| ----------------------- | ----------------------------------------------- |
|
|
508
|
+
| `npm run build` | Build the project |
|
|
509
|
+
| `npm run build:watch` | Build in watch mode |
|
|
510
|
+
| `npm run dev` | Development mode with example |
|
|
511
|
+
| `npm test` | Run tests |
|
|
512
|
+
| `npm run test:watch` | Run tests in watch mode |
|
|
513
|
+
| `npm run test:coverage` | Run tests with coverage |
|
|
514
|
+
| `npm run lint` | Lint and fix code |
|
|
515
|
+
| `npm run format` | Format code with Prettier |
|
|
516
|
+
| `npm run type-check` | Type check without emitting |
|
|
517
|
+
| `npm run validate` | Run all checks (lint, format, type-check, test) |
|
|
518
|
+
| `npm run clean` | Clean build artifacts |
|
|
496
519
|
|
|
497
520
|
## 📋 Requirements
|
|
498
521
|
|
|
499
|
-
- **Node.js**: ≥
|
|
522
|
+
- **Node.js**: ≥ 18.0.0
|
|
500
523
|
- **TypeScript**: ≥ 5.9.3
|
|
501
524
|
- **Zod**: ≥ 4.1.12
|
|
502
525
|
|
|
@@ -509,8 +532,8 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
|
|
|
509
532
|
1. Fork the repository
|
|
510
533
|
2. Create your feature branch: `git checkout -b feature/amazing-feature`
|
|
511
534
|
3. Make your changes
|
|
512
|
-
4. Run tests: `
|
|
513
|
-
5. Run validation: `
|
|
535
|
+
4. Run tests: `npm test`
|
|
536
|
+
5. Run validation: `npm run validate`
|
|
514
537
|
6. Commit your changes: `git commit -m 'feat: add amazing feature'`
|
|
515
538
|
7. Push to the branch: `git push origin feature/amazing-feature`
|
|
516
539
|
8. Open a Pull Request
|
package/SECURITY.md
CHANGED
|
@@ -75,7 +75,7 @@ When using zod-codegen, please follow these security best practices:
|
|
|
75
75
|
|
|
76
76
|
- Keep zod-codegen and its dependencies up to date
|
|
77
77
|
- Regularly audit your dependency tree for known vulnerabilities
|
|
78
|
-
- Use
|
|
78
|
+
- Use `npm audit` to check for security issues
|
|
79
79
|
|
|
80
80
|
## Known Security Considerations
|
|
81
81
|
|
package/dist/src/cli.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
2
5
|
import yargs from 'yargs';
|
|
3
6
|
import { hideBin } from 'yargs/helpers';
|
|
4
7
|
import { Generator } from './generator.js';
|
|
5
|
-
import
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import { dirname, join } from 'node:path';
|
|
8
|
+
import debug from 'debug';
|
|
8
9
|
import loudRejection from 'loud-rejection';
|
|
9
10
|
import { handleErrors } from './utils/error-handler.js';
|
|
10
|
-
import { handleSignals } from './utils/signal-handler.js';
|
|
11
|
-
import debug from 'debug';
|
|
12
11
|
import { Reporter } from './utils/reporter.js';
|
|
12
|
+
import { handleSignals } from './utils/signal-handler.js';
|
|
13
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
14
|
// Read package.json from the project root
|
|
15
15
|
// Handle multiple scenarios:
|
|
@@ -19,7 +19,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
19
19
|
// Try multiple paths to ensure we find package.json
|
|
20
20
|
const possiblePaths = [
|
|
21
21
|
join(__dirname, '..', '..', 'package.json'), // dist/src/cli.js or node_modules/pkg/dist/src/cli.js
|
|
22
|
-
join(__dirname, '..', 'package.json')
|
|
22
|
+
join(__dirname, '..', 'package.json') // src/cli.ts
|
|
23
23
|
];
|
|
24
24
|
let packageJsonPath;
|
|
25
25
|
for (const path of possiblePaths) {
|
|
@@ -51,20 +51,20 @@ const argv = yargs(hideBin(process.argv))
|
|
|
51
51
|
alias: 'i',
|
|
52
52
|
type: 'string',
|
|
53
53
|
description: 'Path or URL to OpenAPI file',
|
|
54
|
-
demandOption: true
|
|
54
|
+
demandOption: true
|
|
55
55
|
})
|
|
56
56
|
.option('output', {
|
|
57
57
|
alias: 'o',
|
|
58
58
|
type: 'string',
|
|
59
|
-
description: '
|
|
60
|
-
default: '
|
|
59
|
+
description: 'Output directory (writes to <output>/api.ts) or path to the generated file (e.g. ./dist/api.ts). Default: generated',
|
|
60
|
+
default: 'api.ts'
|
|
61
61
|
})
|
|
62
62
|
.option('naming-convention', {
|
|
63
63
|
alias: 'n',
|
|
64
64
|
type: 'string',
|
|
65
65
|
description: 'Naming convention to apply to operation IDs',
|
|
66
66
|
choices: ['camelCase', 'PascalCase', 'snake_case', 'kebab-case', 'SCREAMING_SNAKE_CASE', 'SCREAMING-KEBAB-CASE'],
|
|
67
|
-
default: undefined
|
|
67
|
+
default: undefined
|
|
68
68
|
})
|
|
69
69
|
.strict()
|
|
70
70
|
.help()
|
|
@@ -81,14 +81,7 @@ function isValidNamingConvention(value) {
|
|
|
81
81
|
if (value === undefined) {
|
|
82
82
|
return false;
|
|
83
83
|
}
|
|
84
|
-
const validConventions = [
|
|
85
|
-
'camelCase',
|
|
86
|
-
'PascalCase',
|
|
87
|
-
'snake_case',
|
|
88
|
-
'kebab-case',
|
|
89
|
-
'SCREAMING_SNAKE_CASE',
|
|
90
|
-
'SCREAMING-KEBAB-CASE',
|
|
91
|
-
];
|
|
84
|
+
const validConventions = ['camelCase', 'PascalCase', 'snake_case', 'kebab-case', 'SCREAMING_SNAKE_CASE', 'SCREAMING-KEBAB-CASE'];
|
|
92
85
|
return validConventions.includes(value);
|
|
93
86
|
}
|
|
94
87
|
void (async () => {
|
package/dist/src/generator.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Reporter } from './utils/reporter';
|
|
2
1
|
import type { GeneratorOptions } from './types/generator-options';
|
|
2
|
+
import type { Reporter } from './utils/reporter';
|
|
3
3
|
export type { GeneratorOptions } from './types/generator-options';
|
|
4
4
|
export type { NamingConvention, OperationDetails, OperationNameTransformer } from './utils/naming-convention';
|
|
5
5
|
/**
|
|
@@ -47,7 +47,7 @@ export declare class Generator {
|
|
|
47
47
|
* @param _version - The version of the application/library (used in generated file headers)
|
|
48
48
|
* @param reporter - Reporter instance for logging messages and errors
|
|
49
49
|
* @param inputPath - Path or URL to the OpenAPI specification file
|
|
50
|
-
* @param _outputDir -
|
|
50
|
+
* @param _outputDir - Output directory (writes to <dir>/api.ts) or path to the generated file (e.g. ./dist/api.ts)
|
|
51
51
|
* @param options - Optional configuration for code generation
|
|
52
52
|
*/
|
|
53
53
|
constructor(_name: string, _version: string, reporter: Reporter, inputPath: string, _outputDir: string, options?: GeneratorOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAE9G;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,SAAS;IAkBlB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IArB7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC;;;;;;;;;OASG;gBAEgB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EACnC,OAAO,GAAE,gBAAqB;IAQhC;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;YAqBd,QAAQ;IAItB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,SAAS;CAGlB"}
|
package/dist/src/generator.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { extname, resolve } from 'node:path';
|
|
2
2
|
import { TypeScriptCodeGeneratorService } from './services/code-generator.service.js';
|
|
3
|
+
import { OpenApiFileParserService, SyncFileReaderService } from './services/file-reader.service.js';
|
|
3
4
|
import { SyncFileWriterService } from './services/file-writer.service.js';
|
|
4
5
|
/**
|
|
5
6
|
* Main generator class for creating TypeScript code from OpenAPI specifications.
|
|
@@ -46,7 +47,7 @@ export class Generator {
|
|
|
46
47
|
* @param _version - The version of the application/library (used in generated file headers)
|
|
47
48
|
* @param reporter - Reporter instance for logging messages and errors
|
|
48
49
|
* @param inputPath - Path or URL to the OpenAPI specification file
|
|
49
|
-
* @param _outputDir -
|
|
50
|
+
* @param _outputDir - Output directory (writes to <dir>/api.ts) or path to the generated file (e.g. ./dist/api.ts)
|
|
50
51
|
* @param options - Optional configuration for code generation
|
|
51
52
|
*/
|
|
52
53
|
constructor(_name, _version, reporter, inputPath, _outputDir, options = {}) {
|
|
@@ -56,7 +57,8 @@ export class Generator {
|
|
|
56
57
|
this.inputPath = inputPath;
|
|
57
58
|
this._outputDir = _outputDir;
|
|
58
59
|
this.fileWriter = new SyncFileWriterService(this._name, this._version, inputPath);
|
|
59
|
-
|
|
60
|
+
const ext = extname(this._outputDir);
|
|
61
|
+
this.outputPath = ext === '.ts' || ext === '.tsx' ? resolve(this._outputDir) : this.fileWriter.resolveOutputPath(this._outputDir);
|
|
60
62
|
this.codeGenerator = new TypeScriptCodeGeneratorService(options);
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/interfaces/code-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/interfaces/code-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,IAAI,OAAO,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;CAC5C;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACpE"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import type { CodeGenerator, SchemaBuilder } from '../interfaces/code-generator';
|
|
3
|
-
import type { OpenApiSpecType } from '../types/openapi';
|
|
4
3
|
import type { GeneratorOptions } from '../types/generator-options';
|
|
4
|
+
import type { OpenApiSpecType } from '../types/openapi';
|
|
5
5
|
export declare class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuilder {
|
|
6
6
|
private readonly typeBuilder;
|
|
7
7
|
private readonly importBuilder;
|
|
@@ -12,6 +12,7 @@ export declare class TypeScriptCodeGeneratorService implements CodeGenerator, Sc
|
|
|
12
12
|
private currentSchemaName;
|
|
13
13
|
constructor(options?: GeneratorOptions);
|
|
14
14
|
private readonly ZodAST;
|
|
15
|
+
private extractSchemaReferences;
|
|
15
16
|
generate(spec: OpenApiSpecType): string;
|
|
16
17
|
buildSchema(schema: unknown, required?: boolean): ts.CallExpression | ts.Identifier;
|
|
17
18
|
private buildAST;
|
|
@@ -38,6 +39,7 @@ export declare class TypeScriptCodeGeneratorService implements CodeGenerator, Sc
|
|
|
38
39
|
* Builds a TypeScript interface declaration for an object schema.
|
|
39
40
|
*/
|
|
40
41
|
private buildInterfaceDeclaration;
|
|
42
|
+
private buildResponseValidationErrorClass;
|
|
41
43
|
private buildClientClass;
|
|
42
44
|
private buildConstructor;
|
|
43
45
|
private buildGetBaseRequestOptionsMethod;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-generator.service.d.ts","sourceRoot":"","sources":["../../../src/services/code-generator.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"code-generator.service.d.ts","sourceRoot":"","sources":["../../../src/services/code-generator.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAoB,eAAe,EAAiB,MAAM,kBAAkB,CAAC;AAMzF,qBAAa,8BAA+B,YAAW,aAAa,EAAE,aAAa;IACjF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0D;IAClF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+B;IAChE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAGhF,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,iBAAiB,CAAuB;gBAEpC,OAAO,GAAE,gBAAqB;IAK1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGpB;IAEH,OAAO,CAAC,uBAAuB;IA6B/B,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM;IAMvC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,UAAO,GAAG,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,UAAU;IA2BhF,OAAO,CAAC,QAAQ;IAwBhB,OAAO,CAAC,YAAY;IA6CpB,OAAO,CAAC,sBAAsB;IAK9B;;;OAGG;IACH,OAAO,CAAC,6BAA6B;IA+CrC;;OAEG;IACH,OAAO,CAAC,aAAa;IA2BrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsEzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAmBjC,OAAO,CAAC,iCAAiC;IA4FzC,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,gBAAgB;IA8ExB,OAAO,CAAC,gCAAgC;IAqBxC,OAAO,CAAC,yBAAyB;IAkBjC,OAAO,CAAC,sBAAsB;IAkgB9B,OAAO,CAAC,kBAAkB;IA6D1B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAoC9B,OAAO,CAAC,mBAAmB;IAuK3B,OAAO,CAAC,mBAAmB;IAoD3B,OAAO,CAAC,qBAAqB;IAwF7B,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,iBAAiB;IAmCzB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,eAAe;IA6CvB,OAAO,CAAC,wBAAwB;IAqHhC,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,6BAA6B;IAkMrC,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6EzB,OAAO,CAAC,WAAW;IAiCnB,OAAO,CAAC,aAAa;IAgbrB,OAAO,CAAC,iBAAiB;IA6CzB,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,8BAA8B;IActC,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,yBAAyB;IA2BjC,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,kBAAkB;IAuB1B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IA6ElC,OAAO,CAAC,eAAe;CAqCxB"}
|