@redocly/cli 1.3.0 → 1.4.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 (64) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +15 -7
  3. package/lib/__mocks__/@redocly/openapi-core.d.ts +1 -0
  4. package/lib/__mocks__/@redocly/openapi-core.js +4 -3
  5. package/lib/__mocks__/utils.d.ts +2 -0
  6. package/lib/__mocks__/utils.js +3 -1
  7. package/lib/__tests__/commands/build-docs.test.js +2 -2
  8. package/lib/__tests__/commands/bundle.test.js +7 -7
  9. package/lib/__tests__/commands/join.test.js +25 -18
  10. package/lib/__tests__/commands/lint.test.js +15 -15
  11. package/lib/__tests__/commands/push-region.test.js +2 -2
  12. package/lib/__tests__/commands/push.test.js +30 -30
  13. package/lib/__tests__/fetch-with-timeout.test.js +2 -2
  14. package/lib/__tests__/utils.test.js +63 -32
  15. package/lib/__tests__/wrapper.test.js +3 -3
  16. package/lib/assert-node-version.js +1 -1
  17. package/lib/commands/build-docs/index.js +9 -9
  18. package/lib/commands/build-docs/types.d.ts +2 -2
  19. package/lib/commands/build-docs/utils.js +10 -10
  20. package/lib/commands/bundle.d.ts +1 -1
  21. package/lib/commands/bundle.js +25 -25
  22. package/lib/commands/join.d.ts +1 -1
  23. package/lib/commands/join.js +49 -48
  24. package/lib/commands/lint.d.ts +1 -1
  25. package/lib/commands/lint.js +22 -22
  26. package/lib/commands/login.d.ts +1 -1
  27. package/lib/commands/login.js +3 -3
  28. package/lib/commands/preview-docs/index.d.ts +1 -1
  29. package/lib/commands/preview-docs/index.js +7 -7
  30. package/lib/commands/preview-docs/preview-server/hot.js +19 -2
  31. package/lib/commands/preview-docs/preview-server/preview-server.js +15 -14
  32. package/lib/commands/preview-docs/preview-server/server.d.ts +3 -1
  33. package/lib/commands/preview-docs/preview-server/server.js +2 -2
  34. package/lib/commands/push.d.ts +2 -2
  35. package/lib/commands/push.js +31 -31
  36. package/lib/commands/split/__tests__/index.test.js +9 -9
  37. package/lib/commands/split/index.d.ts +2 -2
  38. package/lib/commands/split/index.js +41 -40
  39. package/lib/commands/split/types.d.ts +2 -2
  40. package/lib/commands/split/types.js +2 -2
  41. package/lib/commands/stats.d.ts +1 -1
  42. package/lib/commands/stats.js +9 -9
  43. package/lib/fetch-with-timeout.js +5 -2
  44. package/lib/index.js +11 -12
  45. package/lib/types.d.ts +6 -6
  46. package/lib/update-version-notifier.js +18 -18
  47. package/lib/utils.d.ts +6 -3
  48. package/lib/utils.js +66 -38
  49. package/lib/wrapper.js +5 -5
  50. package/package.json +4 -3
  51. package/src/__mocks__/@redocly/openapi-core.ts +1 -0
  52. package/src/__mocks__/utils.ts +2 -0
  53. package/src/__tests__/commands/join.test.ts +37 -7
  54. package/src/__tests__/utils.test.ts +45 -1
  55. package/src/commands/join.ts +8 -3
  56. package/src/commands/preview-docs/preview-server/hot.js +19 -2
  57. package/src/commands/preview-docs/preview-server/preview-server.ts +6 -4
  58. package/src/commands/preview-docs/preview-server/server.ts +2 -2
  59. package/src/commands/split/__tests__/index.test.ts +14 -5
  60. package/src/commands/split/index.ts +25 -17
  61. package/src/fetch-with-timeout.ts +3 -0
  62. package/src/index.ts +0 -1
  63. package/src/utils.ts +40 -1
  64. package/tsconfig.tsbuildinfo +1 -1
@@ -3,12 +3,13 @@ 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';
6
7
 
7
8
  const utils = require('../../../utils');
8
9
 
9
10
  jest.mock('../../../utils', () => ({
10
11
  ...jest.requireActual('../../../utils'),
11
- writeYaml: jest.fn(),
12
+ writeToFileByExtension: jest.fn(),
12
13
  }));
13
14
 
14
15
  jest.mock('@redocly/openapi-core', () => ({
@@ -65,7 +66,9 @@ describe('#split', () => {
65
66
  openapiDir,
66
67
  path.join(openapiDir, 'paths'),
67
68
  componentsFiles,
68
- '_'
69
+ '_',
70
+ undefined,
71
+ 'yaml'
69
72
  );
70
73
 
71
74
  expect(openapiCore.slash).toHaveBeenCalledWith('paths/test.yaml');
@@ -82,7 +85,9 @@ describe('#split', () => {
82
85
  openapiDir,
83
86
  path.join(openapiDir, 'webhooks'),
84
87
  componentsFiles,
85
- 'webhook_'
88
+ 'webhook_',
89
+ undefined,
90
+ 'yaml'
86
91
  );
87
92
 
88
93
  expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
@@ -99,7 +104,9 @@ describe('#split', () => {
99
104
  openapiDir,
100
105
  path.join(openapiDir, 'webhooks'),
101
106
  componentsFiles,
102
- 'webhook_'
107
+ 'webhook_',
108
+ undefined,
109
+ 'yaml'
103
110
  );
104
111
 
105
112
  expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
@@ -118,7 +125,9 @@ describe('#split', () => {
118
125
  openapiDir,
119
126
  path.join(openapiDir, 'paths'),
120
127
  componentsFiles,
121
- '_'
128
+ '_',
129
+ undefined,
130
+ 'yaml'
122
131
  );
123
132
 
124
133
  expect(utils.escapeLanguageName).nthCalledWith(1, 'C#');
@@ -10,10 +10,11 @@ import {
10
10
  printExecutionTime,
11
11
  pathToFilename,
12
12
  readYaml,
13
- writeYaml,
14
13
  exitWithError,
15
14
  escapeLanguageName,
16
15
  langToExt,
16
+ writeToFileByExtension,
17
+ getAndValidateFileExtension,
17
18
  } from '../../utils';
18
19
  import { isString, isObject, isEmptyObject } from '../../js-utils';
19
20
  import {
@@ -46,8 +47,9 @@ export async function handleSplit(argv: SplitOptions) {
46
47
  const startedAt = performance.now();
47
48
  const { api, outDir, separator } = argv;
48
49
  validateDefinitionFileName(api!);
50
+ const ext = getAndValidateFileExtension(api);
49
51
  const openapi = readYaml(api!) as Oas3Definition | Oas3_1Definition;
50
- splitDefinition(openapi, outDir, separator);
52
+ splitDefinition(openapi, outDir, separator, ext);
51
53
  process.stderr.write(
52
54
  `🪓 Document: ${blue(api!)} ${green('is successfully split')}
53
55
  and all related files are saved to the directory: ${blue(outDir)} \n`
@@ -58,18 +60,21 @@ export async function handleSplit(argv: SplitOptions) {
58
60
  function splitDefinition(
59
61
  openapi: Oas3Definition | Oas3_1Definition,
60
62
  openapiDir: string,
61
- pathSeparator: string
63
+ pathSeparator: string,
64
+ ext: string
62
65
  ) {
63
66
  fs.mkdirSync(openapiDir, { recursive: true });
64
67
 
65
68
  const componentsFiles: ComponentsFiles = {};
66
- iterateComponents(openapi, openapiDir, componentsFiles);
69
+ iterateComponents(openapi, openapiDir, componentsFiles, ext);
67
70
  iteratePathItems(
68
71
  openapi.paths,
69
72
  openapiDir,
70
73
  path.join(openapiDir, 'paths'),
71
74
  componentsFiles,
72
- pathSeparator
75
+ pathSeparator,
76
+ undefined,
77
+ ext
73
78
  );
74
79
  const webhooks =
75
80
  (openapi as Oas3_1Definition).webhooks || (openapi as Oas3Definition)['x-webhooks'];
@@ -80,11 +85,12 @@ function splitDefinition(
80
85
  path.join(openapiDir, 'webhooks'),
81
86
  componentsFiles,
82
87
  pathSeparator,
83
- 'webhook_'
88
+ 'webhook_',
89
+ ext
84
90
  );
85
91
 
86
92
  replace$Refs(openapi, openapiDir, componentsFiles);
87
- writeYaml(openapi, path.join(openapiDir, 'openapi.yaml'));
93
+ writeToFileByExtension(openapi, path.join(openapiDir, `openapi.${ext}`));
88
94
  }
89
95
 
90
96
  function isStartsWithComponents(node: string) {
@@ -135,7 +141,7 @@ function traverseDirectoryDeepCallback(
135
141
  if (isNotYaml(filename)) return;
136
142
  const pathData = readYaml(filename);
137
143
  replace$Refs(pathData, directory, componentsFiles);
138
- writeYaml(pathData, filename);
144
+ writeToFileByExtension(pathData, filename);
139
145
  }
140
146
 
141
147
  function crawl(object: any, visitor: any) {
@@ -251,8 +257,8 @@ function extractFileNameFromPath(filename: string) {
251
257
  return path.basename(filename, path.extname(filename));
252
258
  }
253
259
 
254
- function getFileNamePath(componentDirPath: string, componentName: string) {
255
- return path.join(componentDirPath, componentName) + '.yaml';
260
+ function getFileNamePath(componentDirPath: string, componentName: string, ext: string) {
261
+ return path.join(componentDirPath, componentName) + `.${ext}`;
256
262
  }
257
263
 
258
264
  function gatherComponentsFiles(
@@ -278,13 +284,14 @@ function iteratePathItems(
278
284
  outDir: string,
279
285
  componentsFiles: object,
280
286
  pathSeparator: string,
281
- codeSamplesPathPrefix: string = ''
287
+ codeSamplesPathPrefix: string = '',
288
+ ext: string
282
289
  ) {
283
290
  if (!pathItems) return;
284
291
  fs.mkdirSync(outDir, { recursive: true });
285
292
 
286
293
  for (const pathName of Object.keys(pathItems)) {
287
- const pathFile = `${path.join(outDir, pathToFilename(pathName, pathSeparator))}.yaml`;
294
+ const pathFile = `${path.join(outDir, pathToFilename(pathName, pathSeparator))}.${ext}`;
288
295
  const pathData = pathItems[pathName] as Oas3PathItem;
289
296
 
290
297
  if (isRef(pathData)) continue;
@@ -314,7 +321,7 @@ function iteratePathItems(
314
321
  };
315
322
  }
316
323
  }
317
- writeYaml(pathData, pathFile);
324
+ writeToFileByExtension(pathData, pathFile);
318
325
  pathItems[pathName] = {
319
326
  $ref: slash(path.relative(openapiDir, pathFile)),
320
327
  };
@@ -326,7 +333,8 @@ function iteratePathItems(
326
333
  function iterateComponents(
327
334
  openapi: Oas3Definition | Oas3_1Definition,
328
335
  openapiDir: string,
329
- componentsFiles: ComponentsFiles
336
+ componentsFiles: ComponentsFiles,
337
+ ext: string
330
338
  ) {
331
339
  const { components } = openapi;
332
340
  if (components) {
@@ -340,7 +348,7 @@ function iterateComponents(
340
348
  function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) {
341
349
  const componentDirPath = path.join(componentsDir, componentType);
342
350
  for (const componentName of Object.keys(components?.[componentType] || {})) {
343
- const filename = getFileNamePath(componentDirPath, componentName);
351
+ const filename = getFileNamePath(componentDirPath, componentName, ext);
344
352
  gatherComponentsFiles(components!, componentsFiles, componentType, componentName, filename);
345
353
  }
346
354
  }
@@ -350,7 +358,7 @@ function iterateComponents(
350
358
  const componentDirPath = path.join(componentsDir, componentType);
351
359
  createComponentDir(componentDirPath, componentType);
352
360
  for (const componentName of Object.keys(components?.[componentType] || {})) {
353
- const filename = getFileNamePath(componentDirPath, componentName);
361
+ const filename = getFileNamePath(componentDirPath, componentName, ext);
354
362
  const componentData = components?.[componentType]?.[componentName];
355
363
  replace$Refs(componentData, path.dirname(filename), componentsFiles);
356
364
  implicitlyReferenceDiscriminator(
@@ -369,7 +377,7 @@ function iterateComponents(
369
377
  )
370
378
  );
371
379
  } else {
372
- writeYaml(componentData, filename);
380
+ writeToFileByExtension(componentData, filename);
373
381
  }
374
382
 
375
383
  if (isNotSecurityComponentType(componentType)) {
@@ -12,6 +12,9 @@ export default async (url: string, options = {}) => {
12
12
  controller.abort();
13
13
  }, TIMEOUT);
14
14
 
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
15
18
  const res = await nodeFetch(url, { signal: controller.signal, ...options });
16
19
  clearTimeout(timeout);
17
20
  return res;
package/src/index.ts CHANGED
@@ -127,7 +127,6 @@ yargs
127
127
  describe: 'Output file',
128
128
  alias: 'o',
129
129
  type: 'string',
130
- default: 'openapi.yaml',
131
130
  },
132
131
  config: {
133
132
  description: 'Path to the config file.',
package/src/utils.ts CHANGED
@@ -25,7 +25,14 @@ import {
25
25
  RedoclyClient,
26
26
  } from '@redocly/openapi-core';
27
27
  import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
28
- import { Totals, outputExtensions, Entrypoint, ConfigApis, CommandOptions } from './types';
28
+ import {
29
+ Totals,
30
+ outputExtensions,
31
+ Entrypoint,
32
+ ConfigApis,
33
+ CommandOptions,
34
+ OutputExtensions,
35
+ } from './types';
29
36
  import { isEmptyObject } from '@redocly/openapi-core/lib/utils';
30
37
  import { Arguments } from 'yargs';
31
38
  import { version } from './update-version-notifier';
@@ -209,6 +216,17 @@ export function readYaml(filename: string) {
209
216
  return parseYaml(fs.readFileSync(filename, 'utf-8'), { filename });
210
217
  }
211
218
 
219
+ export function writeToFileByExtension(data: unknown, filePath: string, noRefs?: boolean) {
220
+ const ext = getAndValidateFileExtension(filePath);
221
+
222
+ if (ext === 'json') {
223
+ writeJson(data, filePath);
224
+ return;
225
+ }
226
+
227
+ writeYaml(data, filePath, noRefs);
228
+ }
229
+
212
230
  export function writeYaml(data: any, filename: string, noRefs = false) {
213
231
  const content = stringifyYaml(data, { noRefs });
214
232
 
@@ -220,6 +238,27 @@ export function writeYaml(data: any, filename: string, noRefs = false) {
220
238
  fs.writeFileSync(filename, content);
221
239
  }
222
240
 
241
+ export function writeJson(data: unknown, filename: string) {
242
+ const content = JSON.stringify(data, null, 2);
243
+
244
+ if (process.env.NODE_ENV === 'test') {
245
+ process.stderr.write(content);
246
+ return;
247
+ }
248
+ fs.mkdirSync(dirname(filename), { recursive: true });
249
+ fs.writeFileSync(filename, content);
250
+ }
251
+
252
+ export function getAndValidateFileExtension(fileName: string): NonNullable<OutputExtensions> {
253
+ const ext = fileName.split('.').pop();
254
+
255
+ if (['yaml', 'yml', 'json'].includes(ext!)) {
256
+ return ext as NonNullable<OutputExtensions>;
257
+ }
258
+ process.stderr.write(yellow(`Unsupported file extension: ${ext}. Using yaml.\n`));
259
+ return 'yaml';
260
+ }
261
+
223
262
  export function pluralize(label: string, num: number) {
224
263
  if (label.endsWith('is')) {
225
264
  [label] = label.split(' ');