-
Notifications
You must be signed in to change notification settings - Fork 38
/
eslint.ts
90 lines (81 loc) · 3.23 KB
/
eslint.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import type { Linter } from 'eslint';
import type { BettererESLintRulesConfig } from './types.js';
import { BettererFileTest } from '@betterer/betterer';
import { BettererError } from '@betterer/errors';
import assert from 'node:assert';
import { ESLint } from 'eslint';
/**
* @public Use this test to incrementally introduce new {@link https://eslint.org/ | **ESLint**} rules to
* your codebase. You can pass as many **ESLint** {@link https://eslint.org/docs/rules/ | rule configurations}
* as you like:
*
* @remarks {@link @betterer/eslint#eslint | `eslint`} is a {@link @betterer/betterer#BettererFileTest | `BettererFileTest`},
* so you can use {@link @betterer/betterer#BettererFileTest.include | `include()`},
* {@link @betterer/betterer#BettererFileTest.exclude | `exclude()`}, {@link @betterer/betterer#BettererFileTest.only | `only()`},
* and {@link @betterer/betterer#BettererFileTest.skip | `skip()`}.
*
* @example
* ```typescript
* import { eslint } from '@betterer/eslint';
*
* export default {
* 'new eslint rules': () =>
* eslint({
* 'no-debugger': 'error',
* 'no-unsafe-finally': 'error',
* })
* .include('./src/*.ts')
* };
* ```
*
* @param rules - Additional {@link https://eslint.org/ | **ESLint**} {@link https://eslint.org/docs/rules/ | rules}
* to enable.
*
* @throws {@link @betterer/errors#BettererError | `BettererError` }
* Will throw if the user doesn't pass `rules`.
*/
export function eslint(rules: BettererESLintRulesConfig): BettererFileTest {
if (!rules) {
throw new BettererError(
"for `@betterer/eslint` to work, you need to provide rule options, e.g. `{ 'no-debugger': 'error' }`. ❌"
);
}
return new BettererFileTest(async (filePaths, fileTestResult, resolver) => {
if (!filePaths.length) {
return;
}
const { baseDirectory } = resolver;
const cli = new ESLint({ cwd: baseDirectory });
await Promise.all(
filePaths.map(async (filePath) => {
const linterOptions = (await cli.calculateConfigForFile(filePath)) as Linter.Config;
// Explicitly disable all other configured rules:
const disabledRules: BettererESLintRulesConfig = {};
Object.keys(linterOptions.rules || {}).forEach((ruleName) => {
disabledRules[ruleName] = 'off';
});
const finalRules = { ...disabledRules, ...rules };
const runner = new ESLint({
overrideConfig: { rules: finalRules },
useEslintrc: true,
cwd: baseDirectory
});
const lintResults = await runner.lintFiles([filePath]);
lintResults
.filter((lintResult) => lintResult.source)
.forEach((lintResult) => {
const { messages, source } = lintResult;
assert(source);
const file = fileTestResult.addFile(filePath, source);
messages.forEach((message) => {
const startLine = message.line - 1;
const startColumn = message.column - 1;
const endLine = message.endLine ? message.endLine - 1 : 0;
const endColumn = message.endColumn ? message.endColumn - 1 : 0;
file.addIssue(startLine, startColumn, endLine, endColumn, message.message);
});
});
})
);
});
}