ignore-lint-errors 0.3.0 → 0.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.
- package/README.md +25 -2
- package/dist/bin/ignore-lint-errors.js +1 -1
- package/dist/src/steps/create-options.js +1 -0
- package/dist/src/steps/ignore-errors/eslint.js +1 -9
- package/dist/src/steps/ignore-errors/index.js +1 -0
- package/dist/src/steps/ignore-errors/stylelint.js +16 -0
- package/dist/src/steps/ignore-errors/typescript.js +1 -42
- package/dist/src/steps/ignore-errors.js +7 -1
- package/dist/src/steps/lint-files/index.js +1 -0
- package/dist/src/steps/lint-files/stylelint.js +19 -0
- package/dist/src/steps/lint-files.js +7 -1
- package/dist/src/utils/ignore-errors/eslint.js +9 -0
- package/dist/src/utils/ignore-errors/stylelint.js +9 -0
- package/dist/src/utils/ignore-errors/typescript.js +41 -1
- package/dist/src/utils/linters/eslint.js +3 -0
- package/dist/src/utils/linters/stylelint.js +51 -0
- package/dist/src/utils/linters/typescript.js +3 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,14 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
# ignore-lint-errors
|
|
4
4
|
|
|
5
|
-
_Codemod to
|
|
5
|
+
_Codemod to ignore lint errors per line_
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Why?
|
|
9
|
+
|
|
10
|
+
In large production projects, ignoring lint errors for existing code is the most pragmatic, quickest approach to complete these tasks:
|
|
11
|
+
|
|
12
|
+
- Introduce a new lint rule.
|
|
13
|
+
- Update a linter to the next minor or major version.
|
|
14
|
+
- Migrate code to a different format, e.g. convert `*.{gjs,js}` (JavaScript) to `*.{gts,ts}` (TypeScript).
|
|
15
|
+
|
|
16
|
+
For the sake of reducing noise, some prefer disabling linting for the entire file. They do so by adding a global directive (e.g. `eslint-disable`) or creating a special file to "suppress" files with many errors.
|
|
17
|
+
|
|
18
|
+
Instead, `ignore-lint-error` takes the honest approach: It adds a local directive (e.g. `eslint-disable-next-line`) for every line that has an error. This way, we can easily see and show others (including non-engineers),
|
|
19
|
+
|
|
20
|
+
- How bad our code is in order to create the urgency to fix issues. The number of local ignores (1 per line) always estimates the actual number of errors better than the number of global ignores (1 per file).
|
|
21
|
+
- Which parts of our code are good and should be replicated, and which parts are bad and to be avoided. When there is a global ignore, a linter doesn't check the file for the given rule(s).
|
|
6
22
|
|
|
7
23
|
|
|
8
24
|
## Usage
|
|
9
25
|
|
|
10
26
|
### Arguments
|
|
11
27
|
|
|
12
|
-
|
|
28
|
+
You must pass `--linter` to indicate which linter to run.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
pnpx ignore-lint-errors --linter eslint
|
|
32
|
+
pnpx ignore-lint-errors --linter stylelint
|
|
33
|
+
pnpx ignore-lint-errors --linter typescript
|
|
34
|
+
```
|
|
13
35
|
|
|
14
36
|
<details>
|
|
15
37
|
|
|
@@ -44,6 +66,7 @@ pnpm build
|
|
|
44
66
|
## Compatibility
|
|
45
67
|
|
|
46
68
|
- Node.js v22 or above
|
|
69
|
+
- Tested against `eslint@v9`, `stylelint@v17`, `typescript@v5`
|
|
47
70
|
|
|
48
71
|
|
|
49
72
|
## Contributing
|
|
@@ -8,7 +8,7 @@ process.title = 'ignore-lint-errors';
|
|
|
8
8
|
// Set codemod options
|
|
9
9
|
const argv = yargs(hideBin(process.argv))
|
|
10
10
|
.option('linter', {
|
|
11
|
-
choices: ['eslint', 'typescript'],
|
|
11
|
+
choices: ['eslint', 'stylelint', 'typescript'],
|
|
12
12
|
describe: 'Linter to run',
|
|
13
13
|
type: 'string',
|
|
14
14
|
})
|
|
@@ -9,6 +9,7 @@ function findDependencies(projectRoot) {
|
|
|
9
9
|
eslint: projectDependencies.has('eslint'),
|
|
10
10
|
glint: projectDependencies.has('@glint/core') ||
|
|
11
11
|
projectDependencies.has('@glint/ember-tsc'),
|
|
12
|
+
stylelint: projectDependencies.has('stylelint'),
|
|
12
13
|
typescript: projectDependencies.has('typescript'),
|
|
13
14
|
};
|
|
14
15
|
}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { EOL } from 'node:os';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
3
|
import { removeFiles } from '@codemod-utils/files';
|
|
4
|
+
import { ignoreErrors } from '../../utils/ignore-errors/stylelint.js';
|
|
5
5
|
import { outputFilePath, parseOutputFile } from '../../utils/linters/eslint.js';
|
|
6
|
-
function ignoreErrors(file, lintErrors) {
|
|
7
|
-
const lines = file.split(EOL);
|
|
8
|
-
lintErrors.forEach(({ line, message }) => {
|
|
9
|
-
const ignoreDirective = `// eslint-disable-next-line ${message}`;
|
|
10
|
-
lines.splice(line - 1, 0, ignoreDirective);
|
|
11
|
-
});
|
|
12
|
-
return lines.join(EOL);
|
|
13
|
-
}
|
|
14
6
|
export function ignoreErrorsFromEslint(options) {
|
|
15
7
|
const { projectRoot } = options;
|
|
16
8
|
const outputFile = readFileSync(join(projectRoot, outputFilePath), 'utf8');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { removeFiles } from '@codemod-utils/files';
|
|
4
|
+
import { ignoreErrors } from '../../utils/ignore-errors/stylelint.js';
|
|
5
|
+
import { outputFilePath, parseOutputFile, } from '../../utils/linters/stylelint.js';
|
|
6
|
+
export function ignoreErrorsFromStylelint(options) {
|
|
7
|
+
const { projectRoot } = options;
|
|
8
|
+
const outputFile = readFileSync(join(projectRoot, outputFilePath), 'utf8');
|
|
9
|
+
const filesWithErrors = parseOutputFile(outputFile, projectRoot);
|
|
10
|
+
filesWithErrors.forEach(({ filePath, lintErrors }) => {
|
|
11
|
+
const file = readFileSync(join(projectRoot, filePath), 'utf8');
|
|
12
|
+
const newFile = ignoreErrors(file, lintErrors);
|
|
13
|
+
writeFileSync(join(projectRoot, filePath), newFile, 'utf8');
|
|
14
|
+
});
|
|
15
|
+
removeFiles([outputFilePath], { projectRoot });
|
|
16
|
+
}
|
|
@@ -1,49 +1,8 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { EOL } from 'node:os';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
|
-
import { updateTemplates } from '@codemod-utils/ast-template-tag';
|
|
5
3
|
import { removeFiles } from '@codemod-utils/files';
|
|
6
|
-
import {
|
|
4
|
+
import { ignoreErrors, ignoreErrorsFallback, isParseable, } from '../../utils/ignore-errors/typescript.js';
|
|
7
5
|
import { outputFilePath, parseOutputFile, } from '../../utils/linters/typescript.js';
|
|
8
|
-
function ignoreErrors(file, lintErrors) {
|
|
9
|
-
const linesWithTemplate = findLinesWithTemplate(file);
|
|
10
|
-
const lines = file.split(EOL);
|
|
11
|
-
lintErrors.forEach(({ line, message }) => {
|
|
12
|
-
const erroredInTemplate = linesWithTemplate.some(([lineStart, lineEnd]) => {
|
|
13
|
-
return lineStart <= line && line <= lineEnd;
|
|
14
|
-
});
|
|
15
|
-
const ignoreDirective = erroredInTemplate
|
|
16
|
-
? `{{! @glint-expect-error: ${message} }}`
|
|
17
|
-
: `// @ts-expect-error: ${message}`;
|
|
18
|
-
lines.splice(line - 1, 0, ignoreDirective);
|
|
19
|
-
});
|
|
20
|
-
return lines.join(EOL);
|
|
21
|
-
}
|
|
22
|
-
// For fallback, ignore type checks in templates
|
|
23
|
-
function ignoreErrorsFallback(file, lintErrors) {
|
|
24
|
-
const linesWithTemplate = findLinesWithTemplate(file);
|
|
25
|
-
const lines = file.split(EOL);
|
|
26
|
-
let hasErrorInTemplate = false;
|
|
27
|
-
lintErrors.forEach(({ line, message }) => {
|
|
28
|
-
const erroredInTemplate = linesWithTemplate.some(([lineStart, lineEnd]) => {
|
|
29
|
-
return lineStart <= line && line <= lineEnd;
|
|
30
|
-
});
|
|
31
|
-
if (erroredInTemplate) {
|
|
32
|
-
hasErrorInTemplate = true;
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
const ignoreDirective = `// @ts-expect-error: ${message}`;
|
|
36
|
-
lines.splice(line - 1, 0, ignoreDirective);
|
|
37
|
-
});
|
|
38
|
-
let newFile = lines.join(EOL);
|
|
39
|
-
if (hasErrorInTemplate) {
|
|
40
|
-
newFile = updateTemplates(newFile, (code) => {
|
|
41
|
-
const ignoreDirective = '{{! @glint-nocheck }}';
|
|
42
|
-
return [ignoreDirective, code].join(EOL);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
return newFile;
|
|
46
|
-
}
|
|
47
6
|
export function ignoreErrorsFromTypescript(options) {
|
|
48
7
|
const { projectRoot } = options;
|
|
49
8
|
const outputFile = readFileSync(join(projectRoot, outputFilePath), 'utf8');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ignoreErrorsFromEslint, ignoreErrorsFromTypescript, } from './ignore-errors/index.js';
|
|
1
|
+
import { ignoreErrorsFromEslint, ignoreErrorsFromStylelint, ignoreErrorsFromTypescript, } from './ignore-errors/index.js';
|
|
2
2
|
export function ignoreErrors(options) {
|
|
3
3
|
const { dependencies, linter } = options;
|
|
4
4
|
switch (linter) {
|
|
@@ -8,6 +8,12 @@ export function ignoreErrors(options) {
|
|
|
8
8
|
}
|
|
9
9
|
break;
|
|
10
10
|
}
|
|
11
|
+
case 'stylelint': {
|
|
12
|
+
if (dependencies.stylelint) {
|
|
13
|
+
ignoreErrorsFromStylelint(options);
|
|
14
|
+
}
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
11
17
|
case 'typescript': {
|
|
12
18
|
if (dependencies.typescript) {
|
|
13
19
|
ignoreErrorsFromTypescript(options);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { outputFilePath } from '../../utils/linters/stylelint.js';
|
|
3
|
+
export function runStylelint(options) {
|
|
4
|
+
const { projectRoot } = options;
|
|
5
|
+
const command = [
|
|
6
|
+
'./node_modules/.bin/stylelint "**/*.css"',
|
|
7
|
+
'--allow-empty-input',
|
|
8
|
+
'--formatter json',
|
|
9
|
+
`--output-file ${outputFilePath}`,
|
|
10
|
+
'--quiet',
|
|
11
|
+
'2>/dev/null', // Suppress output to process.stderr
|
|
12
|
+
].join(' ');
|
|
13
|
+
try {
|
|
14
|
+
execSync(command, { cwd: projectRoot });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Do nothing
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { runEslint, runTypescript } from './lint-files/index.js';
|
|
3
|
+
import { runEslint, runStylelint, runTypescript } from './lint-files/index.js';
|
|
4
4
|
export function lintFiles(options) {
|
|
5
5
|
const { dependencies, linter, projectRoot } = options;
|
|
6
6
|
if (!existsSync(join(projectRoot, '.ignore-lint-errors'))) {
|
|
@@ -13,6 +13,12 @@ export function lintFiles(options) {
|
|
|
13
13
|
}
|
|
14
14
|
break;
|
|
15
15
|
}
|
|
16
|
+
case 'stylelint': {
|
|
17
|
+
if (dependencies.stylelint) {
|
|
18
|
+
runStylelint(options);
|
|
19
|
+
}
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
16
22
|
case 'typescript': {
|
|
17
23
|
if (dependencies.typescript) {
|
|
18
24
|
runTypescript(options);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EOL } from 'node:os';
|
|
2
|
+
export function ignoreErrors(file, lintErrors) {
|
|
3
|
+
const lines = file.split(EOL);
|
|
4
|
+
lintErrors.forEach(({ line, message }) => {
|
|
5
|
+
const ignoreDirective = `// eslint-disable-next-line ${message}`;
|
|
6
|
+
lines.splice(line - 1, 0, ignoreDirective);
|
|
7
|
+
});
|
|
8
|
+
return lines.join(EOL);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EOL } from 'node:os';
|
|
2
|
+
export function ignoreErrors(file, lintErrors) {
|
|
3
|
+
const lines = file.split(EOL);
|
|
4
|
+
lintErrors.forEach(({ line, message }) => {
|
|
5
|
+
const ignoreDirective = `/* stylelint-disable-next-line ${message} */`;
|
|
6
|
+
lines.splice(line - 1, 0, ignoreDirective);
|
|
7
|
+
});
|
|
8
|
+
return lines.join(EOL);
|
|
9
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { EOL } from 'node:os';
|
|
1
2
|
import { AST } from '@codemod-utils/ast-template';
|
|
2
|
-
import { findTemplateTags } from '@codemod-utils/ast-template-tag';
|
|
3
|
+
import { findTemplateTags, updateTemplates, } from '@codemod-utils/ast-template-tag';
|
|
3
4
|
export function findLinesWithTemplate(file) {
|
|
4
5
|
function getLOC(file) {
|
|
5
6
|
const matches = file.match(/\r?\n/g);
|
|
@@ -15,6 +16,45 @@ export function findLinesWithTemplate(file) {
|
|
|
15
16
|
});
|
|
16
17
|
return linesWithTemplate;
|
|
17
18
|
}
|
|
19
|
+
export function ignoreErrors(file, lintErrors) {
|
|
20
|
+
const linesWithTemplate = findLinesWithTemplate(file);
|
|
21
|
+
const lines = file.split(EOL);
|
|
22
|
+
lintErrors.forEach(({ line, message }) => {
|
|
23
|
+
const erroredInTemplate = linesWithTemplate.some(([lineStart, lineEnd]) => {
|
|
24
|
+
return lineStart <= line && line <= lineEnd;
|
|
25
|
+
});
|
|
26
|
+
const ignoreDirective = erroredInTemplate
|
|
27
|
+
? `{{! @glint-expect-error: ${message} }}`
|
|
28
|
+
: `// @ts-expect-error: ${message}`;
|
|
29
|
+
lines.splice(line - 1, 0, ignoreDirective);
|
|
30
|
+
});
|
|
31
|
+
return lines.join(EOL);
|
|
32
|
+
}
|
|
33
|
+
// For fallback, ignore type checks in templates
|
|
34
|
+
export function ignoreErrorsFallback(file, lintErrors) {
|
|
35
|
+
const linesWithTemplate = findLinesWithTemplate(file);
|
|
36
|
+
const lines = file.split(EOL);
|
|
37
|
+
let hasErrorInTemplate = false;
|
|
38
|
+
lintErrors.forEach(({ line, message }) => {
|
|
39
|
+
const erroredInTemplate = linesWithTemplate.some(([lineStart, lineEnd]) => {
|
|
40
|
+
return lineStart <= line && line <= lineEnd;
|
|
41
|
+
});
|
|
42
|
+
if (erroredInTemplate) {
|
|
43
|
+
hasErrorInTemplate = true;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const ignoreDirective = `// @ts-expect-error: ${message}`;
|
|
47
|
+
lines.splice(line - 1, 0, ignoreDirective);
|
|
48
|
+
});
|
|
49
|
+
let newFile = lines.join(EOL);
|
|
50
|
+
if (hasErrorInTemplate) {
|
|
51
|
+
newFile = updateTemplates(newFile, (code) => {
|
|
52
|
+
const ignoreDirective = '{{! @glint-nocheck }}';
|
|
53
|
+
return [ignoreDirective, code].join(EOL);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return newFile;
|
|
57
|
+
}
|
|
18
58
|
export function isParseable(file) {
|
|
19
59
|
const traverse = AST.traverse();
|
|
20
60
|
let isParseable = true;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { relative, sep } from 'node:path';
|
|
2
|
+
export const outputFilePath = '.ignore-lint-errors/stylelint.txt';
|
|
3
|
+
export function parseOutputFile(file, projectRoot) {
|
|
4
|
+
const filePathToData = new Map();
|
|
5
|
+
const records = JSON.parse(file);
|
|
6
|
+
records.forEach((record) => {
|
|
7
|
+
const { errored, source: absoluteFilePath, warnings } = record;
|
|
8
|
+
if (!errored) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const data = new Map();
|
|
12
|
+
warnings.forEach(({ line, rule }) => {
|
|
13
|
+
if (data.has(line)) {
|
|
14
|
+
data.get(line).push(rule);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
data.set(line, [rule]);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
const filePath = relative(projectRoot, absoluteFilePath).replaceAll(sep, '/');
|
|
21
|
+
filePathToData.set(filePath, data);
|
|
22
|
+
});
|
|
23
|
+
const filesWithErrors = [];
|
|
24
|
+
filePathToData.forEach((data, filePath) => {
|
|
25
|
+
const lintErrors = [];
|
|
26
|
+
data.forEach((allMessages, line) => {
|
|
27
|
+
const ruleIds = Array.from(new Set(allMessages.sort()));
|
|
28
|
+
lintErrors.push({
|
|
29
|
+
line,
|
|
30
|
+
message: ruleIds.join(', '),
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
if (lintErrors.length === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
lintErrors.sort((a, b) => {
|
|
37
|
+
if (a.line < b.line) {
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
if (b.line < a.line) {
|
|
41
|
+
return -1;
|
|
42
|
+
}
|
|
43
|
+
return 0;
|
|
44
|
+
});
|
|
45
|
+
filesWithErrors.push({
|
|
46
|
+
filePath,
|
|
47
|
+
lintErrors,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
return filesWithErrors;
|
|
51
|
+
}
|
package/package.json
CHANGED