@redocly/cli 1.8.1 → 1.9.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.
@@ -5,6 +5,7 @@ import { Spinner } from '../../utils/spinner';
5
5
  import { DeploymentError } from '../utils';
6
6
  import { yellow } from 'colorette';
7
7
  import { ReuniteApiClient, getApiKeys, getDomain } from '../api';
8
+ import { capitalize } from '../../utils/js-utils';
8
9
 
9
10
  import type { DeploymentStatus, PushResponse, ScorecardItem } from '../api/types';
10
11
 
@@ -177,15 +178,15 @@ function displayDeploymentAndBuildStatus({
177
178
  case 'success':
178
179
  spinner.stop();
179
180
  return process.stdout.write(
180
- `${colors.green(
181
- `🚀 ${buildType.toLocaleUpperCase()} deployment succeeded.`
182
- )}\n${colors.magenta('Preview URL')}: ${colors.cyan(previewUrl!)}\n`
181
+ `${colors.green(`🚀 ${capitalize(buildType)} deploy success.`)}\n${colors.magenta(
182
+ `${capitalize(buildType)} URL`
183
+ )}: ${colors.cyan(previewUrl!)}\n`
183
184
  );
184
185
  case 'failed':
185
186
  spinner.stop();
186
187
  throw new DeploymentError(
187
- `${colors.red(`❌ ${buildType.toLocaleUpperCase()} deployment failed.`)}\n${colors.magenta(
188
- 'Preview URL'
188
+ `${colors.red(`❌ ${capitalize(buildType)} deploy fail.`)}\n${colors.magenta(
189
+ `${capitalize(buildType)} URL`
189
190
  )}: ${colors.cyan(previewUrl!)}`
190
191
  );
191
192
  case 'pending':
@@ -49,6 +49,7 @@ let potentialConflictsTotal = 0;
49
49
  type JoinDocumentContext = {
50
50
  api: string;
51
51
  apiFilename: string;
52
+ apiTitle?: string;
52
53
  tags: Oas3Tag[];
53
54
  potentialConflicts: any;
54
55
  tagsPrefix: string;
@@ -209,6 +210,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
209
210
  const context = {
210
211
  api,
211
212
  apiFilename,
213
+ apiTitle: info?.title,
212
214
  tags,
213
215
  potentialConflicts,
214
216
  tagsPrefix,
@@ -218,7 +220,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
218
220
  populateTags(context);
219
221
  }
220
222
  collectServers(openapi);
221
- collectInfoDescriptions(openapi, context);
222
223
  collectExternalDocs(openapi, context);
223
224
  collectPaths(openapi, context);
224
225
  collectComponents(openapi, context);
@@ -242,6 +243,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
242
243
  function populateTags({
243
244
  api,
244
245
  apiFilename,
246
+ apiTitle,
245
247
  tags,
246
248
  potentialConflicts,
247
249
  tagsPrefix,
@@ -285,9 +287,10 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
285
287
  }
286
288
 
287
289
  if (!withoutXTagGroups) {
288
- createXTagGroups(apiFilename);
290
+ const groupName = apiTitle || apiFilename;
291
+ createXTagGroups(groupName);
289
292
  if (!tagDuplicate) {
290
- populateXTagGroups(entrypointTagName, getIndexGroup(apiFilename));
293
+ populateXTagGroups(entrypointTagName, getIndexGroup(groupName));
291
294
  }
292
295
  }
293
296
 
@@ -302,20 +305,20 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
302
305
  }
303
306
  }
304
307
 
305
- function getIndexGroup(apiFilename: string): number {
306
- return joinedDef[xTagGroups].findIndex((item: any) => item.name === apiFilename);
308
+ function getIndexGroup(name: string): number {
309
+ return joinedDef[xTagGroups].findIndex((item: any) => item.name === name);
307
310
  }
308
311
 
309
- function createXTagGroups(apiFilename: string) {
312
+ function createXTagGroups(name: string) {
310
313
  if (!joinedDef.hasOwnProperty(xTagGroups)) {
311
314
  joinedDef[xTagGroups] = [];
312
315
  }
313
316
 
314
- if (!joinedDef[xTagGroups].some((g: any) => g.name === apiFilename)) {
315
- joinedDef[xTagGroups].push({ name: apiFilename, tags: [] });
317
+ if (!joinedDef[xTagGroups].some((g: any) => g.name === name)) {
318
+ joinedDef[xTagGroups].push({ name, tags: [] });
316
319
  }
317
320
 
318
- const indexGroup = getIndexGroup(apiFilename);
321
+ const indexGroup = getIndexGroup(name);
319
322
 
320
323
  if (!joinedDef[xTagGroups][indexGroup].hasOwnProperty(Tags)) {
321
324
  joinedDef[xTagGroups][indexGroup][Tags] = [];
@@ -344,27 +347,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
344
347
  }
345
348
  }
346
349
 
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
350
  function collectExternalDocs(openapi: Oas3Definition, { api }: JoinDocumentContext) {
369
351
  const { externalDocs } = openapi;
370
352
  if (externalDocs) {
@@ -380,7 +362,14 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
380
362
 
381
363
  function collectPaths(
382
364
  openapi: Oas3Definition,
383
- { apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }: JoinDocumentContext
365
+ {
366
+ apiFilename,
367
+ apiTitle,
368
+ api,
369
+ potentialConflicts,
370
+ tagsPrefix,
371
+ componentsPrefix,
372
+ }: JoinDocumentContext
384
373
  ) {
385
374
  const { paths } = openapi;
386
375
  const operationsSet = new Set(keysOf<typeof OPENAPI3_METHOD>(OPENAPI3_METHOD));
@@ -532,6 +521,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
532
521
  populateTags({
533
522
  api,
534
523
  apiFilename,
524
+ apiTitle,
535
525
  tags: formatTags(tags),
536
526
  potentialConflicts,
537
527
  tagsPrefix,
@@ -542,6 +532,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
542
532
  populateTags({
543
533
  api,
544
534
  apiFilename,
535
+ apiTitle,
545
536
  tags: formatTags(['other']),
546
537
  potentialConflicts,
547
538
  tagsPrefix: tagsPrefix || apiFilename,
@@ -599,7 +590,14 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
599
590
  function collectWebhooks(
600
591
  oasVersion: SpecVersion,
601
592
  openapi: Oas3_1Definition,
602
- { apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }: JoinDocumentContext
593
+ {
594
+ apiFilename,
595
+ apiTitle,
596
+ api,
597
+ potentialConflicts,
598
+ tagsPrefix,
599
+ componentsPrefix,
600
+ }: JoinDocumentContext
603
601
  ) {
604
602
  const webhooks = oasVersion === SpecVersion.OAS3_1 ? 'webhooks' : 'x-webhooks';
605
603
  const openapiWebhooks = openapi[webhooks];
@@ -628,6 +626,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
628
626
  populateTags({
629
627
  api,
630
628
  apiFilename,
629
+ apiTitle,
631
630
  tags: formatTags(tags),
632
631
  potentialConflicts,
633
632
  tagsPrefix,
@@ -802,7 +801,7 @@ async function validateApi(
802
801
  function crawl(object: any, visitor: any) {
803
802
  if (!isObject(object)) return;
804
803
  for (const key of Object.keys(object)) {
805
- visitor(object, key);
804
+ visitor(object[key], key);
806
805
  crawl(object[key], visitor);
807
806
  }
808
807
  }
@@ -55,7 +55,7 @@ export function commonPushHandler({
55
55
  if (project && mountPath) {
56
56
  return handleCMSPush;
57
57
  }
58
- return handlePush;
58
+ return transformPush(handlePush);
59
59
  }
60
60
 
61
61
  export async function handlePush(argv: PushOptions, config: Config): Promise<void> {
@@ -354,8 +354,7 @@ export function getDestinationProps(
354
354
  }
355
355
 
356
356
  type BarePushArgs = Omit<PushOptions, 'destination' | 'branchName'> & {
357
- maybeDestination?: string;
358
- maybeBranchName?: string;
357
+ apis?: string[];
359
358
  branch?: string;
360
359
  destination?: string;
361
360
  };
@@ -364,9 +363,7 @@ export const transformPush =
364
363
  (callback: typeof handlePush) =>
365
364
  (
366
365
  {
367
- api: maybeApiOrDestination,
368
- maybeDestination,
369
- maybeBranchName,
366
+ apis,
370
367
  branch,
371
368
  'batch-id': batchId,
372
369
  'job-id': jobId,
@@ -374,6 +371,8 @@ export const transformPush =
374
371
  }: BarePushArgs & { 'batch-id'?: string },
375
372
  config: Config
376
373
  ) => {
374
+ const [maybeApiOrDestination, maybeDestination, maybeBranchName] = apis || [];
375
+
377
376
  if (batchId) {
378
377
  process.stderr.write(
379
378
  yellow(
@@ -410,12 +409,10 @@ export const transformPush =
410
409
  apiFile = maybeApiOrDestination;
411
410
  }
412
411
 
413
- destination = rest.destination || destination;
414
-
415
412
  return callback(
416
413
  {
417
414
  ...rest,
418
- destination,
415
+ destination: rest.destination ?? destination,
419
416
  api: apiFile,
420
417
  branchName: branch ?? maybeBranchName,
421
418
  'job-id': jobId || batchId,
@@ -46,15 +46,37 @@ function printStatsJson(statsAccumulator: StatsAccumulator) {
46
46
  process.stdout.write(JSON.stringify(json, null, 2));
47
47
  }
48
48
 
49
- function printStats(statsAccumulator: StatsAccumulator, api: string, format: string) {
50
- process.stderr.write(`Document: ${colors.magenta(api)} stats:\n\n`);
49
+ function printStatsMarkdown(statsAccumulator: StatsAccumulator) {
50
+ let output = '| Feature | Count |\n| --- | --- |\n';
51
+ for (const key of Object.keys(statsAccumulator)) {
52
+ output +=
53
+ '| ' +
54
+ statsAccumulator[key as StatsName].metric +
55
+ ' | ' +
56
+ statsAccumulator[key as StatsName].total +
57
+ ' |\n';
58
+ }
59
+ process.stdout.write(output);
60
+ }
61
+
62
+ function printStats(
63
+ statsAccumulator: StatsAccumulator,
64
+ api: string,
65
+ startedAt: number,
66
+ format: string
67
+ ) {
51
68
  switch (format) {
52
69
  case 'stylish':
70
+ process.stderr.write(`Document: ${colors.magenta(api)} stats:\n\n`);
53
71
  printStatsStylish(statsAccumulator);
72
+ printExecutionTime('stats', startedAt, api);
54
73
  break;
55
74
  case 'json':
56
75
  printStatsJson(statsAccumulator);
57
76
  break;
77
+ case 'markdown':
78
+ printStatsMarkdown(statsAccumulator);
79
+ break;
58
80
  }
59
81
  }
60
82
 
@@ -107,6 +129,5 @@ export async function handleStats(argv: StatsOptions, config: Config) {
107
129
  ctx,
108
130
  });
109
131
 
110
- printStats(statsAccumulator, path, argv.format);
111
- printExecutionTime('stats', startedAt, path);
132
+ printStats(statsAccumulator, path, startedAt, argv.format);
112
133
  }
package/src/index.ts CHANGED
@@ -46,7 +46,7 @@ yargs
46
46
  },
47
47
  format: {
48
48
  description: 'Use a specific output format.',
49
- choices: ['stylish', 'json'] as ReadonlyArray<OutputFormat>,
49
+ choices: ['stylish', 'json', 'markdown'] as ReadonlyArray<OutputFormat>,
50
50
  default: 'stylish' as OutputFormat,
51
51
  },
52
52
  }),
@@ -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
+ }
@@ -64,27 +64,27 @@ const renderUpdateBanner = (current: string, latest: string) => {
64
64
  const maxLength = Math.max(...messageLines.map((line) => cleanColors(line).length));
65
65
 
66
66
  const border = yellow('═'.repeat(maxLength + SPACE_TO_BORDER));
67
-
68
- const banner = `
69
- ${yellow('╔' + border + '╗')}
70
- ${yellow('' + ' '.repeat(maxLength + SPACE_TO_BORDER) + '║')}
71
- ${messageLines
72
- .map((line, index) => {
73
- return getLineWithPadding(maxLength, line, index);
74
- })
75
- .join('\n')}
76
- ${yellow('' + ' '.repeat(maxLength + SPACE_TO_BORDER) + '║')}
77
- ${yellow('' + border + '╝')}
78
- `;
79
-
67
+ const extraSpaces = ' '.repeat(SPACE_TO_BORDER);
68
+
69
+ const banner = [
70
+ '',
71
+ extraSpaces + yellow('╔' + border + '╗'),
72
+ extraSpaces + yellow('║' + ' '.repeat(maxLength + SPACE_TO_BORDER) + '║'),
73
+ messageLines.map(getLineWithPadding(maxLength, extraSpaces)).join('\n'),
74
+ extraSpaces + yellow('║' + ' '.repeat(maxLength + SPACE_TO_BORDER) + '║'),
75
+ extraSpaces + yellow('' + border + '╝'),
76
+ '',
77
+ '',
78
+ ].join('\n');
80
79
  process.stderr.write(banner);
81
80
  };
82
81
 
83
- const getLineWithPadding = (maxLength: number, line: string, index: number): string => {
84
- const padding = ' '.repeat(maxLength - cleanColors(line).length);
85
- const extraSpaces = index !== 0 ? ' '.repeat(SPACE_TO_BORDER) : '';
86
- return `${extraSpaces}${yellow('')} ${line}${padding} ${yellow('║')}`;
87
- };
82
+ const getLineWithPadding =
83
+ (maxLength: number, extraSpaces: string) =>
84
+ (line: string): string => {
85
+ const padding = ' '.repeat(maxLength - cleanColors(line).length);
86
+ return `${extraSpaces}${yellow('║')} ${line}${padding} ${yellow('║')}`;
87
+ };
88
88
 
89
89
  const isNeedToBeCached = (): boolean => {
90
90
  try {