@redocly/cli 1.10.5 → 1.11.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.
@@ -1,4 +1,4 @@
1
- import { lint, bundle, getTotals, getMergedConfig } from '@redocly/openapi-core';
1
+ import { bundle, getTotals, getMergedConfig } from '@redocly/openapi-core';
2
2
 
3
3
  import { BundleOptions, handleBundle } from '../../commands/bundle';
4
4
  import { handleError } from '../../utils/miscellaneous';
@@ -25,21 +25,18 @@ describe('bundle', () => {
25
25
  });
26
26
 
27
27
  afterEach(() => {
28
- (lint as jest.Mock).mockClear();
29
28
  (bundle as jest.Mock).mockClear();
30
29
  (getTotals as jest.Mock).mockReset();
31
30
  });
32
31
 
33
- it('bundles definitions w/o linting', async () => {
32
+ it('bundles definitions', async () => {
34
33
  const apis = ['foo.yaml', 'bar.yaml'];
35
34
 
36
35
  await commandWrapper(handleBundle)({
37
36
  apis,
38
37
  ext: 'yaml',
39
- format: 'codeframe',
40
38
  } as Arguments<BundleOptions>);
41
39
 
42
- expect(lint).toBeCalledTimes(0);
43
40
  expect(bundle).toBeCalledTimes(apis.length);
44
41
  });
45
42
 
@@ -49,48 +46,25 @@ describe('bundle', () => {
49
46
  await commandWrapper(handleBundle)({
50
47
  apis,
51
48
  ext: 'yaml',
52
- format: 'codeframe',
53
49
  } as Arguments<BundleOptions>);
54
50
 
55
51
  await exitCb?.();
56
52
  expect(processExitMock).toHaveBeenCalledWith(0);
57
53
  });
58
54
 
59
- it('bundles definitions w/ linting', async () => {
60
- const apis = ['foo.yaml', 'bar.yaml', 'foobar.yaml'];
61
-
62
- (getTotals as jest.Mock).mockReturnValue({
63
- errors: 0,
64
- warnings: 0,
65
- ignored: 0,
66
- });
67
-
68
- await commandWrapper(handleBundle)({
69
- apis,
70
- ext: 'yaml',
71
- format: 'codeframe',
72
- lint: true,
73
- } as Arguments<BundleOptions>);
74
-
75
- expect(lint).toBeCalledTimes(apis.length);
76
- expect(bundle).toBeCalledTimes(apis.length);
77
- });
78
-
79
- it('exits with code 0 when bundles definitions w/linting w/o errors', async () => {
55
+ it('exits with code 0 when bundles definitions w/o errors', async () => {
80
56
  const apis = ['foo.yaml', 'bar.yaml', 'foobar.yaml'];
81
57
 
82
58
  await commandWrapper(handleBundle)({
83
59
  apis,
84
60
  ext: 'yaml',
85
- format: 'codeframe',
86
- lint: true,
87
61
  } as Arguments<BundleOptions>);
88
62
 
89
63
  await exitCb?.();
90
64
  expect(processExitMock).toHaveBeenCalledWith(0);
91
65
  });
92
66
 
93
- it('exits with code 1 when bundles definitions w/linting w/errors', async () => {
67
+ it('exits with code 1 when bundles definitions w/errors', async () => {
94
68
  const apis = ['foo.yaml'];
95
69
 
96
70
  (getTotals as jest.Mock).mockReturnValue({
@@ -102,11 +76,8 @@ describe('bundle', () => {
102
76
  await commandWrapper(handleBundle)({
103
77
  apis,
104
78
  ext: 'yaml',
105
- format: 'codeframe',
106
- lint: true,
107
79
  } as Arguments<BundleOptions>);
108
80
 
109
- expect(lint).toBeCalledTimes(apis.length);
110
81
  await exitCb?.();
111
82
  expect(processExitMock).toHaveBeenCalledWith(1);
112
83
  });
@@ -121,8 +92,6 @@ describe('bundle', () => {
121
92
  await commandWrapper(handleBundle)({
122
93
  apis,
123
94
  ext: 'json',
124
- format: 'codeframe',
125
- lint: false,
126
95
  } as Arguments<BundleOptions>);
127
96
 
128
97
  expect(handleError).toHaveBeenCalledTimes(1);
@@ -141,8 +110,6 @@ describe('bundle', () => {
141
110
  await commandWrapper(handleBundle)({
142
111
  apis,
143
112
  ext: 'yaml',
144
- format: 'codeframe',
145
- lint: false,
146
113
  } as Arguments<BundleOptions>);
147
114
 
148
115
  expect(handleError).toHaveBeenCalledTimes(0);
@@ -165,23 +165,6 @@ describe('handleJoin', () => {
165
165
  expect(config.styleguide.skipPreprocessors).toHaveBeenCalled();
166
166
  });
167
167
 
168
- it('should not call skipDecorators and skipPreprocessors', async () => {
169
- (detectSpec as jest.Mock).mockReturnValue('oas3_0');
170
- await handleJoin(
171
- {
172
- apis: ['first.yaml', 'second.yaml'],
173
- decorate: true,
174
- preprocess: true,
175
- },
176
- ConfigFixture as any,
177
- 'cli-version'
178
- );
179
-
180
- const config = loadConfig();
181
- expect(config.styleguide.skipDecorators).not.toHaveBeenCalled();
182
- expect(config.styleguide.skipPreprocessors).not.toHaveBeenCalled();
183
- });
184
-
185
168
  it('should handle join with prefix-components-with-info-prop and null values', async () => {
186
169
  (detectSpec as jest.Mock).mockReturnValue('oas3_0');
187
170
 
@@ -1,12 +1,4 @@
1
- import {
2
- formatProblems,
3
- getTotals,
4
- getMergedConfig,
5
- lint,
6
- bundle,
7
- Config,
8
- OutputFormat,
9
- } from '@redocly/openapi-core';
1
+ import { formatProblems, getTotals, getMergedConfig, bundle, Config } from '@redocly/openapi-core';
10
2
  import {
11
3
  dumpBundle,
12
4
  getExecutionTime,
@@ -15,8 +7,6 @@ import {
15
7
  handleError,
16
8
  printUnusedWarnings,
17
9
  saveBundle,
18
- printLintTotals,
19
- checkIfRulesetExist,
20
10
  sortTopLevelKeysForOas,
21
11
  } from '../utils/miscellaneous';
22
12
  import type { OutputExtensions, Skips, Totals } from '../types';
@@ -27,15 +17,12 @@ import { checkForDeprecatedOptions } from '../utils/miscellaneous';
27
17
 
28
18
  export type BundleOptions = {
29
19
  apis?: string[];
30
- 'max-problems'?: number;
31
20
  extends?: string[];
32
21
  config?: string;
33
- format?: OutputFormat;
34
22
  output?: string;
35
23
  ext: OutputExtensions;
36
24
  dereferenced?: boolean;
37
25
  force?: boolean;
38
- lint?: boolean;
39
26
  metafile?: string;
40
27
  'remove-unused-components'?: boolean;
41
28
  'keep-url-references'?: boolean;
@@ -47,14 +34,7 @@ export async function handleBundle(argv: BundleOptions, config: Config, version:
47
34
  config.rawConfig?.styleguide?.decorators?.hasOwnProperty('remove-unused-components');
48
35
  const apis = await getFallbackApisOrExit(argv.apis, config);
49
36
  const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
50
- const maxProblems = argv['max-problems'];
51
- const deprecatedOptions: Array<keyof BundleOptions> = [
52
- 'lint',
53
- 'format',
54
- 'skip-rule',
55
- 'extends',
56
- 'max-problems',
57
- ];
37
+ const deprecatedOptions: Array<keyof BundleOptions> = [];
58
38
 
59
39
  checkForDeprecatedOptions(argv, deprecatedOptions);
60
40
 
@@ -64,38 +44,9 @@ export async function handleBundle(argv: BundleOptions, config: Config, version:
64
44
  const resolvedConfig = getMergedConfig(config, alias);
65
45
  const { styleguide } = resolvedConfig;
66
46
 
67
- styleguide.skipRules(argv['skip-rule']);
68
47
  styleguide.skipPreprocessors(argv['skip-preprocessor']);
69
48
  styleguide.skipDecorators(argv['skip-decorator']);
70
49
 
71
- if (argv.lint) {
72
- checkIfRulesetExist(styleguide.rules);
73
- if (config.styleguide.recommendedFallback) {
74
- process.stderr.write(
75
- `No configurations were provided -- using built in ${blue(
76
- 'recommended'
77
- )} configuration by default.\n\n`
78
- );
79
- }
80
- const results = await lint({
81
- ref: path,
82
- config: resolvedConfig,
83
- });
84
- const fileLintTotals = getTotals(results);
85
-
86
- totals.errors += fileLintTotals.errors;
87
- totals.warnings += fileLintTotals.warnings;
88
- totals.ignored += fileLintTotals.ignored;
89
-
90
- formatProblems(results, {
91
- format: argv.format || 'codeframe',
92
- totals: fileLintTotals,
93
- version,
94
- maxProblems: maxProblems,
95
- });
96
- printLintTotals(fileLintTotals, 2);
97
- }
98
-
99
50
  process.stderr.write(gray(`bundling ${path}...\n`));
100
51
 
101
52
  const {
@@ -132,8 +83,7 @@ export async function handleBundle(argv: BundleOptions, config: Config, version:
132
83
  totals.ignored += fileTotals.ignored;
133
84
 
134
85
  formatProblems(problems, {
135
- format: argv.format || 'codeframe',
136
- maxProblems: maxProblems,
86
+ format: 'codeframe',
137
87
  totals: fileTotals,
138
88
  version,
139
89
  });
@@ -6,10 +6,8 @@ import {
6
6
  Config,
7
7
  SpecVersion,
8
8
  BaseResolver,
9
- StyleguideConfig,
10
9
  formatProblems,
11
10
  getTotals,
12
- lintDocument,
13
11
  detectSpec,
14
12
  bundleDocument,
15
13
  isRef,
@@ -17,13 +15,10 @@ import {
17
15
  import {
18
16
  getFallbackApisOrExit,
19
17
  printExecutionTime,
20
- handleError,
21
- printLintTotals,
22
18
  exitWithError,
23
19
  sortTopLevelKeysForOas,
24
20
  getAndValidateFileExtension,
25
21
  writeToFileByExtension,
26
- checkForDeprecatedOptions,
27
22
  } from '../utils/miscellaneous';
28
23
  import { isObject, isString, keysOf } from '../utils/js-utils';
29
24
  import { COMPONENTS, OPENAPI3_METHOD } from './split/types';
@@ -60,9 +55,6 @@ type JoinDocumentContext = {
60
55
 
61
56
  export type JoinOptions = {
62
57
  apis: string[];
63
- lint?: boolean;
64
- decorate?: boolean;
65
- preprocess?: boolean;
66
58
  'prefix-tags-with-info-prop'?: string;
67
59
  'prefix-tags-with-filename'?: boolean;
68
60
  'prefix-components-with-info-prop'?: string;
@@ -79,8 +71,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
79
71
  return exitWithError(`At least 2 apis should be provided. \n\n`);
80
72
  }
81
73
 
82
- checkForDeprecatedOptions(argv, ['lint'] as Array<keyof JoinOptions>);
83
-
84
74
  const fileExtension = getAndValidateFileExtension(argv.output || argv.apis[0]);
85
75
 
86
76
  const {
@@ -111,23 +101,19 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
111
101
  )
112
102
  );
113
103
 
114
- if (!argv.decorate) {
115
- const decorators = new Set([
116
- ...Object.keys(config.styleguide.decorators.oas3_0),
117
- ...Object.keys(config.styleguide.decorators.oas3_1),
118
- ...Object.keys(config.styleguide.decorators.oas2),
119
- ]);
120
- config.styleguide.skipDecorators(Array.from(decorators));
121
- }
104
+ const decorators = new Set([
105
+ ...Object.keys(config.styleguide.decorators.oas3_0),
106
+ ...Object.keys(config.styleguide.decorators.oas3_1),
107
+ ...Object.keys(config.styleguide.decorators.oas2),
108
+ ]);
109
+ config.styleguide.skipDecorators(Array.from(decorators));
122
110
 
123
- if (!argv.preprocess) {
124
- const preprocessors = new Set([
125
- ...Object.keys(config.styleguide.preprocessors.oas3_0),
126
- ...Object.keys(config.styleguide.preprocessors.oas3_1),
127
- ...Object.keys(config.styleguide.preprocessors.oas2),
128
- ]);
129
- config.styleguide.skipPreprocessors(Array.from(preprocessors));
130
- }
111
+ const preprocessors = new Set([
112
+ ...Object.keys(config.styleguide.preprocessors.oas3_0),
113
+ ...Object.keys(config.styleguide.preprocessors.oas3_1),
114
+ ...Object.keys(config.styleguide.preprocessors.oas2),
115
+ ]);
116
+ config.styleguide.skipPreprocessors(Array.from(preprocessors));
131
117
 
132
118
  const bundleResults = await Promise.all(
133
119
  documents.map((document) =>
@@ -146,7 +132,7 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
146
132
  if (fileTotals.errors) {
147
133
  formatProblems(problems, {
148
134
  totals: fileTotals,
149
- version: document.parsed.version,
135
+ version: packageVersion,
150
136
  });
151
137
  exitWithError(
152
138
  `❌ Errors encountered while bundling ${blue(
@@ -179,12 +165,6 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
179
165
  }
180
166
  }
181
167
 
182
- if (argv.lint) {
183
- for (const document of documents) {
184
- await validateApi(document, config.styleguide, externalRefResolver, packageVersion);
185
- }
186
- }
187
-
188
168
  const joinedDef: any = {};
189
169
  const potentialConflicts = {
190
170
  tags: {},
@@ -787,22 +767,6 @@ function getInfoPrefix(info: any, prefixArg: string | undefined, type: string) {
787
767
  return info[prefixArg].replaceAll(/\s/g, '_');
788
768
  }
789
769
 
790
- async function validateApi(
791
- document: Document,
792
- config: StyleguideConfig,
793
- externalRefResolver: BaseResolver,
794
- packageVersion: string
795
- ) {
796
- try {
797
- const results = await lintDocument({ document, config, externalRefResolver });
798
- const fileTotals = getTotals(results);
799
- formatProblems(results, { format: 'stylish', totals: fileTotals, version: packageVersion });
800
- printLintTotals(fileTotals, 2);
801
- } catch (err) {
802
- handleError(err, document.parsed);
803
- }
804
- }
805
-
806
770
  function replace$Refs(obj: unknown, componentsPrefix: string) {
807
771
  crawl(obj, (node: Record<string, unknown>) => {
808
772
  if (node.$ref && typeof node.$ref === 'string' && startsWithComponents(node.$ref)) {
@@ -24,6 +24,8 @@ import { performance } from 'perf_hooks';
24
24
  import type { OutputFormat, ProblemSeverity, Document, RuleSeverity } from '@redocly/openapi-core';
25
25
  import type { ResolvedRefMap } from '@redocly/openapi-core/lib/resolve';
26
26
  import type { CommandOptions, Skips, Totals } from '../types';
27
+ import { getCommandNameFromArgs } from '../utils/getCommandNameFromArgs';
28
+ import { Arguments } from 'yargs';
27
29
 
28
30
  export type LintOptions = {
29
31
  apis?: string[];
@@ -144,7 +146,9 @@ export function lintConfigCallback(
144
146
  version,
145
147
  });
146
148
 
147
- printConfigLintTotals(fileTotals);
149
+ const command = argv ? getCommandNameFromArgs(argv as unknown as Arguments) : undefined;
150
+
151
+ printConfigLintTotals(fileTotals, command);
148
152
 
149
153
  if (fileTotals.errors > 0) {
150
154
  throw new ConfigValidationError();
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import './utils/assert-node-version';
4
4
  import * as yargs from 'yargs';
5
+ import * as colors from 'colorette';
5
6
  import { outputExtensions, PushArguments, regionChoices } from './types';
6
7
  import { RedoclyClient } from '@redocly/openapi-core';
7
8
  import { previewDocs } from './commands/preview-docs';
@@ -104,9 +105,6 @@ yargs
104
105
  demandOption: true,
105
106
  })
106
107
  .option({
107
- lint: { description: 'Lint descriptions', type: 'boolean', default: false, hidden: true },
108
- decorate: { description: 'Run decorators', type: 'boolean', default: false },
109
- preprocess: { description: 'Run preprocessors', type: 'boolean', default: false },
110
108
  'prefix-tags-with-info-prop': {
111
109
  description: 'Prefix tags with property value from info object.',
112
110
  requiresArg: true,
@@ -141,8 +139,47 @@ yargs
141
139
  choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
142
140
  default: 'warn' as RuleSeverity,
143
141
  },
142
+ lint: {
143
+ hidden: true,
144
+ deprecated: true,
145
+ },
146
+ decorate: {
147
+ hidden: true,
148
+ deprecated: true,
149
+ },
150
+ preprocess: {
151
+ hidden: true,
152
+ deprecated: true,
153
+ },
144
154
  }),
145
155
  (argv) => {
156
+ const DEPRECATED_OPTIONS = ['lint', 'preprocess', 'decorate'];
157
+ const DECORATORS_DOCUMENTATION_LINK = 'https://redocly.com/docs/cli/decorators/#decorators';
158
+ const JOIN_COMMAND_DOCUMENTATION_LINK = 'https://redocly.com/docs/cli/commands/join/#join';
159
+
160
+ DEPRECATED_OPTIONS.forEach((option) => {
161
+ if (argv[option]) {
162
+ process.stdout.write(
163
+ `${colors.red(
164
+ `Option --${option} is no longer supported. Please review join command documentation ${JOIN_COMMAND_DOCUMENTATION_LINK}.`
165
+ )}`
166
+ );
167
+ process.stdout.write('\n\n');
168
+
169
+ if (['preprocess', 'decorate'].includes(option)) {
170
+ process.stdout.write(
171
+ `${colors.red(
172
+ `If you are looking for decorators, please review the decorators documentation ${DECORATORS_DOCUMENTATION_LINK}.`
173
+ )}`
174
+ );
175
+ process.stdout.write('\n\n');
176
+ }
177
+
178
+ yargs.showHelp();
179
+ process.exit(1);
180
+ }
181
+ });
182
+
146
183
  process.env.REDOCLY_CLI_COMMAND = 'join';
147
184
  commandWrapper(handleJoin)(argv);
148
185
  }
@@ -360,6 +397,7 @@ yargs
360
397
  'checkstyle',
361
398
  'codeclimate',
362
399
  'summary',
400
+ 'github-actions',
363
401
  ] as ReadonlyArray<OutputFormat>,
364
402
  default: 'codeframe' as OutputFormat,
365
403
  },
@@ -415,28 +453,11 @@ yargs
415
453
  description: 'Output file.',
416
454
  alias: 'o',
417
455
  },
418
- format: {
419
- description: 'Use a specific output format.',
420
- choices: ['stylish', 'codeframe', 'json', 'checkstyle'] as ReadonlyArray<OutputFormat>,
421
- hidden: true,
422
- },
423
- 'max-problems': {
424
- requiresArg: true,
425
- description: 'Reduce output to a maximum of N problems.',
426
- type: 'number',
427
- hidden: true,
428
- },
429
456
  ext: {
430
457
  description: 'Bundle file extension.',
431
458
  requiresArg: true,
432
459
  choices: outputExtensions,
433
460
  },
434
- 'skip-rule': {
435
- description: 'Ignore certain rules.',
436
- array: true,
437
- type: 'string',
438
- hidden: true,
439
- },
440
461
  'skip-preprocessor': {
441
462
  description: 'Ignore certain preprocessors.',
442
463
  array: true,
@@ -461,12 +482,6 @@ yargs
461
482
  description: 'Path to the config file.',
462
483
  type: 'string',
463
484
  },
464
- lint: {
465
- description: 'Lint API descriptions',
466
- type: 'boolean',
467
- default: false,
468
- hidden: true,
469
- },
470
485
  metafile: {
471
486
  description: 'Produce metadata about the bundle',
472
487
  type: 'string',
@@ -493,12 +508,67 @@ yargs
493
508
  choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
494
509
  default: 'warn' as RuleSeverity,
495
510
  },
511
+ format: {
512
+ hidden: true,
513
+ deprecated: true,
514
+ },
515
+ lint: {
516
+ hidden: true,
517
+ deprecated: true,
518
+ },
519
+ 'skip-rule': {
520
+ hidden: true,
521
+ deprecated: true,
522
+ array: true,
523
+ type: 'string',
524
+ },
525
+ 'max-problems': {
526
+ hidden: true,
527
+ deprecated: true,
528
+ },
496
529
  }),
497
530
  (argv) => {
531
+ const DEPRECATED_OPTIONS = ['lint', 'format', 'skip-rule', 'max-problems'];
532
+ const LINT_AND_BUNDLE_DOCUMENTATION_LINK =
533
+ 'https://redocly.com/docs/cli/guides/lint-and-bundle/#lint-and-bundle-api-descriptions-with-redocly-cli';
534
+
535
+ DEPRECATED_OPTIONS.forEach((option) => {
536
+ if (argv[option]) {
537
+ process.stdout.write(
538
+ `${colors.red(
539
+ `Option --${option} is no longer supported. Please use separate commands, as described in the ${LINT_AND_BUNDLE_DOCUMENTATION_LINK}.`
540
+ )}`
541
+ );
542
+ process.stdout.write('\n\n');
543
+ yargs.showHelp();
544
+ process.exit(1);
545
+ }
546
+ });
547
+
498
548
  process.env.REDOCLY_CLI_COMMAND = 'bundle';
499
549
  commandWrapper(handleBundle)(argv);
500
550
  }
501
551
  )
552
+ .command(
553
+ 'check-config',
554
+ 'Lint the Redocly configuration file.',
555
+ async (yargs) =>
556
+ yargs.option({
557
+ config: {
558
+ description: 'Path to the config file.',
559
+ type: 'string',
560
+ },
561
+ 'lint-config': {
562
+ description: 'Severity level for config file linting.',
563
+ choices: ['warn', 'error'] as ReadonlyArray<RuleSeverity>,
564
+ default: 'error' as RuleSeverity,
565
+ },
566
+ }),
567
+ (argv) => {
568
+ process.env.REDOCLY_CLI_COMMAND = 'check-config';
569
+ commandWrapper()(argv);
570
+ }
571
+ )
502
572
  .command(
503
573
  'login',
504
574
  'Login to the Redocly API registry with an access token.',
package/src/types.ts CHANGED
@@ -38,8 +38,14 @@ export type CommandOptions =
38
38
  | PreviewDocsOptions
39
39
  | BuildDocsArgv
40
40
  | PushStatusOptions
41
+ | VerifyConfigOptions
41
42
  | PreviewProjectOptions;
42
43
 
44
+ export type VerifyConfigOptions = {
45
+ config?: string;
46
+ 'lint-config'?: 'warning' | 'error' | 'off';
47
+ };
48
+
43
49
  export type Skips = {
44
50
  'skip-rule'?: string[];
45
51
  'skip-decorator'?: string[];
@@ -0,0 +1,5 @@
1
+ import { Arguments } from 'yargs';
2
+
3
+ export function getCommandNameFromArgs(argv: Arguments | undefined): string | number {
4
+ return argv?._?.[0] ?? '';
5
+ }
@@ -352,7 +352,7 @@ export function printLintTotals(totals: Totals, definitionsCount: number) {
352
352
  process.stderr.write('\n');
353
353
  }
354
354
 
355
- export function printConfigLintTotals(totals: Totals): void {
355
+ export function printConfigLintTotals(totals: Totals, command?: string | number): void {
356
356
  if (totals.errors > 0) {
357
357
  process.stderr.write(
358
358
  red(`❌ Your config has ${totals.errors} ${pluralize('error', totals.errors)}.`)
@@ -361,6 +361,8 @@ export function printConfigLintTotals(totals: Totals): void {
361
361
  process.stderr.write(
362
362
  yellow(`⚠️ Your config has ${totals.warnings} ${pluralize('warning', totals.warnings)}.\n`)
363
363
  );
364
+ } else if (command === 'check-config') {
365
+ process.stderr.write(green('✅ Your config is valid.\n'));
364
366
  }
365
367
  }
366
368
 
package/src/wrapper.ts CHANGED
@@ -11,7 +11,7 @@ import { lintConfigCallback } from './commands/lint';
11
11
  import type { CommandOptions } from './types';
12
12
 
13
13
  export function commandWrapper<T extends CommandOptions>(
14
- commandHandler: (argv: T, config: Config, version: string) => Promise<void>
14
+ commandHandler?: (argv: T, config: Config, version: string) => Promise<void>
15
15
  ) {
16
16
  return async (argv: Arguments<T>) => {
17
17
  let code: ExitCode = 2;
@@ -31,7 +31,9 @@ export function commandWrapper<T extends CommandOptions>(
31
31
  telemetry = config.telemetry;
32
32
  hasConfig = !config.styleguide.recommendedFallback;
33
33
  code = 1;
34
- await commandHandler(argv, config, version);
34
+ if (typeof commandHandler === 'function') {
35
+ await commandHandler(argv, config, version);
36
+ }
35
37
  code = 0;
36
38
  } catch (err) {
37
39
  // Do nothing