@spikers/next-openapi-json-generator 2.0.5 → 2.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.
Files changed (5) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +211 -211
  3. package/dist/index.cjs +164 -85
  4. package/dist/index.js +164 -85
  5. package/package.json +15 -15
package/dist/index.cjs CHANGED
@@ -35,12 +35,14 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/core/generateOpenApiSpec.ts
38
- var import_node_path4 = __toESM(require("path"), 1);
39
38
  var import_package_metadata = __toESM(require("@omer-x/package-metadata"), 1);
39
+ var import_node_path4 = __toESM(require("path"), 1);
40
40
 
41
41
  // src/utils/object.ts
42
42
  function omit(object, ...keys) {
43
- return Object.fromEntries(Object.entries(object).filter(([key]) => !keys.includes(key)));
43
+ return Object.fromEntries(
44
+ Object.entries(object).filter(([key]) => !keys.includes(key))
45
+ );
44
46
  }
45
47
 
46
48
  // src/core/clearUnusedSchemas.ts
@@ -53,17 +55,21 @@ function clearUnusedSchemas({
53
55
  }) {
54
56
  if (!components.schemas) return { paths, components };
55
57
  const stringifiedPaths = JSON.stringify(paths);
56
- const stringifiedSchemas = Object.fromEntries(Object.entries(components.schemas).map(([schemaName, schema]) => {
57
- return [schemaName, JSON.stringify(schema)];
58
- }));
58
+ const stringifiedSchemas = Object.fromEntries(
59
+ Object.entries(components.schemas).map(([schemaName, schema]) => {
60
+ return [schemaName, JSON.stringify(schema)];
61
+ })
62
+ );
59
63
  return {
60
64
  paths,
61
65
  components: {
62
66
  ...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
+ schemas: Object.fromEntries(
68
+ Object.entries(components.schemas).filter(([schemaName]) => {
69
+ const otherSchemas = omit(stringifiedSchemas, schemaName);
70
+ return countReferences(schemaName, stringifiedPaths) > 0 || countReferences(schemaName, Object.values(otherSchemas).join("")) > 0;
71
+ })
72
+ )
67
73
  }
68
74
  };
69
75
  }
@@ -71,8 +77,8 @@ function clearUnusedSchemas({
71
77
  // src/core/dir.ts
72
78
  var import_fs = require("fs");
73
79
  var import_promises = __toESM(require("fs/promises"), 1);
74
- var import_node_path = __toESM(require("path"), 1);
75
80
  var import_minimatch = require("minimatch");
81
+ var import_node_path = __toESM(require("path"), 1);
76
82
  async function directoryExists(dirPath) {
77
83
  try {
78
84
  await import_promises.default.access(dirPath, import_fs.constants.F_OK);
@@ -101,8 +107,12 @@ function filterDirectoryItems(rootPath, items, include, exclude) {
101
107
  const excludedPatterns = exclude.map((pattern) => new import_minimatch.Minimatch(pattern));
102
108
  return items.filter((item) => {
103
109
  const relativePath = import_node_path.default.relative(rootPath, item);
104
- const isIncluded = includedPatterns.some((pattern) => pattern.match(relativePath));
105
- const isExcluded = excludedPatterns.some((pattern) => pattern.match(relativePath));
110
+ const isIncluded = includedPatterns.some(
111
+ (pattern) => pattern.match(relativePath)
112
+ );
113
+ const isExcluded = excludedPatterns.some(
114
+ (pattern) => pattern.match(relativePath)
115
+ );
106
116
  return (isIncluded || !include.length) && !isExcluded;
107
117
  });
108
118
  }
@@ -123,9 +133,9 @@ async function isDocumentedRoute(routePath) {
123
133
  }
124
134
 
125
135
  // src/core/next.ts
136
+ var import_next_openapi_route_handler = require("@spikers/next-openapi-route-handler");
126
137
  var import_promises3 = __toESM(require("fs/promises"), 1);
127
138
  var import_node_path2 = __toESM(require("path"), 1);
128
- var import_next_openapi_route_handler = require("@spikers/next-openapi-route-handler");
129
139
  var import_zod = require("zod");
130
140
 
131
141
  // src/utils/generateRandomString.ts
@@ -136,14 +146,17 @@ function generateRandomString(length) {
136
146
  // src/utils/string-preservation.ts
137
147
  function preserveStrings(code) {
138
148
  let replacements = {};
139
- const output = code.replace(/(['"`])((?:\\.|(?!\1).)*)\1/g, (match, quote, content) => {
140
- const replacementId = generateRandomString(32);
141
- replacements = {
142
- ...replacements,
143
- [replacementId]: `${quote}${content}${quote}`
144
- };
145
- return `<@~${replacementId}~@>`;
146
- });
149
+ const output = code.replace(
150
+ /(['"`])((?:\\.|(?!\1).)*)\1/g,
151
+ (match, quote, content) => {
152
+ const replacementId = generateRandomString(32);
153
+ replacements = {
154
+ ...replacements,
155
+ [replacementId]: `${quote}${content}${quote}`
156
+ };
157
+ return `<@~${replacementId}~@>`;
158
+ }
159
+ );
147
160
  return { output, replacements };
148
161
  }
149
162
  function restoreStrings(code, replacements) {
@@ -155,7 +168,13 @@ function restoreStrings(code, replacements) {
155
168
  // src/core/injectSchemas.ts
156
169
  function injectSchemas(code, refName) {
157
170
  const { output: preservedCode, replacements } = preserveStrings(code);
158
- 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}"]`);
171
+ const preservedCodeWithSchemasInjected = preservedCode.replace(new RegExp(`\\b${refName}\\.`, "g"), `global.schemas[${refName}].`).replace(new RegExp(`\\b${refName}\\b`, "g"), `"${refName}"`).replace(
172
+ new RegExp(`queryParams:\\s*['"\`]${refName}['"\`]`, "g"),
173
+ `queryParams: global.schemas["${refName}"]`
174
+ ).replace(
175
+ new RegExp(`pathParams:\\s*['"\`]${refName}['"\`]`, "g"),
176
+ `pathParams: global.schemas["${refName}"]`
177
+ );
159
178
  return restoreStrings(preservedCodeWithSchemasInjected, replacements);
160
179
  }
161
180
 
@@ -224,7 +243,9 @@ async function safeEval(code, routePath) {
224
243
  fn(exports2, module2, require2);
225
244
  return module2.exports;
226
245
  } catch (error) {
227
- console.log(`An error occured while evaluating the route exports from "${routePath}"`);
246
+ console.log(
247
+ `An error occured while evaluating the route exports from "${routePath}"`
248
+ );
228
249
  throw error;
229
250
  }
230
251
  }
@@ -244,7 +265,12 @@ async function getModuleTranspiler() {
244
265
  async function getRouteExports(routePath, routeDefinerName, schemas) {
245
266
  const rawCode = await import_promises3.default.readFile(routePath, "utf-8");
246
267
  const middlewareName = detectMiddlewareName(rawCode);
247
- const code = transpile(true, rawCode, middlewareName, await getModuleTranspiler());
268
+ const code = transpile(
269
+ true,
270
+ rawCode,
271
+ middlewareName,
272
+ await getModuleTranspiler()
273
+ );
248
274
  const fixedCode = Object.keys(schemas).reduce(injectSchemas, code);
249
275
  global[routeDefinerName] = import_next_openapi_route_handler.defineRoute;
250
276
  global.z = import_zod.z;
@@ -321,7 +347,9 @@ function fixSchema(schema) {
321
347
  case "nonoptional":
322
348
  return fixSchema(schema.unwrap());
323
349
  default:
324
- throw new Error(`${schema._zod.def.type} type is not covered in fixSchema (@omer-x/next-openapi-json-generator")`);
350
+ throw new Error(
351
+ `${schema._zod.def.type} type is not covered in fixSchema (@omer-x/next-openapi-json-generator")`
352
+ );
325
353
  }
326
354
  }
327
355
  if (schema._zod.def.type === "date") {
@@ -330,14 +358,19 @@ function fixSchema(schema) {
330
358
  if (schema._zod.def.type === "object") {
331
359
  const { shape } = schema;
332
360
  const entries = Object.entries(shape);
333
- const alteredEntries = entries.map(([propName, prop]) => [propName, fixSchema(prop)]);
361
+ const alteredEntries = entries.map(([propName, prop]) => [
362
+ propName,
363
+ fixSchema(prop)
364
+ ]);
334
365
  const newShape = Object.fromEntries(alteredEntries);
335
366
  return import_zod2.z.object(newShape);
336
367
  }
337
368
  return schema;
338
369
  }
339
370
  function convertToOpenAPI(schema, isArray) {
340
- return import_zod2.z.toJSONSchema(fixSchema(isArray ? schema.array() : schema));
371
+ return import_zod2.z.toJSONSchema(
372
+ fixSchema(isArray ? schema.array() : schema)
373
+ );
341
374
  }
342
375
 
343
376
  // src/core/mask.ts
@@ -368,16 +401,21 @@ function maskWithReference(schema, storedSchemas, self) {
368
401
  case "object":
369
402
  return {
370
403
  ...schema,
371
- properties: Object.entries(schema.properties ?? {}).reduce((props, [propName, prop]) => ({
372
- ...props,
373
- [propName]: maskWithReference(prop, storedSchemas, true)
374
- }), {})
404
+ properties: Object.entries(schema.properties ?? {}).reduce(
405
+ (props, [propName, prop]) => ({
406
+ ...props,
407
+ [propName]: maskWithReference(prop, storedSchemas, true)
408
+ }),
409
+ {}
410
+ )
375
411
  };
376
412
  case "array":
377
413
  if (Array.isArray(schema.items)) {
378
414
  return {
379
415
  ...schema,
380
- items: schema.items.map((i) => maskWithReference(i, storedSchemas, true))
416
+ items: schema.items.map(
417
+ (i) => maskWithReference(i, storedSchemas, true)
418
+ )
381
419
  };
382
420
  }
383
421
  return {
@@ -395,37 +433,54 @@ function maskSchema(storedSchemas, schema) {
395
433
  }
396
434
  function maskParameterSchema(param, storedSchemas) {
397
435
  if ("$ref" in param) return param;
398
- return { ...param, schema: maskSchema(storedSchemas, param.schema) };
436
+ return {
437
+ ...param,
438
+ schema: maskSchema(storedSchemas, param.schema)
439
+ };
399
440
  }
400
441
  function maskContentSchema(storedSchemas, bodyContent) {
401
442
  if (!bodyContent) return bodyContent;
402
- return Object.entries(bodyContent).reduce((collection, [contentType, content]) => ({
403
- ...collection,
404
- [contentType]: {
405
- ...content,
406
- schema: maskSchema(storedSchemas, content.schema)
407
- }
408
- }), {});
443
+ return Object.entries(bodyContent).reduce(
444
+ (collection, [contentType, content]) => ({
445
+ ...collection,
446
+ [contentType]: {
447
+ ...content,
448
+ schema: maskSchema(storedSchemas, content.schema)
449
+ }
450
+ }),
451
+ {}
452
+ );
409
453
  }
410
454
  function maskRequestBodySchema(storedSchemas, body) {
411
455
  if (!body || "$ref" in body) return body;
412
- return { ...body, content: maskContentSchema(storedSchemas, body.content) };
456
+ return {
457
+ ...body,
458
+ content: maskContentSchema(storedSchemas, body.content)
459
+ };
413
460
  }
414
461
  function maskResponseSchema(storedSchemas, response) {
415
462
  if ("$ref" in response) return response;
416
- return { ...response, content: maskContentSchema(storedSchemas, response.content) };
463
+ return {
464
+ ...response,
465
+ content: maskContentSchema(storedSchemas, response.content)
466
+ };
417
467
  }
418
468
  function maskSchemasInResponses(storedSchemas, responses) {
419
469
  if (!responses) return responses;
420
- return Object.entries(responses).reduce((collection, [key, response]) => ({
421
- ...collection,
422
- [key]: maskResponseSchema(storedSchemas, response)
423
- }), {});
470
+ return Object.entries(responses).reduce(
471
+ (collection, [key, response]) => ({
472
+ ...collection,
473
+ [key]: maskResponseSchema(storedSchemas, response)
474
+ }),
475
+ {}
476
+ );
424
477
  }
425
478
  function maskOperationSchemas(operation, storedSchemas) {
426
479
  return {
427
480
  ...operation,
428
- parameters: operation.parameters?.map((p) => maskParameterSchema(p, storedSchemas)),
481
+ parameters: operation.parameters?.map(
482
+ (p) => maskParameterSchema(p, storedSchemas)
483
+ ),
429
484
  requestBody: maskRequestBodySchema(storedSchemas, operation.requestBody),
430
485
  responses: maskSchemasInResponses(storedSchemas, operation.responses)
431
486
  };
@@ -441,27 +496,36 @@ function createRouteRecord(method, filePath, rootPath, apiData) {
441
496
  }
442
497
  function bundlePaths(source, storedSchemas) {
443
498
  source.sort((a, b) => a.path.localeCompare(b.path));
444
- return source.reduce((collection, route) => ({
445
- ...collection,
446
- [route.path]: {
447
- ...collection[route.path],
448
- [route.method]: maskOperationSchemas(route.apiData, storedSchemas)
449
- }
450
- }), {});
499
+ return source.reduce(
500
+ (collection, route) => ({
501
+ ...collection,
502
+ [route.path]: {
503
+ ...collection[route.path],
504
+ [route.method]: maskOperationSchemas(route.apiData, storedSchemas)
505
+ }
506
+ }),
507
+ {}
508
+ );
451
509
  }
452
510
 
453
511
  // src/core/schema.ts
454
512
  function bundleSchemas(schemas) {
455
- const bundledSchemas = Object.keys(schemas).reduce((collection, schemaName) => {
456
- return {
457
- ...collection,
458
- [schemaName]: convertToOpenAPI(schemas[schemaName], false)
459
- };
460
- }, {});
461
- return Object.entries(bundledSchemas).reduce((bundle, [schemaName, schema]) => ({
462
- ...bundle,
463
- [schemaName]: maskWithReference(schema, schemas, false)
464
- }), {});
513
+ const bundledSchemas = Object.keys(schemas).reduce(
514
+ (collection, schemaName) => {
515
+ return {
516
+ ...collection,
517
+ [schemaName]: convertToOpenAPI(schemas[schemaName], false)
518
+ };
519
+ },
520
+ {}
521
+ );
522
+ return Object.entries(bundledSchemas).reduce(
523
+ (bundle, [schemaName, schema]) => ({
524
+ ...bundle,
525
+ [schemaName]: maskWithReference(schema, schemas, false)
526
+ }),
527
+ {}
528
+ );
465
529
  }
466
530
 
467
531
  // src/core/generateOpenApiSpec.ts
@@ -481,20 +545,33 @@ async function generateOpenApiSpec(schemas, {
481
545
  if (!appFolderPath) throw new Error("This is not a Next.js application!");
482
546
  const rootPath = additionalRootPath ? import_node_path4.default.resolve(appFolderPath, "./" + additionalRootPath) : appFolderPath;
483
547
  const routes = await getDirectoryItems(rootPath, "route.ts");
484
- const verifiedRoutes = filterDirectoryItems(rootPath, routes, verifiedOptions.include, verifiedOptions.exclude);
548
+ const verifiedRoutes = filterDirectoryItems(
549
+ rootPath,
550
+ routes,
551
+ verifiedOptions.include,
552
+ verifiedOptions.exclude
553
+ );
485
554
  const validRoutes = [];
486
555
  for (const route of verifiedRoutes) {
487
556
  const isDocumented = await isDocumentedRoute(route);
488
557
  if (!isDocumented) continue;
489
- const exportedRouteHandlers = await getRouteExports(route, routeDefinerName, schemas);
490
- for (const [method, routeHandler] of Object.entries(exportedRouteHandlers)) {
558
+ const exportedRouteHandlers = await getRouteExports(
559
+ route,
560
+ routeDefinerName,
561
+ schemas
562
+ );
563
+ for (const [method, routeHandler] of Object.entries(
564
+ exportedRouteHandlers
565
+ )) {
491
566
  if (!routeHandler || !routeHandler.apiData) continue;
492
- validRoutes.push(createRouteRecord(
493
- method.toLocaleLowerCase(),
494
- route,
495
- rootPath,
496
- routeHandler.apiData
497
- ));
567
+ validRoutes.push(
568
+ createRouteRecord(
569
+ method.toLocaleLowerCase(),
570
+ route,
571
+ rootPath,
572
+ routeHandler.apiData
573
+ )
574
+ );
498
575
  }
499
576
  }
500
577
  const metadata = (0, import_package_metadata.default)();
@@ -505,18 +582,20 @@ async function generateOpenApiSpec(schemas, {
505
582
  securitySchemes
506
583
  }
507
584
  };
508
- return JSON.parse(JSON.stringify({
509
- openapi: "3.1.0",
510
- info: {
511
- title: metadata.serviceName,
512
- version: metadata.version,
513
- ...info ?? {}
514
- },
515
- servers,
516
- ...clearUnusedSchemasOption ? clearUnusedSchemas(pathsAndComponents) : pathsAndComponents,
517
- security,
518
- tags: []
519
- }));
585
+ return JSON.parse(
586
+ JSON.stringify({
587
+ openapi: "3.1.0",
588
+ info: {
589
+ title: metadata.serviceName,
590
+ version: metadata.version,
591
+ ...info ?? {}
592
+ },
593
+ servers,
594
+ ...clearUnusedSchemasOption ? clearUnusedSchemas(pathsAndComponents) : pathsAndComponents,
595
+ security,
596
+ tags: []
597
+ })
598
+ );
520
599
  }
521
600
 
522
601
  // src/index.ts