nestjs-openapi-next 1.0.3 → 1.0.4

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/README.md CHANGED
@@ -100,7 +100,7 @@ This fork treats tag `summary` and `x-displayName` as equivalent display fields:
100
100
 
101
101
  #### Auto-derived (recommended)
102
102
 
103
- If you use tag `parent` relationships (via `@ApiTag()`), the root-level `x-tagGroups` will be derived automatically:
103
+ If you use tag `parent` relationships (via `@ApiTag()`), the root-level `x-tagGroups` will be derived automatically (both in the scanned output and in the final document), so downstream generators (e.g. `.tags`) can render grouped tags correctly:
104
104
 
105
105
  ```ts
106
106
  @ApiTag({ name: 'Customers' })
@@ -3,7 +3,6 @@ import { HttpServer } from '@nestjs/common/interfaces/http/http-server.interface
3
3
  import { OpenAPIObject, SwaggerCustomOptions, SwaggerDocumentOptions } from './interfaces';
4
4
  export declare class SwaggerModule {
5
5
  private static readonly metadataLoader;
6
- private static readonly HTTP_METHODS;
7
6
  private static collectOperationTagNames;
8
7
  private static mergeWebhooks;
9
8
  private static mergeTags;
@@ -21,6 +21,8 @@ const normalize_rel_path_1 = require("./utils/normalize-rel-path");
21
21
  const resolve_path_util_1 = require("./utils/resolve-path.util");
22
22
  const validate_global_prefix_util_1 = require("./utils/validate-global-prefix.util");
23
23
  const validate_path_util_1 = require("./utils/validate-path.util");
24
+ const build_x_tag_groups_util_1 = require("./utils/build-x-tag-groups.util");
25
+ const collect_operation_tag_names_util_1 = require("./utils/collect-operation-tag-names.util");
24
26
  const NULL_TYPE_SCHEMA = { type: 'null' };
25
27
  function isReferenceObject(value) {
26
28
  return !!(value === null || value === void 0 ? void 0 : value.$ref);
@@ -321,41 +323,7 @@ function normalizeNullableForOas31(document) {
321
323
  }
322
324
  class SwaggerModule {
323
325
  static collectOperationTagNames(paths, webhooks) {
324
- const names = [];
325
- const seen = new Set();
326
- const collectFromItems = (items) => {
327
- if (!items) {
328
- return;
329
- }
330
- for (const pathItem of Object.values(items)) {
331
- if (!pathItem || typeof pathItem !== 'object') {
332
- continue;
333
- }
334
- for (const [key, operation] of Object.entries(pathItem)) {
335
- if (!SwaggerModule.HTTP_METHODS.has(String(key).toLowerCase())) {
336
- continue;
337
- }
338
- const tags = operation === null || operation === void 0 ? void 0 : operation.tags;
339
- if (!Array.isArray(tags)) {
340
- continue;
341
- }
342
- for (const t of tags) {
343
- if (typeof t !== 'string') {
344
- continue;
345
- }
346
- const trimmed = t.trim();
347
- if (!trimmed || seen.has(trimmed)) {
348
- continue;
349
- }
350
- seen.add(trimmed);
351
- names.push(trimmed);
352
- }
353
- }
354
- }
355
- };
356
- collectFromItems(paths);
357
- collectFromItems(webhooks);
358
- return names;
326
+ return (0, collect_operation_tag_names_util_1.collectOperationTagNames)(paths, webhooks);
359
327
  }
360
328
  static mergeWebhooks(configWebhooks, scannedWebhooks) {
361
329
  if (!configWebhooks && !scannedWebhooks) {
@@ -401,55 +369,8 @@ class SwaggerModule {
401
369
  const merged = [...byName.values()];
402
370
  return merged.length > 0 ? merged : undefined;
403
371
  }
404
- static buildXTagGroups(tags) {
405
- if (!tags || tags.length === 0) {
406
- return undefined;
407
- }
408
- const groupToTags = new Map();
409
- const groupToSeen = new Map();
410
- for (const tag of tags) {
411
- const parent = tag.parent;
412
- if (!parent) {
413
- continue;
414
- }
415
- if (!groupToTags.has(parent)) {
416
- groupToTags.set(parent, []);
417
- groupToSeen.set(parent, new Set());
418
- }
419
- let list = groupToTags.get(parent);
420
- if (!list) {
421
- list = [];
422
- groupToTags.set(parent, list);
423
- }
424
- let seen = groupToSeen.get(parent);
425
- if (!seen) {
426
- seen = new Set();
427
- groupToSeen.set(parent, seen);
428
- }
429
- if (!seen.has(tag.name)) {
430
- seen.add(tag.name);
431
- list.push(tag.name);
432
- }
433
- }
434
- if (groupToTags.size === 0) {
435
- return undefined;
436
- }
437
- const allTagNames = new Set(tags.map((t) => t.name));
438
- return [...groupToTags.entries()].map(([name, childTags]) => {
439
- const finalTags = [];
440
- const seen = new Set();
441
- if (allTagNames.has(name)) {
442
- seen.add(name);
443
- finalTags.push(name);
444
- }
445
- for (const t of childTags) {
446
- if (!seen.has(t)) {
447
- seen.add(t);
448
- finalTags.push(t);
449
- }
450
- }
451
- return { name, tags: finalTags };
452
- });
372
+ static buildXTagGroups(tags, operationTagNames) {
373
+ return (0, build_x_tag_groups_util_1.buildXTagGroups)(tags, operationTagNames);
453
374
  }
454
375
  static createDocument(app, config, options = {}) {
455
376
  const swaggerScanner = new swagger_scanner_1.SwaggerScanner();
@@ -463,7 +384,7 @@ class SwaggerModule {
463
384
  normalizeNullableForOas31(mergedDocument);
464
385
  }
465
386
  if (mergedDocument['x-tagGroups'] === undefined) {
466
- const xTagGroups = SwaggerModule.buildXTagGroups(mergedDocument.tags);
387
+ const xTagGroups = SwaggerModule.buildXTagGroups(mergedDocument.tags, operationTagNames);
467
388
  if (xTagGroups) {
468
389
  mergedDocument['x-tagGroups'] = xTagGroups;
469
390
  }
@@ -636,13 +557,3 @@ class SwaggerModule {
636
557
  }
637
558
  exports.SwaggerModule = SwaggerModule;
638
559
  SwaggerModule.metadataLoader = new metadata_loader_1.MetadataLoader();
639
- SwaggerModule.HTTP_METHODS = new Set([
640
- 'get',
641
- 'put',
642
- 'post',
643
- 'delete',
644
- 'options',
645
- 'head',
646
- 'patch',
647
- 'trace'
648
- ]);
@@ -11,6 +11,8 @@ const swagger_explorer_1 = require("./swagger-explorer");
11
11
  const swagger_transformer_1 = require("./swagger-transformer");
12
12
  const get_global_prefix_1 = require("./utils/get-global-prefix");
13
13
  const strip_last_slash_util_1 = require("./utils/strip-last-slash.util");
14
+ const build_x_tag_groups_util_1 = require("./utils/build-x-tag-groups.util");
15
+ const collect_operation_tag_names_util_1 = require("./utils/collect-operation-tag-names.util");
14
16
  class SwaggerScanner {
15
17
  constructor() {
16
18
  this.transformer = new swagger_transformer_1.SwaggerTransformer();
@@ -56,7 +58,10 @@ class SwaggerScanner {
56
58
  });
57
59
  const schemas = this.explorer.getSchemas();
58
60
  this.addExtraModels(schemas, extraModels);
59
- return Object.assign(Object.assign(Object.assign({}, this.transformer.normalizePaths((0, lodash_1.flatten)(denormalizedPaths))), (tagGroups.length > 0 ? { tags: tagGroups } : {})), { components: {
61
+ const normalized = this.transformer.normalizePaths((0, lodash_1.flatten)(denormalizedPaths));
62
+ const operationTagNames = (0, collect_operation_tag_names_util_1.collectOperationTagNames)(normalized.paths, normalized.webhooks);
63
+ const xTagGroups = (0, build_x_tag_groups_util_1.buildXTagGroups)(tagGroups, operationTagNames);
64
+ return Object.assign(Object.assign(Object.assign(Object.assign({}, normalized), (tagGroups.length > 0 ? { tags: tagGroups } : {})), (xTagGroups ? { 'x-tagGroups': xTagGroups } : {})), { components: {
60
65
  schemas: schemas
61
66
  } });
62
67
  }
@@ -0,0 +1,5 @@
1
+ import { TagObject } from '../interfaces/open-api-spec.interface';
2
+ export declare function buildXTagGroups(tags?: TagObject[], operationTagNames?: string[]): {
3
+ name: string;
4
+ tags: string[];
5
+ }[];
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildXTagGroups = buildXTagGroups;
4
+ function buildXTagGroups(tags, operationTagNames) {
5
+ const opNames = new Set((operationTagNames || [])
6
+ .filter((t) => typeof t === 'string')
7
+ .map((t) => t.trim())
8
+ .filter(Boolean));
9
+ if ((!tags || tags.length === 0) && opNames.size === 0) {
10
+ return undefined;
11
+ }
12
+ const groupToChildTags = new Map();
13
+ const groupToSeen = new Map();
14
+ const childToParent = new Map();
15
+ for (const tag of tags || []) {
16
+ const parent = tag.parent;
17
+ if (!parent) {
18
+ continue;
19
+ }
20
+ childToParent.set(tag.name, parent);
21
+ if (!groupToChildTags.has(parent)) {
22
+ groupToChildTags.set(parent, []);
23
+ groupToSeen.set(parent, new Set());
24
+ }
25
+ let list = groupToChildTags.get(parent);
26
+ if (!list) {
27
+ list = [];
28
+ groupToChildTags.set(parent, list);
29
+ }
30
+ let seen = groupToSeen.get(parent);
31
+ if (!seen) {
32
+ seen = new Set();
33
+ groupToSeen.set(parent, seen);
34
+ }
35
+ if (!seen.has(tag.name)) {
36
+ seen.add(tag.name);
37
+ list.push(tag.name);
38
+ }
39
+ }
40
+ for (const name of opNames) {
41
+ if (childToParent.has(name)) {
42
+ continue;
43
+ }
44
+ if (!groupToChildTags.has(name)) {
45
+ groupToChildTags.set(name, []);
46
+ groupToSeen.set(name, new Set());
47
+ }
48
+ }
49
+ if (groupToChildTags.size === 0) {
50
+ return undefined;
51
+ }
52
+ const allTagNames = new Set((tags || []).map((t) => t.name));
53
+ return [...groupToChildTags.entries()].map(([name, childTags]) => {
54
+ const finalTags = [];
55
+ const seen = new Set();
56
+ if (allTagNames.has(name) || opNames.has(name)) {
57
+ seen.add(name);
58
+ finalTags.push(name);
59
+ }
60
+ for (const t of childTags) {
61
+ if (!seen.has(t)) {
62
+ seen.add(t);
63
+ finalTags.push(t);
64
+ }
65
+ }
66
+ return { name, tags: finalTags };
67
+ });
68
+ }
@@ -0,0 +1,2 @@
1
+ import { OpenAPIObject } from '../interfaces/open-api-spec.interface';
2
+ export declare function collectOperationTagNames(paths?: OpenAPIObject['paths'], webhooks?: OpenAPIObject['webhooks']): string[];
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectOperationTagNames = collectOperationTagNames;
4
+ const HTTP_METHODS = new Set([
5
+ 'get',
6
+ 'put',
7
+ 'post',
8
+ 'delete',
9
+ 'options',
10
+ 'head',
11
+ 'patch',
12
+ 'trace',
13
+ 'search',
14
+ 'query'
15
+ ]);
16
+ function collectOperationTagNames(paths, webhooks) {
17
+ const names = [];
18
+ const seen = new Set();
19
+ const collectFromItems = (items) => {
20
+ if (!items) {
21
+ return;
22
+ }
23
+ for (const pathItem of Object.values(items)) {
24
+ if (!pathItem || typeof pathItem !== 'object') {
25
+ continue;
26
+ }
27
+ for (const [key, operation] of Object.entries(pathItem)) {
28
+ if (!HTTP_METHODS.has(String(key).toLowerCase())) {
29
+ continue;
30
+ }
31
+ const tags = operation === null || operation === void 0 ? void 0 : operation.tags;
32
+ if (!Array.isArray(tags)) {
33
+ continue;
34
+ }
35
+ for (const t of tags) {
36
+ if (typeof t !== 'string') {
37
+ continue;
38
+ }
39
+ const trimmed = t.trim();
40
+ if (!trimmed || seen.has(trimmed)) {
41
+ continue;
42
+ }
43
+ seen.add(trimmed);
44
+ names.push(trimmed);
45
+ }
46
+ }
47
+ }
48
+ };
49
+ collectFromItems(paths);
50
+ collectFromItems(webhooks);
51
+ return names;
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-openapi-next",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A fork of @nestjs/swagger support OAS 3.2",
5
5
  "author": "undownding",
6
6
  "license": "MIT",