@redocly/cli 1.18.0 → 1.19.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 (91) hide show
  1. package/CHANGELOG.md +22 -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 +106 -127
  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 -75
  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.js +26 -38
  33. package/lib/commands/bundle.d.ts +2 -2
  34. package/lib/commands/bundle.js +70 -94
  35. package/lib/commands/join.d.ts +2 -2
  36. package/lib/commands/join.js +377 -390
  37. package/lib/commands/lint.d.ts +2 -2
  38. package/lib/commands/lint.js +70 -78
  39. package/lib/commands/login.d.ts +3 -2
  40. package/lib/commands/login.js +9 -21
  41. package/lib/commands/preview-docs/index.d.ts +2 -2
  42. package/lib/commands/preview-docs/index.js +92 -106
  43. package/lib/commands/preview-docs/preview-server/preview-server.js +64 -76
  44. package/lib/commands/preview-docs/preview-server/server.d.ts +0 -3
  45. package/lib/commands/preview-docs/preview-server/server.js +6 -6
  46. package/lib/commands/preview-project/index.d.ts +2 -1
  47. package/lib/commands/preview-project/index.js +5 -14
  48. package/lib/commands/push.d.ts +8 -11
  49. package/lib/commands/push.js +177 -195
  50. package/lib/commands/split/__tests__/index.test.js +31 -25
  51. package/lib/commands/split/index.d.ts +2 -1
  52. package/lib/commands/split/index.js +22 -35
  53. package/lib/commands/stats.d.ts +2 -2
  54. package/lib/commands/stats.js +34 -45
  55. package/lib/index.js +32 -46
  56. package/lib/types.d.ts +2 -2
  57. package/lib/utils/__mocks__/miscellaneous.d.ts +0 -1
  58. package/lib/utils/fetch-with-timeout.js +7 -12
  59. package/lib/utils/getCommandNameFromArgs.js +2 -4
  60. package/lib/utils/js-utils.js +6 -7
  61. package/lib/utils/miscellaneous.d.ts +9 -5
  62. package/lib/utils/miscellaneous.js +150 -160
  63. package/lib/utils/update-version-notifier.js +4 -13
  64. package/lib/wrapper.d.ts +9 -2
  65. package/lib/wrapper.js +27 -16
  66. package/package.json +5 -3
  67. package/src/__mocks__/@redocly/openapi-core.ts +1 -0
  68. package/src/__tests__/commands/build-docs.test.ts +5 -4
  69. package/src/__tests__/commands/join.test.ts +51 -51
  70. package/src/__tests__/commands/push-region.test.ts +10 -8
  71. package/src/__tests__/commands/push.test.ts +127 -102
  72. package/src/__tests__/utils.test.ts +1 -0
  73. package/src/__tests__/wrapper.test.ts +24 -2
  74. package/src/cms/commands/__tests__/push-status.test.ts +70 -56
  75. package/src/cms/commands/__tests__/push.test.ts +30 -24
  76. package/src/cms/commands/push-status.ts +8 -7
  77. package/src/cms/commands/push.ts +19 -13
  78. package/src/commands/build-docs/index.ts +10 -5
  79. package/src/commands/bundle.ts +14 -6
  80. package/src/commands/join.ts +10 -6
  81. package/src/commands/lint.ts +20 -9
  82. package/src/commands/login.ts +4 -2
  83. package/src/commands/preview-docs/index.ts +6 -1
  84. package/src/commands/preview-project/index.ts +5 -4
  85. package/src/commands/push.ts +14 -16
  86. package/src/commands/split/__tests__/index.test.ts +17 -6
  87. package/src/commands/split/index.ts +6 -4
  88. package/src/commands/stats.ts +4 -2
  89. package/src/utils/miscellaneous.ts +55 -26
  90. package/src/wrapper.ts +37 -11
  91. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,6 @@
1
+ import { blue, gray } from 'colorette';
2
+ import { performance } from 'perf_hooks';
1
3
  import {
2
- Config,
3
4
  formatProblems,
4
5
  getMergedConfig,
5
6
  getTotals,
@@ -13,19 +14,19 @@ import {
13
14
  getExecutionTime,
14
15
  getFallbackApisOrExit,
15
16
  handleError,
17
+ notifyAboutIncompatibleConfigOptions,
16
18
  pluralize,
17
19
  printConfigLintTotals,
18
20
  printLintTotals,
19
21
  printUnusedWarnings,
20
22
  } from '../utils/miscellaneous';
21
- import { blue, gray } from 'colorette';
22
- import { performance } from 'perf_hooks';
23
+ import { getCommandNameFromArgs } from '../utils/getCommandNameFromArgs';
23
24
 
25
+ import type { Arguments } from 'yargs';
24
26
  import type { OutputFormat, ProblemSeverity, RuleSeverity } from '@redocly/openapi-core';
25
27
  import type { RawConfigProcessor } from '@redocly/openapi-core/lib/config';
26
28
  import type { CommandOptions, Skips, Totals } from '../types';
27
- import { getCommandNameFromArgs } from '../utils/getCommandNameFromArgs';
28
- import { Arguments } from 'yargs';
29
+ import type { CommandArgs } from '../wrapper';
29
30
 
30
31
  export type LintOptions = {
31
32
  apis?: string[];
@@ -37,7 +38,12 @@ export type LintOptions = {
37
38
  'lint-config'?: RuleSeverity;
38
39
  } & Omit<Skips, 'skip-decorator'>;
39
40
 
40
- 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>) {
41
47
  const apis = await getFallbackApisOrExit(argv.apis, config);
42
48
 
43
49
  if (!apis.length) {
@@ -73,6 +79,7 @@ export async function handleLint(argv: LintOptions, config: Config, version: str
73
79
  const results = await lint({
74
80
  ref: path,
75
81
  config: resolvedConfig,
82
+ collectSpecData,
76
83
  });
77
84
 
78
85
  const fileTotals = getTotals(results);
@@ -130,7 +137,13 @@ export function lintConfigCallback(
130
137
  return;
131
138
  }
132
139
 
133
- return async ({ document, resolvedRefMap, config }) => {
140
+ return async ({ document, resolvedRefMap, config, parsed: { theme = {} } }) => {
141
+ const command = argv ? getCommandNameFromArgs(argv as Arguments) : undefined;
142
+
143
+ if (command === 'check-config') {
144
+ notifyAboutIncompatibleConfigOptions(theme.openapi);
145
+ }
146
+
134
147
  const problems = await lintConfig({
135
148
  document,
136
149
  resolvedRefMap,
@@ -147,8 +160,6 @@ export function lintConfigCallback(
147
160
  version,
148
161
  });
149
162
 
150
- const command = argv ? getCommandNameFromArgs(argv as unknown as Arguments) : undefined;
151
-
152
163
  printConfigLintTotals(fileTotals, command);
153
164
 
154
165
  if (fileTotals.errors > 0) {
@@ -1,7 +1,9 @@
1
- import { Region, RedoclyClient, Config } from '@redocly/openapi-core';
1
+ import { Region, RedoclyClient } from '@redocly/openapi-core';
2
2
  import { blue, green, gray } from 'colorette';
3
3
  import { promptUser } from '../utils/miscellaneous';
4
4
 
5
+ import type { CommandArgs } from '../wrapper';
6
+
5
7
  export function promptClientToken(domain: string) {
6
8
  return promptUser(
7
9
  green(
@@ -17,7 +19,7 @@ export type LoginOptions = {
17
19
  config?: string;
18
20
  };
19
21
 
20
- export async function handleLogin(argv: LoginOptions, config: Config) {
22
+ export async function handleLogin({ argv, config }: CommandArgs<LoginOptions>) {
21
23
  const region = argv.region || config.region;
22
24
  const client = new RedoclyClient(region);
23
25
  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);
@@ -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}`);
@@ -20,12 +20,14 @@ import {
20
20
  exitWithError,
21
21
  printExecutionTime,
22
22
  getFallbackApisOrExit,
23
- pluralize,
24
23
  dumpBundle,
24
+ pluralize,
25
25
  } from '../utils/miscellaneous';
26
26
  import { promptClientToken } from './login';
27
27
  import { handlePush as handleCMSPush } from '../cms/commands/push';
28
28
 
29
+ import type { CommandArgs } from '../wrapper';
30
+
29
31
  const DEFAULT_VERSION = 'latest';
30
32
 
31
33
  export const DESTINATION_REGEX =
@@ -59,7 +61,7 @@ export function commonPushHandler({
59
61
  return transformPush(handlePush);
60
62
  }
61
63
 
62
- export async function handlePush(argv: PushOptions, config: Config): Promise<void> {
64
+ export async function handlePush({ argv, config }: CommandArgs<PushOptions>): Promise<void> {
63
65
  const client = new RedoclyClient(config.region);
64
66
  const isAuthorized = await client.isAuthorizedWithRedoclyByRegion();
65
67
  if (!isAuthorized) {
@@ -366,16 +368,11 @@ type BarePushArgs = Omit<PushOptions, 'destination' | 'branchName'> & {
366
368
 
367
369
  export const transformPush =
368
370
  (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
- ) => {
371
+ ({
372
+ argv: { apis, branch, 'batch-id': batchId, 'job-id': jobId, ...rest },
373
+ config,
374
+ version,
375
+ }: CommandArgs<BarePushArgs & { 'batch-id'?: string }>) => {
379
376
  const [maybeApiOrDestination, maybeDestination, maybeBranchName] = apis || [];
380
377
 
381
378
  if (batchId) {
@@ -414,16 +411,17 @@ export const transformPush =
414
411
  apiFile = maybeApiOrDestination;
415
412
  }
416
413
 
417
- return callback(
418
- {
414
+ return callback({
415
+ argv: {
419
416
  ...rest,
420
417
  destination: rest.destination ?? destination,
421
418
  api: apiFile,
422
419
  branchName: branch ?? maybeBranchName,
423
420
  'job-id': jobId || batchId,
424
421
  },
425
- config
426
- );
422
+ config,
423
+ version,
424
+ });
427
425
  };
428
426
 
429
427
  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(), '_');
@@ -1,9 +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 { dequal } from '@redocly/openapi-core/lib/utils';
4
5
  import * as path from 'path';
5
6
  import { performance } from 'perf_hooks';
6
- const isEqual = require('lodash.isequal');
7
7
  import {
8
8
  printExecutionTime,
9
9
  pathToFilename,
@@ -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')}
@@ -232,7 +234,7 @@ function findComponentTypes(components: any) {
232
234
  }
233
235
 
234
236
  function doesFileDiffer(filename: string, componentData: any) {
235
- return fs.existsSync(filename) && !isEqual(readYaml(filename), componentData);
237
+ return fs.existsSync(filename) && !dequal(readYaml(filename), componentData);
236
238
  }
237
239
 
238
240
  function removeEmptyComponents(
@@ -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,7 +1,6 @@
1
1
  import { performance } from 'perf_hooks';
2
2
  import * as colors from 'colorette';
3
3
  import {
4
- Config,
5
4
  StyleguideConfig,
6
5
  normalizeTypes,
7
6
  BaseResolver,
@@ -15,7 +14,9 @@ import {
15
14
  } from '@redocly/openapi-core';
16
15
  import { getFallbackApisOrExit } from '../utils/miscellaneous';
17
16
  import { printExecutionTime } from '../utils/miscellaneous';
17
+
18
18
  import type { StatsAccumulator, StatsName, WalkContext, OutputFormat } from '@redocly/openapi-core';
19
+ import type { CommandArgs } from '../wrapper';
19
20
 
20
21
  const statsAccumulator: StatsAccumulator = {
21
22
  refs: { metric: '🚗 References', total: 0, color: 'red', items: new Set() },
@@ -86,10 +87,11 @@ export type StatsOptions = {
86
87
  config?: string;
87
88
  };
88
89
 
89
- export async function handleStats(argv: StatsOptions, config: Config) {
90
+ export async function handleStats({ argv, config, collectSpecData }: CommandArgs<StatsOptions>) {
90
91
  const [{ path }] = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
91
92
  const externalRefResolver = new BaseResolver(config.resolve);
92
93
  const { bundle: document } = await bundle({ config, ref: path });
94
+ collectSpecData?.(document.parsed);
93
95
  const lintConfig: StyleguideConfig = config.styleguide;
94
96
  const specVersion = detectSpec(document.parsed);
95
97
  const types = normalizeTypes(
@@ -1,4 +1,4 @@
1
- import fetch from './fetch-with-timeout';
1
+ import * as pluralizeOne from 'pluralize';
2
2
  import { basename, dirname, extname, join, resolve, relative, isAbsolute } from 'path';
3
3
  import { blue, gray, green, red, yellow } from 'colorette';
4
4
  import { performance } from 'perf_hooks';
@@ -9,36 +9,34 @@ import { Writable } from 'stream';
9
9
  import { execSync } from 'child_process';
10
10
  import { promisify } from 'util';
11
11
  import {
12
- BundleOutputFormat,
13
- StyleguideConfig,
14
12
  ResolveError,
15
13
  YamlParseError,
16
- ResolvedApi,
17
14
  parseYaml,
18
15
  stringifyYaml,
19
16
  isAbsoluteUrl,
20
17
  loadConfig,
21
- Region,
22
- Config,
23
- Oas3Definition,
24
- Oas2Definition,
25
18
  RedoclyClient,
26
19
  } from '@redocly/openapi-core';
27
- import {
28
- Totals,
29
- outputExtensions,
30
- Entrypoint,
31
- ConfigApis,
32
- CommandOptions,
33
- OutputExtensions,
34
- } from '../types';
35
- import { isEmptyObject } from '@redocly/openapi-core/lib/utils';
36
- import { Arguments } from 'yargs';
20
+ import { isEmptyObject, isPlainObject } from '@redocly/openapi-core/lib/utils';
21
+ import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
22
+ import { deprecatedRefDocsSchema } from '@redocly/config/lib/reference-docs-config-schema';
23
+ import { outputExtensions } from '../types';
37
24
  import { version } from './update-version-notifier';
38
25
  import { DESTINATION_REGEX } from '../commands/push';
39
- import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
26
+ import fetch from './fetch-with-timeout';
40
27
 
28
+ import type { Arguments } from 'yargs';
29
+ import type {
30
+ BundleOutputFormat,
31
+ StyleguideConfig,
32
+ ResolvedApi,
33
+ Region,
34
+ Config,
35
+ Oas3Definition,
36
+ Oas2Definition,
37
+ } from '@redocly/openapi-core';
41
38
  import type { RawConfigProcessor } from '@redocly/openapi-core/lib/config';
39
+ import type { Totals, Entrypoint, ConfigApis, CommandOptions, OutputExtensions } from '../types';
42
40
 
43
41
  export async function getFallbackApisOrExit(
44
42
  argsApis: string[] | undefined,
@@ -279,12 +277,11 @@ export function getAndValidateFileExtension(fileName: string): NonNullable<Outpu
279
277
  return 'yaml';
280
278
  }
281
279
 
282
- export function pluralize(label: string, num: number) {
283
- if (label.endsWith('is')) {
284
- [label] = label.split(' ');
285
- return num === 1 ? `${label} is` : `${label}s are`;
286
- }
287
- return num === 1 ? `${label}` : `${label}s`;
280
+ export function pluralize(sentence: string, count?: number, inclusive?: boolean) {
281
+ return sentence
282
+ .split(' ')
283
+ .map((word) => pluralizeOne(word, count, inclusive))
284
+ .join(' ');
288
285
  }
289
286
 
290
287
  export function handleError(e: Error, ref: string) {
@@ -525,6 +522,7 @@ export function checkIfRulesetExist(rules: typeof StyleguideConfig.prototype.rul
525
522
  ...rules.oas3_0,
526
523
  ...rules.oas3_1,
527
524
  ...rules.async2,
525
+ ...rules.async3,
528
526
  ...rules.arazzo,
529
527
  };
530
528
 
@@ -543,7 +541,10 @@ export function cleanColors(input: string): string {
543
541
  export async function sendTelemetry(
544
542
  argv: Arguments | undefined,
545
543
  exit_code: ExitCode,
546
- 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
547
548
  ): Promise<void> {
548
549
  try {
549
550
  if (!argv) {
@@ -571,6 +572,9 @@ export async function sendTelemetry(
571
572
  environment_ci: process.env.CI,
572
573
  raw_input: cleanRawInput(process.argv.slice(2)),
573
574
  has_config,
575
+ spec_version,
576
+ spec_keyword,
577
+ spec_full_version,
574
578
  };
575
579
  await fetch(`https://api.redocly.com/registry/telemetry/cli`, {
576
580
  method: 'POST',
@@ -600,6 +604,9 @@ export type Analytics = {
600
604
  environment_ci?: string;
601
605
  raw_input: string;
602
606
  has_config?: boolean;
607
+ spec_version?: string;
608
+ spec_keyword?: string;
609
+ spec_full_version?: string;
603
610
  };
604
611
 
605
612
  function isFile(value: string) {
@@ -664,3 +671,25 @@ export function checkForDeprecatedOptions<T>(argv: T, deprecatedOptions: Array<k
664
671
  }
665
672
  }
666
673
  }
674
+
675
+ export function notifyAboutIncompatibleConfigOptions(
676
+ themeOpenapiOptions: Record<string, unknown> | undefined
677
+ ) {
678
+ if (isPlainObject(themeOpenapiOptions)) {
679
+ const propertiesSet = Object.keys(themeOpenapiOptions);
680
+ const deprecatedSet = Object.keys(deprecatedRefDocsSchema.properties);
681
+ const intersection = propertiesSet.filter((prop) => deprecatedSet.includes(prop));
682
+ if (intersection.length > 0) {
683
+ process.stderr.write(
684
+ yellow(
685
+ `\n${pluralize('Property', intersection.length)} ${gray(
686
+ intersection.map((prop) => `'${prop}'`).join(', ')
687
+ )} ${pluralize(
688
+ 'is',
689
+ intersection.length
690
+ )} only used in API Reference Docs and Redoc version 2.x or earlier.\n\n`
691
+ )
692
+ );
693
+ }
694
+ }
695
+ }
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);