@redocly/cli 1.18.1 → 1.20.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 (104) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/lib/__mocks__/@redocly/openapi-core.d.ts +2 -2
  3. package/lib/__mocks__/@redocly/openapi-core.js +1 -0
  4. package/lib/__mocks__/fs.d.ts +0 -1
  5. package/lib/__mocks__/perf_hooks.d.ts +0 -1
  6. package/lib/__mocks__/redoc.d.ts +0 -1
  7. package/lib/__tests__/commands/build-docs.test.js +21 -23
  8. package/lib/__tests__/commands/bundle.test.js +21 -30
  9. package/lib/__tests__/commands/join.test.js +101 -70
  10. package/lib/__tests__/commands/lint.test.js +54 -54
  11. package/lib/__tests__/commands/push-region.test.js +24 -25
  12. package/lib/__tests__/commands/push.test.js +269 -170
  13. package/lib/__tests__/fetch-with-timeout.test.js +3 -12
  14. package/lib/__tests__/fixtures/config.d.ts +0 -1
  15. package/lib/__tests__/utils.test.js +32 -37
  16. package/lib/__tests__/wrapper.test.js +31 -20
  17. package/lib/cms/api/__tests__/api.client.test.js +29 -38
  18. package/lib/cms/api/api-client.d.ts +0 -2
  19. package/lib/cms/api/api-client.js +107 -128
  20. package/lib/cms/api/api-keys.js +1 -2
  21. package/lib/cms/api/domains.js +1 -2
  22. package/lib/cms/commands/__tests__/push-status.test.js +251 -162
  23. package/lib/cms/commands/__tests__/push.test.js +120 -102
  24. package/lib/cms/commands/__tests__/utils.test.js +12 -21
  25. package/lib/cms/commands/push-status.d.ts +3 -2
  26. package/lib/cms/commands/push-status.js +94 -106
  27. package/lib/cms/commands/push.d.ts +3 -2
  28. package/lib/cms/commands/push.js +66 -74
  29. package/lib/cms/commands/utils.js +20 -34
  30. package/lib/commands/build-docs/index.d.ts +2 -2
  31. package/lib/commands/build-docs/index.js +8 -17
  32. package/lib/commands/build-docs/utils.d.ts +1 -1
  33. package/lib/commands/build-docs/utils.js +27 -39
  34. package/lib/commands/bundle.d.ts +2 -2
  35. package/lib/commands/bundle.js +70 -94
  36. package/lib/commands/join.d.ts +2 -2
  37. package/lib/commands/join.js +375 -388
  38. package/lib/commands/lint.d.ts +2 -2
  39. package/lib/commands/lint.js +64 -75
  40. package/lib/commands/login.d.ts +3 -2
  41. package/lib/commands/login.js +10 -22
  42. package/lib/commands/preview-docs/index.d.ts +2 -2
  43. package/lib/commands/preview-docs/index.js +93 -106
  44. package/lib/commands/preview-docs/preview-server/preview-server.js +64 -76
  45. package/lib/commands/preview-docs/preview-server/server.d.ts +1 -4
  46. package/lib/commands/preview-docs/preview-server/server.js +6 -6
  47. package/lib/commands/preview-project/constants.d.ts +1 -1
  48. package/lib/commands/preview-project/index.d.ts +2 -1
  49. package/lib/commands/preview-project/index.js +5 -14
  50. package/lib/commands/preview-project/types.d.ts +1 -1
  51. package/lib/commands/push.d.ts +9 -12
  52. package/lib/commands/push.js +180 -196
  53. package/lib/commands/split/__tests__/index.test.js +31 -25
  54. package/lib/commands/split/index.d.ts +2 -1
  55. package/lib/commands/split/index.js +20 -33
  56. package/lib/commands/stats.d.ts +2 -2
  57. package/lib/commands/stats.js +36 -47
  58. package/lib/index.js +34 -49
  59. package/lib/types.d.ts +4 -5
  60. package/lib/utils/__mocks__/miscellaneous.d.ts +0 -1
  61. package/lib/utils/fetch-with-timeout.js +7 -12
  62. package/lib/utils/getCommandNameFromArgs.d.ts +1 -1
  63. package/lib/utils/getCommandNameFromArgs.js +2 -4
  64. package/lib/utils/js-utils.js +6 -7
  65. package/lib/utils/miscellaneous.d.ts +4 -1
  66. package/lib/utils/miscellaneous.js +130 -152
  67. package/lib/utils/update-version-notifier.js +4 -13
  68. package/lib/wrapper.d.ts +9 -2
  69. package/lib/wrapper.js +27 -16
  70. package/package.json +3 -3
  71. package/src/__mocks__/@redocly/openapi-core.ts +1 -0
  72. package/src/__tests__/commands/build-docs.test.ts +5 -4
  73. package/src/__tests__/commands/join.test.ts +51 -51
  74. package/src/__tests__/commands/push-region.test.ts +10 -8
  75. package/src/__tests__/commands/push.test.ts +127 -102
  76. package/src/__tests__/utils.test.ts +1 -0
  77. package/src/__tests__/wrapper.test.ts +24 -2
  78. package/src/cms/api/api-client.ts +2 -1
  79. package/src/cms/commands/__tests__/push-status.test.ts +70 -56
  80. package/src/cms/commands/__tests__/push.test.ts +30 -24
  81. package/src/cms/commands/push-status.ts +8 -7
  82. package/src/cms/commands/push.ts +12 -9
  83. package/src/commands/build-docs/index.ts +10 -5
  84. package/src/commands/build-docs/utils.ts +4 -4
  85. package/src/commands/bundle.ts +14 -6
  86. package/src/commands/join.ts +6 -2
  87. package/src/commands/lint.ts +9 -3
  88. package/src/commands/login.ts +5 -2
  89. package/src/commands/preview-docs/index.ts +7 -1
  90. package/src/commands/preview-docs/preview-server/preview-server.ts +4 -3
  91. package/src/commands/preview-docs/preview-server/server.ts +2 -1
  92. package/src/commands/preview-project/constants.ts +1 -1
  93. package/src/commands/preview-project/index.ts +5 -4
  94. package/src/commands/preview-project/types.ts +1 -1
  95. package/src/commands/push.ts +15 -18
  96. package/src/commands/split/__tests__/index.test.ts +17 -6
  97. package/src/commands/split/index.ts +4 -2
  98. package/src/commands/stats.ts +13 -6
  99. package/src/index.ts +13 -7
  100. package/src/types.ts +2 -3
  101. package/src/utils/getCommandNameFromArgs.ts +1 -1
  102. package/src/utils/miscellaneous.ts +11 -1
  103. package/src/wrapper.ts +37 -11
  104. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,7 @@
1
- import { formatProblems, getTotals, getMergedConfig, bundle, Config } from '@redocly/openapi-core';
1
+ import { performance } from 'perf_hooks';
2
+ import { blue, gray, green, yellow } from 'colorette';
3
+ import { writeFileSync } from 'fs';
4
+ import { formatProblems, getTotals, getMergedConfig, bundle } from '@redocly/openapi-core';
2
5
  import {
3
6
  dumpBundle,
4
7
  getExecutionTime,
@@ -8,12 +11,11 @@ import {
8
11
  printUnusedWarnings,
9
12
  saveBundle,
10
13
  sortTopLevelKeysForOas,
14
+ checkForDeprecatedOptions,
11
15
  } from '../utils/miscellaneous';
16
+
12
17
  import type { OutputExtensions, Skips, Totals } from '../types';
13
- import { performance } from 'perf_hooks';
14
- import { blue, gray, green, yellow } from 'colorette';
15
- import { writeFileSync } from 'fs';
16
- import { checkForDeprecatedOptions } from '../utils/miscellaneous';
18
+ import type { CommandArgs } from '../wrapper';
17
19
 
18
20
  export type BundleOptions = {
19
21
  apis?: string[];
@@ -28,7 +30,12 @@ export type BundleOptions = {
28
30
  'keep-url-references'?: boolean;
29
31
  } & Skips;
30
32
 
31
- export async function handleBundle(argv: BundleOptions, config: Config, version: string) {
33
+ export async function handleBundle({
34
+ argv,
35
+ config,
36
+ version,
37
+ collectSpecData,
38
+ }: CommandArgs<BundleOptions>) {
32
39
  const removeUnusedComponents =
33
40
  argv['remove-unused-components'] ||
34
41
  config.rawConfig?.styleguide?.decorators?.hasOwnProperty('remove-unused-components');
@@ -59,6 +66,7 @@ export async function handleBundle(argv: BundleOptions, config: Config, version:
59
66
  dereference: argv.dereferenced,
60
67
  removeUnusedComponents,
61
68
  keepUrlRefs: argv['keep-url-references'],
69
+ collectSpecData,
62
70
  });
63
71
 
64
72
  const fileTotals = getTotals(problems);
@@ -2,7 +2,6 @@ import * as path from 'path';
2
2
  import { red, blue, yellow, green } from 'colorette';
3
3
  import { performance } from 'perf_hooks';
4
4
  import {
5
- Config,
6
5
  SpecVersion,
7
6
  BaseResolver,
8
7
  formatProblems,
@@ -38,6 +37,7 @@ import type {
38
37
  Oas3Server,
39
38
  Oas3_1Definition,
40
39
  } from '@redocly/openapi-core/lib/typings/openapi';
40
+ import type { CommandArgs } from '../wrapper';
41
41
 
42
42
  const Tags = 'tags';
43
43
  const xTagGroups = 'x-tagGroups';
@@ -64,7 +64,11 @@ export type JoinOptions = {
64
64
  'lint-config'?: RuleSeverity;
65
65
  };
66
66
 
67
- export async function handleJoin(argv: JoinOptions, config: Config, packageVersion: string) {
67
+ export async function handleJoin({
68
+ argv,
69
+ config,
70
+ version: packageVersion,
71
+ }: CommandArgs<JoinOptions>) {
68
72
  const startedAt = performance.now();
69
73
 
70
74
  if (argv.apis.length < 2) {
@@ -1,7 +1,6 @@
1
1
  import { blue, gray } from 'colorette';
2
2
  import { performance } from 'perf_hooks';
3
3
  import {
4
- Config,
5
4
  formatProblems,
6
5
  getMergedConfig,
7
6
  getTotals,
@@ -23,10 +22,11 @@ import {
23
22
  } from '../utils/miscellaneous';
24
23
  import { getCommandNameFromArgs } from '../utils/getCommandNameFromArgs';
25
24
 
25
+ import type { Arguments } from 'yargs';
26
26
  import type { OutputFormat, ProblemSeverity, RuleSeverity } from '@redocly/openapi-core';
27
27
  import type { RawConfigProcessor } from '@redocly/openapi-core/lib/config';
28
28
  import type { CommandOptions, Skips, Totals } from '../types';
29
- import type { Arguments } from 'yargs';
29
+ import type { CommandArgs } from '../wrapper';
30
30
 
31
31
  export type LintOptions = {
32
32
  apis?: string[];
@@ -38,7 +38,12 @@ export type LintOptions = {
38
38
  'lint-config'?: RuleSeverity;
39
39
  } & Omit<Skips, 'skip-decorator'>;
40
40
 
41
- export async function handleLint(argv: LintOptions, config: Config, version: string) {
41
+ export async function handleLint({
42
+ argv,
43
+ config,
44
+ version,
45
+ collectSpecData,
46
+ }: CommandArgs<LintOptions>) {
42
47
  const apis = await getFallbackApisOrExit(argv.apis, config);
43
48
 
44
49
  if (!apis.length) {
@@ -74,6 +79,7 @@ export async function handleLint(argv: LintOptions, config: Config, version: str
74
79
  const results = await lint({
75
80
  ref: path,
76
81
  config: resolvedConfig,
82
+ collectSpecData,
77
83
  });
78
84
 
79
85
  const fileTotals = getTotals(results);
@@ -1,7 +1,10 @@
1
- import { Region, RedoclyClient, Config } from '@redocly/openapi-core';
2
1
  import { blue, green, gray } from 'colorette';
2
+ import { RedoclyClient } from '@redocly/openapi-core';
3
3
  import { promptUser } from '../utils/miscellaneous';
4
4
 
5
+ import type { CommandArgs } from '../wrapper';
6
+ import type { Region } from '@redocly/openapi-core';
7
+
5
8
  export function promptClientToken(domain: string) {
6
9
  return promptUser(
7
10
  green(
@@ -17,7 +20,7 @@ export type LoginOptions = {
17
20
  config?: string;
18
21
  };
19
22
 
20
- export async function handleLogin(argv: LoginOptions, config: Config) {
23
+ export async function handleLogin({ argv, config }: CommandArgs<LoginOptions>) {
21
24
  const region = argv.region || config.region;
22
25
  const client = new RedoclyClient(region);
23
26
  const clientToken = await promptClientToken(client.domain);
@@ -7,7 +7,9 @@ import {
7
7
  loadConfigAndHandleErrors,
8
8
  } from '../../utils/miscellaneous';
9
9
  import startPreviewServer from './preview-server/preview-server';
10
+
10
11
  import type { Skips } from '../../types';
12
+ import type { CommandArgs } from '../../wrapper';
11
13
 
12
14
  export type PreviewDocsOptions = {
13
15
  port: number;
@@ -18,7 +20,10 @@ export type PreviewDocsOptions = {
18
20
  force?: boolean;
19
21
  } & Omit<Skips, 'skip-rule'>;
20
22
 
21
- export async function previewDocs(argv: PreviewDocsOptions, configFromFile: Config) {
23
+ export async function previewDocs({
24
+ argv,
25
+ config: configFromFile,
26
+ }: CommandArgs<PreviewDocsOptions>) {
22
27
  let isAuthorizedWithRedocly = false;
23
28
  let redocOptions: any = {};
24
29
  let config = await reloadConfig(configFromFile);
@@ -151,6 +156,7 @@ export async function previewDocs(argv: PreviewDocsOptions, configFromFile: Conf
151
156
  }
152
157
  }
153
158
 
159
+ // eslint-disable-next-line @typescript-eslint/ban-types
154
160
  export function debounce(func: Function, wait: number, immediate?: boolean) {
155
161
  let timeout: NodeJS.Timeout | null;
156
162
 
@@ -3,11 +3,11 @@ import * as colorette from 'colorette';
3
3
  import { getPort } from 'get-port-please';
4
4
  import { readFileSync, promises as fsPromises } from 'fs';
5
5
  import * as path from 'path';
6
-
7
6
  import { startHttpServer, startWsServer, respondWithGzip, mimeTypes } from './server';
8
- import type { IncomingMessage } from 'http';
9
7
  import { isSubdir } from '../../../utils/miscellaneous';
10
8
 
9
+ import type { IncomingMessage } from 'http';
10
+
11
11
  function getPageHTML(
12
12
  htmlTemplate: string,
13
13
  redocOptions: object = {},
@@ -60,7 +60,8 @@ export default async function startPreviewServer(
60
60
  getBundle,
61
61
  getOptions,
62
62
  useRedocPro,
63
- }: { getBundle: Function; getOptions: Function; useRedocPro: boolean }
63
+ }: // eslint-disable-next-line @typescript-eslint/ban-types
64
+ { getBundle: Function; getOptions: Function; useRedocPro: boolean }
64
65
  ) {
65
66
  const defaultTemplate = path.join(__dirname, 'default.hbs');
66
67
  const handler = async (request: IncomingMessage, response: any) => {
@@ -1,6 +1,7 @@
1
1
  import * as http from 'http';
2
2
  import * as zlib from 'zlib';
3
- import { ReadStream } from 'fs';
3
+
4
+ import type { ReadStream } from 'fs';
4
5
 
5
6
  const SocketServer = require('simple-websocket/server.js');
6
7
 
@@ -1,4 +1,4 @@
1
- import { Product } from './types';
1
+ import type { Product } from './types';
2
2
 
3
3
  export const PRODUCT_PACKAGES = {
4
4
  realm: '@redocly/realm',
@@ -4,12 +4,13 @@ import { spawn } from 'child_process';
4
4
  import { PRODUCT_NAMES, PRODUCT_PACKAGES } from './constants';
5
5
 
6
6
  import type { PreviewProjectOptions, Product } from './types';
7
+ import type { CommandArgs } from '../../wrapper';
7
8
 
8
- export const previewProject = async (args: PreviewProjectOptions) => {
9
- const { plan, port } = args;
10
- const projectDir = args['source-dir'];
9
+ export const previewProject = async ({ argv }: CommandArgs<PreviewProjectOptions>) => {
10
+ const { plan, port } = argv;
11
+ const projectDir = argv['source-dir'];
11
12
 
12
- const product = args.product || tryGetProductFromPackageJson(projectDir);
13
+ const product = argv.product || tryGetProductFromPackageJson(projectDir);
13
14
 
14
15
  if (!isValidProduct(product)) {
15
16
  process.stderr.write(`Invalid product ${product}`);
@@ -1,4 +1,4 @@
1
- import { PRODUCT_PACKAGES, PRODUCT_PLANS } from './constants';
1
+ import type { PRODUCT_PACKAGES, PRODUCT_PLANS } from './constants';
2
2
 
3
3
  export type Product = keyof typeof PRODUCT_PACKAGES;
4
4
  export type ProductPlan = typeof PRODUCT_PLANS[number];
@@ -6,13 +6,10 @@ import { yellow, green, blue, red } from 'colorette';
6
6
  import { createHash } from 'crypto';
7
7
  import {
8
8
  bundle,
9
- Config,
10
9
  RedoclyClient,
11
10
  IGNORE_FILE,
12
- BundleOutputFormat,
13
11
  getTotals,
14
12
  slash,
15
- Region,
16
13
  getMergedConfig,
17
14
  getProxyAgent,
18
15
  } from '@redocly/openapi-core';
@@ -26,9 +23,13 @@ import {
26
23
  import { promptClientToken } from './login';
27
24
  import { handlePush as handleCMSPush } from '../cms/commands/push';
28
25
 
26
+ import type { Config, BundleOutputFormat, Region } from '@redocly/openapi-core';
27
+ import type { CommandArgs } from '../wrapper';
28
+
29
29
  const DEFAULT_VERSION = 'latest';
30
30
 
31
31
  export const DESTINATION_REGEX =
32
+ // eslint-disable-next-line no-useless-escape
32
33
  /^(@(?<organizationId>[\w\-\s]+)\/)?(?<name>[^@]*)@(?<version>[\w\.\-]+)$/;
33
34
 
34
35
  export type PushOptions = {
@@ -59,7 +60,7 @@ export function commonPushHandler({
59
60
  return transformPush(handlePush);
60
61
  }
61
62
 
62
- export async function handlePush(argv: PushOptions, config: Config): Promise<void> {
63
+ export async function handlePush({ argv, config }: CommandArgs<PushOptions>): Promise<void> {
63
64
  const client = new RedoclyClient(config.region);
64
65
  const isAuthorized = await client.isAuthorizedWithRedoclyByRegion();
65
66
  if (!isAuthorized) {
@@ -366,16 +367,11 @@ type BarePushArgs = Omit<PushOptions, 'destination' | 'branchName'> & {
366
367
 
367
368
  export const transformPush =
368
369
  (callback: typeof handlePush) =>
369
- (
370
- {
371
- apis,
372
- branch,
373
- 'batch-id': batchId,
374
- 'job-id': jobId,
375
- ...rest
376
- }: BarePushArgs & { 'batch-id'?: string },
377
- config: Config
378
- ) => {
370
+ ({
371
+ argv: { apis, branch, 'batch-id': batchId, 'job-id': jobId, ...rest },
372
+ config,
373
+ version,
374
+ }: CommandArgs<BarePushArgs & { 'batch-id'?: string }>) => {
379
375
  const [maybeApiOrDestination, maybeDestination, maybeBranchName] = apis || [];
380
376
 
381
377
  if (batchId) {
@@ -414,16 +410,17 @@ export const transformPush =
414
410
  apiFile = maybeApiOrDestination;
415
411
  }
416
412
 
417
- return callback(
418
- {
413
+ return callback({
414
+ argv: {
419
415
  ...rest,
420
416
  destination: rest.destination ?? destination,
421
417
  api: apiFile,
422
418
  branchName: branch ?? maybeBranchName,
423
419
  'job-id': jobId || batchId,
424
420
  },
425
- config
426
- );
421
+ config,
422
+ version,
423
+ });
427
424
  };
428
425
 
429
426
  export function getApiRoot({
@@ -3,6 +3,9 @@ import * as path from 'path';
3
3
  import * as openapiCore from '@redocly/openapi-core';
4
4
  import { ComponentsFiles } from '../types';
5
5
  import { blue, green } from 'colorette';
6
+ import { loadConfigAndHandleErrors } from '../../../utils/__mocks__/miscellaneous';
7
+
8
+ import type { Config } from '@redocly/openapi-core';
6
9
 
7
10
  const utils = require('../../../utils/miscellaneous');
8
11
 
@@ -25,9 +28,13 @@ describe('#split', () => {
25
28
  jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
26
29
 
27
30
  await handleSplit({
28
- api: filePath,
29
- outDir: openapiDir,
30
- separator: '_',
31
+ argv: {
32
+ api: filePath,
33
+ outDir: openapiDir,
34
+ separator: '_',
35
+ },
36
+ config: loadConfigAndHandleErrors() as any as Config,
37
+ version: 'cli-version',
31
38
  });
32
39
 
33
40
  expect(process.stderr.write).toBeCalledTimes(2);
@@ -46,9 +53,13 @@ describe('#split', () => {
46
53
  jest.spyOn(utils, 'pathToFilename').mockImplementation(() => 'newFilePath');
47
54
 
48
55
  await handleSplit({
49
- api: filePath,
50
- outDir: openapiDir,
51
- separator: '_',
56
+ argv: {
57
+ api: filePath,
58
+ outDir: openapiDir,
59
+ separator: '_',
60
+ },
61
+ config: loadConfigAndHandleErrors() as any as Config,
62
+ version: 'cli-version',
52
63
  });
53
64
 
54
65
  expect(utils.pathToFilename).toBeCalledWith(expect.anything(), '_');
@@ -36,6 +36,7 @@ import type {
36
36
  Oas3PathItem,
37
37
  Referenced,
38
38
  } from './types';
39
+ import type { CommandArgs } from '../../wrapper';
39
40
 
40
41
  export type SplitOptions = {
41
42
  api: string;
@@ -44,12 +45,13 @@ export type SplitOptions = {
44
45
  config?: string;
45
46
  };
46
47
 
47
- export async function handleSplit(argv: SplitOptions) {
48
+ export async function handleSplit({ argv, collectSpecData }: CommandArgs<SplitOptions>) {
48
49
  const startedAt = performance.now();
49
50
  const { api, outDir, separator } = argv;
50
51
  validateDefinitionFileName(api!);
51
52
  const ext = getAndValidateFileExtension(api);
52
53
  const openapi = readYaml(api!) as Oas3Definition | Oas3_1Definition;
54
+ collectSpecData?.(openapi);
53
55
  splitDefinition(openapi, outDir, separator, ext);
54
56
  process.stderr.write(
55
57
  `🪓 Document: ${blue(api!)} ${green('is successfully split')}
@@ -292,7 +294,7 @@ function iteratePathItems(
292
294
 
293
295
  for (const pathName of Object.keys(pathItems)) {
294
296
  const pathFile = `${path.join(outDir, pathToFilename(pathName, pathSeparator))}.${ext}`;
295
- const pathData = pathItems[pathName] as Oas3PathItem;
297
+ const pathData = pathItems[pathName];
296
298
 
297
299
  if (isRef(pathData)) continue;
298
300
 
@@ -1,8 +1,6 @@
1
1
  import { performance } from 'perf_hooks';
2
2
  import * as colors from 'colorette';
3
3
  import {
4
- Config,
5
- StyleguideConfig,
6
4
  normalizeTypes,
7
5
  BaseResolver,
8
6
  resolveDocument,
@@ -13,9 +11,16 @@ import {
13
11
  Stats,
14
12
  bundle,
15
13
  } from '@redocly/openapi-core';
16
- import { getFallbackApisOrExit } from '../utils/miscellaneous';
17
- import { printExecutionTime } from '../utils/miscellaneous';
18
- import type { StatsAccumulator, StatsName, WalkContext, OutputFormat } from '@redocly/openapi-core';
14
+ import { getFallbackApisOrExit, printExecutionTime } from '../utils/miscellaneous';
15
+
16
+ import type { CommandArgs } from '../wrapper';
17
+ import type {
18
+ StatsAccumulator,
19
+ StatsName,
20
+ WalkContext,
21
+ OutputFormat,
22
+ StyleguideConfig,
23
+ } from '@redocly/openapi-core';
19
24
 
20
25
  const statsAccumulator: StatsAccumulator = {
21
26
  refs: { metric: '🚗 References', total: 0, color: 'red', items: new Set() },
@@ -24,6 +29,7 @@ const statsAccumulator: StatsAccumulator = {
24
29
  parameters: { metric: '👉 Parameters', total: 0, color: 'yellow', items: new Set() },
25
30
  links: { metric: '🔗 Links', total: 0, color: 'cyan', items: new Set() },
26
31
  pathItems: { metric: '🔀 Path Items', total: 0, color: 'green' },
32
+ webhooks: { metric: '🎣 Webhooks', total: 0, color: 'green' },
27
33
  operations: { metric: '👷 Operations', total: 0, color: 'yellow' },
28
34
  tags: { metric: '🔖 Tags', total: 0, color: 'white', items: new Set() },
29
35
  };
@@ -86,10 +92,11 @@ export type StatsOptions = {
86
92
  config?: string;
87
93
  };
88
94
 
89
- export async function handleStats(argv: StatsOptions, config: Config) {
95
+ export async function handleStats({ argv, config, collectSpecData }: CommandArgs<StatsOptions>) {
90
96
  const [{ path }] = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
91
97
  const externalRefResolver = new BaseResolver(config.resolve);
92
98
  const { bundle: document } = await bundle({ config, ref: path });
99
+ collectSpecData?.(document.parsed);
93
100
  const lintConfig: StyleguideConfig = config.styleguide;
94
101
  const specVersion = detectSpec(document.parsed);
95
102
  const types = normalizeTypes(
package/src/index.ts CHANGED
@@ -3,27 +3,33 @@
3
3
  import './utils/assert-node-version';
4
4
  import * as yargs from 'yargs';
5
5
  import * as colors from 'colorette';
6
- import { outputExtensions, PushArguments, regionChoices } from './types';
7
6
  import { RedoclyClient } from '@redocly/openapi-core';
7
+ import { outputExtensions, regionChoices } from './types';
8
8
  import { previewDocs } from './commands/preview-docs';
9
9
  import { handleStats } from './commands/stats';
10
10
  import { handleSplit } from './commands/split';
11
11
  import { handleJoin } from './commands/join';
12
- import { handlePushStatus, PushStatusOptions } from './cms/commands/push-status';
12
+ import { handlePushStatus } from './cms/commands/push-status';
13
13
  import { handleLint } from './commands/lint';
14
14
  import { handleBundle } from './commands/bundle';
15
15
  import { handleLogin } from './commands/login';
16
16
  import { handlerBuildCommand } from './commands/build-docs';
17
- import { cacheLatestVersion, notifyUpdateCliVersion } from './utils/update-version-notifier';
17
+ import {
18
+ cacheLatestVersion,
19
+ notifyUpdateCliVersion,
20
+ version,
21
+ } from './utils/update-version-notifier';
18
22
  import { commandWrapper } from './wrapper';
19
- import { version } from './utils/update-version-notifier';
20
- import type { Arguments } from 'yargs';
21
- import type { OutputFormat, RuleSeverity } from '@redocly/openapi-core';
22
- import type { BuildDocsArgv } from './commands/build-docs/types';
23
23
  import { previewProject } from './commands/preview-project';
24
24
  import { PRODUCT_PLANS } from './commands/preview-project/constants';
25
25
  import { commonPushHandler } from './commands/push';
26
26
 
27
+ import type { Arguments } from 'yargs';
28
+ import type { OutputFormat, RuleSeverity } from '@redocly/openapi-core';
29
+ import type { BuildDocsArgv } from './commands/build-docs/types';
30
+ import type { PushStatusOptions } from './cms/commands/push-status';
31
+ import type { PushArguments } from './types';
32
+
27
33
  if (!('replaceAll' in String.prototype)) {
28
34
  require('core-js/actual/string/replace-all');
29
35
  }
package/src/types.ts CHANGED
@@ -9,9 +9,8 @@ import type { StatsOptions } from './commands/stats';
9
9
  import type { SplitOptions } from './commands/split';
10
10
  import type { PreviewDocsOptions } from './commands/preview-docs';
11
11
  import type { BuildDocsArgv } from './commands/build-docs/types';
12
- import type { PushOptions as PushBhOptions } from './cms/commands/push';
13
- import type { PushStatusOptions } from './cms/commands/push-status';
14
12
  import type { PushOptions as CMSPushOptions } from './cms/commands/push';
13
+ import type { PushStatusOptions } from './cms/commands/push-status';
15
14
  import type { PreviewProjectOptions } from './commands/preview-project/types';
16
15
 
17
16
  export type Totals = {
@@ -31,7 +30,7 @@ export type CommandOptions =
31
30
  | SplitOptions
32
31
  | JoinOptions
33
32
  | PushOptions
34
- | PushBhOptions
33
+ | CMSPushOptions
35
34
  | LintOptions
36
35
  | BundleOptions
37
36
  | LoginOptions
@@ -1,4 +1,4 @@
1
- import { Arguments } from 'yargs';
1
+ import type { Arguments } from 'yargs';
2
2
 
3
3
  export function getCommandNameFromArgs(argv: Arguments | undefined): string | number {
4
4
  return argv?._?.[0] ?? '';
@@ -522,6 +522,7 @@ export function checkIfRulesetExist(rules: typeof StyleguideConfig.prototype.rul
522
522
  ...rules.oas3_0,
523
523
  ...rules.oas3_1,
524
524
  ...rules.async2,
525
+ ...rules.async3,
525
526
  ...rules.arazzo,
526
527
  };
527
528
 
@@ -540,7 +541,10 @@ export function cleanColors(input: string): string {
540
541
  export async function sendTelemetry(
541
542
  argv: Arguments | undefined,
542
543
  exit_code: ExitCode,
543
- has_config: boolean | undefined
544
+ has_config: boolean | undefined,
545
+ spec_version: string | undefined,
546
+ spec_keyword: string | undefined,
547
+ spec_full_version: string | undefined
544
548
  ): Promise<void> {
545
549
  try {
546
550
  if (!argv) {
@@ -568,6 +572,9 @@ export async function sendTelemetry(
568
572
  environment_ci: process.env.CI,
569
573
  raw_input: cleanRawInput(process.argv.slice(2)),
570
574
  has_config,
575
+ spec_version,
576
+ spec_keyword,
577
+ spec_full_version,
571
578
  };
572
579
  await fetch(`https://api.redocly.com/registry/telemetry/cli`, {
573
580
  method: 'POST',
@@ -597,6 +604,9 @@ export type Analytics = {
597
604
  environment_ci?: string;
598
605
  raw_input: string;
599
606
  has_config?: boolean;
607
+ spec_version?: string;
608
+ spec_keyword?: string;
609
+ spec_full_version?: string;
600
610
  };
601
611
 
602
612
  function isFile(value: string) {
package/src/wrapper.ts CHANGED
@@ -1,22 +1,48 @@
1
- import { Config, Region, doesYamlFileExist } from '@redocly/openapi-core';
2
- import type { Arguments } from 'yargs';
1
+ import { detectSpec, doesYamlFileExist } from '@redocly/openapi-core';
2
+ import { isPlainObject } from '@redocly/openapi-core/lib/utils';
3
3
  import { version } from './utils/update-version-notifier';
4
- import {
5
- ExitCode,
6
- exitWithError,
7
- loadConfigAndHandleErrors,
8
- sendTelemetry,
9
- } from './utils/miscellaneous';
4
+ import { exitWithError, loadConfigAndHandleErrors, sendTelemetry } from './utils/miscellaneous';
10
5
  import { lintConfigCallback } from './commands/lint';
6
+
7
+ import type { Arguments } from 'yargs';
8
+ import type { Config, Region } from '@redocly/openapi-core';
9
+ import type { CollectFn } from '@redocly/openapi-core/lib/utils';
10
+ import type { ExitCode } from './utils/miscellaneous';
11
11
  import type { CommandOptions } from './types';
12
12
 
13
+ export type CommandArgs<T extends CommandOptions> = {
14
+ argv: T;
15
+ config: Config;
16
+ version: string;
17
+ collectSpecData?: CollectFn;
18
+ };
19
+
13
20
  export function commandWrapper<T extends CommandOptions>(
14
- commandHandler?: (argv: T, config: Config, version: string) => Promise<unknown>
21
+ commandHandler?: (wrapperArgs: CommandArgs<T>) => Promise<unknown>
15
22
  ) {
16
23
  return async (argv: Arguments<T>) => {
17
24
  let code: ExitCode = 2;
18
25
  let hasConfig;
19
26
  let telemetry;
27
+ let specVersion: string | undefined;
28
+ let specKeyword: string | undefined;
29
+ let specFullVersion: string | undefined;
30
+ const collectSpecData: CollectFn = (document) => {
31
+ specVersion = detectSpec(document);
32
+ if (!isPlainObject(document)) return;
33
+ specKeyword = document?.openapi
34
+ ? 'openapi'
35
+ : document?.swagger
36
+ ? 'swagger'
37
+ : document?.asyncapi
38
+ ? 'asyncapi'
39
+ : document?.arazzo
40
+ ? 'arazzo'
41
+ : undefined;
42
+ if (specKeyword) {
43
+ specFullVersion = document[specKeyword] as string;
44
+ }
45
+ };
20
46
  try {
21
47
  if (argv.config && !doesYamlFileExist(argv.config)) {
22
48
  exitWithError('Please provide a valid path to the configuration file.');
@@ -32,14 +58,14 @@ export function commandWrapper<T extends CommandOptions>(
32
58
  hasConfig = !config.styleguide.recommendedFallback;
33
59
  code = 1;
34
60
  if (typeof commandHandler === 'function') {
35
- await commandHandler(argv, config, version);
61
+ await commandHandler({ argv, config, version, collectSpecData });
36
62
  }
37
63
  code = 0;
38
64
  } catch (err) {
39
65
  // Do nothing
40
66
  } finally {
41
67
  if (process.env.REDOCLY_TELEMETRY !== 'off' && telemetry !== 'off') {
42
- await sendTelemetry(argv, code, hasConfig);
68
+ await sendTelemetry(argv, code, hasConfig, specVersion, specKeyword, specFullVersion);
43
69
  }
44
70
  process.once('beforeExit', () => {
45
71
  process.exit(code);