@redocly/cli 1.8.2 → 1.9.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.
@@ -4,22 +4,16 @@ import { performance } from 'perf_hooks';
4
4
  const isEqual = require('lodash.isequal');
5
5
  import {
6
6
  Config,
7
- Oas3Definition,
8
7
  SpecVersion,
9
8
  BaseResolver,
10
- Document,
11
9
  StyleguideConfig,
12
- Oas3Tag,
13
10
  formatProblems,
14
11
  getTotals,
15
12
  lintDocument,
16
13
  detectSpec,
17
14
  bundleDocument,
18
- Referenced,
19
15
  isRef,
20
- RuleSeverity,
21
16
  } from '@redocly/openapi-core';
22
-
23
17
  import {
24
18
  getFallbackApisOrExit,
25
19
  printExecutionTime,
@@ -32,16 +26,24 @@ import {
32
26
  checkForDeprecatedOptions,
33
27
  } from '../utils/miscellaneous';
34
28
  import { isObject, isString, keysOf } from '../utils/js-utils';
35
- import {
29
+ import { COMPONENTS, OPENAPI3_METHOD } from './split/types';
30
+ import { crawl, startsWithComponents } from './split';
31
+
32
+ import type {
33
+ Oas3Definition,
34
+ Document,
35
+ Oas3Tag,
36
+ Referenced,
37
+ RuleSeverity,
38
+ } from '@redocly/openapi-core';
39
+ import type { BundleResult } from '@redocly/openapi-core/lib/bundle';
40
+ import type {
36
41
  Oas3Parameter,
37
42
  Oas3PathItem,
38
43
  Oas3Server,
39
44
  Oas3_1Definition,
40
45
  } from '@redocly/openapi-core/lib/typings/openapi';
41
- import { OPENAPI3_METHOD } from './split/types';
42
- import { BundleResult } from '@redocly/openapi-core/lib/bundle';
43
46
 
44
- const COMPONENTS = 'components';
45
47
  const Tags = 'tags';
46
48
  const xTagGroups = 'x-tagGroups';
47
49
  let potentialConflictsTotal = 0;
@@ -49,6 +51,7 @@ let potentialConflictsTotal = 0;
49
51
  type JoinDocumentContext = {
50
52
  api: string;
51
53
  apiFilename: string;
54
+ apiTitle?: string;
52
55
  tags: Oas3Tag[];
53
56
  potentialConflicts: any;
54
57
  tagsPrefix: string;
@@ -209,6 +212,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
209
212
  const context = {
210
213
  api,
211
214
  apiFilename,
215
+ apiTitle: info?.title,
212
216
  tags,
213
217
  potentialConflicts,
214
218
  tagsPrefix,
@@ -218,7 +222,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
218
222
  populateTags(context);
219
223
  }
220
224
  collectServers(openapi);
221
- collectInfoDescriptions(openapi, context);
222
225
  collectExternalDocs(openapi, context);
223
226
  collectPaths(openapi, context);
224
227
  collectComponents(openapi, context);
@@ -242,6 +245,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
242
245
  function populateTags({
243
246
  api,
244
247
  apiFilename,
248
+ apiTitle,
245
249
  tags,
246
250
  potentialConflicts,
247
251
  tagsPrefix,
@@ -285,9 +289,10 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
285
289
  }
286
290
 
287
291
  if (!withoutXTagGroups) {
288
- createXTagGroups(apiFilename);
292
+ const groupName = apiTitle || apiFilename;
293
+ createXTagGroups(groupName);
289
294
  if (!tagDuplicate) {
290
- populateXTagGroups(entrypointTagName, getIndexGroup(apiFilename));
295
+ populateXTagGroups(entrypointTagName, getIndexGroup(groupName));
291
296
  }
292
297
  }
293
298
 
@@ -302,20 +307,20 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
302
307
  }
303
308
  }
304
309
 
305
- function getIndexGroup(apiFilename: string): number {
306
- return joinedDef[xTagGroups].findIndex((item: any) => item.name === apiFilename);
310
+ function getIndexGroup(name: string): number {
311
+ return joinedDef[xTagGroups].findIndex((item: any) => item.name === name);
307
312
  }
308
313
 
309
- function createXTagGroups(apiFilename: string) {
314
+ function createXTagGroups(name: string) {
310
315
  if (!joinedDef.hasOwnProperty(xTagGroups)) {
311
316
  joinedDef[xTagGroups] = [];
312
317
  }
313
318
 
314
- if (!joinedDef[xTagGroups].some((g: any) => g.name === apiFilename)) {
315
- joinedDef[xTagGroups].push({ name: apiFilename, tags: [] });
319
+ if (!joinedDef[xTagGroups].some((g: any) => g.name === name)) {
320
+ joinedDef[xTagGroups].push({ name, tags: [] });
316
321
  }
317
322
 
318
- const indexGroup = getIndexGroup(apiFilename);
323
+ const indexGroup = getIndexGroup(name);
319
324
 
320
325
  if (!joinedDef[xTagGroups][indexGroup].hasOwnProperty(Tags)) {
321
326
  joinedDef[xTagGroups][indexGroup][Tags] = [];
@@ -344,27 +349,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
344
349
  }
345
350
  }
346
351
 
347
- function collectInfoDescriptions(
348
- openapi: Oas3Definition,
349
- { apiFilename, componentsPrefix }: JoinDocumentContext
350
- ) {
351
- const { info } = openapi;
352
- if (info?.description) {
353
- const groupIndex = joinedDef[xTagGroups] ? getIndexGroup(apiFilename) : -1;
354
- if (
355
- joinedDef.hasOwnProperty(xTagGroups) &&
356
- groupIndex !== -1 &&
357
- joinedDef[xTagGroups][groupIndex]['tags'] &&
358
- joinedDef[xTagGroups][groupIndex]['tags'].length
359
- ) {
360
- joinedDef[xTagGroups][groupIndex]['description'] = addComponentsPrefix(
361
- info.description,
362
- componentsPrefix!
363
- );
364
- }
365
- }
366
- }
367
-
368
352
  function collectExternalDocs(openapi: Oas3Definition, { api }: JoinDocumentContext) {
369
353
  const { externalDocs } = openapi;
370
354
  if (externalDocs) {
@@ -380,7 +364,14 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
380
364
 
381
365
  function collectPaths(
382
366
  openapi: Oas3Definition,
383
- { apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }: JoinDocumentContext
367
+ {
368
+ apiFilename,
369
+ apiTitle,
370
+ api,
371
+ potentialConflicts,
372
+ tagsPrefix,
373
+ componentsPrefix,
374
+ }: JoinDocumentContext
384
375
  ) {
385
376
  const { paths } = openapi;
386
377
  const operationsSet = new Set(keysOf<typeof OPENAPI3_METHOD>(OPENAPI3_METHOD));
@@ -532,6 +523,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
532
523
  populateTags({
533
524
  api,
534
525
  apiFilename,
526
+ apiTitle,
535
527
  tags: formatTags(tags),
536
528
  potentialConflicts,
537
529
  tagsPrefix,
@@ -542,6 +534,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
542
534
  populateTags({
543
535
  api,
544
536
  apiFilename,
537
+ apiTitle,
545
538
  tags: formatTags(['other']),
546
539
  potentialConflicts,
547
540
  tagsPrefix: tagsPrefix || apiFilename,
@@ -599,7 +592,14 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
599
592
  function collectWebhooks(
600
593
  oasVersion: SpecVersion,
601
594
  openapi: Oas3_1Definition,
602
- { apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }: JoinDocumentContext
595
+ {
596
+ apiFilename,
597
+ apiTitle,
598
+ api,
599
+ potentialConflicts,
600
+ tagsPrefix,
601
+ componentsPrefix,
602
+ }: JoinDocumentContext
603
603
  ) {
604
604
  const webhooks = oasVersion === SpecVersion.OAS3_1 ? 'webhooks' : 'x-webhooks';
605
605
  const openapiWebhooks = openapi[webhooks];
@@ -628,6 +628,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
628
628
  populateTags({
629
629
  api,
630
630
  apiFilename,
631
+ apiTitle,
631
632
  tags: formatTags(tags),
632
633
  potentialConflicts,
633
634
  tagsPrefix,
@@ -757,8 +758,11 @@ function addComponentsPrefix(description: string, componentsPrefix: string) {
757
758
  function addSecurityPrefix(security: any, componentsPrefix: string) {
758
759
  return componentsPrefix
759
760
  ? security?.map((s: any) => {
760
- const key = Object.keys(s)[0];
761
- return { [componentsPrefix + '_' + key]: s[key] };
761
+ const joinedSecuritySchema = {};
762
+ for (const [key, value] of Object.entries(s)) {
763
+ Object.assign(joinedSecuritySchema, { [componentsPrefix + '_' + key]: value });
764
+ }
765
+ return joinedSecuritySchema;
762
766
  })
763
767
  : security;
764
768
  }
@@ -799,30 +803,19 @@ async function validateApi(
799
803
  }
800
804
  }
801
805
 
802
- function crawl(object: any, visitor: any) {
803
- if (!isObject(object)) return;
804
- for (const key of Object.keys(object)) {
805
- visitor(object, key);
806
- crawl(object[key], visitor);
807
- }
808
- }
809
-
810
- function replace$Refs(obj: any, componentsPrefix: string) {
811
- crawl(obj, (node: any) => {
812
- if (node.$ref && isString(node.$ref) && node.$ref.startsWith(`#/${COMPONENTS}/`)) {
806
+ function replace$Refs(obj: unknown, componentsPrefix: string) {
807
+ crawl(obj, (node: Record<string, unknown>) => {
808
+ if (node.$ref && typeof node.$ref === 'string' && startsWithComponents(node.$ref)) {
813
809
  const name = path.basename(node.$ref);
814
810
  node.$ref = node.$ref.replace(name, componentsPrefix + '_' + name);
815
- } else if (
816
- node.discriminator &&
817
- node.discriminator.mapping &&
818
- isObject(node.discriminator.mapping)
819
- ) {
811
+ } else if (isObject(node.discriminator) && isObject(node.discriminator.mapping)) {
820
812
  const { mapping } = node.discriminator;
821
813
  for (const name of Object.keys(mapping)) {
822
- if (isString(mapping[name]) && mapping[name].startsWith(`#/${COMPONENTS}/`)) {
823
- mapping[name] = mapping[name]
814
+ const mappingPointer = mapping[name];
815
+ if (typeof mappingPointer === 'string' && startsWithComponents(mappingPointer)) {
816
+ mapping[name] = mappingPointer
824
817
  .split('/')
825
- .map((name: string, i: number, arr: []) => {
818
+ .map((name, i, arr) => {
826
819
  return arr.length - 1 === i && !name.includes(componentsPrefix)
827
820
  ? componentsPrefix + '_' + name
828
821
  : name;
@@ -1,11 +1,9 @@
1
1
  import { red, blue, yellow, green } from 'colorette';
2
2
  import * as fs from 'fs';
3
3
  import { parseYaml, slash, isRef, isTruthy } from '@redocly/openapi-core';
4
- import type { OasRef } from '@redocly/openapi-core';
5
4
  import * as path from 'path';
6
5
  import { performance } from 'perf_hooks';
7
6
  const isEqual = require('lodash.isequal');
8
-
9
7
  import {
10
8
  printExecutionTime,
11
9
  pathToFilename,
@@ -16,8 +14,16 @@ import {
16
14
  writeToFileByExtension,
17
15
  getAndValidateFileExtension,
18
16
  } from '../../utils/miscellaneous';
19
- import { isString, isObject, isEmptyObject } from '../../utils/js-utils';
17
+ import { isObject, isEmptyObject } from '../../utils/js-utils';
20
18
  import {
19
+ OPENAPI3_COMPONENT,
20
+ COMPONENTS,
21
+ OPENAPI3_METHOD_NAMES,
22
+ OPENAPI3_COMPONENT_NAMES,
23
+ } from './types';
24
+
25
+ import type { OasRef } from '@redocly/openapi-core';
26
+ import type {
21
27
  Definition,
22
28
  Oas2Definition,
23
29
  Oas3Schema,
@@ -26,13 +32,8 @@ import {
26
32
  Oas3Components,
27
33
  Oas3ComponentName,
28
34
  ComponentsFiles,
29
- refObj,
35
+ RefObject,
30
36
  Oas3PathItem,
31
- OPENAPI3_COMPONENT,
32
- COMPONENTS,
33
- componentsPath,
34
- OPENAPI3_METHOD_NAMES,
35
- OPENAPI3_COMPONENT_NAMES,
36
37
  Referenced,
37
38
  } from './types';
38
39
 
@@ -93,8 +94,8 @@ function splitDefinition(
93
94
  writeToFileByExtension(openapi, path.join(openapiDir, `openapi.${ext}`));
94
95
  }
95
96
 
96
- function isStartsWithComponents(node: string) {
97
- return node.startsWith(componentsPath);
97
+ export function startsWithComponents(node: string) {
98
+ return node.startsWith(`#/${COMPONENTS}/`);
98
99
  }
99
100
 
100
101
  function isSupportedExtension(filename: string) {
@@ -144,33 +145,31 @@ function traverseDirectoryDeepCallback(
144
145
  writeToFileByExtension(pathData, filename);
145
146
  }
146
147
 
147
- function crawl(object: any, visitor: any) {
148
+ export function crawl(object: unknown, visitor: (node: Record<string, unknown>) => void) {
148
149
  if (!isObject(object)) return;
150
+
151
+ visitor(object);
149
152
  for (const key of Object.keys(object)) {
150
- visitor(object, key);
151
153
  crawl(object[key], visitor);
152
154
  }
153
155
  }
154
156
 
155
- function replace$Refs(obj: any, relativeFrom: string, componentFiles = {} as ComponentsFiles) {
156
- crawl(obj, (node: any) => {
157
- if (node.$ref && isString(node.$ref) && isStartsWithComponents(node.$ref)) {
158
- replace(node, '$ref');
159
- } else if (
160
- node.discriminator &&
161
- node.discriminator.mapping &&
162
- isObject(node.discriminator.mapping)
163
- ) {
157
+ function replace$Refs(obj: unknown, relativeFrom: string, componentFiles = {} as ComponentsFiles) {
158
+ crawl(obj, (node: Record<string, unknown>) => {
159
+ if (node.$ref && typeof node.$ref === 'string' && startsWithComponents(node.$ref)) {
160
+ replace(node as RefObject, '$ref');
161
+ } else if (isObject(node.discriminator) && isObject(node.discriminator.mapping)) {
164
162
  const { mapping } = node.discriminator;
165
163
  for (const name of Object.keys(mapping)) {
166
- if (isString(mapping[name]) && isStartsWithComponents(mapping[name])) {
167
- replace(node.discriminator.mapping, name);
164
+ const mappingPointer = mapping[name];
165
+ if (typeof mappingPointer === 'string' && startsWithComponents(mappingPointer)) {
166
+ replace(node.discriminator.mapping as RefObject, name);
168
167
  }
169
168
  }
170
169
  }
171
170
  });
172
171
 
173
- function replace(node: refObj, key: string) {
172
+ function replace(node: RefObject, key: string) {
174
173
  const splittedNode = node[key].split('/');
175
174
  const name = splittedNode.pop();
176
175
  const groupName = splittedNode[2];
@@ -28,7 +28,7 @@ export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition;
28
28
  export interface ComponentsFiles {
29
29
  [schemas: string]: any;
30
30
  }
31
- export interface refObj {
31
+ export interface RefObject {
32
32
  [$ref: string]: string;
33
33
  }
34
34
 
@@ -36,8 +36,6 @@ export const COMPONENTS = 'components';
36
36
  export const PATHS = 'paths';
37
37
  export const WEBHOOKS = 'webhooks';
38
38
  export const xWEBHOOKS = 'x-webhooks';
39
- export const componentsPath = `#/${COMPONENTS}/`;
40
-
41
39
  export enum OPENAPI3_METHOD {
42
40
  get = 'get',
43
41
  put = 'put',
@@ -1,4 +1,4 @@
1
- export function isObject(obj: any) {
1
+ export function isObject(obj: unknown): obj is Record<string, unknown> {
2
2
  const type = typeof obj;
3
3
  return type === 'function' || (type === 'object' && !!obj);
4
4
  }
@@ -15,3 +15,10 @@ export function keysOf<T>(obj: T) {
15
15
  if (!obj) return [];
16
16
  return Object.keys(obj) as (keyof T)[];
17
17
  }
18
+
19
+ export function capitalize(s: string) {
20
+ if (s?.length > 0) {
21
+ return s[0].toUpperCase() + s.slice(1);
22
+ }
23
+ return s;
24
+ }