@redocly/cli 1.12.2 → 1.14.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @redocly/cli
2
2
 
3
+ ## 1.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added the ability to exclude some operations or entire paths from the `security-defined` rule.
8
+
9
+ ### Patch Changes
10
+
11
+ - Improved error messages.
12
+ - Updated @redocly/openapi-core to v1.14.0.
13
+
14
+ ## 1.13.0
15
+
16
+ ### Minor Changes
17
+
18
+ - Added support for the linting command to output markdown format.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated @redocly/openapi-core to v1.13.0.
23
+
3
24
  ## 1.12.2
4
25
 
5
26
  ### Patch Changes
@@ -22,7 +22,7 @@ describe('handleJoin', () => {
22
22
  colloreteYellowMock.mockImplementation((string) => string);
23
23
  it('should call exitWithError because only one entrypoint', () => __awaiter(void 0, void 0, void 0, function* () {
24
24
  yield (0, join_1.handleJoin)({ apis: ['first.yaml'] }, {}, 'cli-version');
25
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided. \n\n`);
25
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided.`);
26
26
  }));
27
27
  it('should call exitWithError because passed all 3 options for tags', () => __awaiter(void 0, void 0, void 0, function* () {
28
28
  yield (0, join_1.handleJoin)({
@@ -31,7 +31,7 @@ describe('handleJoin', () => {
31
31
  'without-x-tag-groups': true,
32
32
  'prefix-tags-with-filename': true,
33
33
  }, {}, 'cli-version');
34
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`You use prefix-tags-with-filename, prefix-tags-with-info-prop, without-x-tag-groups together.\nPlease choose only one! \n\n`);
34
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`You use prefix-tags-with-filename, prefix-tags-with-info-prop, without-x-tag-groups together.\nPlease choose only one!`);
35
35
  }));
36
36
  it('should call exitWithError because passed all 2 options for tags', () => __awaiter(void 0, void 0, void 0, function* () {
37
37
  yield (0, join_1.handleJoin)({
@@ -39,13 +39,13 @@ describe('handleJoin', () => {
39
39
  'without-x-tag-groups': true,
40
40
  'prefix-tags-with-filename': true,
41
41
  }, {}, 'cli-version');
42
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`You use prefix-tags-with-filename, without-x-tag-groups together.\nPlease choose only one! \n\n`);
42
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`You use prefix-tags-with-filename, without-x-tag-groups together.\nPlease choose only one!`);
43
43
  }));
44
44
  it('should call exitWithError because Only OpenAPI 3.0 and OpenAPI 3.1 are supported', () => __awaiter(void 0, void 0, void 0, function* () {
45
45
  yield (0, join_1.handleJoin)({
46
46
  apis: ['first.yaml', 'second.yaml'],
47
47
  }, config_1.ConfigFixture, 'cli-version');
48
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('Only OpenAPI 3.0 and OpenAPI 3.1 are supported: undefined \n\n');
48
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('Only OpenAPI 3.0 and OpenAPI 3.1 are supported: undefined.');
49
49
  }));
50
50
  it('should call exitWithError if mixing OpenAPI 3.0 and 3.1', () => __awaiter(void 0, void 0, void 0, function* () {
51
51
  openapi_core_1.detectSpec
@@ -54,7 +54,7 @@ describe('handleJoin', () => {
54
54
  yield (0, join_1.handleJoin)({
55
55
  apis: ['first.yaml', 'second.yaml'],
56
56
  }, config_1.ConfigFixture, 'cli-version');
57
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('All APIs must use the same OpenAPI version: undefined \n\n');
57
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('All APIs must use the same OpenAPI version: undefined.');
58
58
  }));
59
59
  it('should call writeToFileByExtension function', () => __awaiter(void 0, void 0, void 0, function* () {
60
60
  openapi_core_1.detectSpec.mockReturnValue('oas3_0');
@@ -48,7 +48,7 @@ describe('handleLint', () => {
48
48
  describe('loadConfig and getEnrtypoints stage', () => {
49
49
  it('should fail if config file does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
50
50
  yield (0, wrapper_1.commandWrapper)(lint_1.handleLint)(Object.assign(Object.assign({}, argvMock), { config: 'config.yaml' }));
51
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('Please, provide valid path to the configuration file');
51
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('Please provide a valid path to the configuration file.');
52
52
  }));
53
53
  it('should call loadConfigAndHandleErrors and getFallbackApisOrExit', () => __awaiter(void 0, void 0, void 0, function* () {
54
54
  yield (0, wrapper_1.commandWrapper)(lint_1.handleLint)(argvMock);
@@ -78,7 +78,7 @@ describe('handleLint', () => {
78
78
  it('should fail if apis not provided', () => __awaiter(void 0, void 0, void 0, function* () {
79
79
  yield (0, wrapper_1.commandWrapper)(lint_1.handleLint)(Object.assign(Object.assign({}, argvMock), { apis: [] }));
80
80
  expect(miscellaneous_1.getFallbackApisOrExit).toHaveBeenCalledTimes(1);
81
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('No APIs were provided');
81
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith('No APIs were provided.');
82
82
  }));
83
83
  });
84
84
  describe('loop through entrypoints and lint stage', () => {
@@ -186,7 +186,7 @@ describe('push', () => {
186
186
  'job-id': '123',
187
187
  'batch-size': 2,
188
188
  }, mockConfig);
189
- expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`Destination argument value is not valid, please use the right format: ${(0, colorette_1.yellow)('<api-name@api-version>')}`);
189
+ expect(miscellaneous_1.exitWithError).toHaveBeenCalledWith(`Destination argument value is not valid, please use the right format: ${(0, colorette_1.yellow)('<api-name@api-version>')}.`);
190
190
  }));
191
191
  it('push should work and encode name with spaces', () => __awaiter(void 0, void 0, void 0, function* () {
192
192
  const encodeURIComponentSpy = jest.spyOn(global, 'encodeURIComponent');
@@ -25,7 +25,7 @@ function handlePush(argv, config) {
25
25
  const { organization, project: projectId, 'mount-path': mountPath, verbose } = argv;
26
26
  const orgId = organization || config.organization;
27
27
  if (!argv.message || !argv.author || !argv.branch) {
28
- (0, miscellaneous_1.exitWithError)('Error: message, author and branch are required for push to the CMS');
28
+ (0, miscellaneous_1.exitWithError)('Error: message, author and branch are required for push to the CMS.');
29
29
  }
30
30
  if (!orgId) {
31
31
  return (0, miscellaneous_1.exitWithError)(`No organization provided, please use --organization option or specify the 'organization' field in the config file.`);
@@ -26,7 +26,7 @@ function handleJoin(argv, config, packageVersion) {
26
26
  return __awaiter(this, void 0, void 0, function* () {
27
27
  const startedAt = perf_hooks_1.performance.now();
28
28
  if (argv.apis.length < 2) {
29
- return (0, miscellaneous_1.exitWithError)(`At least 2 apis should be provided. \n\n`);
29
+ return (0, miscellaneous_1.exitWithError)(`At least 2 apis should be provided.`);
30
30
  }
31
31
  const fileExtension = (0, miscellaneous_1.getAndValidateFileExtension)(argv.output || argv.apis[0]);
32
32
  const { 'prefix-components-with-info-prop': prefixComponentsWithInfoProp, 'prefix-tags-with-filename': prefixTagsWithFilename, 'prefix-tags-with-info-prop': prefixTagsWithInfoProp, 'without-x-tag-groups': withoutXTagGroups, output: specFilename = `openapi.${fileExtension}`, } = argv;
@@ -36,7 +36,7 @@ function handleJoin(argv, config, packageVersion) {
36
36
  withoutXTagGroups && 'without-x-tag-groups',
37
37
  ].filter(Boolean);
38
38
  if (usedTagsOptions.length > 1) {
39
- return (0, miscellaneous_1.exitWithError)(`You use ${(0, colorette_1.yellow)(usedTagsOptions.join(', '))} together.\nPlease choose only one! \n\n`);
39
+ return (0, miscellaneous_1.exitWithError)(`You use ${(0, colorette_1.yellow)(usedTagsOptions.join(', '))} together.\nPlease choose only one!`);
40
40
  }
41
41
  const apis = yield (0, miscellaneous_1.getFallbackApisOrExit)(argv.apis, config);
42
42
  const externalRefResolver = new openapi_core_1.BaseResolver(config.resolve);
@@ -67,7 +67,7 @@ function handleJoin(argv, config, packageVersion) {
67
67
  totals: fileTotals,
68
68
  version: packageVersion,
69
69
  });
70
- (0, miscellaneous_1.exitWithError)(`❌ Errors encountered while bundling ${(0, colorette_1.blue)(document.source.absoluteRef)}: join will not proceed.\n`);
70
+ (0, miscellaneous_1.exitWithError)(`❌ Errors encountered while bundling ${(0, colorette_1.blue)(document.source.absoluteRef)}: join will not proceed.`);
71
71
  }
72
72
  }
73
73
  let oasVersion = null;
@@ -75,15 +75,15 @@ function handleJoin(argv, config, packageVersion) {
75
75
  try {
76
76
  const version = (0, openapi_core_1.detectSpec)(document.parsed);
77
77
  if (version !== openapi_core_1.SpecVersion.OAS3_0 && version !== openapi_core_1.SpecVersion.OAS3_1) {
78
- return (0, miscellaneous_1.exitWithError)(`Only OpenAPI 3.0 and OpenAPI 3.1 are supported: ${(0, colorette_1.blue)(document.source.absoluteRef)} \n\n`);
78
+ return (0, miscellaneous_1.exitWithError)(`Only OpenAPI 3.0 and OpenAPI 3.1 are supported: ${(0, colorette_1.blue)(document.source.absoluteRef)}.`);
79
79
  }
80
80
  oasVersion = oasVersion !== null && oasVersion !== void 0 ? oasVersion : version;
81
81
  if (oasVersion !== version) {
82
- return (0, miscellaneous_1.exitWithError)(`All APIs must use the same OpenAPI version: ${(0, colorette_1.blue)(document.source.absoluteRef)} \n\n`);
82
+ return (0, miscellaneous_1.exitWithError)(`All APIs must use the same OpenAPI version: ${(0, colorette_1.blue)(document.source.absoluteRef)}.`);
83
83
  }
84
84
  }
85
85
  catch (e) {
86
- return (0, miscellaneous_1.exitWithError)(`${e.message}: ${(0, colorette_1.blue)(document.source.absoluteRef)}`);
86
+ return (0, miscellaneous_1.exitWithError)(`${e.message}: ${(0, colorette_1.blue)(document.source.absoluteRef)}.`);
87
87
  }
88
88
  }
89
89
  const joinedDef = {};
@@ -276,7 +276,7 @@ function handleJoin(argv, config, packageVersion) {
276
276
  for (const pathServer of joinedDef.paths[path].servers) {
277
277
  if (pathServer.url === server.url) {
278
278
  if (!isServersEqual(pathServer, server)) {
279
- (0, miscellaneous_1.exitWithError)(`Different server values for (${server.url}) in ${path}`);
279
+ (0, miscellaneous_1.exitWithError)(`Different server values for (${server.url}) in ${path}.`);
280
280
  }
281
281
  isFoundServer = true;
282
282
  }
@@ -307,7 +307,7 @@ function handleJoin(argv, config, packageVersion) {
307
307
  if (!(0, openapi_core_1.isRef)(pathParameter) && !(0, openapi_core_1.isRef)(parameter)) {
308
308
  if (pathParameter.name === parameter.name && pathParameter.in === parameter.in) {
309
309
  if (!isEqual(pathParameter.schema, parameter.schema)) {
310
- (0, miscellaneous_1.exitWithError)(`Different parameter schemas for (${parameter.name}) in ${path}`);
310
+ (0, miscellaneous_1.exitWithError)(`Different parameter schemas for (${parameter.name}) in ${path}.`);
311
311
  }
312
312
  isFoundParameter = true;
313
313
  }
@@ -441,9 +441,9 @@ function handleJoin(argv, config, packageVersion) {
441
441
  const openapi = firstApi.parsed;
442
442
  const componentsPrefix = getInfoPrefix(openapi.info, prefixComponentsWithInfoProp, types_1.COMPONENTS);
443
443
  if (!openapi.openapi)
444
- (0, miscellaneous_1.exitWithError)('Version of specification is not found in. \n');
444
+ (0, miscellaneous_1.exitWithError)('Version of specification is not found.');
445
445
  if (!openapi.info)
446
- (0, miscellaneous_1.exitWithError)('Info section is not found in specification. \n');
446
+ (0, miscellaneous_1.exitWithError)('Info section is not found in specification.');
447
447
  if ((_a = openapi.info) === null || _a === void 0 ? void 0 : _a.description) {
448
448
  openapi.info.description = addComponentsPrefix(openapi.info.description, componentsPrefix);
449
449
  }
@@ -542,13 +542,13 @@ function getInfoPrefix(info, prefixArg, type) {
542
542
  if (!prefixArg)
543
543
  return '';
544
544
  if (!info)
545
- (0, miscellaneous_1.exitWithError)('Info section is not found in specification. \n');
545
+ (0, miscellaneous_1.exitWithError)('Info section is not found in specification.');
546
546
  if (!info[prefixArg])
547
- (0, miscellaneous_1.exitWithError)(`${(0, colorette_1.yellow)(`prefix-${type}-with-info-prop`)} argument value is not found in info section. \n`);
547
+ (0, miscellaneous_1.exitWithError)(`${(0, colorette_1.yellow)(`prefix-${type}-with-info-prop`)} argument value is not found in info section.`);
548
548
  if (!(0, js_utils_1.isString)(info[prefixArg]))
549
- (0, miscellaneous_1.exitWithError)(`${(0, colorette_1.yellow)(`prefix-${type}-with-info-prop`)} argument value should be string. \n\n`);
549
+ (0, miscellaneous_1.exitWithError)(`${(0, colorette_1.yellow)(`prefix-${type}-with-info-prop`)} argument value should be string.`);
550
550
  if (info[prefixArg].length > 50)
551
- (0, miscellaneous_1.exitWithError)(`${(0, colorette_1.yellow)(`prefix-${type}-with-info-prop`)} argument value length should not exceed 50 characters. \n\n`);
551
+ (0, miscellaneous_1.exitWithError)(`${(0, colorette_1.yellow)(`prefix-${type}-with-info-prop`)} argument value length should not exceed 50 characters.`);
552
552
  return info[prefixArg].replaceAll(/\s/g, '_');
553
553
  }
554
554
  function replace$Refs(obj, componentsPrefix) {
@@ -20,7 +20,7 @@ function handleLint(argv, config, version) {
20
20
  return __awaiter(this, void 0, void 0, function* () {
21
21
  const apis = yield (0, miscellaneous_1.getFallbackApisOrExit)(argv.apis, config);
22
22
  if (!apis.length) {
23
- (0, miscellaneous_1.exitWithError)('No APIs were provided');
23
+ (0, miscellaneous_1.exitWithError)('No APIs were provided.');
24
24
  }
25
25
  if (argv['generate-ignore-file']) {
26
26
  config.styleguide.ignore = {}; // clear ignore
@@ -53,7 +53,7 @@ function handlePush(argv, config) {
53
53
  const jobId = argv['job-id'];
54
54
  const batchSize = argv['batch-size'];
55
55
  if (destination && !exports.DESTINATION_REGEX.test(destination)) {
56
- (0, miscellaneous_1.exitWithError)(`Destination argument value is not valid, please use the right format: ${(0, colorette_1.yellow)('<api-name@api-version>')}`);
56
+ (0, miscellaneous_1.exitWithError)(`Destination argument value is not valid, please use the right format: ${(0, colorette_1.yellow)('<api-name@api-version>')}.`);
57
57
  }
58
58
  const destinationProps = getDestinationProps(destination, config.organization);
59
59
  const organizationId = argv.organization || destinationProps.organizationId;
@@ -108,7 +108,7 @@ function handlePush(argv, config) {
108
108
  const uploadResponse = yield uploadFileToS3(signedUploadUrl, file.contents || file.filePath);
109
109
  const fileCounter = `(${++uploaded}/${filesToUpload.files.length})`;
110
110
  if (!uploadResponse.ok) {
111
- (0, miscellaneous_1.exitWithError)(`✗ ${fileCounter}\nFile upload failed\n`);
111
+ (0, miscellaneous_1.exitWithError)(`✗ ${fileCounter}\nFile upload failed.`);
112
112
  }
113
113
  process.stdout.write((0, colorette_1.green)(`✓ ${fileCounter}\n`));
114
114
  }
@@ -128,14 +128,14 @@ function handlePush(argv, config) {
128
128
  }
129
129
  catch (error) {
130
130
  if (error.message === 'ORGANIZATION_NOT_FOUND') {
131
- (0, miscellaneous_1.exitWithError)(`Organization ${(0, colorette_1.blue)(organizationId)} not found`);
131
+ (0, miscellaneous_1.exitWithError)(`Organization ${(0, colorette_1.blue)(organizationId)} not found.`);
132
132
  }
133
133
  if (error.message === 'API_VERSION_NOT_FOUND') {
134
- (0, miscellaneous_1.exitWithError)(`The definition version ${(0, colorette_1.blue)(`${name}@${version}`)} does not exist in organization ${(0, colorette_1.blue)(organizationId)}!\n${(0, colorette_1.yellow)('Suggestion:')} please use ${(0, colorette_1.blue)('-u')} or ${(0, colorette_1.blue)('--upsert')} to create definition.\n\n`);
134
+ (0, miscellaneous_1.exitWithError)(`The definition version ${(0, colorette_1.blue)(`${name}@${version}`)} does not exist in organization ${(0, colorette_1.blue)(organizationId)}!\n${(0, colorette_1.yellow)('Suggestion:')} please use ${(0, colorette_1.blue)('-u')} or ${(0, colorette_1.blue)('--upsert')} to create definition.`);
135
135
  }
136
136
  throw error;
137
137
  }
138
- process.stdout.write(`Definition: ${(0, colorette_1.blue)(api)} is successfully pushed to Redocly API Registry \n`);
138
+ process.stdout.write(`Definition: ${(0, colorette_1.blue)(api)} is successfully pushed to Redocly API Registry.\n`);
139
139
  }
140
140
  (0, miscellaneous_1.printExecutionTime)('push', startedAt, api || `apis in organization ${organizationId}`);
141
141
  });
@@ -171,7 +171,7 @@ function collectFilesToUpload(api, config) {
171
171
  process.stdout.write(`Created a bundle for ${(0, colorette_1.blue)(api)} ${fileTotals.warnings > 0 ? 'with warnings' : ''}\n`);
172
172
  }
173
173
  else {
174
- (0, miscellaneous_1.exitWithError)(`Failed to create a bundle for ${(0, colorette_1.blue)(api)}\n`);
174
+ (0, miscellaneous_1.exitWithError)(`Failed to create a bundle for ${(0, colorette_1.blue)(api)}.`);
175
175
  }
176
176
  const fileExt = path.extname(apiPath).split('.').pop();
177
177
  files.push(getFileEntry(apiPath, (0, miscellaneous_1.dumpBundle)(openapiBundle.parsed, fileExt)));
@@ -61,12 +61,12 @@ function loadFile(fileName) {
61
61
  }
62
62
  function validateDefinitionFileName(fileName) {
63
63
  if (!fs.existsSync(fileName))
64
- (0, miscellaneous_1.exitWithError)(`File ${(0, colorette_1.blue)(fileName)} does not exist \n`);
64
+ (0, miscellaneous_1.exitWithError)(`File ${(0, colorette_1.blue)(fileName)} does not exist.`);
65
65
  const file = loadFile(fileName);
66
66
  if (file.swagger)
67
- (0, miscellaneous_1.exitWithError)('OpenAPI 2 is not supported by this command');
67
+ (0, miscellaneous_1.exitWithError)('OpenAPI 2 is not supported by this command.');
68
68
  if (!file.openapi)
69
- (0, miscellaneous_1.exitWithError)('File does not conform to the OpenAPI Specification. OpenAPI version is not specified');
69
+ (0, miscellaneous_1.exitWithError)('File does not conform to the OpenAPI Specification. OpenAPI version is not specified.');
70
70
  return true;
71
71
  }
72
72
  function traverseDirectoryDeep(directory, callback, componentsFiles) {
package/lib/index.js CHANGED
@@ -364,6 +364,7 @@ yargs
364
364
  'checkstyle',
365
365
  'codeclimate',
366
366
  'summary',
367
+ 'markdown',
367
368
  'github-actions',
368
369
  ],
369
370
  default: 'codeframe',
@@ -636,7 +637,7 @@ yargs
636
637
  },
637
638
  t: {
638
639
  alias: 'template',
639
- describe: 'Path to handlebars page template, see https://git.io/vh8fP for the example.',
640
+ describe: 'Path to handlebars page template, see https://github.com/Redocly/redocly-cli/blob/main/packages/cli/src/commands/build-docs/template.hbs for the example.',
640
641
  type: 'string',
641
642
  },
642
643
  templateOptions: {
package/lib/wrapper.js CHANGED
@@ -21,7 +21,7 @@ function commandWrapper(commandHandler) {
21
21
  let telemetry;
22
22
  try {
23
23
  if (argv.config && !(0, openapi_core_1.doesYamlFileExist)(argv.config)) {
24
- (0, miscellaneous_1.exitWithError)('Please, provide valid path to the configuration file');
24
+ (0, miscellaneous_1.exitWithError)('Please provide a valid path to the configuration file.');
25
25
  }
26
26
  const config = (yield (0, miscellaneous_1.loadConfigAndHandleErrors)({
27
27
  configPath: argv.config,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/cli",
3
- "version": "1.12.2",
3
+ "version": "1.14.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -36,7 +36,7 @@
36
36
  "Roman Hotsiy <roman@redoc.ly> (https://redoc.ly/)"
37
37
  ],
38
38
  "dependencies": {
39
- "@redocly/openapi-core": "1.12.2",
39
+ "@redocly/openapi-core": "1.14.0",
40
40
  "abort-controller": "^3.0.0",
41
41
  "chokidar": "^3.5.1",
42
42
  "colorette": "^1.2.0",
@@ -15,7 +15,7 @@ describe('handleJoin', () => {
15
15
 
16
16
  it('should call exitWithError because only one entrypoint', async () => {
17
17
  await handleJoin({ apis: ['first.yaml'] }, {} as any, 'cli-version');
18
- expect(exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided. \n\n`);
18
+ expect(exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided.`);
19
19
  });
20
20
 
21
21
  it('should call exitWithError because passed all 3 options for tags', async () => {
@@ -31,7 +31,7 @@ describe('handleJoin', () => {
31
31
  );
32
32
 
33
33
  expect(exitWithError).toHaveBeenCalledWith(
34
- `You use prefix-tags-with-filename, prefix-tags-with-info-prop, without-x-tag-groups together.\nPlease choose only one! \n\n`
34
+ `You use prefix-tags-with-filename, prefix-tags-with-info-prop, without-x-tag-groups together.\nPlease choose only one!`
35
35
  );
36
36
  });
37
37
 
@@ -47,7 +47,7 @@ describe('handleJoin', () => {
47
47
  );
48
48
 
49
49
  expect(exitWithError).toHaveBeenCalledWith(
50
- `You use prefix-tags-with-filename, without-x-tag-groups together.\nPlease choose only one! \n\n`
50
+ `You use prefix-tags-with-filename, without-x-tag-groups together.\nPlease choose only one!`
51
51
  );
52
52
  });
53
53
 
@@ -60,7 +60,7 @@ describe('handleJoin', () => {
60
60
  'cli-version'
61
61
  );
62
62
  expect(exitWithError).toHaveBeenCalledWith(
63
- 'Only OpenAPI 3.0 and OpenAPI 3.1 are supported: undefined \n\n'
63
+ 'Only OpenAPI 3.0 and OpenAPI 3.1 are supported: undefined.'
64
64
  );
65
65
  });
66
66
 
@@ -77,7 +77,7 @@ describe('handleJoin', () => {
77
77
  );
78
78
 
79
79
  expect(exitWithError).toHaveBeenCalledWith(
80
- 'All APIs must use the same OpenAPI version: undefined \n\n'
80
+ 'All APIs must use the same OpenAPI version: undefined.'
81
81
  );
82
82
  });
83
83
 
@@ -62,7 +62,7 @@ describe('handleLint', () => {
62
62
  it('should fail if config file does not exist', async () => {
63
63
  await commandWrapper(handleLint)({ ...argvMock, config: 'config.yaml' });
64
64
  expect(exitWithError).toHaveBeenCalledWith(
65
- 'Please, provide valid path to the configuration file'
65
+ 'Please provide a valid path to the configuration file.'
66
66
  );
67
67
  });
68
68
 
@@ -102,7 +102,7 @@ describe('handleLint', () => {
102
102
  it('should fail if apis not provided', async () => {
103
103
  await commandWrapper(handleLint)({ ...argvMock, apis: [] });
104
104
  expect(getFallbackApisOrExit).toHaveBeenCalledTimes(1);
105
- expect(exitWithError).toHaveBeenCalledWith('No APIs were provided');
105
+ expect(exitWithError).toHaveBeenCalledWith('No APIs were provided.');
106
106
  });
107
107
  });
108
108
 
@@ -248,7 +248,7 @@ describe('push', () => {
248
248
  expect(exitWithError).toHaveBeenCalledWith(
249
249
  `Destination argument value is not valid, please use the right format: ${yellow(
250
250
  '<api-name@api-version>'
251
- )}`
251
+ )}.`
252
252
  );
253
253
  });
254
254
 
@@ -51,7 +51,7 @@ export async function handlePush(
51
51
  const orgId = organization || config.organization;
52
52
 
53
53
  if (!argv.message || !argv.author || !argv.branch) {
54
- exitWithError('Error: message, author and branch are required for push to the CMS');
54
+ exitWithError('Error: message, author and branch are required for push to the CMS.');
55
55
  }
56
56
 
57
57
  if (!orgId) {
@@ -68,7 +68,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
68
68
  const startedAt = performance.now();
69
69
 
70
70
  if (argv.apis.length < 2) {
71
- return exitWithError(`At least 2 apis should be provided. \n\n`);
71
+ return exitWithError(`At least 2 apis should be provided.`);
72
72
  }
73
73
 
74
74
  const fileExtension = getAndValidateFileExtension(argv.output || argv.apis[0]);
@@ -89,7 +89,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
89
89
 
90
90
  if (usedTagsOptions.length > 1) {
91
91
  return exitWithError(
92
- `You use ${yellow(usedTagsOptions.join(', '))} together.\nPlease choose only one! \n\n`
92
+ `You use ${yellow(usedTagsOptions.join(', '))} together.\nPlease choose only one!`
93
93
  );
94
94
  }
95
95
 
@@ -137,7 +137,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
137
137
  exitWithError(
138
138
  `❌ Errors encountered while bundling ${blue(
139
139
  document.source.absoluteRef
140
- )}: join will not proceed.\n`
140
+ )}: join will not proceed.`
141
141
  );
142
142
  }
143
143
  }
@@ -148,20 +148,18 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
148
148
  const version = detectSpec(document.parsed);
149
149
  if (version !== SpecVersion.OAS3_0 && version !== SpecVersion.OAS3_1) {
150
150
  return exitWithError(
151
- `Only OpenAPI 3.0 and OpenAPI 3.1 are supported: ${blue(
152
- document.source.absoluteRef
153
- )} \n\n`
151
+ `Only OpenAPI 3.0 and OpenAPI 3.1 are supported: ${blue(document.source.absoluteRef)}.`
154
152
  );
155
153
  }
156
154
 
157
155
  oasVersion = oasVersion ?? version;
158
156
  if (oasVersion !== version) {
159
157
  return exitWithError(
160
- `All APIs must use the same OpenAPI version: ${blue(document.source.absoluteRef)} \n\n`
158
+ `All APIs must use the same OpenAPI version: ${blue(document.source.absoluteRef)}.`
161
159
  );
162
160
  }
163
161
  } catch (e) {
164
- return exitWithError(`${e.message}: ${blue(document.source.absoluteRef)}`);
162
+ return exitWithError(`${e.message}: ${blue(document.source.absoluteRef)}.`);
165
163
  }
166
164
  }
167
165
 
@@ -417,7 +415,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
417
415
  for (const pathServer of joinedDef.paths[path].servers) {
418
416
  if (pathServer.url === server.url) {
419
417
  if (!isServersEqual(pathServer, server)) {
420
- exitWithError(`Different server values for (${server.url}) in ${path}`);
418
+ exitWithError(`Different server values for (${server.url}) in ${path}.`);
421
419
  }
422
420
  isFoundServer = true;
423
421
  }
@@ -452,7 +450,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
452
450
  if (!isRef(pathParameter) && !isRef(parameter)) {
453
451
  if (pathParameter.name === parameter.name && pathParameter.in === parameter.in) {
454
452
  if (!isEqual(pathParameter.schema, parameter.schema)) {
455
- exitWithError(`Different parameter schemas for (${parameter.name}) in ${path}`);
453
+ exitWithError(`Different parameter schemas for (${parameter.name}) in ${path}.`);
456
454
  }
457
455
  isFoundParameter = true;
458
456
  }
@@ -627,8 +625,8 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
627
625
  const firstApi = documents[0];
628
626
  const openapi = firstApi.parsed;
629
627
  const componentsPrefix = getInfoPrefix(openapi.info, prefixComponentsWithInfoProp, COMPONENTS);
630
- if (!openapi.openapi) exitWithError('Version of specification is not found in. \n');
631
- if (!openapi.info) exitWithError('Info section is not found in specification. \n');
628
+ if (!openapi.openapi) exitWithError('Version of specification is not found.');
629
+ if (!openapi.info) exitWithError('Info section is not found in specification.');
632
630
  if (openapi.info?.description) {
633
631
  openapi.info.description = addComponentsPrefix(openapi.info.description, componentsPrefix);
634
632
  }
@@ -749,20 +747,18 @@ function addSecurityPrefix(security: any, componentsPrefix: string) {
749
747
 
750
748
  function getInfoPrefix(info: any, prefixArg: string | undefined, type: string) {
751
749
  if (!prefixArg) return '';
752
- if (!info) exitWithError('Info section is not found in specification. \n');
750
+ if (!info) exitWithError('Info section is not found in specification.');
753
751
  if (!info[prefixArg])
754
752
  exitWithError(
755
- `${yellow(`prefix-${type}-with-info-prop`)} argument value is not found in info section. \n`
753
+ `${yellow(`prefix-${type}-with-info-prop`)} argument value is not found in info section.`
756
754
  );
757
755
  if (!isString(info[prefixArg]))
758
- exitWithError(
759
- `${yellow(`prefix-${type}-with-info-prop`)} argument value should be string. \n\n`
760
- );
756
+ exitWithError(`${yellow(`prefix-${type}-with-info-prop`)} argument value should be string.`);
761
757
  if (info[prefixArg].length > 50)
762
758
  exitWithError(
763
759
  `${yellow(
764
760
  `prefix-${type}-with-info-prop`
765
- )} argument value length should not exceed 50 characters. \n\n`
761
+ )} argument value length should not exceed 50 characters.`
766
762
  );
767
763
  return info[prefixArg].replaceAll(/\s/g, '_');
768
764
  }
@@ -41,7 +41,7 @@ export async function handleLint(argv: LintOptions, config: Config, version: str
41
41
  const apis = await getFallbackApisOrExit(argv.apis, config);
42
42
 
43
43
  if (!apis.length) {
44
- exitWithError('No APIs were provided');
44
+ exitWithError('No APIs were provided.');
45
45
  }
46
46
 
47
47
  if (argv['generate-ignore-file']) {
@@ -76,7 +76,7 @@ export async function handlePush(argv: PushOptions, config: Config): Promise<voi
76
76
  exitWithError(
77
77
  `Destination argument value is not valid, please use the right format: ${yellow(
78
78
  '<api-name@api-version>'
79
- )}`
79
+ )}.`
80
80
  );
81
81
  }
82
82
 
@@ -176,7 +176,7 @@ export async function handlePush(argv: PushOptions, config: Config): Promise<voi
176
176
  const fileCounter = `(${++uploaded}/${filesToUpload.files.length})`;
177
177
 
178
178
  if (!uploadResponse.ok) {
179
- exitWithError(`✗ ${fileCounter}\nFile upload failed\n`);
179
+ exitWithError(`✗ ${fileCounter}\nFile upload failed.`);
180
180
  }
181
181
 
182
182
  process.stdout.write(green(`✓ ${fileCounter}\n`));
@@ -198,7 +198,7 @@ export async function handlePush(argv: PushOptions, config: Config): Promise<voi
198
198
  });
199
199
  } catch (error) {
200
200
  if (error.message === 'ORGANIZATION_NOT_FOUND') {
201
- exitWithError(`Organization ${blue(organizationId)} not found`);
201
+ exitWithError(`Organization ${blue(organizationId)} not found.`);
202
202
  }
203
203
 
204
204
  if (error.message === 'API_VERSION_NOT_FOUND') {
@@ -207,7 +207,7 @@ export async function handlePush(argv: PushOptions, config: Config): Promise<voi
207
207
  `${name}@${version}`
208
208
  )} does not exist in organization ${blue(organizationId)}!\n${yellow(
209
209
  'Suggestion:'
210
- )} please use ${blue('-u')} or ${blue('--upsert')} to create definition.\n\n`
210
+ )} please use ${blue('-u')} or ${blue('--upsert')} to create definition.`
211
211
  );
212
212
  }
213
213
 
@@ -215,7 +215,7 @@ export async function handlePush(argv: PushOptions, config: Config): Promise<voi
215
215
  }
216
216
 
217
217
  process.stdout.write(
218
- `Definition: ${blue(api!)} is successfully pushed to Redocly API Registry \n`
218
+ `Definition: ${blue(api!)} is successfully pushed to Redocly API Registry.\n`
219
219
  );
220
220
  }
221
221
  printExecutionTime('push', startedAt, api || `apis in organization ${organizationId}`);
@@ -254,7 +254,7 @@ async function collectFilesToUpload(api: string, config: Config) {
254
254
  `Created a bundle for ${blue(api)} ${fileTotals.warnings > 0 ? 'with warnings' : ''}\n`
255
255
  );
256
256
  } else {
257
- exitWithError(`Failed to create a bundle for ${blue(api)}\n`);
257
+ exitWithError(`Failed to create a bundle for ${blue(api)}.`);
258
258
  }
259
259
 
260
260
  const fileExt = path.extname(apiPath).split('.').pop();
@@ -111,12 +111,13 @@ function loadFile(fileName: string) {
111
111
  }
112
112
 
113
113
  function validateDefinitionFileName(fileName: string) {
114
- if (!fs.existsSync(fileName)) exitWithError(`File ${blue(fileName)} does not exist \n`);
114
+ if (!fs.existsSync(fileName)) exitWithError(`File ${blue(fileName)} does not exist.`);
115
115
  const file = loadFile(fileName);
116
- if ((file as Oas2Definition).swagger) exitWithError('OpenAPI 2 is not supported by this command');
116
+ if ((file as Oas2Definition).swagger)
117
+ exitWithError('OpenAPI 2 is not supported by this command.');
117
118
  if (!(file as Oas3Definition | Oas3_1Definition).openapi)
118
119
  exitWithError(
119
- 'File does not conform to the OpenAPI Specification. OpenAPI version is not specified'
120
+ 'File does not conform to the OpenAPI Specification. OpenAPI version is not specified.'
120
121
  );
121
122
  return true;
122
123
  }
package/src/index.ts CHANGED
@@ -408,6 +408,7 @@ yargs
408
408
  'checkstyle',
409
409
  'codeclimate',
410
410
  'summary',
411
+ 'markdown',
411
412
  'github-actions',
412
413
  ] as ReadonlyArray<OutputFormat>,
413
414
  default: 'codeframe' as OutputFormat,
@@ -725,7 +726,8 @@ yargs
725
726
  },
726
727
  t: {
727
728
  alias: 'template',
728
- describe: 'Path to handlebars page template, see https://git.io/vh8fP for the example.',
729
+ describe:
730
+ 'Path to handlebars page template, see https://github.com/Redocly/redocly-cli/blob/main/packages/cli/src/commands/build-docs/template.hbs for the example.',
729
731
  type: 'string',
730
732
  },
731
733
  templateOptions: {
package/src/wrapper.ts CHANGED
@@ -19,7 +19,7 @@ export function commandWrapper<T extends CommandOptions>(
19
19
  let telemetry;
20
20
  try {
21
21
  if (argv.config && !doesYamlFileExist(argv.config)) {
22
- exitWithError('Please, provide valid path to the configuration file');
22
+ exitWithError('Please provide a valid path to the configuration file.');
23
23
  }
24
24
  const config: Config = (await loadConfigAndHandleErrors({
25
25
  configPath: argv.config,