@spikers/next-openapi-json-generator 1.1.3 → 2.0.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.
Files changed (5) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +211 -211
  3. package/dist/index.cjs +103 -78
  4. package/dist/index.js +103 -78
  5. package/package.json +22 -18
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Omer Mecitoglu
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Omer Mecitoglu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,211 +1,211 @@
1
- # Next OpenAPI JSON Generator
2
-
3
- [![npm version](https://img.shields.io/npm/v/@omer-x/next-openapi-json-generator?logo=npm&logoColor=CB3837&color=CB3837)](https://www.npmjs.com/package/@omer-x/next-openapi-json-generator)
4
- [![npm downloads](https://img.shields.io/npm/dm/@omer-x/next-openapi-json-generator?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTI4OCAzMmMwLTE3LjctMTQuMy0zMi0zMi0zMnMtMzIgMTQuMy0zMiAzMmwwIDI0Mi43LTczLjQtNzMuNGMtMTIuNS0xMi41LTMyLjgtMTIuNS00NS4zIDBzLTEyLjUgMzIuOCAwIDQ1LjNsMTI4IDEyOGMxMi41IDEyLjUgMzIuOCAxMi41IDQ1LjMgMGwxMjgtMTI4YzEyLjUtMTIuNSAxMi41LTMyLjggMC00NS4zcy0zMi44LTEyLjUtNDUuMyAwTDI4OCAyNzQuNyAyODggMzJ6TTY0IDM1MmMtMzUuMyAwLTY0IDI4LjctNjQgNjRsMCAzMmMwIDM1LjMgMjguNyA2NCA2NCA2NGwzODQgMGMzNS4zIDAgNjQtMjguNyA2NC02NGwwLTMyYzAtMzUuMy0yOC43LTY0LTY0LTY0bC0xMDEuNSAwLTQ1LjMgNDUuM2MtMjUgMjUtNjUuNSAyNS05MC41IDBMMTY1LjUgMzUyIDY0IDM1MnptMzY4IDU2YTI0IDI0IDAgMSAxIDAgNDggMjQgMjQgMCAxIDEgMC00OHoiIGZpbGw9IiMwMDc4MjAiIC8+PC9zdmc+&color=007820)](https://www.npmjs.com/package/@omer-x/next-openapi-json-generator)
5
- [![codecov](https://codecov.io/gh/omermecitoglu/next-openapi-json-generator/branch/main/graph/badge.svg)](https://codecov.io/gh/omermecitoglu/next-openapi-json-generator)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4NCAzMmwxMjggMGMxNy43IDAgMzIgMTQuMyAzMiAzMnMtMTQuMyAzMi0zMiAzMkwzOTguNCA5NmMtNS4yIDI1LjgtMjIuOSA0Ny4xLTQ2LjQgNTcuM0wzNTIgNDQ4bDE2MCAwYzE3LjcgMCAzMiAxNC4zIDMyIDMycy0xNC4zIDMyLTMyIDMybC0xOTIgMC0xOTIgMGMtMTcuNyAwLTMyLTE0LjMtMzItMzJzMTQuMy0zMiAzMi0zMmwxNjAgMCAwLTI5NC43Yy0yMy41LTEwLjMtNDEuMi0zMS42LTQ2LjQtNTcuM0wxMjggOTZjLTE3LjcgMC0zMi0xNC4zLTMyLTMyczE0LjMtMzIgMzItMzJsMTI4IDBjMTQuNi0xOS40IDM3LjgtMzIgNjQtMzJzNDkuNCAxMi42IDY0IDMyem01NS42IDI4OGwxNDQuOSAwTDUxMiAxOTUuOCA0MzkuNiAzMjB6TTUxMiA0MTZjLTYyLjkgMC0xMTUuMi0zNC0xMjYtNzguOWMtMi42LTExIDEtMjIuMyA2LjctMzIuMWw5NS4yLTE2My4yYzUtOC42IDE0LjItMTMuOCAyNC4xLTEzLjhzMTkuMSA1LjMgMjQuMSAxMy44bDk1LjIgMTYzLjJjNS43IDkuOCA5LjMgMjEuMSA2LjcgMzIuMUM2MjcuMiAzODIgNTc0LjkgNDE2IDUxMiA0MTZ6TTEyNi44IDE5NS44TDU0LjQgMzIwbDE0NC45IDBMMTI2LjggMTk1Ljh6TS45IDMzNy4xYy0yLjYtMTEgMS0yMi4zIDYuNy0zMi4xbDk1LjItMTYzLjJjNS04LjYgMTQuMi0xMy44IDI0LjEtMTMuOHMxOS4xIDUuMyAyNC4xIDEzLjhsOTUuMiAxNjMuMmM1LjcgOS44IDkuMyAyMS4xIDYuNyAzMi4xQzI0MiAzODIgMTg5LjcgNDE2IDEyNi44IDQxNlMxMS43IDM4MiAuOSAzMzcuMXoiIGZpbGw9IiNEMEE4MUMiIC8+PC9zdmc+)](https://opensource.org/licenses/MIT)
7
- [![GitHub last commit](https://img.shields.io/github/last-commit/omermecitoglu/next-openapi-json-generator?color=c977be&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTMyMCAzMzZhODAgODAgMCAxIDAgMC0xNjAgODAgODAgMCAxIDAgMCAxNjB6bTE1Ni44LTQ4QzQ2MiAzNjEgMzk3LjQgNDE2IDMyMCA0MTZzLTE0Mi01NS0xNTYuOC0xMjhMMzIgMjg4Yy0xNy43IDAtMzItMTQuMy0zMi0zMnMxNC4zLTMyIDMyLTMybDEzMS4yIDBDMTc4IDE1MSAyNDIuNiA5NiAzMjAgOTZzMTQyIDU1IDE1Ni44IDEyOEw2MDggMjI0YzE3LjcgMCAzMiAxNC4zIDMyIDMycy0xNC4zIDMyLTMyIDMybC0xMzEuMiAweiIgZmlsbD0iI0M5NzdCRSIgLz48L3N2Zz4=)](https://github.com/omermecitoglu/next-openapi-json-generator/commits/main/)
8
- [![GitHub issues](https://img.shields.io/github/issues/omermecitoglu/next-openapi-json-generator?color=a38eed&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ij4KICA8cGF0aCBkPSJNOCA5LjVhMS41IDEuNSAwIDEgMCAwLTMgMS41IDEuNSAwIDAgMCAwIDNaIiBmaWxsPSIjQTM4RUVEIj48L3BhdGg+CiAgPHBhdGggZD0iTTggMGE4IDggMCAxIDEgMCAxNkE4IDggMCAwIDEgOCAwWk0xLjUgOGE2LjUgNi41IDAgMSAwIDEzIDAgNi41IDYuNSAwIDAgMC0xMyAwWiIgZmlsbD0iI0EzOEVFRCI+PC9wYXRoPgo8L3N2Zz4=)](https://github.com/omermecitoglu/next-openapi-json-generator/issues)
9
- [![GitHub stars](https://img.shields.io/github/stars/omermecitoglu/next-openapi-json-generator?style=social)](https://github.com/omermecitoglu/next-openapi-json-generator)
10
-
11
- ## Overview
12
-
13
- `Next OpenAPI JSON Generator` is an open-source Next.js plugin that extracts and generates OpenAPI JSON specifications from your route handlers defined using `@omer-x/next-openapi-route-handler`. It simplifies the process of generating and maintaining OpenAPI documentation by leveraging TypeScript and Zod schemas.
14
-
15
- **Key Features:**
16
- - **Automated OpenAPI Generation**: Automatically generates OpenAPI JSON specs from your route handlers.
17
- - **TypeScript Integration**: Seamlessly integrates with TypeScript for strong type-checking.
18
- - **Zod Schema Support**: Uses Zod schemas for validation and generates JSON schemas for OpenAPI.
19
- - **Next.js Compatibility**: Works seamlessly with Next.js and integrates with other server-side libraries.
20
-
21
- > **Note:** This package works in conjunction with [`Next OpenAPI Route Handler`](https://www.npmjs.com/package/@omer-x/next-openapi-route-handler) to extract the generated OpenAPI JSON.
22
-
23
- ## Requirements
24
-
25
- To use `@omer-x/next-openapi-json-generator`, you'll need the following dependencies in your Next.js project:
26
-
27
- - [TypeScript](https://www.typescriptlang.org/) >= v5
28
- - [Next.js](https://nextjs.org/) >= v13
29
- - [Zod](https://zod.dev/) >= v3
30
- - [Next OpenAPI Route Handler](https://www.npmjs.com/package/@omer-x/next-openapi-route-handler)
31
-
32
- ## Installation
33
-
34
- To install this package, along with its peer dependency, run:
35
-
36
- ```sh
37
- npm install @omer-x/next-openapi-json-generator @omer-x/next-openapi-route-handler
38
- ```
39
-
40
- ## Usage
41
-
42
- The `generateOpenApiSpec` function is used to extract and generate the OpenAPI JSON specification from your defined models. Here's a description of how to use it:
43
-
44
- ### Example
45
-
46
- ```typescript
47
- import generateOpenApiSpec from "@omer-x/next-openapi-json-generator";
48
- import { NewUserDTO, UserDTO, UserPatchDTO } from "../models/user";
49
-
50
- const Page = async () => {
51
- const spec = await generateOpenApiSpec({
52
- UserDTO,
53
- NewUserDTO,
54
- UserPatchDTO,
55
- }, {
56
- // options
57
- });
58
- return <ReactSwagger spec={spec} />;
59
- };
60
-
61
- export default Page;
62
- ```
63
-
64
- ### Parameters
65
-
66
- The `generateOpenApiSpec` function takes an object with the following properties:
67
-
68
- | Property | Type | Description |
69
- | -------- | ------------------------------------------ | ---------------------------------------------------------------------------------- |
70
- | models | Record<string, [ZodType](https://zod.dev)> | An object where keys are model names and values are Zod schemas |
71
- | options | Object | `(Optional)` An object to customize the functionality of the generator (see below) |
72
-
73
- #### Options
74
-
75
- | Property | Type | Description |
76
- | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77
- | include | string[] | `(Optional)` An array of strings which specifies the routes will be included to the JSON output |
78
- | exclude | string[] | `(Optional)` An array of strings which specifies the routes will be excluded from the JSON output |
79
- | routeDefinerName | string | `(Optional)` Name of the function that was exported from the [`Next OpenAPI Route Handler`](https://www.npmjs.com/package/@omer-x/next-openapi-route-handler) (Default: `defineRoute`) |
80
-
81
- ### Result
82
-
83
- The function returns a promise that resolves to an OpenAPI JSON specification.
84
-
85
- ```json
86
- {
87
- "openapi": "3.1.0",
88
- "info": {
89
- "title": "User Service",
90
- "version": "1.0.0"
91
- },
92
- "paths": {
93
- "/users": {
94
- "get": {
95
- ...
96
- },
97
- "post": {
98
- ...
99
- }
100
- },
101
- "/users/{id}": {
102
- "get": {
103
- "operationId": "getUser",
104
- "summary": "Get a specific user by ID",
105
- "description": "Retrieve details of a specific user by their ID",
106
- "tags": [
107
- "Users"
108
- ],
109
- "parameters": [
110
- {
111
- "in": "path",
112
- "name": "id",
113
- "required": true,
114
- "description": "ID of the user",
115
- "schema": {
116
- "type": "string",
117
- "description": "ID of the user"
118
- }
119
- }
120
- ],
121
- "responses": {
122
- "200": {
123
- "description": "User details retrieved successfully",
124
- "content": {
125
- "application/json": {
126
- "schema": {
127
- "$ref": "#/components/schemas/UserDTO"
128
- }
129
- }
130
- }
131
- },
132
- "404": {
133
- "description": "User not found"
134
- }
135
- }
136
- },
137
- "patch": {
138
- ...
139
- },
140
- "delete": {
141
- ...
142
- }
143
- }
144
- },
145
- "components": {
146
- "schemas": {
147
- "UserDTO": {
148
- "type": "object",
149
- "properties": {
150
- "id": {
151
- "type": "string",
152
- "format": "uuid",
153
- "description": "Unique identifier of the user"
154
- },
155
- "name": {
156
- "type": "string",
157
- "description": "Display name of the user"
158
- },
159
- "email": {
160
- "type": "string",
161
- "description": "Email address of the user"
162
- },
163
- "password": {
164
- "type": "string",
165
- "maxLength": 72,
166
- "description": "Encrypted password of the user"
167
- },
168
- "createdAt": {
169
- "type": "string",
170
- "format": "date-time",
171
- "description": "Creation date of the user"
172
- },
173
- "updatedAt": {
174
- "type": "string",
175
- "format": "date-time",
176
- "description": "Modification date of the user"
177
- }
178
- },
179
- "required": [
180
- "id",
181
- "name",
182
- "email",
183
- "password",
184
- "createdAt",
185
- "updatedAt"
186
- ],
187
- "additionalProperties": false,
188
- "description": "Represents the data of a user in the system."
189
- },
190
- "NewUserDTO": {
191
- ...
192
- },
193
- "UserPatchDTO": {
194
- ...
195
- }
196
- }
197
- }
198
- }
199
- ```
200
-
201
- [An example can be found here](https://github.com/omermecitoglu/example-user-service)
202
-
203
- ## Screenshots
204
-
205
- | <a href="https://i.imgur.com/ru3muBc.png" target="_blank"><img src="https://i.imgur.com/ru3muBc.png" alt="screenshot-1"></a> | <a href="https://i.imgur.com/utHaZ6X.png" target="_blank"><img src="https://i.imgur.com/utHaZ6X.png" alt="screenshot-2"></a> | <a href="https://i.imgur.com/2f24kPE.png" target="_blank"><img src="https://i.imgur.com/2f24kPE.png" alt="screenshot-3"></a> | <a href="https://i.imgur.com/z3KIJQ1.png" target="_blank"><img src="https://i.imgur.com/z3KIJQ1.png" alt="screenshot-4"></a> |
206
- |:--------------:|:--------------:|:--------------:|:--------------:|
207
- | <a href="https://i.imgur.com/IFKXOiX.png" target="_blank"><img src="https://i.imgur.com/IFKXOiX.png" alt="screenshot-5"></a> | <a href="https://i.imgur.com/xzVjAPq.png" target="_blank"><img src="https://i.imgur.com/xzVjAPq.png" alt="screenshot-6"></a> | <a href="https://i.imgur.com/HrWuHOR.png" target="_blank"><img src="https://i.imgur.com/HrWuHOR.png" alt="screenshot-7"></a> | |
208
-
209
- ## License
210
-
211
- This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
1
+ # Next OpenAPI JSON Generator
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@omer-x/next-openapi-json-generator?logo=npm&logoColor=CB3837&color=CB3837)](https://www.npmjs.com/package/@omer-x/next-openapi-json-generator)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@omer-x/next-openapi-json-generator?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTI4OCAzMmMwLTE3LjctMTQuMy0zMi0zMi0zMnMtMzIgMTQuMy0zMiAzMmwwIDI0Mi43LTczLjQtNzMuNGMtMTIuNS0xMi41LTMyLjgtMTIuNS00NS4zIDBzLTEyLjUgMzIuOCAwIDQ1LjNsMTI4IDEyOGMxMi41IDEyLjUgMzIuOCAxMi41IDQ1LjMgMGwxMjgtMTI4YzEyLjUtMTIuNSAxMi41LTMyLjggMC00NS4zcy0zMi44LTEyLjUtNDUuMyAwTDI4OCAyNzQuNyAyODggMzJ6TTY0IDM1MmMtMzUuMyAwLTY0IDI4LjctNjQgNjRsMCAzMmMwIDM1LjMgMjguNyA2NCA2NCA2NGwzODQgMGMzNS4zIDAgNjQtMjguNyA2NC02NGwwLTMyYzAtMzUuMy0yOC43LTY0LTY0LTY0bC0xMDEuNSAwLTQ1LjMgNDUuM2MtMjUgMjUtNjUuNSAyNS05MC41IDBMMTY1LjUgMzUyIDY0IDM1MnptMzY4IDU2YTI0IDI0IDAgMSAxIDAgNDggMjQgMjQgMCAxIDEgMC00OHoiIGZpbGw9IiMwMDc4MjAiIC8+PC9zdmc+&color=007820)](https://www.npmjs.com/package/@omer-x/next-openapi-json-generator)
5
+ [![codecov](https://codecov.io/gh/omermecitoglu/next-openapi-json-generator/branch/main/graph/badge.svg)](https://codecov.io/gh/omermecitoglu/next-openapi-json-generator)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4NCAzMmwxMjggMGMxNy43IDAgMzIgMTQuMyAzMiAzMnMtMTQuMyAzMi0zMiAzMkwzOTguNCA5NmMtNS4yIDI1LjgtMjIuOSA0Ny4xLTQ2LjQgNTcuM0wzNTIgNDQ4bDE2MCAwYzE3LjcgMCAzMiAxNC4zIDMyIDMycy0xNC4zIDMyLTMyIDMybC0xOTIgMC0xOTIgMGMtMTcuNyAwLTMyLTE0LjMtMzItMzJzMTQuMy0zMiAzMi0zMmwxNjAgMCAwLTI5NC43Yy0yMy41LTEwLjMtNDEuMi0zMS42LTQ2LjQtNTcuM0wxMjggOTZjLTE3LjcgMC0zMi0xNC4zLTMyLTMyczE0LjMtMzIgMzItMzJsMTI4IDBjMTQuNi0xOS40IDM3LjgtMzIgNjQtMzJzNDkuNCAxMi42IDY0IDMyem01NS42IDI4OGwxNDQuOSAwTDUxMiAxOTUuOCA0MzkuNiAzMjB6TTUxMiA0MTZjLTYyLjkgMC0xMTUuMi0zNC0xMjYtNzguOWMtMi42LTExIDEtMjIuMyA2LjctMzIuMWw5NS4yLTE2My4yYzUtOC42IDE0LjItMTMuOCAyNC4xLTEzLjhzMTkuMSA1LjMgMjQuMSAxMy44bDk1LjIgMTYzLjJjNS43IDkuOCA5LjMgMjEuMSA2LjcgMzIuMUM2MjcuMiAzODIgNTc0LjkgNDE2IDUxMiA0MTZ6TTEyNi44IDE5NS44TDU0LjQgMzIwbDE0NC45IDBMMTI2LjggMTk1Ljh6TS45IDMzNy4xYy0yLjYtMTEgMS0yMi4zIDYuNy0zMi4xbDk1LjItMTYzLjJjNS04LjYgMTQuMi0xMy44IDI0LjEtMTMuOHMxOS4xIDUuMyAyNC4xIDEzLjhsOTUuMiAxNjMuMmM1LjcgOS44IDkuMyAyMS4xIDYuNyAzMi4xQzI0MiAzODIgMTg5LjcgNDE2IDEyNi44IDQxNlMxMS43IDM4MiAuOSAzMzcuMXoiIGZpbGw9IiNEMEE4MUMiIC8+PC9zdmc+)](https://opensource.org/licenses/MIT)
7
+ [![GitHub last commit](https://img.shields.io/github/last-commit/omermecitoglu/next-openapi-json-generator?color=c977be&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTMyMCAzMzZhODAgODAgMCAxIDAgMC0xNjAgODAgODAgMCAxIDAgMCAxNjB6bTE1Ni44LTQ4QzQ2MiAzNjEgMzk3LjQgNDE2IDMyMCA0MTZzLTE0Mi01NS0xNTYuOC0xMjhMMzIgMjg4Yy0xNy43IDAtMzItMTQuMy0zMi0zMnMxNC4zLTMyIDMyLTMybDEzMS4yIDBDMTc4IDE1MSAyNDIuNiA5NiAzMjAgOTZzMTQyIDU1IDE1Ni44IDEyOEw2MDggMjI0YzE3LjcgMCAzMiAxNC4zIDMyIDMycy0xNC4zIDMyLTMyIDMybC0xMzEuMiAweiIgZmlsbD0iI0M5NzdCRSIgLz48L3N2Zz4=)](https://github.com/omermecitoglu/next-openapi-json-generator/commits/main/)
8
+ [![GitHub issues](https://img.shields.io/github/issues/omermecitoglu/next-openapi-json-generator?color=a38eed&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ij4KICA8cGF0aCBkPSJNOCA5LjVhMS41IDEuNSAwIDEgMCAwLTMgMS41IDEuNSAwIDAgMCAwIDNaIiBmaWxsPSIjQTM4RUVEIj48L3BhdGg+CiAgPHBhdGggZD0iTTggMGE4IDggMCAxIDEgMCAxNkE4IDggMCAwIDEgOCAwWk0xLjUgOGE2LjUgNi41IDAgMSAwIDEzIDAgNi41IDYuNSAwIDAgMC0xMyAwWiIgZmlsbD0iI0EzOEVFRCI+PC9wYXRoPgo8L3N2Zz4=)](https://github.com/omermecitoglu/next-openapi-json-generator/issues)
9
+ [![GitHub stars](https://img.shields.io/github/stars/omermecitoglu/next-openapi-json-generator?style=social)](https://github.com/omermecitoglu/next-openapi-json-generator)
10
+
11
+ ## Overview
12
+
13
+ `Next OpenAPI JSON Generator` is an open-source Next.js plugin that extracts and generates OpenAPI JSON specifications from your route handlers defined using `@omer-x/next-openapi-route-handler`. It simplifies the process of generating and maintaining OpenAPI documentation by leveraging TypeScript and Zod schemas.
14
+
15
+ **Key Features:**
16
+ - **Automated OpenAPI Generation**: Automatically generates OpenAPI JSON specs from your route handlers.
17
+ - **TypeScript Integration**: Seamlessly integrates with TypeScript for strong type-checking.
18
+ - **Zod Schema Support**: Uses Zod schemas for validation and generates JSON schemas for OpenAPI.
19
+ - **Next.js Compatibility**: Works seamlessly with Next.js and integrates with other server-side libraries.
20
+
21
+ > **Note:** This package works in conjunction with [`Next OpenAPI Route Handler`](https://www.npmjs.com/package/@omer-x/next-openapi-route-handler) to extract the generated OpenAPI JSON.
22
+
23
+ ## Requirements
24
+
25
+ To use `@omer-x/next-openapi-json-generator`, you'll need the following dependencies in your Next.js project:
26
+
27
+ - [TypeScript](https://www.typescriptlang.org/) >= v5
28
+ - [Next.js](https://nextjs.org/) >= v13
29
+ - [Zod](https://zod.dev/) >= v3
30
+ - [Next OpenAPI Route Handler](https://www.npmjs.com/package/@omer-x/next-openapi-route-handler)
31
+
32
+ ## Installation
33
+
34
+ To install this package, along with its peer dependency, run:
35
+
36
+ ```sh
37
+ npm install @omer-x/next-openapi-json-generator @omer-x/next-openapi-route-handler
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ The `generateOpenApiSpec` function is used to extract and generate the OpenAPI JSON specification from your defined models. Here's a description of how to use it:
43
+
44
+ ### Example
45
+
46
+ ```typescript
47
+ import generateOpenApiSpec from "@omer-x/next-openapi-json-generator";
48
+ import { NewUserDTO, UserDTO, UserPatchDTO } from "../models/user";
49
+
50
+ const Page = async () => {
51
+ const spec = await generateOpenApiSpec({
52
+ UserDTO,
53
+ NewUserDTO,
54
+ UserPatchDTO,
55
+ }, {
56
+ // options
57
+ });
58
+ return <ReactSwagger spec={spec} />;
59
+ };
60
+
61
+ export default Page;
62
+ ```
63
+
64
+ ### Parameters
65
+
66
+ The `generateOpenApiSpec` function takes an object with the following properties:
67
+
68
+ | Property | Type | Description |
69
+ | -------- | ------------------------------------------ | ---------------------------------------------------------------------------------- |
70
+ | models | Record<string, [ZodType](https://zod.dev)> | An object where keys are model names and values are Zod schemas |
71
+ | options | Object | `(Optional)` An object to customize the functionality of the generator (see below) |
72
+
73
+ #### Options
74
+
75
+ | Property | Type | Description |
76
+ | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77
+ | include | string[] | `(Optional)` An array of strings which specifies the routes will be included to the JSON output |
78
+ | exclude | string[] | `(Optional)` An array of strings which specifies the routes will be excluded from the JSON output |
79
+ | routeDefinerName | string | `(Optional)` Name of the function that was exported from the [`Next OpenAPI Route Handler`](https://www.npmjs.com/package/@omer-x/next-openapi-route-handler) (Default: `defineRoute`) |
80
+
81
+ ### Result
82
+
83
+ The function returns a promise that resolves to an OpenAPI JSON specification.
84
+
85
+ ```json
86
+ {
87
+ "openapi": "3.1.0",
88
+ "info": {
89
+ "title": "User Service",
90
+ "version": "1.0.0"
91
+ },
92
+ "paths": {
93
+ "/users": {
94
+ "get": {
95
+ ...
96
+ },
97
+ "post": {
98
+ ...
99
+ }
100
+ },
101
+ "/users/{id}": {
102
+ "get": {
103
+ "operationId": "getUser",
104
+ "summary": "Get a specific user by ID",
105
+ "description": "Retrieve details of a specific user by their ID",
106
+ "tags": [
107
+ "Users"
108
+ ],
109
+ "parameters": [
110
+ {
111
+ "in": "path",
112
+ "name": "id",
113
+ "required": true,
114
+ "description": "ID of the user",
115
+ "schema": {
116
+ "type": "string",
117
+ "description": "ID of the user"
118
+ }
119
+ }
120
+ ],
121
+ "responses": {
122
+ "200": {
123
+ "description": "User details retrieved successfully",
124
+ "content": {
125
+ "application/json": {
126
+ "schema": {
127
+ "$ref": "#/components/schemas/UserDTO"
128
+ }
129
+ }
130
+ }
131
+ },
132
+ "404": {
133
+ "description": "User not found"
134
+ }
135
+ }
136
+ },
137
+ "patch": {
138
+ ...
139
+ },
140
+ "delete": {
141
+ ...
142
+ }
143
+ }
144
+ },
145
+ "components": {
146
+ "schemas": {
147
+ "UserDTO": {
148
+ "type": "object",
149
+ "properties": {
150
+ "id": {
151
+ "type": "string",
152
+ "format": "uuid",
153
+ "description": "Unique identifier of the user"
154
+ },
155
+ "name": {
156
+ "type": "string",
157
+ "description": "Display name of the user"
158
+ },
159
+ "email": {
160
+ "type": "string",
161
+ "description": "Email address of the user"
162
+ },
163
+ "password": {
164
+ "type": "string",
165
+ "maxLength": 72,
166
+ "description": "Encrypted password of the user"
167
+ },
168
+ "createdAt": {
169
+ "type": "string",
170
+ "format": "date-time",
171
+ "description": "Creation date of the user"
172
+ },
173
+ "updatedAt": {
174
+ "type": "string",
175
+ "format": "date-time",
176
+ "description": "Modification date of the user"
177
+ }
178
+ },
179
+ "required": [
180
+ "id",
181
+ "name",
182
+ "email",
183
+ "password",
184
+ "createdAt",
185
+ "updatedAt"
186
+ ],
187
+ "additionalProperties": false,
188
+ "description": "Represents the data of a user in the system."
189
+ },
190
+ "NewUserDTO": {
191
+ ...
192
+ },
193
+ "UserPatchDTO": {
194
+ ...
195
+ }
196
+ }
197
+ }
198
+ }
199
+ ```
200
+
201
+ [An example can be found here](https://github.com/omermecitoglu/example-user-service)
202
+
203
+ ## Screenshots
204
+
205
+ | <a href="https://i.imgur.com/ru3muBc.png" target="_blank"><img src="https://i.imgur.com/ru3muBc.png" alt="screenshot-1"></a> | <a href="https://i.imgur.com/utHaZ6X.png" target="_blank"><img src="https://i.imgur.com/utHaZ6X.png" alt="screenshot-2"></a> | <a href="https://i.imgur.com/2f24kPE.png" target="_blank"><img src="https://i.imgur.com/2f24kPE.png" alt="screenshot-3"></a> | <a href="https://i.imgur.com/z3KIJQ1.png" target="_blank"><img src="https://i.imgur.com/z3KIJQ1.png" alt="screenshot-4"></a> |
206
+ |:--------------:|:--------------:|:--------------:|:--------------:|
207
+ | <a href="https://i.imgur.com/IFKXOiX.png" target="_blank"><img src="https://i.imgur.com/IFKXOiX.png" alt="screenshot-5"></a> | <a href="https://i.imgur.com/xzVjAPq.png" target="_blank"><img src="https://i.imgur.com/xzVjAPq.png" alt="screenshot-6"></a> | <a href="https://i.imgur.com/HrWuHOR.png" target="_blank"><img src="https://i.imgur.com/HrWuHOR.png" alt="screenshot-7"></a> | |
208
+
209
+ ## License
210
+
211
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
package/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/core/generateOpenApiSpec.ts
38
38
  var import_node_path4 = __toESM(require("path"), 1);
39
+ var import_package_metadata = __toESM(require("@omer-x/package-metadata"), 1);
39
40
 
40
41
  // src/utils/object.ts
41
42
  function omit(object, ...keys) {
@@ -90,7 +91,7 @@ async function getDirectoryItems(dirPath, targetFileName) {
90
91
  const children = await getDirectoryItems(itemPath, targetFileName);
91
92
  collection.push(...children);
92
93
  } else if (itemName === targetFileName) {
93
- collection.push(itemPath.split(import_node_path.default.sep).join("/").replace(/^[A-Z]:/i, ""));
94
+ collection.push(itemPath);
94
95
  }
95
96
  }
96
97
  return collection;
@@ -108,10 +109,10 @@ function filterDirectoryItems(rootPath, items, include, exclude) {
108
109
 
109
110
  // src/core/isDocumentedRoute.ts
110
111
  var import_promises2 = __toESM(require("fs/promises"), 1);
111
- async function isDocumentedRoute(routePath) {
112
+ async function isDocumentedRoute(routePath2) {
112
113
  try {
113
- const rawCode = await import_promises2.default.readFile(routePath, "utf-8");
114
- return rawCode.includes("@omer-x/next-openapi-route-handler");
114
+ const rawCode = await import_promises2.default.readFile(routePath2, "utf-8");
115
+ return rawCode.includes("@spikers/next-openapi-route-handler");
115
116
  } catch {
116
117
  return false;
117
118
  }
@@ -120,6 +121,8 @@ async function isDocumentedRoute(routePath) {
120
121
  // src/core/next.ts
121
122
  var import_promises3 = __toESM(require("fs/promises"), 1);
122
123
  var import_node_path2 = __toESM(require("path"), 1);
124
+ var import_next_openapi_route_handler = require("@omer-x/next-openapi-route-handler");
125
+ var import_zod = require("zod");
123
126
 
124
127
  // src/utils/generateRandomString.ts
125
128
  function generateRandomString(length) {
@@ -127,9 +130,9 @@ function generateRandomString(length) {
127
130
  }
128
131
 
129
132
  // src/utils/string-preservation.ts
130
- function preserveStrings(code) {
133
+ function preserveStrings(code2) {
131
134
  let replacements = {};
132
- const output = code.replace(/(['"`])((?:\\.|(?!\1).)*)\1/g, (match, quote, content) => {
135
+ const output = code2.replace(/(['"`])((?:\\.|(?!\1).)*)\1/g, (match, quote, content) => {
133
136
  const replacementId = generateRandomString(32);
134
137
  replacements = {
135
138
  ...replacements,
@@ -139,55 +142,61 @@ function preserveStrings(code) {
139
142
  });
140
143
  return { output, replacements };
141
144
  }
142
- function restoreStrings(code, replacements) {
143
- return code.replace(/<@~(.*?)~@>/g, (_, replacementId) => {
145
+ function restoreStrings(code2, replacements) {
146
+ return code2.replace(/<@~(.*?)~@>/g, (_, replacementId) => {
144
147
  return replacements[replacementId];
145
148
  });
146
149
  }
147
150
 
148
151
  // src/core/injectSchemas.ts
149
- function injectSchemas(code, refName) {
150
- const { output: preservedCode, replacements } = preserveStrings(code);
152
+ function injectSchemas(code2, refName) {
153
+ const { output: preservedCode, replacements } = preserveStrings(code2);
151
154
  const preservedCodeWithSchemasInjected = preservedCode.replace(new RegExp(`\\b${refName}\\.`, "g"), `global.schemas[${refName}].`).replace(new RegExp(`\\b${refName}\\b`, "g"), `"${refName}"`).replace(new RegExp(`queryParams:\\s*['"\`]${refName}['"\`]`, "g"), `queryParams: global.schemas["${refName}"]`).replace(new RegExp(`pathParams:\\s*['"\`]${refName}['"\`]`, "g"), `pathParams: global.schemas["${refName}"]`);
152
155
  return restoreStrings(preservedCodeWithSchemasInjected, replacements);
153
156
  }
154
157
 
155
158
  // src/core/middleware.ts
156
- function detectMiddlewareName(code) {
157
- const match = code.match(/middleware:\s*(\w+)/);
159
+ function detectMiddlewareName(code2) {
160
+ const match = code2.match(/middleware:\s*(\w+)/);
158
161
  return match ? match[1] : null;
159
162
  }
160
163
 
161
- // src/core/transpile.ts
162
- var import_typescript = require("typescript");
163
-
164
164
  // src/utils/removeImports.ts
165
- function removeImports(code) {
166
- return code.replace(/(^import\s+[^;]+;?$|^import\s+[^;]*\sfrom\s.+;?$)/gm, "").replace(/(^import\s+{[\s\S]+?}\s+from\s+["'][^"']+["'];?)/gm, "").trim();
165
+ function removeImports(code2) {
166
+ return code2.replace(/(^import\s+[^;]+;?$|^import\s+[^;]*\sfrom\s.+;?$)/gm, "").replace(/(^import\s+{[\s\S]+?}\s+from\s+["'][^"']+["'];?)/gm, "").trim();
167
167
  }
168
168
 
169
169
  // src/core/transpile.ts
170
- function fixExports(code) {
170
+ function fixExportsInCommonJS(code2) {
171
171
  const validMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"];
172
172
  const exportFixer1 = validMethods.map((method) => `exports.${method} = void 0;
173
- `);
174
- const exportFixer2 = `module.exports = { ${validMethods.map((m) => `${m}: exports.${m}`).join(", ")} }`;
173
+ `).join("\n");
174
+ const exportFixer2 = `module.exports = { ${validMethods.map((m) => `${m}: exports.${m}`).join(", ")} };`;
175
175
  return `${exportFixer1}
176
- ${code}
176
+ ${code2}
177
177
  ${exportFixer2}`;
178
178
  }
179
179
  function injectMiddlewareFixer(middlewareName) {
180
180
  return `const ${middlewareName} = (handler) => handler;`;
181
181
  }
182
- function transpile(rawCode, routeDefinerName, middlewareName) {
183
- const code = fixExports(removeImports(rawCode));
182
+ function transpile(isCommonJS, rawCode, middlewareName, transpileModule) {
184
183
  const parts = [
185
- `import ${routeDefinerName} from '@omer-x/next-openapi-route-handler';`,
186
- "import z from 'zod';",
187
184
  middlewareName ? injectMiddlewareFixer(middlewareName) : "",
188
- code
185
+ removeImports(rawCode)
189
186
  ];
190
- return (0, import_typescript.transpile)(parts.join("\n"));
187
+ const output = transpileModule(parts.join("\n"), {
188
+ compilerOptions: {
189
+ module: isCommonJS ? 3 : 99,
190
+ target: 99,
191
+ sourceMap: false,
192
+ inlineSourceMap: false,
193
+ inlineSources: false
194
+ }
195
+ });
196
+ if (isCommonJS) {
197
+ return fixExportsInCommonJS(output.outputText);
198
+ }
199
+ return output.outputText;
191
200
  }
192
201
 
193
202
  // src/core/next.ts
@@ -202,33 +211,46 @@ async function findAppFolderPath() {
202
211
  }
203
212
  return null;
204
213
  }
205
- function safeEval(code, routePath) {
214
+ async function safeEval(code, routePath) {
206
215
  try {
207
- const fn = new Function("global", "require", `
208
- const module = { exports: {} };
209
- const exports = module.exports;
210
-
211
- const NextResponse = { json: () => ({}) };
212
- const next = { server: { NextResponse } };
213
-
214
- ${code}
215
-
216
- return module.exports;
217
- `);
218
- return fn(global, require);
216
+ if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
217
+ return eval(code);
218
+ }
219
+ return await import(
220
+ /* webpackIgnore: true */
221
+ `data:text/javascript,${encodeURIComponent(code)}`
222
+ );
219
223
  } catch (error) {
220
224
  console.log(`An error occured while evaluating the route exports from "${routePath}"`);
221
225
  throw error;
222
226
  }
223
227
  }
224
- async function getRouteExports(routePath, routeDefinerName, schemas) {
225
- const rawCode = await import_promises3.default.readFile(routePath, "utf-8");
228
+ async function getModuleTranspiler() {
229
+ if (typeof require !== "undefined" && typeof exports !== "undefined") {
230
+ return require(
231
+ /* webpackIgnore: true */
232
+ "typescript"
233
+ ).transpileModule;
234
+ }
235
+ const { transpileModule } = await import(
236
+ /* webpackIgnore: true */
237
+ "typescript"
238
+ );
239
+ return transpileModule;
240
+ }
241
+ async function getRouteExports(routePath2, routeDefinerName, schemas) {
242
+ const rawCode = await import_promises3.default.readFile(routePath2, "utf-8");
226
243
  const middlewareName = detectMiddlewareName(rawCode);
227
- const code = transpile(rawCode, routeDefinerName, middlewareName);
228
- const fixedCode = Object.keys(schemas).reduce(injectSchemas, code);
244
+ const isCommonJS = typeof module !== "undefined" && typeof module.exports !== "undefined";
245
+ const code2 = transpile(isCommonJS, rawCode, middlewareName, await getModuleTranspiler());
246
+ const fixedCode = Object.keys(schemas).reduce(injectSchemas, code2);
247
+ global[routeDefinerName] = import_next_openapi_route_handler.defineRoute;
248
+ global.z = import_zod.z;
229
249
  global.schemas = schemas;
230
- const result = safeEval(fixedCode, routePath);
250
+ const result = await safeEval(fixedCode, routePath2);
231
251
  delete global.schemas;
252
+ delete global[routeDefinerName];
253
+ delete global.z;
232
254
  return result;
233
255
  }
234
256
 
@@ -256,7 +278,7 @@ function verifyOptions(include, exclude) {
256
278
  var import_node_path3 = __toESM(require("path"), 1);
257
279
  function getRoutePathName(filePath, rootPath) {
258
280
  const dirName = import_node_path3.default.dirname(filePath);
259
- return "/" + import_node_path3.default.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/").replace(/\([^)]+\)\/|\/\([^)]+\)|\([^)]+\)/g, "");
281
+ return "/" + import_node_path3.default.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/");
260
282
  }
261
283
 
262
284
  // src/utils/deepEqual.ts
@@ -282,36 +304,38 @@ function deepEqual(a, b) {
282
304
  }
283
305
 
284
306
  // src/core/zod-to-openapi.ts
285
- var import_zod_to_json_schema = require("zod-to-json-schema");
286
-
287
- // src/utils/zod-schema.ts
288
- function isFile(schema) {
289
- const file = new File([], "nothing.txt");
290
- const plainObject = { name: "test", size: 0 };
291
- const fileResult = schema.safeParse(file);
292
- const objectResult = schema.safeParse(plainObject);
293
- return fileResult.success && !objectResult.success;
294
- }
295
-
296
- // src/core/zod-to-openapi.ts
297
- function convertToOpenAPI(schema, isArray) {
298
- const result = (0, import_zod_to_json_schema.zodToJsonSchema)(isArray ? schema.array() : schema, {
299
- target: "openApi3",
300
- $refStrategy: "none"
301
- });
302
- if (result.type === "object" && result.properties) {
303
- for (const [propName, prop] of Object.entries(schema.shape)) {
304
- if (isFile(prop)) {
305
- result.properties[propName] = {
306
- type: "string",
307
- format: "binary",
308
- description: prop.description
309
- // contentEncoding: "base64", // swagger-ui-react doesn't support this
310
- };
311
- }
307
+ var import_zod2 = require("zod");
308
+ function fixSchema(schema) {
309
+ if ("unwrap" in schema && typeof schema.unwrap === "function") {
310
+ switch (schema._zod.def.type) {
311
+ case "nullable":
312
+ return fixSchema(schema.unwrap()).nullable();
313
+ case "optional":
314
+ return fixSchema(schema.unwrap()).optional();
315
+ case "readonly":
316
+ return fixSchema(schema.unwrap()).readonly();
317
+ case "array":
318
+ return fixSchema(schema.unwrap()).array();
319
+ case "nonoptional":
320
+ return fixSchema(schema.unwrap());
321
+ default:
322
+ throw new Error(`${schema._zod.def.type} type is not covered in fixSchema (@omer-x/next-openapi-json-generator")`);
312
323
  }
313
324
  }
314
- return result;
325
+ if (schema._zod.def.type === "date") {
326
+ return import_zod2.z.iso.datetime();
327
+ }
328
+ if (schema._zod.def.type === "object") {
329
+ const { shape } = schema;
330
+ const entries = Object.entries(shape);
331
+ const alteredEntries = entries.map(([propName, prop]) => [propName, fixSchema(prop)]);
332
+ const newShape = Object.fromEntries(alteredEntries);
333
+ return import_zod2.z.object(newShape);
334
+ }
335
+ return schema;
336
+ }
337
+ function convertToOpenAPI(schema, isArray) {
338
+ return import_zod2.z.toJSONSchema(fixSchema(isArray ? schema.array() : schema));
315
339
  }
316
340
 
317
341
  // src/core/mask.ts
@@ -471,6 +495,7 @@ async function generateOpenApiSpec(schemas, {
471
495
  ));
472
496
  }
473
497
  }
498
+ const metadata = (0, import_package_metadata.default)();
474
499
  const pathsAndComponents = {
475
500
  paths: bundlePaths(validRoutes, schemas),
476
501
  components: {
@@ -478,18 +503,18 @@ async function generateOpenApiSpec(schemas, {
478
503
  securitySchemes
479
504
  }
480
505
  };
481
- return {
506
+ return JSON.parse(JSON.stringify({
482
507
  openapi: "3.1.0",
483
508
  info: {
484
- title: "API Documentation",
485
- version: "1.0.0",
509
+ title: metadata.serviceName,
510
+ version: metadata.version,
486
511
  ...info ?? {}
487
512
  },
488
513
  servers,
489
514
  ...clearUnusedSchemasOption ? clearUnusedSchemas(pathsAndComponents) : pathsAndComponents,
490
515
  security,
491
516
  tags: []
492
- };
517
+ }));
493
518
  }
494
519
 
495
520
  // src/index.ts
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
 
8
8
  // src/core/generateOpenApiSpec.ts
9
9
  import path4 from "path";
10
+ import getPackageMetadata from "@omer-x/package-metadata";
10
11
 
11
12
  // src/utils/object.ts
12
13
  function omit(object, ...keys) {
@@ -61,7 +62,7 @@ async function getDirectoryItems(dirPath, targetFileName) {
61
62
  const children = await getDirectoryItems(itemPath, targetFileName);
62
63
  collection.push(...children);
63
64
  } else if (itemName === targetFileName) {
64
- collection.push(itemPath.split(path.sep).join("/").replace(/^[A-Z]:/i, ""));
65
+ collection.push(itemPath);
65
66
  }
66
67
  }
67
68
  return collection;
@@ -79,10 +80,10 @@ function filterDirectoryItems(rootPath, items, include, exclude) {
79
80
 
80
81
  // src/core/isDocumentedRoute.ts
81
82
  import fs2 from "fs/promises";
82
- async function isDocumentedRoute(routePath) {
83
+ async function isDocumentedRoute(routePath2) {
83
84
  try {
84
- const rawCode = await fs2.readFile(routePath, "utf-8");
85
- return rawCode.includes("@omer-x/next-openapi-route-handler");
85
+ const rawCode = await fs2.readFile(routePath2, "utf-8");
86
+ return rawCode.includes("@spikers/next-openapi-route-handler");
86
87
  } catch {
87
88
  return false;
88
89
  }
@@ -91,6 +92,8 @@ async function isDocumentedRoute(routePath) {
91
92
  // src/core/next.ts
92
93
  import fs3 from "fs/promises";
93
94
  import path2 from "path";
95
+ import { defineRoute } from "@omer-x/next-openapi-route-handler";
96
+ import { z } from "zod";
94
97
 
95
98
  // src/utils/generateRandomString.ts
96
99
  function generateRandomString(length) {
@@ -98,9 +101,9 @@ function generateRandomString(length) {
98
101
  }
99
102
 
100
103
  // src/utils/string-preservation.ts
101
- function preserveStrings(code) {
104
+ function preserveStrings(code2) {
102
105
  let replacements = {};
103
- const output = code.replace(/(['"`])((?:\\.|(?!\1).)*)\1/g, (match, quote, content) => {
106
+ const output = code2.replace(/(['"`])((?:\\.|(?!\1).)*)\1/g, (match, quote, content) => {
104
107
  const replacementId = generateRandomString(32);
105
108
  replacements = {
106
109
  ...replacements,
@@ -110,55 +113,61 @@ function preserveStrings(code) {
110
113
  });
111
114
  return { output, replacements };
112
115
  }
113
- function restoreStrings(code, replacements) {
114
- return code.replace(/<@~(.*?)~@>/g, (_, replacementId) => {
116
+ function restoreStrings(code2, replacements) {
117
+ return code2.replace(/<@~(.*?)~@>/g, (_, replacementId) => {
115
118
  return replacements[replacementId];
116
119
  });
117
120
  }
118
121
 
119
122
  // src/core/injectSchemas.ts
120
- function injectSchemas(code, refName) {
121
- const { output: preservedCode, replacements } = preserveStrings(code);
123
+ function injectSchemas(code2, refName) {
124
+ const { output: preservedCode, replacements } = preserveStrings(code2);
122
125
  const preservedCodeWithSchemasInjected = preservedCode.replace(new RegExp(`\\b${refName}\\.`, "g"), `global.schemas[${refName}].`).replace(new RegExp(`\\b${refName}\\b`, "g"), `"${refName}"`).replace(new RegExp(`queryParams:\\s*['"\`]${refName}['"\`]`, "g"), `queryParams: global.schemas["${refName}"]`).replace(new RegExp(`pathParams:\\s*['"\`]${refName}['"\`]`, "g"), `pathParams: global.schemas["${refName}"]`);
123
126
  return restoreStrings(preservedCodeWithSchemasInjected, replacements);
124
127
  }
125
128
 
126
129
  // src/core/middleware.ts
127
- function detectMiddlewareName(code) {
128
- const match = code.match(/middleware:\s*(\w+)/);
130
+ function detectMiddlewareName(code2) {
131
+ const match = code2.match(/middleware:\s*(\w+)/);
129
132
  return match ? match[1] : null;
130
133
  }
131
134
 
132
- // src/core/transpile.ts
133
- import { transpile as tsTranspile } from "typescript";
134
-
135
135
  // src/utils/removeImports.ts
136
- function removeImports(code) {
137
- return code.replace(/(^import\s+[^;]+;?$|^import\s+[^;]*\sfrom\s.+;?$)/gm, "").replace(/(^import\s+{[\s\S]+?}\s+from\s+["'][^"']+["'];?)/gm, "").trim();
136
+ function removeImports(code2) {
137
+ return code2.replace(/(^import\s+[^;]+;?$|^import\s+[^;]*\sfrom\s.+;?$)/gm, "").replace(/(^import\s+{[\s\S]+?}\s+from\s+["'][^"']+["'];?)/gm, "").trim();
138
138
  }
139
139
 
140
140
  // src/core/transpile.ts
141
- function fixExports(code) {
141
+ function fixExportsInCommonJS(code2) {
142
142
  const validMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"];
143
143
  const exportFixer1 = validMethods.map((method) => `exports.${method} = void 0;
144
- `);
145
- const exportFixer2 = `module.exports = { ${validMethods.map((m) => `${m}: exports.${m}`).join(", ")} }`;
144
+ `).join("\n");
145
+ const exportFixer2 = `module.exports = { ${validMethods.map((m) => `${m}: exports.${m}`).join(", ")} };`;
146
146
  return `${exportFixer1}
147
- ${code}
147
+ ${code2}
148
148
  ${exportFixer2}`;
149
149
  }
150
150
  function injectMiddlewareFixer(middlewareName) {
151
151
  return `const ${middlewareName} = (handler) => handler;`;
152
152
  }
153
- function transpile(rawCode, routeDefinerName, middlewareName) {
154
- const code = fixExports(removeImports(rawCode));
153
+ function transpile(isCommonJS, rawCode, middlewareName, transpileModule) {
155
154
  const parts = [
156
- `import ${routeDefinerName} from '@omer-x/next-openapi-route-handler';`,
157
- "import z from 'zod';",
158
155
  middlewareName ? injectMiddlewareFixer(middlewareName) : "",
159
- code
156
+ removeImports(rawCode)
160
157
  ];
161
- return tsTranspile(parts.join("\n"));
158
+ const output = transpileModule(parts.join("\n"), {
159
+ compilerOptions: {
160
+ module: isCommonJS ? 3 : 99,
161
+ target: 99,
162
+ sourceMap: false,
163
+ inlineSourceMap: false,
164
+ inlineSources: false
165
+ }
166
+ });
167
+ if (isCommonJS) {
168
+ return fixExportsInCommonJS(output.outputText);
169
+ }
170
+ return output.outputText;
162
171
  }
163
172
 
164
173
  // src/core/next.ts
@@ -173,33 +182,46 @@ async function findAppFolderPath() {
173
182
  }
174
183
  return null;
175
184
  }
176
- function safeEval(code, routePath) {
185
+ async function safeEval(code, routePath) {
177
186
  try {
178
- const fn = new Function("global", "require", `
179
- const module = { exports: {} };
180
- const exports = module.exports;
181
-
182
- const NextResponse = { json: () => ({}) };
183
- const next = { server: { NextResponse } };
184
-
185
- ${code}
186
-
187
- return module.exports;
188
- `);
189
- return fn(global, __require);
187
+ if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
188
+ return eval(code);
189
+ }
190
+ return await import(
191
+ /* webpackIgnore: true */
192
+ `data:text/javascript,${encodeURIComponent(code)}`
193
+ );
190
194
  } catch (error) {
191
195
  console.log(`An error occured while evaluating the route exports from "${routePath}"`);
192
196
  throw error;
193
197
  }
194
198
  }
195
- async function getRouteExports(routePath, routeDefinerName, schemas) {
196
- const rawCode = await fs3.readFile(routePath, "utf-8");
199
+ async function getModuleTranspiler() {
200
+ if (typeof __require !== "undefined" && typeof exports !== "undefined") {
201
+ return __require(
202
+ /* webpackIgnore: true */
203
+ "typescript"
204
+ ).transpileModule;
205
+ }
206
+ const { transpileModule } = await import(
207
+ /* webpackIgnore: true */
208
+ "typescript"
209
+ );
210
+ return transpileModule;
211
+ }
212
+ async function getRouteExports(routePath2, routeDefinerName, schemas) {
213
+ const rawCode = await fs3.readFile(routePath2, "utf-8");
197
214
  const middlewareName = detectMiddlewareName(rawCode);
198
- const code = transpile(rawCode, routeDefinerName, middlewareName);
199
- const fixedCode = Object.keys(schemas).reduce(injectSchemas, code);
215
+ const isCommonJS = typeof module !== "undefined" && typeof module.exports !== "undefined";
216
+ const code2 = transpile(isCommonJS, rawCode, middlewareName, await getModuleTranspiler());
217
+ const fixedCode = Object.keys(schemas).reduce(injectSchemas, code2);
218
+ global[routeDefinerName] = defineRoute;
219
+ global.z = z;
200
220
  global.schemas = schemas;
201
- const result = safeEval(fixedCode, routePath);
221
+ const result = await safeEval(fixedCode, routePath2);
202
222
  delete global.schemas;
223
+ delete global[routeDefinerName];
224
+ delete global.z;
203
225
  return result;
204
226
  }
205
227
 
@@ -227,7 +249,7 @@ function verifyOptions(include, exclude) {
227
249
  import path3 from "path";
228
250
  function getRoutePathName(filePath, rootPath) {
229
251
  const dirName = path3.dirname(filePath);
230
- return "/" + path3.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/").replace(/\([^)]+\)\/|\/\([^)]+\)|\([^)]+\)/g, "");
252
+ return "/" + path3.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/");
231
253
  }
232
254
 
233
255
  // src/utils/deepEqual.ts
@@ -253,36 +275,38 @@ function deepEqual(a, b) {
253
275
  }
254
276
 
255
277
  // src/core/zod-to-openapi.ts
256
- import { zodToJsonSchema } from "zod-to-json-schema";
257
-
258
- // src/utils/zod-schema.ts
259
- function isFile(schema) {
260
- const file = new File([], "nothing.txt");
261
- const plainObject = { name: "test", size: 0 };
262
- const fileResult = schema.safeParse(file);
263
- const objectResult = schema.safeParse(plainObject);
264
- return fileResult.success && !objectResult.success;
265
- }
266
-
267
- // src/core/zod-to-openapi.ts
268
- function convertToOpenAPI(schema, isArray) {
269
- const result = zodToJsonSchema(isArray ? schema.array() : schema, {
270
- target: "openApi3",
271
- $refStrategy: "none"
272
- });
273
- if (result.type === "object" && result.properties) {
274
- for (const [propName, prop] of Object.entries(schema.shape)) {
275
- if (isFile(prop)) {
276
- result.properties[propName] = {
277
- type: "string",
278
- format: "binary",
279
- description: prop.description
280
- // contentEncoding: "base64", // swagger-ui-react doesn't support this
281
- };
282
- }
278
+ import { z as z2 } from "zod";
279
+ function fixSchema(schema) {
280
+ if ("unwrap" in schema && typeof schema.unwrap === "function") {
281
+ switch (schema._zod.def.type) {
282
+ case "nullable":
283
+ return fixSchema(schema.unwrap()).nullable();
284
+ case "optional":
285
+ return fixSchema(schema.unwrap()).optional();
286
+ case "readonly":
287
+ return fixSchema(schema.unwrap()).readonly();
288
+ case "array":
289
+ return fixSchema(schema.unwrap()).array();
290
+ case "nonoptional":
291
+ return fixSchema(schema.unwrap());
292
+ default:
293
+ throw new Error(`${schema._zod.def.type} type is not covered in fixSchema (@omer-x/next-openapi-json-generator")`);
283
294
  }
284
295
  }
285
- return result;
296
+ if (schema._zod.def.type === "date") {
297
+ return z2.iso.datetime();
298
+ }
299
+ if (schema._zod.def.type === "object") {
300
+ const { shape } = schema;
301
+ const entries = Object.entries(shape);
302
+ const alteredEntries = entries.map(([propName, prop]) => [propName, fixSchema(prop)]);
303
+ const newShape = Object.fromEntries(alteredEntries);
304
+ return z2.object(newShape);
305
+ }
306
+ return schema;
307
+ }
308
+ function convertToOpenAPI(schema, isArray) {
309
+ return z2.toJSONSchema(fixSchema(isArray ? schema.array() : schema));
286
310
  }
287
311
 
288
312
  // src/core/mask.ts
@@ -442,6 +466,7 @@ async function generateOpenApiSpec(schemas, {
442
466
  ));
443
467
  }
444
468
  }
469
+ const metadata = getPackageMetadata();
445
470
  const pathsAndComponents = {
446
471
  paths: bundlePaths(validRoutes, schemas),
447
472
  components: {
@@ -449,18 +474,18 @@ async function generateOpenApiSpec(schemas, {
449
474
  securitySchemes
450
475
  }
451
476
  };
452
- return {
477
+ return JSON.parse(JSON.stringify({
453
478
  openapi: "3.1.0",
454
479
  info: {
455
- title: "API Documentation",
456
- version: "1.0.0",
480
+ title: metadata.serviceName,
481
+ version: metadata.version,
457
482
  ...info ?? {}
458
483
  },
459
484
  servers,
460
485
  ...clearUnusedSchemasOption ? clearUnusedSchemas(pathsAndComponents) : pathsAndComponents,
461
486
  security,
462
487
  tags: []
463
- };
488
+ }));
464
489
  }
465
490
 
466
491
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spikers/next-openapi-json-generator",
3
- "version": "1.1.3",
3
+ "version": "2.0.1",
4
4
  "description": "a Next.js plugin to generate OpenAPI documentation from route handlers",
5
5
  "keywords": [
6
6
  "next.js",
@@ -35,28 +35,32 @@
35
35
  }
36
36
  },
37
37
  "scripts": {
38
- "test": "jest",
38
+ "lint": "eslint --flag unstable_native_nodejs_ts_config",
39
+ "lint:fix": "eslint --fix --flag unstable_native_nodejs_ts_config",
40
+ "test": "vitest run --coverage",
41
+ "test:watch": "vitest --coverage",
39
42
  "dev": "tsup --watch",
40
43
  "build": "tsup"
41
44
  },
45
+ "peerDependencies": {
46
+ "@omer-x/json-schema-types": "^1",
47
+ "@omer-x/next-openapi-route-handler": "^2",
48
+ "@omer-x/openapi-types": "^1",
49
+ "typescript": "^5",
50
+ "zod": "^4"
51
+ },
42
52
  "dependencies": {
43
- "minimatch": "^10.0.1",
44
- "typescript": "^5.7.2",
45
- "zod-to-json-schema": "^3.24.1"
53
+ "@omer-x/openapi-optimizer": "alpha",
54
+ "@omer-x/package-metadata": "^1.0.2",
55
+ "minimatch": "^10.1.1"
46
56
  },
47
57
  "devDependencies": {
48
- "@omer-x/eslint-config": "^2.1.2",
49
- "@types/node": "^22.10.2",
50
- "conventional-changelog-conventionalcommits": "^9.0.0",
51
- "eslint": "^9.16.0",
52
- "semantic-release": "^24.2.0",
53
- "ts-jest": "^29.2.5",
54
- "ts-node": "^10.9.2",
55
- "tsup": "^8.3.5",
56
- "zod": "^3.24.1"
57
- },
58
- "peerDependencies": {
59
- "@omer-x/next-openapi-route-handler": "^1",
60
- "@omer-x/openapi-types": "^1"
58
+ "@omer-x/eslint-config": "^2.2.6",
59
+ "@types/node": "^25.0.3",
60
+ "@vitest/coverage-v8": "^4.0.16",
61
+ "eslint": "^9.39.2",
62
+ "semantic-release": "^25.0.2",
63
+ "tsup": "^8.5.1",
64
+ "vitest": "^4.0.16"
61
65
  }
62
66
  }