@omer-x/next-openapi-json-generator 2.0.3 → 2.1.0-rc.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/dist/index.cjs +24 -141
- package/dist/index.js +24 -141
- package/package.json +9 -6
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_openapi_optimizer = require("@omer-x/openapi-optimizer");
|
|
39
40
|
var import_package_metadata = __toESM(require("@omer-x/package-metadata"), 1);
|
|
40
41
|
|
|
41
42
|
// src/utils/object.ts
|
|
@@ -281,26 +282,23 @@ function getRoutePathName(filePath, rootPath) {
|
|
|
281
282
|
return "/" + import_node_path3.default.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/");
|
|
282
283
|
}
|
|
283
284
|
|
|
284
|
-
// src/
|
|
285
|
-
function
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
285
|
+
// src/core/route.ts
|
|
286
|
+
function createRouteRecord(method, filePath, rootPath, apiData) {
|
|
287
|
+
return {
|
|
288
|
+
method: method.toLocaleLowerCase(),
|
|
289
|
+
path: getRoutePathName(filePath, rootPath),
|
|
290
|
+
apiData
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function bundlePaths(source) {
|
|
294
|
+
source.sort((a, b) => a.path.localeCompare(b.path));
|
|
295
|
+
return source.reduce((collection, route) => ({
|
|
296
|
+
...collection,
|
|
297
|
+
[route.path]: {
|
|
298
|
+
...collection[route.path],
|
|
299
|
+
[route.method]: route.apiData
|
|
297
300
|
}
|
|
298
|
-
|
|
299
|
-
case "symbol":
|
|
300
|
-
return false;
|
|
301
|
-
default:
|
|
302
|
-
return a === b;
|
|
303
|
-
}
|
|
301
|
+
}), {});
|
|
304
302
|
}
|
|
305
303
|
|
|
306
304
|
// src/core/zod-to-openapi.ts
|
|
@@ -336,128 +334,12 @@ function convertToOpenAPI(schema, isArray) {
|
|
|
336
334
|
return import_zod2.z.toJSONSchema(fixSchema(isArray ? schema.array() : schema));
|
|
337
335
|
}
|
|
338
336
|
|
|
339
|
-
// src/core/mask.ts
|
|
340
|
-
function maskWithReference(schema, storedSchemas, self) {
|
|
341
|
-
if (self) {
|
|
342
|
-
for (const [schemaName, zodSchema] of Object.entries(storedSchemas)) {
|
|
343
|
-
if (deepEqual(schema, convertToOpenAPI(zodSchema, false))) {
|
|
344
|
-
return {
|
|
345
|
-
$ref: `#/components/schemas/${schemaName}`
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if ("$ref" in schema) return schema;
|
|
351
|
-
if (schema.oneOf) {
|
|
352
|
-
return {
|
|
353
|
-
...schema,
|
|
354
|
-
oneOf: schema.oneOf.map((i) => maskWithReference(i, storedSchemas, true))
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
if (schema.anyOf) {
|
|
358
|
-
return {
|
|
359
|
-
...schema,
|
|
360
|
-
anyOf: schema.anyOf.map((i) => maskWithReference(i, storedSchemas, true))
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
switch (schema.type) {
|
|
364
|
-
case "object":
|
|
365
|
-
return {
|
|
366
|
-
...schema,
|
|
367
|
-
properties: Object.entries(schema.properties ?? {}).reduce((props, [propName, prop]) => ({
|
|
368
|
-
...props,
|
|
369
|
-
[propName]: maskWithReference(prop, storedSchemas, true)
|
|
370
|
-
}), {})
|
|
371
|
-
};
|
|
372
|
-
case "array":
|
|
373
|
-
if (Array.isArray(schema.items)) {
|
|
374
|
-
return {
|
|
375
|
-
...schema,
|
|
376
|
-
items: schema.items.map((i) => maskWithReference(i, storedSchemas, true))
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
return {
|
|
380
|
-
...schema,
|
|
381
|
-
items: maskWithReference(schema.items, storedSchemas, true)
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
return schema;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// src/core/operation-mask.ts
|
|
388
|
-
function maskSchema(storedSchemas, schema) {
|
|
389
|
-
if (!schema) return schema;
|
|
390
|
-
return maskWithReference(schema, storedSchemas, true);
|
|
391
|
-
}
|
|
392
|
-
function maskParameterSchema(param, storedSchemas) {
|
|
393
|
-
if ("$ref" in param) return param;
|
|
394
|
-
return { ...param, schema: maskSchema(storedSchemas, param.schema) };
|
|
395
|
-
}
|
|
396
|
-
function maskContentSchema(storedSchemas, bodyContent) {
|
|
397
|
-
if (!bodyContent) return bodyContent;
|
|
398
|
-
return Object.entries(bodyContent).reduce((collection, [contentType, content]) => ({
|
|
399
|
-
...collection,
|
|
400
|
-
[contentType]: {
|
|
401
|
-
...content,
|
|
402
|
-
schema: maskSchema(storedSchemas, content.schema)
|
|
403
|
-
}
|
|
404
|
-
}), {});
|
|
405
|
-
}
|
|
406
|
-
function maskRequestBodySchema(storedSchemas, body) {
|
|
407
|
-
if (!body || "$ref" in body) return body;
|
|
408
|
-
return { ...body, content: maskContentSchema(storedSchemas, body.content) };
|
|
409
|
-
}
|
|
410
|
-
function maskResponseSchema(storedSchemas, response) {
|
|
411
|
-
if ("$ref" in response) return response;
|
|
412
|
-
return { ...response, content: maskContentSchema(storedSchemas, response.content) };
|
|
413
|
-
}
|
|
414
|
-
function maskSchemasInResponses(storedSchemas, responses) {
|
|
415
|
-
if (!responses) return responses;
|
|
416
|
-
return Object.entries(responses).reduce((collection, [key, response]) => ({
|
|
417
|
-
...collection,
|
|
418
|
-
[key]: maskResponseSchema(storedSchemas, response)
|
|
419
|
-
}), {});
|
|
420
|
-
}
|
|
421
|
-
function maskOperationSchemas(operation, storedSchemas) {
|
|
422
|
-
return {
|
|
423
|
-
...operation,
|
|
424
|
-
parameters: operation.parameters?.map((p) => maskParameterSchema(p, storedSchemas)),
|
|
425
|
-
requestBody: maskRequestBodySchema(storedSchemas, operation.requestBody),
|
|
426
|
-
responses: maskSchemasInResponses(storedSchemas, operation.responses)
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// src/core/route.ts
|
|
431
|
-
function createRouteRecord(method, filePath, rootPath, apiData) {
|
|
432
|
-
return {
|
|
433
|
-
method: method.toLocaleLowerCase(),
|
|
434
|
-
path: getRoutePathName(filePath, rootPath),
|
|
435
|
-
apiData
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
function bundlePaths(source, storedSchemas) {
|
|
439
|
-
source.sort((a, b) => a.path.localeCompare(b.path));
|
|
440
|
-
return source.reduce((collection, route) => ({
|
|
441
|
-
...collection,
|
|
442
|
-
[route.path]: {
|
|
443
|
-
...collection[route.path],
|
|
444
|
-
[route.method]: maskOperationSchemas(route.apiData, storedSchemas)
|
|
445
|
-
}
|
|
446
|
-
}), {});
|
|
447
|
-
}
|
|
448
|
-
|
|
449
337
|
// src/core/schema.ts
|
|
450
338
|
function bundleSchemas(schemas) {
|
|
451
|
-
const
|
|
452
|
-
return
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
};
|
|
456
|
-
}, {});
|
|
457
|
-
return Object.entries(bundledSchemas).reduce((bundle, [schemaName, schema]) => ({
|
|
458
|
-
...bundle,
|
|
459
|
-
[schemaName]: maskWithReference(schema, schemas, false)
|
|
460
|
-
}), {});
|
|
339
|
+
const entries = Object.entries(schemas).map(([schemaName, schema]) => {
|
|
340
|
+
return [schemaName, convertToOpenAPI(schema, false)];
|
|
341
|
+
});
|
|
342
|
+
return Object.fromEntries(entries);
|
|
461
343
|
}
|
|
462
344
|
|
|
463
345
|
// src/core/generateOpenApiSpec.ts
|
|
@@ -495,13 +377,13 @@ async function generateOpenApiSpec(schemas, {
|
|
|
495
377
|
}
|
|
496
378
|
const metadata = (0, import_package_metadata.default)();
|
|
497
379
|
const pathsAndComponents = {
|
|
498
|
-
paths: bundlePaths(validRoutes
|
|
380
|
+
paths: bundlePaths(validRoutes),
|
|
499
381
|
components: {
|
|
500
382
|
schemas: bundleSchemas(schemas),
|
|
501
383
|
securitySchemes
|
|
502
384
|
}
|
|
503
385
|
};
|
|
504
|
-
|
|
386
|
+
const spec = JSON.parse(JSON.stringify({
|
|
505
387
|
openapi: "3.1.0",
|
|
506
388
|
info: {
|
|
507
389
|
title: metadata.serviceName,
|
|
@@ -513,6 +395,7 @@ async function generateOpenApiSpec(schemas, {
|
|
|
513
395
|
security,
|
|
514
396
|
tags: []
|
|
515
397
|
}));
|
|
398
|
+
return (0, import_openapi_optimizer.optimizeOpenApiSpec)(spec);
|
|
516
399
|
}
|
|
517
400
|
|
|
518
401
|
// 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 { optimizeOpenApiSpec } from "@omer-x/openapi-optimizer";
|
|
10
11
|
import getPackageMetadata from "@omer-x/package-metadata";
|
|
11
12
|
|
|
12
13
|
// src/utils/object.ts
|
|
@@ -252,26 +253,23 @@ function getRoutePathName(filePath, rootPath) {
|
|
|
252
253
|
return "/" + path3.relative(rootPath, dirName).replaceAll("[", "{").replaceAll("]", "}").replaceAll("\\", "/");
|
|
253
254
|
}
|
|
254
255
|
|
|
255
|
-
// src/
|
|
256
|
-
function
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
256
|
+
// src/core/route.ts
|
|
257
|
+
function createRouteRecord(method, filePath, rootPath, apiData) {
|
|
258
|
+
return {
|
|
259
|
+
method: method.toLocaleLowerCase(),
|
|
260
|
+
path: getRoutePathName(filePath, rootPath),
|
|
261
|
+
apiData
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function bundlePaths(source) {
|
|
265
|
+
source.sort((a, b) => a.path.localeCompare(b.path));
|
|
266
|
+
return source.reduce((collection, route) => ({
|
|
267
|
+
...collection,
|
|
268
|
+
[route.path]: {
|
|
269
|
+
...collection[route.path],
|
|
270
|
+
[route.method]: route.apiData
|
|
268
271
|
}
|
|
269
|
-
|
|
270
|
-
case "symbol":
|
|
271
|
-
return false;
|
|
272
|
-
default:
|
|
273
|
-
return a === b;
|
|
274
|
-
}
|
|
272
|
+
}), {});
|
|
275
273
|
}
|
|
276
274
|
|
|
277
275
|
// src/core/zod-to-openapi.ts
|
|
@@ -307,128 +305,12 @@ function convertToOpenAPI(schema, isArray) {
|
|
|
307
305
|
return z2.toJSONSchema(fixSchema(isArray ? schema.array() : schema));
|
|
308
306
|
}
|
|
309
307
|
|
|
310
|
-
// src/core/mask.ts
|
|
311
|
-
function maskWithReference(schema, storedSchemas, self) {
|
|
312
|
-
if (self) {
|
|
313
|
-
for (const [schemaName, zodSchema] of Object.entries(storedSchemas)) {
|
|
314
|
-
if (deepEqual(schema, convertToOpenAPI(zodSchema, false))) {
|
|
315
|
-
return {
|
|
316
|
-
$ref: `#/components/schemas/${schemaName}`
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if ("$ref" in schema) return schema;
|
|
322
|
-
if (schema.oneOf) {
|
|
323
|
-
return {
|
|
324
|
-
...schema,
|
|
325
|
-
oneOf: schema.oneOf.map((i) => maskWithReference(i, storedSchemas, true))
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
if (schema.anyOf) {
|
|
329
|
-
return {
|
|
330
|
-
...schema,
|
|
331
|
-
anyOf: schema.anyOf.map((i) => maskWithReference(i, storedSchemas, true))
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
switch (schema.type) {
|
|
335
|
-
case "object":
|
|
336
|
-
return {
|
|
337
|
-
...schema,
|
|
338
|
-
properties: Object.entries(schema.properties ?? {}).reduce((props, [propName, prop]) => ({
|
|
339
|
-
...props,
|
|
340
|
-
[propName]: maskWithReference(prop, storedSchemas, true)
|
|
341
|
-
}), {})
|
|
342
|
-
};
|
|
343
|
-
case "array":
|
|
344
|
-
if (Array.isArray(schema.items)) {
|
|
345
|
-
return {
|
|
346
|
-
...schema,
|
|
347
|
-
items: schema.items.map((i) => maskWithReference(i, storedSchemas, true))
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
return {
|
|
351
|
-
...schema,
|
|
352
|
-
items: maskWithReference(schema.items, storedSchemas, true)
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
return schema;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// src/core/operation-mask.ts
|
|
359
|
-
function maskSchema(storedSchemas, schema) {
|
|
360
|
-
if (!schema) return schema;
|
|
361
|
-
return maskWithReference(schema, storedSchemas, true);
|
|
362
|
-
}
|
|
363
|
-
function maskParameterSchema(param, storedSchemas) {
|
|
364
|
-
if ("$ref" in param) return param;
|
|
365
|
-
return { ...param, schema: maskSchema(storedSchemas, param.schema) };
|
|
366
|
-
}
|
|
367
|
-
function maskContentSchema(storedSchemas, bodyContent) {
|
|
368
|
-
if (!bodyContent) return bodyContent;
|
|
369
|
-
return Object.entries(bodyContent).reduce((collection, [contentType, content]) => ({
|
|
370
|
-
...collection,
|
|
371
|
-
[contentType]: {
|
|
372
|
-
...content,
|
|
373
|
-
schema: maskSchema(storedSchemas, content.schema)
|
|
374
|
-
}
|
|
375
|
-
}), {});
|
|
376
|
-
}
|
|
377
|
-
function maskRequestBodySchema(storedSchemas, body) {
|
|
378
|
-
if (!body || "$ref" in body) return body;
|
|
379
|
-
return { ...body, content: maskContentSchema(storedSchemas, body.content) };
|
|
380
|
-
}
|
|
381
|
-
function maskResponseSchema(storedSchemas, response) {
|
|
382
|
-
if ("$ref" in response) return response;
|
|
383
|
-
return { ...response, content: maskContentSchema(storedSchemas, response.content) };
|
|
384
|
-
}
|
|
385
|
-
function maskSchemasInResponses(storedSchemas, responses) {
|
|
386
|
-
if (!responses) return responses;
|
|
387
|
-
return Object.entries(responses).reduce((collection, [key, response]) => ({
|
|
388
|
-
...collection,
|
|
389
|
-
[key]: maskResponseSchema(storedSchemas, response)
|
|
390
|
-
}), {});
|
|
391
|
-
}
|
|
392
|
-
function maskOperationSchemas(operation, storedSchemas) {
|
|
393
|
-
return {
|
|
394
|
-
...operation,
|
|
395
|
-
parameters: operation.parameters?.map((p) => maskParameterSchema(p, storedSchemas)),
|
|
396
|
-
requestBody: maskRequestBodySchema(storedSchemas, operation.requestBody),
|
|
397
|
-
responses: maskSchemasInResponses(storedSchemas, operation.responses)
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// src/core/route.ts
|
|
402
|
-
function createRouteRecord(method, filePath, rootPath, apiData) {
|
|
403
|
-
return {
|
|
404
|
-
method: method.toLocaleLowerCase(),
|
|
405
|
-
path: getRoutePathName(filePath, rootPath),
|
|
406
|
-
apiData
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
function bundlePaths(source, storedSchemas) {
|
|
410
|
-
source.sort((a, b) => a.path.localeCompare(b.path));
|
|
411
|
-
return source.reduce((collection, route) => ({
|
|
412
|
-
...collection,
|
|
413
|
-
[route.path]: {
|
|
414
|
-
...collection[route.path],
|
|
415
|
-
[route.method]: maskOperationSchemas(route.apiData, storedSchemas)
|
|
416
|
-
}
|
|
417
|
-
}), {});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
308
|
// src/core/schema.ts
|
|
421
309
|
function bundleSchemas(schemas) {
|
|
422
|
-
const
|
|
423
|
-
return
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
};
|
|
427
|
-
}, {});
|
|
428
|
-
return Object.entries(bundledSchemas).reduce((bundle, [schemaName, schema]) => ({
|
|
429
|
-
...bundle,
|
|
430
|
-
[schemaName]: maskWithReference(schema, schemas, false)
|
|
431
|
-
}), {});
|
|
310
|
+
const entries = Object.entries(schemas).map(([schemaName, schema]) => {
|
|
311
|
+
return [schemaName, convertToOpenAPI(schema, false)];
|
|
312
|
+
});
|
|
313
|
+
return Object.fromEntries(entries);
|
|
432
314
|
}
|
|
433
315
|
|
|
434
316
|
// src/core/generateOpenApiSpec.ts
|
|
@@ -466,13 +348,13 @@ async function generateOpenApiSpec(schemas, {
|
|
|
466
348
|
}
|
|
467
349
|
const metadata = getPackageMetadata();
|
|
468
350
|
const pathsAndComponents = {
|
|
469
|
-
paths: bundlePaths(validRoutes
|
|
351
|
+
paths: bundlePaths(validRoutes),
|
|
470
352
|
components: {
|
|
471
353
|
schemas: bundleSchemas(schemas),
|
|
472
354
|
securitySchemes
|
|
473
355
|
}
|
|
474
356
|
};
|
|
475
|
-
|
|
357
|
+
const spec = JSON.parse(JSON.stringify({
|
|
476
358
|
openapi: "3.1.0",
|
|
477
359
|
info: {
|
|
478
360
|
title: metadata.serviceName,
|
|
@@ -484,6 +366,7 @@ async function generateOpenApiSpec(schemas, {
|
|
|
484
366
|
security,
|
|
485
367
|
tags: []
|
|
486
368
|
}));
|
|
369
|
+
return optimizeOpenApiSpec(spec);
|
|
487
370
|
}
|
|
488
371
|
|
|
489
372
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omer-x/next-openapi-json-generator",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.1.0-rc.1",
|
|
4
4
|
"description": "a Next.js plugin to generate OpenAPI documentation from route handlers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"next.js",
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
|
+
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
|
44
|
+
"lint:fix": "eslint --fix --flag unstable_native_nodejs_ts_config",
|
|
43
45
|
"test": "vitest run --coverage",
|
|
44
46
|
"test:watch": "vitest --coverage",
|
|
45
47
|
"dev": "tsup --watch",
|
|
@@ -48,20 +50,21 @@
|
|
|
48
50
|
"peerDependencies": {
|
|
49
51
|
"@omer-x/next-openapi-route-handler": "^2",
|
|
50
52
|
"@omer-x/openapi-types": "^1",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
+
"typescript": "^5",
|
|
54
|
+
"zod": "^4"
|
|
53
55
|
},
|
|
54
56
|
"dependencies": {
|
|
57
|
+
"@omer-x/openapi-optimizer": "alpha",
|
|
55
58
|
"@omer-x/package-metadata": "^1.0.2",
|
|
56
59
|
"minimatch": "^10.1.1"
|
|
57
60
|
},
|
|
58
61
|
"devDependencies": {
|
|
59
62
|
"@omer-x/eslint-config": "^2.2.6",
|
|
60
|
-
"@types/node": "^25.0.
|
|
61
|
-
"@vitest/coverage-v8": "^4.0.
|
|
63
|
+
"@types/node": "^25.0.3",
|
|
64
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
62
65
|
"eslint": "^9.39.2",
|
|
63
66
|
"semantic-release": "^25.0.2",
|
|
64
67
|
"tsup": "^8.5.1",
|
|
65
|
-
"vitest": "^4.0.
|
|
68
|
+
"vitest": "^4.0.16"
|
|
66
69
|
}
|
|
67
70
|
}
|