@omer-x/next-openapi-json-generator 1.1.2 → 1.2.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/dist/index.cjs +58 -12
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +58 -12
- package/package.json +9 -9
package/dist/index.cjs
CHANGED
|
@@ -35,8 +35,39 @@ __export(src_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(src_exports);
|
|
36
36
|
|
|
37
37
|
// src/core/generateOpenApiSpec.ts
|
|
38
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
38
39
|
var import_package_metadata = __toESM(require("@omer-x/package-metadata"), 1);
|
|
39
40
|
|
|
41
|
+
// src/utils/object.ts
|
|
42
|
+
function omit(object, ...keys) {
|
|
43
|
+
return Object.fromEntries(Object.entries(object).filter(([key]) => !keys.includes(key)));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/core/clearUnusedSchemas.ts
|
|
47
|
+
function countReferences(schemaName, source) {
|
|
48
|
+
return (source.match(new RegExp(`"#/components/schemas/${schemaName}"`, "g")) ?? []).length;
|
|
49
|
+
}
|
|
50
|
+
function clearUnusedSchemas({
|
|
51
|
+
paths,
|
|
52
|
+
components
|
|
53
|
+
}) {
|
|
54
|
+
if (!components.schemas) return { paths, components };
|
|
55
|
+
const stringifiedPaths = JSON.stringify(paths);
|
|
56
|
+
const stringifiedSchemas = Object.fromEntries(Object.entries(components.schemas).map(([schemaName, schema]) => {
|
|
57
|
+
return [schemaName, JSON.stringify(schema)];
|
|
58
|
+
}));
|
|
59
|
+
return {
|
|
60
|
+
paths,
|
|
61
|
+
components: {
|
|
62
|
+
...components,
|
|
63
|
+
schemas: Object.fromEntries(Object.entries(components.schemas).filter(([schemaName]) => {
|
|
64
|
+
const otherSchemas = omit(stringifiedSchemas, schemaName);
|
|
65
|
+
return countReferences(schemaName, stringifiedPaths) > 0 || countReferences(schemaName, Object.values(otherSchemas).join("")) > 0;
|
|
66
|
+
}))
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
40
71
|
// src/core/dir.ts
|
|
41
72
|
var import_fs = require("fs");
|
|
42
73
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
@@ -46,7 +77,7 @@ async function directoryExists(dirPath) {
|
|
|
46
77
|
try {
|
|
47
78
|
await import_promises.default.access(dirPath, import_fs.constants.F_OK);
|
|
48
79
|
return true;
|
|
49
|
-
} catch
|
|
80
|
+
} catch {
|
|
50
81
|
return false;
|
|
51
82
|
}
|
|
52
83
|
}
|
|
@@ -183,6 +214,13 @@ function verifyOptions(include, exclude) {
|
|
|
183
214
|
};
|
|
184
215
|
}
|
|
185
216
|
|
|
217
|
+
// src/core/getRoutePathName.ts
|
|
218
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
219
|
+
function getRoutePathName(filePath, rootPath) {
|
|
220
|
+
const dirName = import_node_path3.default.dirname(filePath);
|
|
221
|
+
return "/" + import_node_path3.default.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/");
|
|
222
|
+
}
|
|
223
|
+
|
|
186
224
|
// src/utils/deepEqual.ts
|
|
187
225
|
function deepEqual(a, b) {
|
|
188
226
|
if (typeof a !== typeof b) return false;
|
|
@@ -327,9 +365,6 @@ function maskOperationSchemas(operation, storedSchemas) {
|
|
|
327
365
|
}
|
|
328
366
|
|
|
329
367
|
// src/core/route.ts
|
|
330
|
-
function getRoutePathName(filePath, rootPath) {
|
|
331
|
-
return filePath.replace(rootPath, "").replace("[", "{").replace("]", "}").replaceAll("\\", "/").replace("/route.ts", "");
|
|
332
|
-
}
|
|
333
368
|
function createRouteRecord(method, filePath, rootPath, apiData) {
|
|
334
369
|
return {
|
|
335
370
|
method: method.toLocaleLowerCase(),
|
|
@@ -366,13 +401,18 @@ function bundleSchemas(schemas) {
|
|
|
366
401
|
async function generateOpenApiSpec(schemas, {
|
|
367
402
|
include: includeOption = [],
|
|
368
403
|
exclude: excludeOption = [],
|
|
369
|
-
routeDefinerName = "defineRoute"
|
|
404
|
+
routeDefinerName = "defineRoute",
|
|
405
|
+
rootPath: additionalRootPath,
|
|
406
|
+
servers,
|
|
407
|
+
security,
|
|
408
|
+
securitySchemes
|
|
370
409
|
} = {}) {
|
|
371
410
|
const verifiedOptions = verifyOptions(includeOption, excludeOption);
|
|
372
411
|
const appFolderPath = await findAppFolderPath();
|
|
373
412
|
if (!appFolderPath) throw new Error("This is not a Next.js application!");
|
|
374
|
-
const
|
|
375
|
-
const
|
|
413
|
+
const rootPath = additionalRootPath ? import_node_path4.default.resolve(appFolderPath, "./" + additionalRootPath) : appFolderPath;
|
|
414
|
+
const routes = await getDirectoryItems(rootPath, "route.ts");
|
|
415
|
+
const verifiedRoutes = filterDirectoryItems(rootPath, routes, verifiedOptions.include, verifiedOptions.exclude);
|
|
376
416
|
const validRoutes = [];
|
|
377
417
|
for (const route of verifiedRoutes) {
|
|
378
418
|
const isDocumented = await isDocumentedRoute(route);
|
|
@@ -383,22 +423,28 @@ async function generateOpenApiSpec(schemas, {
|
|
|
383
423
|
validRoutes.push(createRouteRecord(
|
|
384
424
|
method.toLocaleLowerCase(),
|
|
385
425
|
route,
|
|
386
|
-
|
|
426
|
+
rootPath,
|
|
387
427
|
routeHandler.apiData
|
|
388
428
|
));
|
|
389
429
|
}
|
|
390
430
|
}
|
|
391
431
|
const metadata = (0, import_package_metadata.default)();
|
|
432
|
+
const pathsAndComponents = {
|
|
433
|
+
paths: bundlePaths(validRoutes, schemas),
|
|
434
|
+
components: {
|
|
435
|
+
schemas: bundleSchemas(schemas),
|
|
436
|
+
securitySchemes
|
|
437
|
+
}
|
|
438
|
+
};
|
|
392
439
|
return {
|
|
393
440
|
openapi: "3.1.0",
|
|
394
441
|
info: {
|
|
395
442
|
title: metadata.serviceName,
|
|
396
443
|
version: metadata.version
|
|
397
444
|
},
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
},
|
|
445
|
+
servers,
|
|
446
|
+
...clearUnusedSchemas(pathsAndComponents),
|
|
447
|
+
security,
|
|
402
448
|
tags: []
|
|
403
449
|
};
|
|
404
450
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { OpenApiDocument } from '@omer-x/openapi-types';
|
|
2
|
+
import { ComponentsObject } from '@omer-x/openapi-types/components';
|
|
3
|
+
import { ServerObject } from '@omer-x/openapi-types/server';
|
|
2
4
|
import { ZodType } from 'zod';
|
|
3
5
|
|
|
4
6
|
type GeneratorOptions = {
|
|
5
7
|
include?: string[];
|
|
6
8
|
exclude?: string[];
|
|
7
9
|
routeDefinerName?: string;
|
|
10
|
+
rootPath?: string;
|
|
11
|
+
servers?: ServerObject[];
|
|
12
|
+
security?: OpenApiDocument["security"];
|
|
13
|
+
securitySchemes?: ComponentsObject["securitySchemes"];
|
|
8
14
|
};
|
|
9
|
-
declare function generateOpenApiSpec(schemas: Record<string, ZodType>, { include: includeOption, exclude: excludeOption, routeDefinerName, }?: GeneratorOptions): Promise<Omit<OpenApiDocument, "components"> & Required<Pick<OpenApiDocument, "components">>>;
|
|
15
|
+
declare function generateOpenApiSpec(schemas: Record<string, ZodType>, { include: includeOption, exclude: excludeOption, routeDefinerName, rootPath: additionalRootPath, servers, security, securitySchemes, }?: GeneratorOptions): Promise<Omit<OpenApiDocument, "components"> & Required<Pick<OpenApiDocument, "components">>>;
|
|
10
16
|
|
|
11
17
|
export { generateOpenApiSpec as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { OpenApiDocument } from '@omer-x/openapi-types';
|
|
2
|
+
import { ComponentsObject } from '@omer-x/openapi-types/components';
|
|
3
|
+
import { ServerObject } from '@omer-x/openapi-types/server';
|
|
2
4
|
import { ZodType } from 'zod';
|
|
3
5
|
|
|
4
6
|
type GeneratorOptions = {
|
|
5
7
|
include?: string[];
|
|
6
8
|
exclude?: string[];
|
|
7
9
|
routeDefinerName?: string;
|
|
10
|
+
rootPath?: string;
|
|
11
|
+
servers?: ServerObject[];
|
|
12
|
+
security?: OpenApiDocument["security"];
|
|
13
|
+
securitySchemes?: ComponentsObject["securitySchemes"];
|
|
8
14
|
};
|
|
9
|
-
declare function generateOpenApiSpec(schemas: Record<string, ZodType>, { include: includeOption, exclude: excludeOption, routeDefinerName, }?: GeneratorOptions): Promise<Omit<OpenApiDocument, "components"> & Required<Pick<OpenApiDocument, "components">>>;
|
|
15
|
+
declare function generateOpenApiSpec(schemas: Record<string, ZodType>, { include: includeOption, exclude: excludeOption, routeDefinerName, rootPath: additionalRootPath, servers, security, securitySchemes, }?: GeneratorOptions): Promise<Omit<OpenApiDocument, "components"> & Required<Pick<OpenApiDocument, "components">>>;
|
|
10
16
|
|
|
11
17
|
export { generateOpenApiSpec as default };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
// src/core/generateOpenApiSpec.ts
|
|
2
|
+
import path4 from "node:path";
|
|
2
3
|
import getPackageMetadata from "@omer-x/package-metadata";
|
|
3
4
|
|
|
5
|
+
// src/utils/object.ts
|
|
6
|
+
function omit(object, ...keys) {
|
|
7
|
+
return Object.fromEntries(Object.entries(object).filter(([key]) => !keys.includes(key)));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/core/clearUnusedSchemas.ts
|
|
11
|
+
function countReferences(schemaName, source) {
|
|
12
|
+
return (source.match(new RegExp(`"#/components/schemas/${schemaName}"`, "g")) ?? []).length;
|
|
13
|
+
}
|
|
14
|
+
function clearUnusedSchemas({
|
|
15
|
+
paths,
|
|
16
|
+
components
|
|
17
|
+
}) {
|
|
18
|
+
if (!components.schemas) return { paths, components };
|
|
19
|
+
const stringifiedPaths = JSON.stringify(paths);
|
|
20
|
+
const stringifiedSchemas = Object.fromEntries(Object.entries(components.schemas).map(([schemaName, schema]) => {
|
|
21
|
+
return [schemaName, JSON.stringify(schema)];
|
|
22
|
+
}));
|
|
23
|
+
return {
|
|
24
|
+
paths,
|
|
25
|
+
components: {
|
|
26
|
+
...components,
|
|
27
|
+
schemas: Object.fromEntries(Object.entries(components.schemas).filter(([schemaName]) => {
|
|
28
|
+
const otherSchemas = omit(stringifiedSchemas, schemaName);
|
|
29
|
+
return countReferences(schemaName, stringifiedPaths) > 0 || countReferences(schemaName, Object.values(otherSchemas).join("")) > 0;
|
|
30
|
+
}))
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
4
35
|
// src/core/dir.ts
|
|
5
36
|
import { constants } from "fs";
|
|
6
37
|
import fs from "fs/promises";
|
|
@@ -10,7 +41,7 @@ async function directoryExists(dirPath) {
|
|
|
10
41
|
try {
|
|
11
42
|
await fs.access(dirPath, constants.F_OK);
|
|
12
43
|
return true;
|
|
13
|
-
} catch
|
|
44
|
+
} catch {
|
|
14
45
|
return false;
|
|
15
46
|
}
|
|
16
47
|
}
|
|
@@ -147,6 +178,13 @@ function verifyOptions(include, exclude) {
|
|
|
147
178
|
};
|
|
148
179
|
}
|
|
149
180
|
|
|
181
|
+
// src/core/getRoutePathName.ts
|
|
182
|
+
import path3 from "node:path";
|
|
183
|
+
function getRoutePathName(filePath, rootPath) {
|
|
184
|
+
const dirName = path3.dirname(filePath);
|
|
185
|
+
return "/" + path3.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/");
|
|
186
|
+
}
|
|
187
|
+
|
|
150
188
|
// src/utils/deepEqual.ts
|
|
151
189
|
function deepEqual(a, b) {
|
|
152
190
|
if (typeof a !== typeof b) return false;
|
|
@@ -291,9 +329,6 @@ function maskOperationSchemas(operation, storedSchemas) {
|
|
|
291
329
|
}
|
|
292
330
|
|
|
293
331
|
// src/core/route.ts
|
|
294
|
-
function getRoutePathName(filePath, rootPath) {
|
|
295
|
-
return filePath.replace(rootPath, "").replace("[", "{").replace("]", "}").replaceAll("\\", "/").replace("/route.ts", "");
|
|
296
|
-
}
|
|
297
332
|
function createRouteRecord(method, filePath, rootPath, apiData) {
|
|
298
333
|
return {
|
|
299
334
|
method: method.toLocaleLowerCase(),
|
|
@@ -330,13 +365,18 @@ function bundleSchemas(schemas) {
|
|
|
330
365
|
async function generateOpenApiSpec(schemas, {
|
|
331
366
|
include: includeOption = [],
|
|
332
367
|
exclude: excludeOption = [],
|
|
333
|
-
routeDefinerName = "defineRoute"
|
|
368
|
+
routeDefinerName = "defineRoute",
|
|
369
|
+
rootPath: additionalRootPath,
|
|
370
|
+
servers,
|
|
371
|
+
security,
|
|
372
|
+
securitySchemes
|
|
334
373
|
} = {}) {
|
|
335
374
|
const verifiedOptions = verifyOptions(includeOption, excludeOption);
|
|
336
375
|
const appFolderPath = await findAppFolderPath();
|
|
337
376
|
if (!appFolderPath) throw new Error("This is not a Next.js application!");
|
|
338
|
-
const
|
|
339
|
-
const
|
|
377
|
+
const rootPath = additionalRootPath ? path4.resolve(appFolderPath, "./" + additionalRootPath) : appFolderPath;
|
|
378
|
+
const routes = await getDirectoryItems(rootPath, "route.ts");
|
|
379
|
+
const verifiedRoutes = filterDirectoryItems(rootPath, routes, verifiedOptions.include, verifiedOptions.exclude);
|
|
340
380
|
const validRoutes = [];
|
|
341
381
|
for (const route of verifiedRoutes) {
|
|
342
382
|
const isDocumented = await isDocumentedRoute(route);
|
|
@@ -347,22 +387,28 @@ async function generateOpenApiSpec(schemas, {
|
|
|
347
387
|
validRoutes.push(createRouteRecord(
|
|
348
388
|
method.toLocaleLowerCase(),
|
|
349
389
|
route,
|
|
350
|
-
|
|
390
|
+
rootPath,
|
|
351
391
|
routeHandler.apiData
|
|
352
392
|
));
|
|
353
393
|
}
|
|
354
394
|
}
|
|
355
395
|
const metadata = getPackageMetadata();
|
|
396
|
+
const pathsAndComponents = {
|
|
397
|
+
paths: bundlePaths(validRoutes, schemas),
|
|
398
|
+
components: {
|
|
399
|
+
schemas: bundleSchemas(schemas),
|
|
400
|
+
securitySchemes
|
|
401
|
+
}
|
|
402
|
+
};
|
|
356
403
|
return {
|
|
357
404
|
openapi: "3.1.0",
|
|
358
405
|
info: {
|
|
359
406
|
title: metadata.serviceName,
|
|
360
407
|
version: metadata.version
|
|
361
408
|
},
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
},
|
|
409
|
+
servers,
|
|
410
|
+
...clearUnusedSchemas(pathsAndComponents),
|
|
411
|
+
security,
|
|
366
412
|
tags: []
|
|
367
413
|
};
|
|
368
414
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omer-x/next-openapi-json-generator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "a Next.js plugin to generate OpenAPI documentation from route handlers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"next.js",
|
|
@@ -48,20 +48,20 @@
|
|
|
48
48
|
"@omer-x/package-metadata": "^1.0.0",
|
|
49
49
|
"minimatch": "^10.0.1",
|
|
50
50
|
"typescript": "^5.6.3",
|
|
51
|
-
"zod-to-json-schema": "^3.23.
|
|
51
|
+
"zod-to-json-schema": "^3.23.5"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@omer-x/eslint-config": "^
|
|
55
|
-
"@
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"semantic-release": "^24.1.3",
|
|
54
|
+
"@omer-x/eslint-config": "^2.0.2",
|
|
55
|
+
"@types/node": "^22.8.4",
|
|
56
|
+
"eslint": "^9.13.0",
|
|
57
|
+
"semantic-release": "^24.2.0",
|
|
59
58
|
"ts-jest": "^29.2.5",
|
|
60
59
|
"ts-node": "^10.9.2",
|
|
61
|
-
"tsup": "^8.3.
|
|
60
|
+
"tsup": "^8.3.5",
|
|
62
61
|
"zod": "^3.23.8"
|
|
63
62
|
},
|
|
64
63
|
"peerDependencies": {
|
|
65
|
-
"@omer-x/next-openapi-route-handler": "^1"
|
|
64
|
+
"@omer-x/next-openapi-route-handler": "^1",
|
|
65
|
+
"@omer-x/openapi-types": "^1"
|
|
66
66
|
}
|
|
67
67
|
}
|