zod-codegen 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/CONTRIBUTING.md +1 -1
- package/EXAMPLES.md +704 -0
- package/PERFORMANCE.md +59 -0
- package/README.md +270 -58
- package/dist/src/cli.js +25 -5
- package/dist/src/services/code-generator.service.js +211 -26
- package/dist/src/types/openapi.js +1 -1
- package/dist/tests/unit/code-generator.test.js +219 -0
- package/dist/tests/unit/file-reader.test.js +110 -0
- package/dist/tests/unit/generator.test.js +77 -7
- package/eslint.config.mjs +1 -0
- package/examples/.gitkeep +3 -0
- package/examples/README.md +74 -0
- package/examples/petstore/README.md +121 -0
- package/examples/petstore/authenticated-usage.ts +60 -0
- package/examples/petstore/basic-usage.ts +51 -0
- package/examples/petstore/server-variables-usage.ts +63 -0
- package/examples/petstore/type.ts +217 -0
- package/examples/pokeapi/README.md +105 -0
- package/examples/pokeapi/basic-usage.ts +57 -0
- package/examples/pokeapi/custom-client.ts +56 -0
- package/examples/pokeapi/type.ts +109 -0
- package/package.json +4 -2
- package/samples/pokeapi-openapi.json +212 -0
- package/samples/server-variables-example.yaml +49 -0
- package/src/cli.ts +30 -5
- package/src/services/code-generator.service.ts +641 -57
- package/src/types/openapi.ts +1 -1
- package/tests/unit/code-generator.test.ts +243 -0
- package/tests/unit/file-reader.test.ts +134 -0
- package/tests/unit/generator.test.ts +99 -7
- package/tsconfig.examples.json +17 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "PokéAPI",
|
|
5
|
+
"version": "2.0.0",
|
|
6
|
+
"description": "PokéAPI is a RESTful API that provides data about Pokémon"
|
|
7
|
+
},
|
|
8
|
+
"servers": [
|
|
9
|
+
{
|
|
10
|
+
"url": "https://pokeapi.co/api/v2",
|
|
11
|
+
"description": "PokéAPI production server"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"paths": {
|
|
15
|
+
"/pokemon/{id}": {
|
|
16
|
+
"get": {
|
|
17
|
+
"operationId": "getPokemonById",
|
|
18
|
+
"summary": "Get Pokémon by ID or name",
|
|
19
|
+
"parameters": [
|
|
20
|
+
{
|
|
21
|
+
"name": "id",
|
|
22
|
+
"in": "path",
|
|
23
|
+
"required": true,
|
|
24
|
+
"description": "Pokémon ID or name",
|
|
25
|
+
"schema": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"responses": {
|
|
31
|
+
"200": {
|
|
32
|
+
"description": "Pokémon data",
|
|
33
|
+
"content": {
|
|
34
|
+
"application/json": {
|
|
35
|
+
"schema": {
|
|
36
|
+
"$ref": "#/components/schemas/Pokemon"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"/pokemon": {
|
|
45
|
+
"get": {
|
|
46
|
+
"operationId": "getPokemonList",
|
|
47
|
+
"summary": "Get list of Pokémon",
|
|
48
|
+
"parameters": [
|
|
49
|
+
{
|
|
50
|
+
"name": "limit",
|
|
51
|
+
"in": "query",
|
|
52
|
+
"required": false,
|
|
53
|
+
"schema": {
|
|
54
|
+
"type": "integer",
|
|
55
|
+
"default": 20
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "offset",
|
|
60
|
+
"in": "query",
|
|
61
|
+
"required": false,
|
|
62
|
+
"schema": {
|
|
63
|
+
"type": "integer",
|
|
64
|
+
"default": 0
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"responses": {
|
|
69
|
+
"200": {
|
|
70
|
+
"description": "List of Pokémon",
|
|
71
|
+
"content": {
|
|
72
|
+
"application/json": {
|
|
73
|
+
"schema": {
|
|
74
|
+
"$ref": "#/components/schemas/PokemonListResponse"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"components": {
|
|
84
|
+
"schemas": {
|
|
85
|
+
"Pokemon": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"properties": {
|
|
88
|
+
"id": {
|
|
89
|
+
"type": "integer",
|
|
90
|
+
"description": "Pokémon ID"
|
|
91
|
+
},
|
|
92
|
+
"name": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"description": "Pokémon name"
|
|
95
|
+
},
|
|
96
|
+
"height": {
|
|
97
|
+
"type": "integer",
|
|
98
|
+
"description": "Height in decimeters"
|
|
99
|
+
},
|
|
100
|
+
"weight": {
|
|
101
|
+
"type": "integer",
|
|
102
|
+
"description": "Weight in hectograms"
|
|
103
|
+
},
|
|
104
|
+
"base_experience": {
|
|
105
|
+
"type": "integer",
|
|
106
|
+
"description": "Base experience"
|
|
107
|
+
},
|
|
108
|
+
"types": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": {
|
|
111
|
+
"$ref": "#/components/schemas/PokemonType"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"abilities": {
|
|
115
|
+
"type": "array",
|
|
116
|
+
"items": {
|
|
117
|
+
"$ref": "#/components/schemas/PokemonAbility"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"sprites": {
|
|
121
|
+
"$ref": "#/components/schemas/PokemonSprites"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"required": ["id", "name"]
|
|
125
|
+
},
|
|
126
|
+
"PokemonType": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"properties": {
|
|
129
|
+
"slot": {
|
|
130
|
+
"type": "integer"
|
|
131
|
+
},
|
|
132
|
+
"type": {
|
|
133
|
+
"$ref": "#/components/schemas/NamedAPIResource"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"PokemonAbility": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"properties": {
|
|
140
|
+
"ability": {
|
|
141
|
+
"$ref": "#/components/schemas/NamedAPIResource"
|
|
142
|
+
},
|
|
143
|
+
"is_hidden": {
|
|
144
|
+
"type": "boolean"
|
|
145
|
+
},
|
|
146
|
+
"slot": {
|
|
147
|
+
"type": "integer"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"PokemonSprites": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"properties": {
|
|
154
|
+
"front_default": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"format": "uri"
|
|
157
|
+
},
|
|
158
|
+
"front_shiny": {
|
|
159
|
+
"type": "string",
|
|
160
|
+
"format": "uri"
|
|
161
|
+
},
|
|
162
|
+
"back_default": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"format": "uri"
|
|
165
|
+
},
|
|
166
|
+
"back_shiny": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"format": "uri"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"NamedAPIResource": {
|
|
173
|
+
"type": "object",
|
|
174
|
+
"properties": {
|
|
175
|
+
"name": {
|
|
176
|
+
"type": "string"
|
|
177
|
+
},
|
|
178
|
+
"url": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"format": "uri"
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"required": ["name", "url"]
|
|
184
|
+
},
|
|
185
|
+
"PokemonListResponse": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"properties": {
|
|
188
|
+
"count": {
|
|
189
|
+
"type": "integer"
|
|
190
|
+
},
|
|
191
|
+
"next": {
|
|
192
|
+
"type": "string",
|
|
193
|
+
"format": "uri",
|
|
194
|
+
"nullable": true
|
|
195
|
+
},
|
|
196
|
+
"previous": {
|
|
197
|
+
"type": "string",
|
|
198
|
+
"format": "uri",
|
|
199
|
+
"nullable": true
|
|
200
|
+
},
|
|
201
|
+
"results": {
|
|
202
|
+
"type": "array",
|
|
203
|
+
"items": {
|
|
204
|
+
"$ref": "#/components/schemas/NamedAPIResource"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"required": ["count", "results"]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
openapi: 3.0.0
|
|
2
|
+
info:
|
|
3
|
+
title: Server Variables Example API
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
description: Example API demonstrating server variables for different environments
|
|
6
|
+
servers:
|
|
7
|
+
- url: https://{environment}.example.com:{port}/v{version}
|
|
8
|
+
description: Main API server with templated variables
|
|
9
|
+
variables:
|
|
10
|
+
environment:
|
|
11
|
+
default: api
|
|
12
|
+
enum:
|
|
13
|
+
- api
|
|
14
|
+
- api.dev
|
|
15
|
+
- api.staging
|
|
16
|
+
description: The environment to use (api, api.dev, api.staging)
|
|
17
|
+
port:
|
|
18
|
+
default: '443'
|
|
19
|
+
enum:
|
|
20
|
+
- '443'
|
|
21
|
+
- '8443'
|
|
22
|
+
description: The port number
|
|
23
|
+
version:
|
|
24
|
+
default: '2'
|
|
25
|
+
description: API version
|
|
26
|
+
- url: https://api.production.example.com/v2
|
|
27
|
+
description: Production server (no variables)
|
|
28
|
+
paths:
|
|
29
|
+
/users:
|
|
30
|
+
get:
|
|
31
|
+
operationId: getUsers
|
|
32
|
+
summary: Get list of users
|
|
33
|
+
responses:
|
|
34
|
+
'200':
|
|
35
|
+
description: Success
|
|
36
|
+
content:
|
|
37
|
+
'application/json':
|
|
38
|
+
schema:
|
|
39
|
+
type: array
|
|
40
|
+
items:
|
|
41
|
+
type: object
|
|
42
|
+
properties:
|
|
43
|
+
id:
|
|
44
|
+
type: integer
|
|
45
|
+
name:
|
|
46
|
+
type: string
|
|
47
|
+
required:
|
|
48
|
+
- id
|
|
49
|
+
- name
|
package/src/cli.ts
CHANGED
|
@@ -11,17 +11,42 @@ import loudRejection from 'loud-rejection';
|
|
|
11
11
|
import {handleErrors} from './utils/error-handler.js';
|
|
12
12
|
import {handleSignals} from './utils/signal-handler.js';
|
|
13
13
|
import debug from 'debug';
|
|
14
|
-
import {isManifest} from './utils/manifest.js';
|
|
15
14
|
import {Reporter} from './utils/reporter.js';
|
|
16
15
|
|
|
17
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
|
|
17
|
+
// Read package.json from the project root
|
|
18
|
+
// Handle multiple scenarios:
|
|
19
|
+
// 1. Built locally: dist/src/cli.js -> go up 2 levels
|
|
20
|
+
// 2. Source: src/cli.ts -> go up 1 level
|
|
21
|
+
// 3. Installed via npm: node_modules/zod-codegen/dist/src/cli.js -> go up 2 levels
|
|
22
|
+
// Try multiple paths to ensure we find package.json
|
|
23
|
+
const possiblePaths = [
|
|
24
|
+
join(__dirname, '..', '..', 'package.json'), // dist/src/cli.js or node_modules/pkg/dist/src/cli.js
|
|
25
|
+
join(__dirname, '..', 'package.json'), // src/cli.ts
|
|
26
|
+
];
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
let packageJsonPath: string | undefined;
|
|
29
|
+
for (const path of possiblePaths) {
|
|
30
|
+
try {
|
|
31
|
+
readFileSync(path, 'utf-8');
|
|
32
|
+
packageJsonPath = path;
|
|
33
|
+
break;
|
|
34
|
+
} catch {
|
|
35
|
+
// Try next path
|
|
36
|
+
}
|
|
22
37
|
}
|
|
23
38
|
|
|
24
|
-
|
|
39
|
+
if (!packageJsonPath) {
|
|
40
|
+
throw new Error('Could not find package.json. Please ensure the package is properly installed.');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const packageData = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {
|
|
44
|
+
name: string;
|
|
45
|
+
version: string;
|
|
46
|
+
description: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const {name, description, version} = packageData;
|
|
25
50
|
const reporter = new Reporter(process.stdout);
|
|
26
51
|
const startTime = process.hrtime.bigint();
|
|
27
52
|
|