@redocly/cli 1.22.1 → 1.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/lib/__tests__/commands/bundle.test.js +146 -1
  3. package/lib/__tests__/fetch-with-timeout.test.js +29 -5
  4. package/lib/__tests__/utils.test.js +54 -32
  5. package/lib/cms/api/__tests__/api.client.test.js +17 -9
  6. package/lib/cms/api/api-client.d.ts +26 -7
  7. package/lib/cms/api/api-client.js +103 -72
  8. package/lib/cms/commands/__tests__/push-status.test.js +1 -1
  9. package/lib/cms/commands/__tests__/push.test.js +41 -1
  10. package/lib/cms/commands/__tests__/utils.test.js +1 -1
  11. package/lib/cms/commands/push-status.d.ts +1 -1
  12. package/lib/cms/commands/push-status.js +3 -7
  13. package/lib/cms/commands/push.js +4 -4
  14. package/lib/cms/commands/utils.d.ts +3 -0
  15. package/lib/cms/commands/utils.js +8 -1
  16. package/lib/commands/bundle.d.ts +1 -1
  17. package/lib/commands/bundle.js +15 -9
  18. package/lib/commands/eject.d.ts +1 -1
  19. package/lib/commands/eject.js +1 -1
  20. package/lib/commands/preview-project/index.js +1 -1
  21. package/lib/index.js +1 -2
  22. package/lib/types.d.ts +1 -0
  23. package/lib/utils/__mocks__/miscellaneous.d.ts +1 -0
  24. package/lib/utils/__mocks__/miscellaneous.js +2 -1
  25. package/lib/utils/fetch-with-timeout.d.ts +6 -1
  26. package/lib/utils/fetch-with-timeout.js +16 -14
  27. package/lib/utils/miscellaneous.d.ts +10 -1
  28. package/lib/utils/miscellaneous.js +24 -20
  29. package/lib/utils/update-version-notifier.js +8 -4
  30. package/package.json +2 -2
  31. package/src/__tests__/commands/bundle.test.ts +172 -4
  32. package/src/__tests__/fetch-with-timeout.test.ts +36 -6
  33. package/src/__tests__/utils.test.ts +58 -33
  34. package/src/cms/api/__tests__/api.client.test.ts +20 -11
  35. package/src/cms/api/api-client.ts +158 -91
  36. package/src/cms/commands/__tests__/push-status.test.ts +1 -1
  37. package/src/cms/commands/__tests__/push.test.ts +49 -2
  38. package/src/cms/commands/__tests__/utils.test.ts +1 -1
  39. package/src/cms/commands/push-status.ts +5 -9
  40. package/src/cms/commands/push.ts +5 -6
  41. package/src/cms/commands/utils.ts +15 -1
  42. package/src/commands/bundle.ts +20 -12
  43. package/src/commands/eject.ts +2 -2
  44. package/src/commands/preview-project/index.ts +1 -1
  45. package/src/index.ts +1 -2
  46. package/src/types.ts +1 -0
  47. package/src/utils/__mocks__/miscellaneous.ts +1 -0
  48. package/src/utils/fetch-with-timeout.ts +23 -14
  49. package/src/utils/miscellaneous.ts +45 -30
  50. package/src/utils/update-version-notifier.ts +11 -5
  51. package/tsconfig.tsbuildinfo +1 -1
@@ -1,24 +1,33 @@
1
- import nodeFetch from 'node-fetch';
1
+ import nodeFetch, { type RequestInit } from 'node-fetch';
2
2
  import AbortController from 'abort-controller';
3
3
  import { getProxyAgent } from '@redocly/openapi-core';
4
4
 
5
- const TIMEOUT = 3000;
5
+ export const DEFAULT_FETCH_TIMEOUT = 3000;
6
6
 
7
- export default async (url: string, options = {}) => {
8
- try {
9
- const controller = new AbortController();
10
- const timeout = setTimeout(() => {
11
- controller.abort();
12
- }, TIMEOUT);
7
+ export type FetchWithTimeoutOptions = RequestInit & {
8
+ timeout?: number;
9
+ };
13
10
 
14
- const res = await nodeFetch(url, {
15
- signal: controller.signal,
11
+ export default async (url: string, { timeout, ...options }: FetchWithTimeoutOptions = {}) => {
12
+ if (!timeout) {
13
+ return nodeFetch(url, {
16
14
  ...options,
17
15
  agent: getProxyAgent(),
18
16
  });
19
- clearTimeout(timeout);
20
- return res;
21
- } catch (e) {
22
- return;
23
17
  }
18
+
19
+ const controller = new AbortController();
20
+ const timeoutId = setTimeout(() => {
21
+ controller.abort();
22
+ }, timeout);
23
+
24
+ const res = await nodeFetch(url, {
25
+ signal: controller.signal,
26
+ ...options,
27
+ agent: getProxyAgent(),
28
+ });
29
+
30
+ clearTimeout(timeoutId);
31
+
32
+ return res;
24
33
  };
@@ -16,13 +16,19 @@ import {
16
16
  loadConfig,
17
17
  RedoclyClient,
18
18
  } from '@redocly/openapi-core';
19
- import { isEmptyObject, isPlainObject, pluralize } from '@redocly/openapi-core/lib/utils';
19
+ import {
20
+ isEmptyObject,
21
+ isNotEmptyArray,
22
+ isNotEmptyObject,
23
+ isPlainObject,
24
+ pluralize,
25
+ } from '@redocly/openapi-core/lib/utils';
20
26
  import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
21
27
  import { deprecatedRefDocsSchema } from '@redocly/config/lib/reference-docs-config-schema';
22
28
  import { outputExtensions } from '../types';
23
29
  import { version } from './update-version-notifier';
24
30
  import { DESTINATION_REGEX } from '../commands/push';
25
- import fetch from './fetch-with-timeout';
31
+ import fetch, { DEFAULT_FETCH_TIMEOUT } from './fetch-with-timeout';
26
32
 
27
33
  import type { Arguments } from 'yargs';
28
34
  import type {
@@ -42,8 +48,7 @@ export async function getFallbackApisOrExit(
42
48
  config: ConfigApis
43
49
  ): Promise<Entrypoint[]> {
44
50
  const { apis } = config;
45
- const shouldFallbackToAllDefinitions =
46
- !isNotEmptyArray(argsApis) && apis && Object.keys(apis).length > 0;
51
+ const shouldFallbackToAllDefinitions = !isNotEmptyArray(argsApis) && isNotEmptyObject(apis);
47
52
  const res = shouldFallbackToAllDefinitions
48
53
  ? fallbackToAllDefinitions(apis, config)
49
54
  : await expandGlobsInEntrypoints(argsApis!, config);
@@ -64,10 +69,6 @@ function getConfigDirectory(config: ConfigApis) {
64
69
  return config.configFile ? dirname(config.configFile) : process.cwd();
65
70
  }
66
71
 
67
- function isNotEmptyArray<T>(args?: T[]): boolean {
68
- return Array.isArray(args) && !!args.length;
69
- }
70
-
71
72
  function isApiPathValid(apiPath: string): string | void {
72
73
  if (!apiPath.trim()) {
73
74
  exitWithError('Path cannot be empty.');
@@ -80,15 +81,21 @@ function fallbackToAllDefinitions(
80
81
  apis: Record<string, ResolvedApi>,
81
82
  config: ConfigApis
82
83
  ): Entrypoint[] {
83
- return Object.entries(apis).map(([alias, { root }]) => ({
84
+ return Object.entries(apis).map(([alias, { root, output }]) => ({
84
85
  path: isAbsoluteUrl(root) ? root : resolve(getConfigDirectory(config), root),
85
86
  alias,
87
+ output: output && resolve(getConfigDirectory(config), output),
86
88
  }));
87
89
  }
88
90
 
89
91
  function getAliasOrPath(config: ConfigApis, aliasOrPath: string): Entrypoint {
90
- return config.apis[aliasOrPath]
91
- ? { path: config.apis[aliasOrPath]?.root, alias: aliasOrPath }
92
+ const aliasApi = config.apis[aliasOrPath];
93
+ return aliasApi
94
+ ? {
95
+ path: aliasApi.root,
96
+ alias: aliasOrPath,
97
+ output: aliasApi.output,
98
+ }
92
99
  : {
93
100
  path: aliasOrPath,
94
101
  // find alias by path, take the first match
@@ -99,10 +106,10 @@ function getAliasOrPath(config: ConfigApis, aliasOrPath: string): Entrypoint {
99
106
  };
100
107
  }
101
108
 
102
- async function expandGlobsInEntrypoints(args: string[], config: ConfigApis) {
109
+ async function expandGlobsInEntrypoints(argApis: string[], config: ConfigApis) {
103
110
  return (
104
111
  await Promise.all(
105
- (args as string[]).map(async (aliasOrPath) => {
112
+ argApis.map(async (aliasOrPath) => {
106
113
  return glob.hasMagic(aliasOrPath) && !isAbsoluteUrl(aliasOrPath)
107
114
  ? (await promisify(glob)(aliasOrPath)).map((g: string) => getAliasOrPath(config, g))
108
115
  : getAliasOrPath(config, aliasOrPath);
@@ -356,29 +363,36 @@ export function printConfigLintTotals(totals: Totals, command?: string | number)
356
363
  }
357
364
  }
358
365
 
359
- export function getOutputFileName(
360
- entrypoint: string,
361
- entries: number,
362
- output?: string,
363
- ext?: BundleOutputFormat
364
- ) {
365
- if (!output) {
366
- return { outputFile: 'stdout', ext: ext || 'yaml' };
366
+ export function getOutputFileName({
367
+ entrypoint,
368
+ output,
369
+ argvOutput,
370
+ ext,
371
+ entries,
372
+ }: {
373
+ entrypoint: string;
374
+ output?: string;
375
+ argvOutput?: string;
376
+ ext?: BundleOutputFormat;
377
+ entries: number;
378
+ }) {
379
+ let outputFile = output || argvOutput;
380
+ if (!outputFile) {
381
+ return { ext: ext || 'yaml' };
367
382
  }
368
383
 
369
- let outputFile = output;
370
- if (entries > 1) {
384
+ if (entries > 1 && argvOutput) {
371
385
  ext = ext || (extname(entrypoint).substring(1) as BundleOutputFormat);
372
- if (!outputExtensions.includes(ext as any)) {
386
+ if (!outputExtensions.includes(ext)) {
373
387
  throw new Error(`Invalid file extension: ${ext}.`);
374
388
  }
375
- outputFile = join(output, basename(entrypoint, extname(entrypoint))) + '.' + ext;
389
+ outputFile = join(argvOutput, basename(entrypoint, extname(entrypoint))) + '.' + ext;
376
390
  } else {
377
- if (output) {
378
- ext = ext || (extname(output).substring(1) as BundleOutputFormat);
379
- }
380
- ext = ext || (extname(entrypoint).substring(1) as BundleOutputFormat);
381
- if (!outputExtensions.includes(ext as any)) {
391
+ ext =
392
+ ext ||
393
+ (extname(outputFile).substring(1) as BundleOutputFormat) ||
394
+ (extname(entrypoint).substring(1) as BundleOutputFormat);
395
+ if (!outputExtensions.includes(ext)) {
382
396
  throw new Error(`Invalid file extension: ${ext}.`);
383
397
  }
384
398
  outputFile = join(dirname(outputFile), basename(outputFile, extname(outputFile))) + '.' + ext;
@@ -569,6 +583,7 @@ export async function sendTelemetry(
569
583
  spec_full_version,
570
584
  };
571
585
  await fetch(`https://api.redocly.com/registry/telemetry/cli`, {
586
+ timeout: DEFAULT_FETCH_TIMEOUT,
572
587
  method: 'POST',
573
588
  headers: {
574
589
  'content-type': 'application/json',
@@ -2,7 +2,7 @@ import { tmpdir } from 'os';
2
2
  import { join } from 'path';
3
3
  import { existsSync, writeFileSync, readFileSync, statSync } from 'fs';
4
4
  import { compare } from 'semver';
5
- import fetch from './fetch-with-timeout';
5
+ import fetch, { DEFAULT_FETCH_TIMEOUT } from './fetch-with-timeout';
6
6
  import { cyan, green, yellow } from 'colorette';
7
7
  import { cleanColors } from './miscellaneous';
8
8
 
@@ -34,10 +34,16 @@ const isNewVersionAvailable = (current: string, latest: string) => compare(curre
34
34
 
35
35
  const getLatestVersion = async (packageName: string): Promise<string | undefined> => {
36
36
  const latestUrl = `http://registry.npmjs.org/${packageName}/latest`;
37
- const response = await fetch(latestUrl);
38
- if (!response) return;
39
- const info = await response.json();
40
- return info.version;
37
+
38
+ try {
39
+ const response = await fetch(latestUrl, { timeout: DEFAULT_FETCH_TIMEOUT });
40
+ const info = await response.json();
41
+
42
+ return info.version;
43
+ } catch {
44
+ // Do nothing
45
+ return;
46
+ }
41
47
  };
42
48
 
43
49
  export const cacheLatestVersion = () => {