@redocly/cli 1.6.0 → 1.8.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 (126) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +5 -5
  3. package/lib/__tests__/commands/build-docs.test.js +3 -3
  4. package/lib/__tests__/commands/bundle.test.js +5 -5
  5. package/lib/__tests__/commands/join.test.js +11 -11
  6. package/lib/__tests__/commands/lint.test.js +14 -14
  7. package/lib/__tests__/commands/push-region.test.js +1 -1
  8. package/lib/__tests__/commands/push.test.js +11 -11
  9. package/lib/__tests__/fetch-with-timeout.test.js +4 -13
  10. package/lib/__tests__/spinner.test.js +43 -0
  11. package/lib/__tests__/utils.test.js +54 -36
  12. package/lib/__tests__/wrapper.test.js +8 -8
  13. package/lib/cms/api/__tests__/api-keys.test.d.ts +1 -0
  14. package/lib/cms/api/__tests__/api-keys.test.js +26 -0
  15. package/lib/cms/api/__tests__/api.client.test.d.ts +1 -0
  16. package/lib/cms/api/__tests__/api.client.test.js +217 -0
  17. package/lib/cms/api/__tests__/domains.test.d.ts +1 -0
  18. package/lib/cms/api/__tests__/domains.test.js +13 -0
  19. package/lib/cms/api/api-client.d.ts +50 -0
  20. package/lib/cms/api/api-client.js +148 -0
  21. package/lib/cms/api/api-keys.d.ts +1 -0
  22. package/lib/cms/api/api-keys.js +24 -0
  23. package/lib/cms/api/domains.d.ts +1 -0
  24. package/lib/cms/api/domains.js +12 -0
  25. package/lib/cms/api/index.d.ts +3 -0
  26. package/lib/cms/api/index.js +19 -0
  27. package/lib/cms/api/types.d.ts +91 -0
  28. package/lib/cms/api/types.js +2 -0
  29. package/lib/cms/commands/__tests__/push-status.test.d.ts +1 -0
  30. package/lib/cms/commands/__tests__/push-status.test.js +164 -0
  31. package/lib/cms/commands/__tests__/push.test.d.ts +1 -0
  32. package/lib/cms/commands/__tests__/push.test.js +226 -0
  33. package/lib/cms/commands/push-status.d.ts +12 -0
  34. package/lib/cms/commands/push-status.js +150 -0
  35. package/lib/cms/commands/push.d.ts +23 -0
  36. package/lib/cms/commands/push.js +142 -0
  37. package/lib/cms/utils.d.ts +2 -0
  38. package/lib/cms/utils.js +6 -0
  39. package/lib/commands/build-docs/index.js +4 -4
  40. package/lib/commands/build-docs/utils.js +2 -2
  41. package/lib/commands/bundle.js +13 -13
  42. package/lib/commands/join.js +25 -25
  43. package/lib/commands/lint.js +10 -10
  44. package/lib/commands/login.js +2 -2
  45. package/lib/commands/preview-docs/index.js +4 -4
  46. package/lib/commands/preview-docs/preview-server/preview-server.js +2 -2
  47. package/lib/commands/preview-project/constants.d.ts +14 -0
  48. package/lib/commands/preview-project/constants.js +22 -0
  49. package/lib/commands/preview-project/index.d.ts +2 -0
  50. package/lib/commands/preview-project/index.js +58 -0
  51. package/lib/commands/preview-project/types.d.ts +10 -0
  52. package/lib/commands/preview-project/types.js +2 -0
  53. package/lib/commands/push.d.ts +5 -0
  54. package/lib/commands/push.js +25 -17
  55. package/lib/commands/split/__tests__/index.test.js +2 -2
  56. package/lib/commands/split/index.js +20 -20
  57. package/lib/commands/stats.js +4 -4
  58. package/lib/index.d.ts +1 -1
  59. package/lib/index.js +169 -25
  60. package/lib/types.d.ts +9 -1
  61. package/lib/{__mocks__/utils.js → utils/__mocks__/miscellaneous.js} +1 -1
  62. package/lib/utils/assert-node-version.d.ts +1 -0
  63. package/lib/{fetch-with-timeout.js → utils/fetch-with-timeout.js} +2 -7
  64. package/lib/{utils.d.ts → utils/miscellaneous.d.ts} +1 -1
  65. package/lib/{utils.js → utils/miscellaneous.js} +20 -2
  66. package/lib/utils/spinner.d.ts +10 -0
  67. package/lib/utils/spinner.js +42 -0
  68. package/lib/{update-version-notifier.js → utils/update-version-notifier.js} +4 -4
  69. package/lib/wrapper.js +5 -5
  70. package/package.json +5 -3
  71. package/src/__tests__/commands/build-docs.test.ts +2 -2
  72. package/src/__tests__/commands/bundle.test.ts +2 -2
  73. package/src/__tests__/commands/join.test.ts +2 -2
  74. package/src/__tests__/commands/lint.test.ts +3 -3
  75. package/src/__tests__/commands/push-region.test.ts +1 -1
  76. package/src/__tests__/commands/push.test.ts +2 -2
  77. package/src/__tests__/fetch-with-timeout.test.ts +4 -16
  78. package/src/__tests__/spinner.test.ts +51 -0
  79. package/src/__tests__/utils.test.ts +20 -5
  80. package/src/__tests__/wrapper.test.ts +2 -2
  81. package/src/cms/api/__tests__/api-keys.test.ts +37 -0
  82. package/src/cms/api/__tests__/api.client.test.ts +275 -0
  83. package/src/cms/api/__tests__/domains.test.ts +15 -0
  84. package/src/cms/api/api-client.ts +199 -0
  85. package/src/cms/api/api-keys.ts +26 -0
  86. package/src/cms/api/domains.ts +11 -0
  87. package/src/cms/api/index.ts +3 -0
  88. package/src/cms/api/types.ts +101 -0
  89. package/src/cms/commands/__tests__/push-status.test.ts +212 -0
  90. package/src/cms/commands/__tests__/push.test.ts +293 -0
  91. package/src/cms/commands/push-status.ts +203 -0
  92. package/src/cms/commands/push.ts +215 -0
  93. package/src/cms/utils.ts +1 -0
  94. package/src/commands/build-docs/index.ts +1 -1
  95. package/src/commands/build-docs/utils.ts +1 -1
  96. package/src/commands/bundle.ts +2 -2
  97. package/src/commands/join.ts +2 -2
  98. package/src/commands/lint.ts +1 -1
  99. package/src/commands/login.ts +1 -1
  100. package/src/commands/preview-docs/index.ts +5 -1
  101. package/src/commands/preview-docs/preview-server/preview-server.ts +1 -1
  102. package/src/commands/preview-project/constants.ts +23 -0
  103. package/src/commands/preview-project/index.ts +58 -0
  104. package/src/commands/preview-project/types.ts +12 -0
  105. package/src/commands/push.ts +15 -1
  106. package/src/commands/split/__tests__/index.test.ts +3 -4
  107. package/src/commands/split/index.ts +5 -5
  108. package/src/commands/stats.ts +2 -2
  109. package/src/index.ts +184 -28
  110. package/src/types.ts +12 -1
  111. package/src/{__mocks__/utils.ts → utils/__mocks__/miscellaneous.ts} +1 -1
  112. package/src/{fetch-with-timeout.ts → utils/fetch-with-timeout.ts} +1 -6
  113. package/src/{utils.ts → utils/miscellaneous.ts} +20 -2
  114. package/src/utils/spinner.ts +50 -0
  115. package/src/{update-version-notifier.ts → utils/update-version-notifier.ts} +2 -2
  116. package/src/wrapper.ts +7 -2
  117. package/tsconfig.tsbuildinfo +1 -1
  118. /package/lib/{assert-node-version.d.ts → __tests__/spinner.test.d.ts} +0 -0
  119. /package/lib/{__mocks__/utils.d.ts → utils/__mocks__/miscellaneous.d.ts} +0 -0
  120. /package/lib/{assert-node-version.js → utils/assert-node-version.js} +0 -0
  121. /package/lib/{fetch-with-timeout.d.ts → utils/fetch-with-timeout.d.ts} +0 -0
  122. /package/lib/{js-utils.d.ts → utils/js-utils.d.ts} +0 -0
  123. /package/lib/{js-utils.js → utils/js-utils.js} +0 -0
  124. /package/lib/{update-version-notifier.d.ts → utils/update-version-notifier.d.ts} +0 -0
  125. /package/src/{assert-node-version.ts → utils/assert-node-version.ts} +0 -0
  126. /package/src/{js-utils.ts → utils/js-utils.ts} +0 -0
@@ -6,7 +6,7 @@ import { performance } from 'perf_hooks';
6
6
  import { getObjectOrJSON, getPageHTML } from './utils';
7
7
  import type { BuildDocsArgv } from './types';
8
8
  import { Config, getMergedConfig, isAbsoluteUrl } from '@redocly/openapi-core';
9
- import { exitWithError, getExecutionTime, getFallbackApisOrExit } from '../../utils';
9
+ import { exitWithError, getExecutionTime, getFallbackApisOrExit } from '../../utils/miscellaneous';
10
10
 
11
11
  export const handlerBuildCommand = async (argv: BuildDocsArgv, configFromFile: Config) => {
12
12
  const startedAt = performance.now();
@@ -10,7 +10,7 @@ import { existsSync, lstatSync, readFileSync } from 'fs';
10
10
 
11
11
  import type { BuildDocsOptions } from './types';
12
12
  import { red } from 'colorette';
13
- import { exitWithError } from '../../utils';
13
+ import { exitWithError } from '../../utils/miscellaneous';
14
14
 
15
15
  export function getObjectOrJSON(
16
16
  openapiOptions: string | Record<string, unknown>,
@@ -18,12 +18,12 @@ import {
18
18
  printLintTotals,
19
19
  checkIfRulesetExist,
20
20
  sortTopLevelKeysForOas,
21
- } from '../utils';
21
+ } from '../utils/miscellaneous';
22
22
  import type { OutputExtensions, Skips, Totals } from '../types';
23
23
  import { performance } from 'perf_hooks';
24
24
  import { blue, gray, green, yellow } from 'colorette';
25
25
  import { writeFileSync } from 'fs';
26
- import { checkForDeprecatedOptions } from '../utils';
26
+ import { checkForDeprecatedOptions } from '../utils/miscellaneous';
27
27
 
28
28
  export type BundleOptions = {
29
29
  apis?: string[];
@@ -30,8 +30,8 @@ import {
30
30
  getAndValidateFileExtension,
31
31
  writeToFileByExtension,
32
32
  checkForDeprecatedOptions,
33
- } from '../utils';
34
- import { isObject, isString, keysOf } from '../js-utils';
33
+ } from '../utils/miscellaneous';
34
+ import { isObject, isString, keysOf } from '../utils/js-utils';
35
35
  import {
36
36
  Oas3Parameter,
37
37
  Oas3PathItem,
@@ -17,7 +17,7 @@ import {
17
17
  printConfigLintTotals,
18
18
  printLintTotals,
19
19
  printUnusedWarnings,
20
- } from '../utils';
20
+ } from '../utils/miscellaneous';
21
21
  import { blue, gray } from 'colorette';
22
22
  import { performance } from 'perf_hooks';
23
23
 
@@ -1,6 +1,6 @@
1
1
  import { Region, RedoclyClient, Config } from '@redocly/openapi-core';
2
2
  import { blue, green, gray } from 'colorette';
3
- import { promptUser } from '../utils';
3
+ import { promptUser } from '../utils/miscellaneous';
4
4
 
5
5
  export function promptClientToken(domain: string) {
6
6
  return promptUser(
@@ -1,7 +1,11 @@
1
1
  import * as colorette from 'colorette';
2
2
  import * as chockidar from 'chokidar';
3
3
  import { bundle, RedoclyClient, getTotals, getMergedConfig, Config } from '@redocly/openapi-core';
4
- import { getFallbackApisOrExit, handleError, loadConfigAndHandleErrors } from '../../utils';
4
+ import {
5
+ getFallbackApisOrExit,
6
+ handleError,
7
+ loadConfigAndHandleErrors,
8
+ } from '../../utils/miscellaneous';
5
9
  import startPreviewServer from './preview-server/preview-server';
6
10
  import type { Skips } from '../../types';
7
11
 
@@ -6,7 +6,7 @@ import * as path from 'path';
6
6
 
7
7
  import { startHttpServer, startWsServer, respondWithGzip, mimeTypes } from './server';
8
8
  import type { IncomingMessage } from 'http';
9
- import { isSubdir } from '../../../utils';
9
+ import { isSubdir } from '../../../utils/miscellaneous';
10
10
 
11
11
  function getPageHTML(
12
12
  htmlTemplate: string,
@@ -0,0 +1,23 @@
1
+ import { Product } from './types';
2
+
3
+ export const PRODUCT_PACKAGES = {
4
+ realm: '@redocly/realm',
5
+ 'redoc-revel': '@redocly/redoc-revel',
6
+ 'revel-reef': '@redocly/revel-reef',
7
+ 'redoc-reef': '@redocly/redoc-reef',
8
+ redoc: '@redocly/redoc',
9
+ revel: '@redocly/revel',
10
+ reef: '@redocly/reef',
11
+ };
12
+
13
+ export const PRODUCT_NAMES: { [key in Product]: string } = {
14
+ redoc: 'Redoc',
15
+ revel: 'Revel',
16
+ reef: 'Reef',
17
+ realm: 'Realm',
18
+ 'redoc-revel': 'Redoc + Revel',
19
+ 'redoc-reef': 'Redoc + Reef',
20
+ 'revel-reef': 'Revel + Reef',
21
+ };
22
+
23
+ export const PRODUCT_PLANS = ['pro', 'enterprise'] as const;
@@ -0,0 +1,58 @@
1
+ import path = require('path');
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { spawn } from 'child_process';
4
+ import { PRODUCT_NAMES, PRODUCT_PACKAGES } from './constants';
5
+
6
+ import type { PreviewProjectOptions, Product } from './types';
7
+
8
+ export const previewProject = async (args: PreviewProjectOptions) => {
9
+ const { plan, port } = args;
10
+ const projectDir = args['source-dir'];
11
+
12
+ const product = args.product || tryGetProductFromPackageJson(projectDir);
13
+
14
+ if (!isValidProduct(product)) {
15
+ process.stderr.write(`Invalid product ${product}`);
16
+ throw new Error(`Project preview launch failed`);
17
+ }
18
+
19
+ const productName = PRODUCT_NAMES[product];
20
+ const packageName = PRODUCT_PACKAGES[product];
21
+
22
+ process.stdout.write(`\nLaunching preview of ${productName} ${plan} using NPX\n\n`);
23
+
24
+ spawn('npx', ['-y', packageName, 'develop', `--plan=${plan}`, `--port=${port || 4000}`], {
25
+ stdio: 'inherit',
26
+ cwd: projectDir,
27
+ });
28
+ };
29
+
30
+ const isValidProduct = (product: string | undefined): product is Product => {
31
+ if (!product) {
32
+ return false;
33
+ }
34
+
35
+ return !!PRODUCT_NAMES[product as Product];
36
+ };
37
+
38
+ const tryGetProductFromPackageJson = (projectDir: string): Product => {
39
+ const packageJsonPath = path.join(process.cwd(), projectDir, 'package.json');
40
+
41
+ if (existsSync(packageJsonPath)) {
42
+ try {
43
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
44
+ const packageJsonDeps = packageJson.dependencies || {};
45
+
46
+ for (const [product, packageName] of Object.entries(PRODUCT_PACKAGES)) {
47
+ if (packageJsonDeps[packageName]) {
48
+ process.stdout.write(`\n${packageName} detected in project's 'package.json'`);
49
+ return product as Product;
50
+ }
51
+ }
52
+ } catch (error) {
53
+ process.stdout.write(`Invalid 'package.json': ${packageJsonPath}. Using Realm.`);
54
+ }
55
+ }
56
+
57
+ return 'realm';
58
+ };
@@ -0,0 +1,12 @@
1
+ import { PRODUCT_PACKAGES, PRODUCT_PLANS } from './constants';
2
+
3
+ export type Product = keyof typeof PRODUCT_PACKAGES;
4
+ export type ProductPlan = typeof PRODUCT_PLANS[number];
5
+
6
+ export type PreviewProjectOptions = {
7
+ product?: Product | string;
8
+ plan: ProductPlan | string;
9
+ port?: number;
10
+ 'source-dir': string;
11
+ config?: string;
12
+ };
@@ -21,8 +21,9 @@ import {
21
21
  getFallbackApisOrExit,
22
22
  pluralize,
23
23
  dumpBundle,
24
- } from '../utils';
24
+ } from '../utils/miscellaneous';
25
25
  import { promptClientToken } from './login';
26
+ import { handlePush as handleCMSPush } from '../cms/commands/push';
26
27
 
27
28
  const DEFAULT_VERSION = 'latest';
28
29
 
@@ -44,6 +45,19 @@ export type PushOptions = {
44
45
  config?: string;
45
46
  };
46
47
 
48
+ export function commonPushHandler({
49
+ project,
50
+ 'mount-path': mountPath,
51
+ }: {
52
+ project?: string;
53
+ 'mount-path'?: string;
54
+ }) {
55
+ if (project && mountPath) {
56
+ return handleCMSPush;
57
+ }
58
+ return handlePush;
59
+ }
60
+
47
61
  export async function handlePush(argv: PushOptions, config: Config): Promise<void> {
48
62
  const client = new RedoclyClient(config.region);
49
63
  const isAuthorized = await client.isAuthorizedWithRedoclyByRegion();
@@ -3,12 +3,11 @@ 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 { writeToFileByExtension } from '../../../utils';
7
6
 
8
- const utils = require('../../../utils');
7
+ const utils = require('../../../utils/miscellaneous');
9
8
 
10
- jest.mock('../../../utils', () => ({
11
- ...jest.requireActual('../../../utils'),
9
+ jest.mock('../../../utils/miscellaneous', () => ({
10
+ ...jest.requireActual('../../../utils/miscellaneous'),
12
11
  writeToFileByExtension: jest.fn(),
13
12
  }));
14
13
 
@@ -15,8 +15,8 @@ import {
15
15
  langToExt,
16
16
  writeToFileByExtension,
17
17
  getAndValidateFileExtension,
18
- } from '../../utils';
19
- import { isString, isObject, isEmptyObject } from '../../js-utils';
18
+ } from '../../utils/miscellaneous';
19
+ import { isString, isObject, isEmptyObject } from '../../utils/js-utils';
20
20
  import {
21
21
  Definition,
22
22
  Oas2Definition,
@@ -97,8 +97,8 @@ function isStartsWithComponents(node: string) {
97
97
  return node.startsWith(componentsPath);
98
98
  }
99
99
 
100
- function isNotYaml(filename: string) {
101
- return !(filename.endsWith('.yaml') || filename.endsWith('.yml'));
100
+ function isSupportedExtension(filename: string) {
101
+ return filename.endsWith('.yaml') || filename.endsWith('.yml') || filename.endsWith('.json');
102
102
  }
103
103
 
104
104
  function loadFile(fileName: string) {
@@ -138,7 +138,7 @@ function traverseDirectoryDeepCallback(
138
138
  directory: string,
139
139
  componentsFiles: object
140
140
  ) {
141
- if (isNotYaml(filename)) return;
141
+ if (!isSupportedExtension(filename)) return;
142
142
  const pathData = readYaml(filename);
143
143
  replace$Refs(pathData, directory, componentsFiles);
144
144
  writeToFileByExtension(pathData, filename);
@@ -13,8 +13,8 @@ import {
13
13
  Stats,
14
14
  bundle,
15
15
  } from '@redocly/openapi-core';
16
- import { getFallbackApisOrExit } from '../utils';
17
- import { printExecutionTime } from '../utils';
16
+ import { getFallbackApisOrExit } from '../utils/miscellaneous';
17
+ import { printExecutionTime } from '../utils/miscellaneous';
18
18
  import type { StatsAccumulator, StatsName, WalkContext, OutputFormat } from '@redocly/openapi-core';
19
19
 
20
20
  const statsAccumulator: StatsAccumulator = {
package/src/index.ts CHANGED
@@ -1,24 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import './assert-node-version';
3
+ import './utils/assert-node-version';
4
4
  import * as yargs from 'yargs';
5
- import { outputExtensions, regionChoices } from './types';
5
+ import { outputExtensions, PushArguments, regionChoices } from './types';
6
6
  import { RedoclyClient } from '@redocly/openapi-core';
7
7
  import { previewDocs } from './commands/preview-docs';
8
8
  import { handleStats } from './commands/stats';
9
9
  import { handleSplit } from './commands/split';
10
10
  import { handleJoin } from './commands/join';
11
- import { handlePush, transformPush } from './commands/push';
11
+ import { handlePushStatus, PushStatusOptions } from './cms/commands/push-status';
12
12
  import { handleLint } from './commands/lint';
13
13
  import { handleBundle } from './commands/bundle';
14
14
  import { handleLogin } from './commands/login';
15
15
  import { handlerBuildCommand } from './commands/build-docs';
16
- import { cacheLatestVersion, notifyUpdateCliVersion } from './update-version-notifier';
16
+ import { cacheLatestVersion, notifyUpdateCliVersion } from './utils/update-version-notifier';
17
17
  import { commandWrapper } from './wrapper';
18
- import { version } from './update-version-notifier';
18
+ import { version } from './utils/update-version-notifier';
19
19
  import type { Arguments } from 'yargs';
20
20
  import type { OutputFormat, RuleSeverity } from '@redocly/openapi-core';
21
21
  import type { BuildDocsArgv } from './commands/build-docs/types';
22
+ import { previewProject } from './commands/preview-project';
23
+ import { PRODUCT_PLANS } from './commands/preview-project/constants';
24
+ import { commonPushHandler } from './commands/push';
22
25
 
23
26
  if (!('replaceAll' in String.prototype)) {
24
27
  require('core-js/actual/string/replace-all');
@@ -92,7 +95,7 @@ yargs
92
95
  )
93
96
  .command(
94
97
  'join [apis...]',
95
- 'Join definitions [experimental].',
98
+ 'Join multiple API descriptions into one [experimental].',
96
99
  (yargs) =>
97
100
  yargs
98
101
  .positional('apis', {
@@ -101,7 +104,7 @@ yargs
101
104
  demandOption: true,
102
105
  })
103
106
  .option({
104
- lint: { description: 'Lint definitions', type: 'boolean', default: false, hidden: true },
107
+ lint: { description: 'Lint descriptions', type: 'boolean', default: false, hidden: true },
105
108
  decorate: { description: 'Run decorators', type: 'boolean', default: false },
106
109
  preprocess: { description: 'Run preprocessors', type: 'boolean', default: false },
107
110
  'prefix-tags-with-info-prop': {
@@ -124,7 +127,7 @@ yargs
124
127
  type: 'boolean',
125
128
  },
126
129
  output: {
127
- describe: 'Output file',
130
+ description: 'Output file.',
128
131
  alias: 'o',
129
132
  type: 'string',
130
133
  },
@@ -144,23 +147,75 @@ yargs
144
147
  commandWrapper(handleJoin)(argv);
145
148
  }
146
149
  )
147
-
148
150
  .command(
149
- 'push [api] [maybeDestination] [maybeBranchName]',
150
- 'Push an API description to the Redocly API registry.',
151
+ 'push-status [pushId]',
152
+ false,
151
153
  (yargs) =>
152
154
  yargs
153
- .usage('push [api]')
154
- .positional('api', { type: 'string' })
155
- .positional('maybeDestination', { type: 'string' })
156
- .hide('maybeDestination')
157
- .hide('maybeBranchName')
155
+ .positional('pushId', {
156
+ description: 'Push id.',
157
+ type: 'string',
158
+ required: true,
159
+ })
160
+ .implies('max-execution-time', 'wait')
158
161
  .option({
159
162
  organization: {
160
163
  description: 'Name of the organization to push to.',
161
164
  type: 'string',
162
165
  alias: 'o',
163
166
  },
167
+ project: {
168
+ description: 'Name of the project to push to.',
169
+ type: 'string',
170
+ alias: 'p',
171
+ },
172
+ domain: { description: 'Specify a domain.', alias: 'd', type: 'string' },
173
+ wait: {
174
+ description: 'Wait for build to finish.',
175
+ type: 'boolean',
176
+ default: false,
177
+ },
178
+ 'max-execution-time': {
179
+ description: 'Maximum execution time in seconds.',
180
+ type: 'number',
181
+ },
182
+ }),
183
+ (argv) => {
184
+ process.env.REDOCLY_CLI_COMMAND = 'push-status';
185
+ commandWrapper(handlePushStatus)(argv as Arguments<PushStatusOptions>);
186
+ }
187
+ )
188
+ .command(
189
+ 'push [apis...]',
190
+ 'Push an API description to the Redocly API registry.',
191
+ (yargs) =>
192
+ yargs
193
+ .positional('apis', {
194
+ type: 'string',
195
+ array: true,
196
+ required: true,
197
+ default: [],
198
+ })
199
+ .hide('project')
200
+ .hide('domain')
201
+ .hide('mount-path')
202
+ .hide('author')
203
+ .hide('message')
204
+ .hide('default-branch')
205
+ .hide('verbose')
206
+ .hide('commit-sha')
207
+ .hide('commit-url')
208
+ .hide('namespace')
209
+ .hide('repository')
210
+ .hide('wait-for-deployment')
211
+ .hide('created-at')
212
+ .hide('max-execution-time')
213
+ .deprecateOption('batch-id', 'use --job-id')
214
+ .implies('job-id', 'batch-size')
215
+ .implies('batch-id', 'batch-size')
216
+ .implies('batch-size', 'job-id')
217
+ .implies('max-execution-time', 'wait-for-deployment')
218
+ .option({
164
219
  destination: {
165
220
  description: 'API name and version in the format `name@version`.',
166
221
  type: 'string',
@@ -215,20 +270,85 @@ yargs
215
270
  choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
216
271
  default: 'warn' as RuleSeverity,
217
272
  },
218
- })
219
- .deprecateOption('batch-id', 'use --job-id')
220
- .deprecateOption('maybeDestination')
221
- .implies('job-id', 'batch-size')
222
- .implies('batch-id', 'batch-size')
223
- .implies('batch-size', 'job-id'),
273
+ organization: {
274
+ description: 'Name of the organization to push to.',
275
+ type: 'string',
276
+ alias: 'o',
277
+ },
278
+ project: {
279
+ description: 'Name of the project to push to.',
280
+ type: 'string',
281
+ alias: 'p',
282
+ },
283
+ 'mount-path': {
284
+ description: 'The path files should be pushed to.',
285
+ type: 'string',
286
+ alias: 'mp',
287
+ },
288
+ author: {
289
+ description: 'Author of the commit.',
290
+ type: 'string',
291
+ alias: 'a',
292
+ },
293
+ message: {
294
+ description: 'Commit message.',
295
+ type: 'string',
296
+ alias: 'm',
297
+ },
298
+ 'commit-sha': {
299
+ description: 'Commit SHA.',
300
+ type: 'string',
301
+ alias: 'sha',
302
+ },
303
+ 'commit-url': {
304
+ description: 'Commit URL.',
305
+ type: 'string',
306
+ alias: 'url',
307
+ },
308
+ namespace: {
309
+ description: 'Repository namespace.',
310
+ type: 'string',
311
+ },
312
+ repository: {
313
+ description: 'Repository name.',
314
+ type: 'string',
315
+ },
316
+ 'created-at': {
317
+ description: 'Commit creation date.',
318
+ type: 'string',
319
+ },
320
+ domain: { description: 'Specify a domain.', alias: 'd', type: 'string' },
321
+ config: {
322
+ description: 'Path to the config file.',
323
+ requiresArg: true,
324
+ type: 'string',
325
+ },
326
+ 'default-branch': {
327
+ type: 'string',
328
+ default: 'main',
329
+ },
330
+ 'max-execution-time': {
331
+ description: 'Maximum execution time in seconds.',
332
+ type: 'number',
333
+ },
334
+ 'wait-for-deployment': {
335
+ description: 'Wait for build to finish.',
336
+ type: 'boolean',
337
+ default: false,
338
+ },
339
+ verbose: {
340
+ type: 'boolean',
341
+ default: false,
342
+ },
343
+ }),
224
344
  (argv) => {
225
345
  process.env.REDOCLY_CLI_COMMAND = 'push';
226
- commandWrapper(transformPush(handlePush))(argv);
346
+ commandWrapper(commonPushHandler(argv))(argv as PushArguments);
227
347
  }
228
348
  )
229
349
  .command(
230
350
  'lint [apis...]',
231
- 'Lint definition.',
351
+ 'Lint an API description.',
232
352
  (yargs) =>
233
353
  yargs.positional('apis', { array: true, type: 'string', demandOption: true }).option({
234
354
  format: {
@@ -287,10 +407,14 @@ yargs
287
407
  )
288
408
  .command(
289
409
  'bundle [apis...]',
290
- 'Bundle definition.',
410
+ 'Bundle a multi-file API description to a single file.',
291
411
  (yargs) =>
292
412
  yargs.positional('apis', { array: true, type: 'string', demandOption: true }).options({
293
- output: { type: 'string', alias: 'o' },
413
+ output: {
414
+ type: 'string',
415
+ description: 'Output file.',
416
+ alias: 'o',
417
+ },
294
418
  format: {
295
419
  description: 'Use a specific output format.',
296
420
  choices: ['stylish', 'codeframe', 'json', 'checkstyle'] as ReadonlyArray<OutputFormat>,
@@ -413,9 +537,41 @@ yargs
413
537
  })(argv);
414
538
  }
415
539
  )
540
+ .command(
541
+ 'preview',
542
+ 'Preview Redocly project using one of the product NPM packages.',
543
+ (yargs) =>
544
+ yargs.options({
545
+ product: {
546
+ type: 'string',
547
+ choices: ['redoc', 'revel', 'reef', 'realm', 'redoc-revel', 'redoc-reef', 'revel-reef'],
548
+ description:
549
+ "Product used to launch preview. Default is inferred from project's package.json or 'realm' is used.",
550
+ },
551
+ plan: {
552
+ type: 'string',
553
+ choices: PRODUCT_PLANS,
554
+ default: 'enterprise',
555
+ },
556
+ port: {
557
+ type: 'number',
558
+ description: 'Preview port.',
559
+ default: 4000,
560
+ },
561
+ 'source-dir': {
562
+ alias: 'd',
563
+ type: 'string',
564
+ description: 'Project directory.',
565
+ default: '.',
566
+ },
567
+ }),
568
+ (argv) => {
569
+ commandWrapper(previewProject)(argv);
570
+ }
571
+ )
416
572
  .command(
417
573
  'preview-docs [api]',
418
- 'Preview API reference docs for the specified definition.',
574
+ 'Preview API reference docs for an API description.',
419
575
  (yargs) =>
420
576
  yargs.positional('api', { type: 'string' }).options({
421
577
  port: {
@@ -519,7 +675,7 @@ yargs
519
675
  commandWrapper(handlerBuildCommand)(argv as Arguments<BuildDocsArgv>);
520
676
  }
521
677
  )
522
- .completion('completion', 'Generate completion script.')
678
+ .completion('completion', 'Generate autocomplete script for `redocly` command.')
523
679
  .demandCommand(1)
524
680
  .middleware([notifyUpdateCliVersion])
525
681
  .strict().argv;
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { BundleOutputFormat, Region, Config } from '@redocly/openapi-core';
2
+ import type { ArgumentsCamelCase } from 'yargs';
2
3
  import type { LintOptions } from './commands/lint';
3
4
  import type { BundleOptions } from './commands/bundle';
4
5
  import type { JoinOptions } from './commands/join';
@@ -8,6 +9,10 @@ import type { StatsOptions } from './commands/stats';
8
9
  import type { SplitOptions } from './commands/split';
9
10
  import type { PreviewDocsOptions } from './commands/preview-docs';
10
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
+ import type { PushOptions as CMSPushOptions } from './cms/commands/push';
15
+ import type { PreviewProjectOptions } from './commands/preview-project/types';
11
16
 
12
17
  export type Totals = {
13
18
  errors: number;
@@ -26,11 +31,15 @@ export type CommandOptions =
26
31
  | SplitOptions
27
32
  | JoinOptions
28
33
  | PushOptions
34
+ | PushBhOptions
29
35
  | LintOptions
30
36
  | BundleOptions
31
37
  | LoginOptions
32
38
  | PreviewDocsOptions
33
- | BuildDocsArgv;
39
+ | BuildDocsArgv
40
+ | PushStatusOptions
41
+ | PreviewProjectOptions;
42
+
34
43
  export type Skips = {
35
44
  'skip-rule'?: string[];
36
45
  'skip-decorator'?: string[];
@@ -38,3 +47,5 @@ export type Skips = {
38
47
  };
39
48
 
40
49
  export type ConfigApis = Pick<Config, 'apis' | 'configFile'>;
50
+
51
+ export type PushArguments = ArgumentsCamelCase<PushOptions & CMSPushOptions & { apis: string[] }>;
@@ -1,4 +1,4 @@
1
- import { ConfigFixture } from '../__tests__/fixtures/config';
1
+ import { ConfigFixture } from '../../__tests__/fixtures/config';
2
2
 
3
3
  export const getFallbackApisOrExit = jest.fn((entrypoints) =>
4
4
  entrypoints.map((path: string) => ({ path }))
@@ -1,20 +1,15 @@
1
1
  import nodeFetch from 'node-fetch';
2
+ import AbortController from 'abort-controller';
2
3
 
3
4
  const TIMEOUT = 3000;
4
5
 
5
6
  export default async (url: string, options = {}) => {
6
7
  try {
7
- if (!global.AbortController) {
8
- return nodeFetch(url, options);
9
- }
10
8
  const controller = new AbortController();
11
9
  const timeout = setTimeout(() => {
12
10
  controller.abort();
13
11
  }, TIMEOUT);
14
12
 
15
- // FIXME: fix this (possibly along with this issue: https://github.com/Redocly/redocly-cli/issues/1260)
16
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
17
- // @ts-ignore
18
13
  const res = await nodeFetch(url, { signal: controller.signal, ...options });
19
14
  clearTimeout(timeout);
20
15
  return res;