Skip to content

Commit a95721f

Browse files
mdjermanovicnzakas
andauthored
feat: Add --pass-on-unpruned-suppressions CLI option (#19773)
* feat: Add `--pass-on-unpruned-suppressions` CLI option Fixes #19723 * Update docs/src/use/command-line-interface.md Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com> * Update tests/bin/eslint.js Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com> * assert error message * fix typo --------- Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
1 parent a467de3 commit a95721f

File tree

5 files changed

+123
-10
lines changed

5 files changed

+123
-10
lines changed

docs/src/use/command-line-interface.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ Suppressing Violations:
142142
--suppress-rule [String] Suppress specific rules
143143
--suppressions-location path::String Specify the location of the suppressions file
144144
--prune-suppressions Prune unused suppressions - default: false
145+
--pass-on-unpruned-suppressions Ignore unused suppressions - default: false
145146
146147
Miscellaneous:
147148
--init Run config initialization wizard - default: false
@@ -904,6 +905,19 @@ Prune unused suppressions from the suppressions file. This option is useful when
904905
args: ["\"src/**/*.js\"", "--prune-suppressions"]
905906
}) }}
906907

908+
#### `--pass-on-unpruned-suppressions`
909+
910+
Ignore unused suppressions. By default, ESLint exits with exit code `2` and displays an error message if there are unused suppressions in the suppressions file. When you use this flag, unused suppressions do not affect the exit code and ESLint doesn't output an error about unused suppressions.
911+
912+
- **Argument Type**: No argument.
913+
914+
##### `--pass-on-unpruned-suppressions` example
915+
916+
{{ npx_tabs ({
917+
package: "eslint",
918+
args: ["\"src/**/*.js\"", "--pass-on-unpruned-suppressions"]
919+
}) }}
920+
907921
### Miscellaneous
908922

909923
#### `--init`

docs/src/use/suppressions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,10 @@ To remove the suppressions that are no longer needed, you can use the `--prune-s
6060
eslint --prune-suppressions
6161
```
6262

63+
To ignore unused suppressions when calculating the exit code and not report an error about unused suppressions, you can use the `--pass-on-unpruned-suppressions` flag.
64+
65+
```bash
66+
eslint --pass-on-unpruned-suppressions
67+
```
68+
6369
For more information on the available CLI options, refer to [Command Line Interface](./command-line-interface).

lib/cli.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -734,17 +734,21 @@ const cli = {
734734
);
735735
}
736736

737-
const unusedSuppressionsCount =
738-
Object.keys(unusedSuppressions).length;
737+
if (!options.passOnUnprunedSuppressions) {
738+
const unusedSuppressionsCount =
739+
Object.keys(unusedSuppressions).length;
739740

740-
if (unusedSuppressionsCount > 0) {
741-
log.error(
742-
"There are suppressions left that do not occur anymore. Consider re-running the command with `--prune-suppressions`.",
743-
);
744-
debug(JSON.stringify(unusedSuppressions, null, 2));
741+
if (unusedSuppressionsCount > 0) {
742+
log.error(
743+
"There are suppressions left that do not occur anymore. Consider re-running the command with `--prune-suppressions`.",
744+
);
745+
debug(JSON.stringify(unusedSuppressions, null, 2));
746+
747+
return 2;
748+
}
745749
}
746750

747-
if (shouldExitForFatalErrors || unusedSuppressionsCount > 0) {
751+
if (shouldExitForFatalErrors) {
748752
return 2;
749753
}
750754

lib/options.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const optionator = require("optionator");
6666
* @property {string[]} [suppressRule] Suppress specific rules
6767
* @property {string} [suppressionsLocation] Path to the suppressions file or directory
6868
* @property {boolean} [pruneSuppressions] Prune unused suppressions
69+
* @property {boolean} [passOnUnprunedSuppressions] Ignore unused suppressions
6970
*/
7071

7172
//------------------------------------------------------------------------------
@@ -449,6 +450,12 @@ module.exports = function (usingFlatConfig) {
449450
default: "false",
450451
description: "Prune unused suppressions",
451452
},
453+
{
454+
option: "pass-on-unpruned-suppressions",
455+
type: "Boolean",
456+
default: "false",
457+
description: "Ignore unused suppressions",
458+
},
452459
{
453460
heading: "Miscellaneous",
454461
},

tests/bin/eslint.js

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,8 @@ describe("bin/eslint.js", () => {
528528
const ARGS_WITH_PRUNE_SUPPRESSIONS = ARGS_WITHOUT_SUPPRESSIONS.concat(
529529
"--prune-suppressions",
530530
);
531+
const ARGS_WITH_PASS_ON_UNPRUNED_SUPPRESSIONS =
532+
ARGS_WITHOUT_SUPPRESSIONS.concat("--pass-on-unpruned-suppressions");
531533

532534
const SUPPRESSIONS_FILE_WITH_INDENT = {
533535
[SOURCE_PATH]: {
@@ -1058,7 +1060,7 @@ describe("bin/eslint.js", () => {
10581060
return Promise.all([exitCodeAssertion, outputAssertion]);
10591061
});
10601062

1061-
it("exits with code 2, when there are unused violations", () => {
1063+
it("exits with code 2, when there are unused suppressions", () => {
10621064
const suppressions = structuredClone(
10631065
SUPPRESSIONS_FILE_ALL_ERRORS,
10641066
);
@@ -1071,7 +1073,87 @@ describe("bin/eslint.js", () => {
10711073

10721074
const child = runESLint(ARGS_WITHOUT_SUPPRESSIONS);
10731075

1074-
return assertExitCode(child, 2);
1076+
const exitCodeAssertion = assertExitCode(child, 2);
1077+
const outputAssertion = getOutput(child).then(output => {
1078+
assert.include(
1079+
output.stderr,
1080+
"There are suppressions left that do not occur anymore. Consider re-running the command with `--prune-suppressions`.",
1081+
);
1082+
});
1083+
1084+
return Promise.all([exitCodeAssertion, outputAssertion]);
1085+
});
1086+
1087+
it("exits with code 0, when there are unused suppressions and the --pass-on-unpruned-suppressions flag is used", () => {
1088+
const suppressions = structuredClone(
1089+
SUPPRESSIONS_FILE_ALL_ERRORS,
1090+
);
1091+
1092+
suppressions[SOURCE_PATH].indent.count = 10;
1093+
fs.writeFileSync(
1094+
SUPPRESSIONS_PATH,
1095+
JSON.stringify(suppressions, null, 2),
1096+
);
1097+
1098+
const child = runESLint(
1099+
ARGS_WITH_PASS_ON_UNPRUNED_SUPPRESSIONS,
1100+
);
1101+
1102+
const exitCodeAssertion = assertExitCode(child, 0);
1103+
const outputAssertion = getOutput(child).then(output => {
1104+
assert.notInclude(output.stderr, "suppressions left");
1105+
});
1106+
1107+
return Promise.all([exitCodeAssertion, outputAssertion]);
1108+
});
1109+
1110+
it("exits with code 1 if there are unsuppressed lint errors, when there are unused suppressions and the --pass-on-unpruned-suppressions flag is used (1)", () => {
1111+
const suppressions = structuredClone(
1112+
SUPPRESSIONS_FILE_ALL_ERRORS,
1113+
);
1114+
1115+
suppressions[SOURCE_PATH].indent.count = 10;
1116+
suppressions[SOURCE_PATH]["no-sparse-arrays"].count--;
1117+
fs.writeFileSync(
1118+
SUPPRESSIONS_PATH,
1119+
JSON.stringify(suppressions, null, 2),
1120+
);
1121+
1122+
const child = runESLint(
1123+
ARGS_WITH_PASS_ON_UNPRUNED_SUPPRESSIONS,
1124+
);
1125+
1126+
const exitCodeAssertion = assertExitCode(child, 1);
1127+
const outputAssertion = getOutput(child).then(output => {
1128+
assert.notInclude(output.stderr, "suppressions left");
1129+
});
1130+
1131+
return Promise.all([exitCodeAssertion, outputAssertion]);
1132+
});
1133+
1134+
it("exits with code 1 if there are unsuppressed lint errors, when there are unused suppressions and the --pass-on-unpruned-suppressions flag is used (2)", () => {
1135+
const suppressions = structuredClone(
1136+
SUPPRESSIONS_FILE_ALL_ERRORS,
1137+
);
1138+
1139+
suppressions[SOURCE_PATH].indent.count = 10;
1140+
fs.writeFileSync(
1141+
SUPPRESSIONS_PATH,
1142+
JSON.stringify(suppressions, null, 2),
1143+
);
1144+
1145+
const child = runESLint(
1146+
ARGS_WITH_PASS_ON_UNPRUNED_SUPPRESSIONS.concat(
1147+
"--rule=no-restricted-syntax:[error, 'IfStatement']",
1148+
),
1149+
);
1150+
1151+
const exitCodeAssertion = assertExitCode(child, 1);
1152+
const outputAssertion = getOutput(child).then(output => {
1153+
assert.notInclude(output.stderr, "suppressions left");
1154+
});
1155+
1156+
return Promise.all([exitCodeAssertion, outputAssertion]);
10751157
});
10761158

10771159
it("prunes the suppressions file, when the --prune-suppressions flag is used", () => {

0 commit comments

Comments
 (0)