Skip to content

Commit ec228f5

Browse files
committed
Make linter configurable + add some rules
1 parent 77575ef commit ec228f5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+5340
-1464
lines changed

package-lock.json

Lines changed: 3345 additions & 1089 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/usr/bin/env node
2-
require('@fresha/openapi-lint/build/index');
2+
require('@fresha/openapi-lint/cli');

packages/openapi-lint/package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22
"name": "@fresha/openapi-lint",
33
"version": "0.3.1",
44
"description": "Linting tool for OpenAPI schemas",
5-
"main": "build/index.js",
65
"bin": {
76
"fresha-openapi-lint": "./bin/fresha-openapi-lint.js"
87
},
8+
"main": "build/api.js",
9+
"types": "build/api.d.ts",
10+
"exports": {
11+
"./package.json": "./package.json",
12+
".": "./build/api.js",
13+
"./cli": "./build/cli.js"
14+
},
915
"publishConfig": {
1016
"access": "public"
1117
},
@@ -63,9 +69,11 @@
6369
"typescript": "^5.0.2"
6470
},
6571
"dependencies": {
72+
"@fresha/api-tools-core": "^0.6.1",
6673
"@fresha/openapi-model": "^0.8.1",
6774
"chalk": "^4.1.0",
6875
"glob": "^8.0.3",
76+
"yaml": "^2.3.1",
6977
"yargs": "^17.6.2"
7078
},
7179
"gitHead": "5830273395fc060b19d06814b9396ce07eea778d"

packages/openapi-lint/src/Linter.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

packages/openapi-lint/src/LinterResult.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.

packages/openapi-lint/src/api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './types';
2+
export { loadConfigFromFile, createLinter } from './linter';
3+
export { createFormatter } from './formatters';

packages/openapi-lint/src/cli.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import console from 'console';
2+
3+
import { OpenAPIReader, OpenAPIWriter } from '@fresha/openapi-model/build/3.0.3';
4+
import yargs from 'yargs';
5+
import { hideBin } from 'yargs/helpers';
6+
7+
import { createFormatter } from './formatters';
8+
import { loadConfigFromFile, createLinter } from './linter';
9+
10+
try {
11+
const argv = yargs(hideBin(process.argv))
12+
.epilog(
13+
'For more information, see https://github.com/fresha/api-tools/tree/main/packages/openapi-lint',
14+
)
15+
.usage('Usage: $0 [OPTIONS] FILES')
16+
.string('config')
17+
.alias('config', 'c')
18+
.describe('config', 'Path to configuration file')
19+
.boolean('print-config')
20+
.describe('print-config', 'Print configuration and exit')
21+
.number('max-warnings')
22+
.describe('max-warnings', 'Number of warnings to trigger nonzero exit code')
23+
.boolean('fix')
24+
.describe('fix', 'Automatically fix problems')
25+
.choices('formatter', ['simple', 'json'])
26+
.default('formatter', 'simple')
27+
.alias('formatter', 'f')
28+
.boolean('verbose')
29+
.alias('verbose', 'v')
30+
.describe('verbose', 'Print more information on console')
31+
.parseSync();
32+
33+
const config = loadConfigFromFile(argv.config ?? '.openapi-lint.yaml');
34+
if (argv.printConfig) {
35+
config.print();
36+
}
37+
38+
const linter = createLinter({
39+
config,
40+
maxWarnings: argv.maxWarnings ?? -1,
41+
autoFix: !!argv.fix,
42+
verbose: !!argv.verbose,
43+
});
44+
45+
for (const elem of argv._) {
46+
const fpath = String(elem);
47+
try {
48+
const openapi = new OpenAPIReader().parseFromFile(fpath);
49+
openapi.setExtension('__filename', fpath);
50+
51+
if (linter.run(openapi)) {
52+
const writer = new OpenAPIWriter();
53+
openapi.deleteExtension('__filename');
54+
writer.writeToFile(openapi, fpath);
55+
}
56+
} catch (err) {
57+
if (err instanceof Error) {
58+
console.log(err instanceof Error ? err.message : err);
59+
}
60+
}
61+
}
62+
63+
const formatter = createFormatter(argv.formatter);
64+
formatter.format(linter.result);
65+
66+
process.exit(linter.result.isFailure ? 1 : 0);
67+
} catch (err) {
68+
console.log(err instanceof Error ? err.message : err);
69+
process.exit(1);
70+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import console from 'console';
2+
3+
import type { Formatter, Result } from '../types';
4+
5+
export class JSONFormatter implements Formatter {
6+
// eslint-disable-next-line class-methods-use-this
7+
format(result: Result): void {
8+
const json = JSON.stringify(result, null, 2);
9+
console.log(json);
10+
}
11+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import console from 'console';
2+
3+
import chalk from 'chalk';
4+
5+
import type { Severity, Formatter, Issue, Result } from '../types';
6+
7+
export class SimpleFormatter implements Formatter {
8+
// eslint-disable-next-line class-methods-use-this
9+
format(result: Result): void {
10+
const issuesPerFile = new Map<string, Issue[]>();
11+
const severityCount = new Map<Severity, number>();
12+
13+
for (const issue of result.issues()) {
14+
issuesPerFile.set(issue.file, [...(issuesPerFile.get(issue.file) ?? []), issue]);
15+
severityCount.set(issue.severity, (severityCount.get(issue.severity) ?? 0) + 1);
16+
}
17+
18+
const fileNames = Array.from(issuesPerFile.keys()).sort();
19+
for (const fileName of fileNames) {
20+
console.log(fileName);
21+
for (const item of issuesPerFile.get(fileName)!) {
22+
let str = ' ';
23+
if (item.severity === 'error') {
24+
str += chalk.red('[error] ');
25+
} else if (item.severity === 'warning') {
26+
str += chalk.yellow('[warn] ');
27+
}
28+
str += item.message;
29+
console.log(str);
30+
}
31+
}
32+
if (result.issueCount > 0) {
33+
let str = 'Summary: ';
34+
const errorCount = severityCount.get('error') ?? 0;
35+
if (errorCount > 0) {
36+
str += chalk.red(`${errorCount} errors`);
37+
}
38+
const warningCount = severityCount.get('warning') ?? 0;
39+
if (warningCount > 0) {
40+
str += chalk.red(`${warningCount} warnings`);
41+
}
42+
console.log(str);
43+
}
44+
}
45+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { JSONFormatter } from './JSONFormatter';
2+
import { SimpleFormatter } from './SimpleFormatter';
3+
4+
import type { Formatter } from '../types';
5+
6+
export const createFormatter = (format: string): Formatter => {
7+
switch (format) {
8+
case 'json':
9+
return new JSONFormatter();
10+
case 'simple':
11+
return new SimpleFormatter();
12+
default:
13+
throw new Error(`Unknown format: ${format}`);
14+
}
15+
};

0 commit comments

Comments
 (0)