diff --git a/docs/src/use/command-line-interface.md b/docs/src/use/command-line-interface.md index d88e35cfa04..da4faf70eba 100644 --- a/docs/src/use/command-line-interface.md +++ b/docs/src/use/command-line-interface.md @@ -110,6 +110,7 @@ Miscellaneous: --env-info Output execution environment information - default: false --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched --exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false + --no-warn-ignored Suppress warnings when the file list includes ignored files. *Flat Config Mode Only* --debug Output debugging information -h, --help Show help -v, --version Output the version number @@ -703,6 +704,18 @@ This option causes ESLint to exit with exit code 2 if one or more fatal parsing npx eslint --exit-on-fatal-error file.js ``` +#### `--no-warn-ignored` + +**Flat Config Mode Only.** This option suppresses both `File ignored by default` and `File ignored because of a matching ignore pattern` warnings when an ignored filename is passed explicitly. It is useful when paired with `--max-warnings 0` as it will prevent exit code 1 due to the aforementioned warning. + +* **Argument Type**: No argument. + +##### `--no-warn-ignored` example + +```shell +npx eslint --no-warn-ignored --max-warnings 0 ignored-file.js +``` + #### `--debug` This option outputs debugging information to the console. Add this flag to an ESLint command line invocation in order to get extra debugging information while the command runs. diff --git a/docs/src/use/configure/ignore.md b/docs/src/use/configure/ignore.md index ffc23428ee0..16f1bfbcdc9 100644 --- a/docs/src/use/configure/ignore.md +++ b/docs/src/use/configure/ignore.md @@ -149,7 +149,7 @@ You'll see this warning: ```text foo.js - 0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override. + 0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to disable file ignore settings or use "--no-warn-ignored" to suppress this warning. ✖ 1 problem (0 errors, 1 warning) ``` diff --git a/lib/cli.js b/lib/cli.js index a14930e9b0f..807d28a0d1b 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -91,7 +91,8 @@ async function translateOptions({ reportUnusedDisableDirectives, resolvePluginsRelativeTo, rule, - rulesdir + rulesdir, + warnIgnored }, configType) { let overrideConfig, overrideConfigFile; @@ -182,6 +183,7 @@ async function translateOptions({ if (configType === "flat") { options.ignorePatterns = ignorePattern; + options.warnIgnored = warnIgnored; } else { options.resolvePluginsRelativeTo = resolvePluginsRelativeTo; options.rulePaths = rulesdir; @@ -385,7 +387,9 @@ const cli = { if (useStdin) { results = await engine.lintText(text, { filePath: options.stdinFilename, - warnIgnored: true + + // flatConfig respects CLI flag and constructor warnIgnored, eslintrc forces true for backwards compatibility + warnIgnored: usingFlatConfig ? void 0 : true }); } else { results = await engine.lintFiles(files); diff --git a/lib/eslint/eslint-helpers.js b/lib/eslint/eslint-helpers.js index e25b10e8bc4..72828363c3d 100644 --- a/lib/eslint/eslint-helpers.js +++ b/lib/eslint/eslint-helpers.js @@ -594,9 +594,9 @@ function createIgnoreResult(filePath, baseDir) { const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules"); if (isInNodeModules) { - message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override."; + message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."; } else { - message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."; + message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."; } return { @@ -676,6 +676,7 @@ function processOptions({ overrideConfigFile = null, plugins = {}, reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that. + warnIgnored = true, ...unknownOptions }) { const errors = []; @@ -781,6 +782,9 @@ function processOptions({ ) { errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null."); } + if (typeof warnIgnored !== "boolean") { + errors.push("'warnIgnored' must be a boolean."); + } if (errors.length > 0) { throw new ESLintInvalidOptionsError(errors); } @@ -802,7 +806,8 @@ function processOptions({ globInputPaths, ignore, ignorePatterns, - reportUnusedDisableDirectives + reportUnusedDisableDirectives, + warnIgnored }; } diff --git a/lib/eslint/flat-eslint.js b/lib/eslint/flat-eslint.js index 4ef38611361..306c80de1d6 100644 --- a/lib/eslint/flat-eslint.js +++ b/lib/eslint/flat-eslint.js @@ -84,6 +84,7 @@ const LintResultCache = require("../cli-engine/lint-result-cache"); * when a string. * @property {Record} [plugins] An array of plugin implementations. * @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives. + * @property {boolean} warnIgnored Show warnings when the file list includes ignored files */ //------------------------------------------------------------------------------ @@ -749,7 +750,8 @@ class FlatESLint { fixTypes, reportUnusedDisableDirectives, globInputPaths, - errorOnUnmatchedPattern + errorOnUnmatchedPattern, + warnIgnored } = eslintOptions; const startTime = Date.now(); const fixTypesSet = fixTypes ? new Set(fixTypes) : null; @@ -795,7 +797,11 @@ class FlatESLint { * pattern, then notify the user. */ if (ignored) { - return createIgnoreResult(filePath, cwd); + if (warnIgnored) { + return createIgnoreResult(filePath, cwd); + } + + return void 0; } const config = configs.getConfig(filePath); @@ -908,7 +914,7 @@ class FlatESLint { const { filePath, - warnIgnored = false, + warnIgnored, ...unknownOptions } = options || {}; @@ -922,7 +928,7 @@ class FlatESLint { throw new Error("'options.filePath' must be a non-empty string or undefined"); } - if (typeof warnIgnored !== "boolean") { + if (typeof warnIgnored !== "boolean" && typeof warnIgnored !== "undefined") { throw new Error("'options.warnIgnored' must be a boolean or undefined"); } @@ -937,7 +943,8 @@ class FlatESLint { allowInlineConfig, cwd, fix, - reportUnusedDisableDirectives + reportUnusedDisableDirectives, + warnIgnored: constructorWarnIgnored } = eslintOptions; const results = []; const startTime = Date.now(); @@ -945,7 +952,9 @@ class FlatESLint { // Clear the last used config arrays. if (resolvedFilename && await this.isPathIgnored(resolvedFilename)) { - if (warnIgnored) { + const shouldWarnIgnored = typeof warnIgnored === "boolean" ? warnIgnored : constructorWarnIgnored; + + if (shouldWarnIgnored) { results.push(createIgnoreResult(resolvedFilename, cwd)); } } else { diff --git a/lib/options.js b/lib/options.js index 2bc4018afb5..ae9a5d5552a 100644 --- a/lib/options.js +++ b/lib/options.js @@ -55,6 +55,7 @@ const optionator = require("optionator"); * @property {string} [stdinFilename] Specify filename to process STDIN as * @property {boolean} quiet Report errors only * @property {boolean} [version] Output the version number + * @property {boolean} warnIgnored Show warnings when the file list includes ignored files * @property {string[]} _ Positional filenames or patterns */ @@ -139,6 +140,17 @@ module.exports = function(usingFlatConfig) { }; } + let warnIgnoredFlag; + + if (usingFlatConfig) { + warnIgnoredFlag = { + option: "warn-ignored", + type: "Boolean", + default: "true", + description: "Suppress warnings when the file list includes ignored files" + }; + } + return optionator({ prepend: "eslint [options] file.js [file.js] [dir]", defaults: { @@ -349,6 +361,7 @@ module.exports = function(usingFlatConfig) { default: "false", description: "Exit with exit code 2 in case of fatal error" }, + warnIgnoredFlag, { option: "debug", type: "Boolean", diff --git a/tests/lib/cli.js b/tests/lib/cli.js index 15556e5cfd1..ad0b8ad2327 100644 --- a/tests/lib/cli.js +++ b/tests/lib/cli.js @@ -801,6 +801,32 @@ describe("cli", () => { assert.isFalse(log.info.called); assert.strictEqual(exit, 0); }); + + it(`should suppress the warning if --no-warn-ignored is passed with configType:${configType}`, async () => { + const options = useFlatConfig + ? `--config ${getFixturePath("eslint.config_with_ignores.js")}` + : `--ignore-path ${getFixturePath(".eslintignore")}`; + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`${options} --no-warn-ignored ${filePath}`, null, useFlatConfig); + + assert.isFalse(log.info.called); + + // When eslintrc is used, we get an exit code of 2 because the --no-warn-ignored option is unrecognized. + assert.strictEqual(exit, useFlatConfig ? 0 : 2); + }); + + it(`should suppress the warning if --no-warn-ignored is passed and an ignored file is passed via stdin with configType:${configType}`, async () => { + const options = useFlatConfig + ? `--config ${getFixturePath("eslint.config_with_ignores.js")}` + : `--ignore-path ${getFixturePath(".eslintignore")}`; + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`${options} --no-warn-ignored --stdin --stdin-filename ${filePath}`, "foo", useFlatConfig); + + assert.isFalse(log.info.called); + + // When eslintrc is used, we get an exit code of 2 because the --no-warn-ignored option is unrecognized. + assert.strictEqual(exit, useFlatConfig ? 0 : 2); + }); }); describe("when given a pattern to ignore", () => { diff --git a/tests/lib/eslint/flat-eslint.js b/tests/lib/eslint/flat-eslint.js index 9e0ca12458f..31af43c5aea 100644 --- a/tests/lib/eslint/flat-eslint.js +++ b/tests/lib/eslint/flat-eslint.js @@ -211,7 +211,8 @@ describe("FlatESLint", () => { overrideConfig: "", overrideConfigFile: "", plugins: "", - reportUnusedDisableDirectives: "" + reportUnusedDisableDirectives: "", + warnIgnored: "" }), new RegExp(escapeStringRegExp([ "Invalid Options:", @@ -229,7 +230,8 @@ describe("FlatESLint", () => { "- 'overrideConfig' must be an object or null.", "- 'overrideConfigFile' must be a non-empty string, null, or true.", "- 'plugins' must be an object or null.", - "- 'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null." + "- 'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.", + "- 'warnIgnored' must be a boolean." ].join("\n")), "u") ); }); @@ -369,7 +371,31 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].filePath, getFixturePath("passing.js")); assert.strictEqual(results[0].messages[0].severity, 1); - assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."); + assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."); + assert.strictEqual(results[0].messages[0].output, void 0); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 1); + assert.strictEqual(results[0].fatalErrorCount, 0); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); + + it("should return a warning when given a filename by --stdin-filename in excluded files list if constructor warnIgnored is false, but lintText warnIgnored is true", async () => { + eslint = new FlatESLint({ + cwd: getFixturePath(".."), + overrideConfigFile: "fixtures/eslint.config_with_ignores.js", + warnIgnored: false + }); + + const options = { filePath: "fixtures/passing.js", warnIgnored: true }; + const results = await eslint.lintText("var bar = foo;", options); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, getFixturePath("passing.js")); + assert.strictEqual(results[0].messages[0].severity, 1); + assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."); assert.strictEqual(results[0].messages[0].output, void 0); assert.strictEqual(results[0].errorCount, 0); assert.strictEqual(results[0].warningCount, 1); @@ -397,18 +423,31 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 0); }); - it("should suppress excluded file warnings by default", async () => { + it("should not return a warning when given a filename by --stdin-filename in excluded files list if constructor warnIgnored is false", async () => { eslint = new FlatESLint({ cwd: getFixturePath(".."), - overrideConfigFile: "fixtures/eslint.config_with_ignores.js" + overrideConfigFile: "fixtures/eslint.config_with_ignores.js", + warnIgnored: false }); const options = { filePath: "fixtures/passing.js" }; const results = await eslint.lintText("var bar = foo;", options); - // should not report anything because there are no errors + // should not report anything because the warning is suppressed assert.strictEqual(results.length, 0); }); + it("should show excluded file warnings by default", async () => { + eslint = new FlatESLint({ + cwd: getFixturePath(".."), + overrideConfigFile: "fixtures/eslint.config_with_ignores.js" + }); + const options = { filePath: "fixtures/passing.js" }; + const results = await eslint.lintText("var bar = foo;", options); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."); + }); + it("should return a message when given a filename by --stdin-filename in excluded files list and ignore is off", async () => { eslint = new FlatESLint({ cwd: getFixturePath(".."), @@ -685,7 +724,7 @@ describe("FlatESLint", () => { ignore: false }); const results = await eslint.lintText("var bar = foo;", { filePath: "node_modules/passing.js", warnIgnored: true }); - const expectedMsg = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override."; + const expectedMsg = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."; assert.strictEqual(results.length, 1); assert.strictEqual(results[0].filePath, getFixturePath("node_modules/passing.js")); @@ -1311,7 +1350,7 @@ describe("FlatESLint", () => { cwd: getFixturePath("cli-engine") }); const results = await eslint.lintFiles(["node_modules/foo.js"]); - const expectedMsg = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override."; + const expectedMsg = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."; assert.strictEqual(results.length, 1); assert.strictEqual(results[0].errorCount, 0); @@ -1329,7 +1368,7 @@ describe("FlatESLint", () => { cwd: getFixturePath("cli-engine") }); const results = await eslint.lintFiles(["nested_node_modules/subdir/node_modules/text.js"]); - const expectedMsg = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override."; + const expectedMsg = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."; assert.strictEqual(results.length, 1); assert.strictEqual(results[0].errorCount, 0); @@ -1348,7 +1387,7 @@ describe("FlatESLint", () => { ignorePatterns: ["*.js"] }); const results = await eslint.lintFiles(["node_modules_cleaner.js"]); - const expectedMsg = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."; + const expectedMsg = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."; assert.strictEqual(results.length, 1); assert.strictEqual(results[0].errorCount, 0); @@ -1361,6 +1400,16 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].suppressedMessages.length, 0); }); + it("should suppress the warning when a file in the node_modules folder passed explicitly and warnIgnored is false", async () => { + eslint = new FlatESLint({ + cwd: getFixturePath("cli-engine"), + warnIgnored: false + }); + const results = await eslint.lintFiles(["node_modules/foo.js"]); + + assert.strictEqual(results.length, 0); + }); + it("should report on globs with explicit inclusion of dotfiles", async () => { eslint = new FlatESLint({ cwd: getFixturePath("cli-engine"), @@ -1483,7 +1532,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].filePath, filePath); assert.strictEqual(results[0].messages[0].severity, 1); - assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."); + assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."); assert.strictEqual(results[0].errorCount, 0); assert.strictEqual(results[0].warningCount, 1); assert.strictEqual(results[0].fatalErrorCount, 0); @@ -1492,6 +1541,18 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].suppressedMessages.length, 0); }); + it("should suppress the warning when an explicitly given file is ignored and warnIgnored is false", async () => { + eslint = new FlatESLint({ + overrideConfigFile: "eslint.config_with_ignores.js", + cwd: getFixturePath(), + warnIgnored: false + }); + const filePath = getFixturePath("passing.js"); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 0); + }); + it("should return a warning about matching ignore patterns when an explicitly given dotfile is ignored", async () => { eslint = new FlatESLint({ overrideConfigFile: "eslint.config_with_ignores.js", @@ -1503,7 +1564,7 @@ describe("FlatESLint", () => { assert.strictEqual(results.length, 1); assert.strictEqual(results[0].filePath, filePath); assert.strictEqual(results[0].messages[0].severity, 1); - assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."); + assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning."); assert.strictEqual(results[0].errorCount, 0); assert.strictEqual(results[0].warningCount, 1); assert.strictEqual(results[0].fatalErrorCount, 0); @@ -5400,7 +5461,7 @@ describe("FlatESLint", () => { { ruleId: null, fatal: false, - message: "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override.", + message: "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.", severity: 1, nodeType: null } diff --git a/tests/lib/options.js b/tests/lib/options.js index d8f795b78a2..b663e8623e3 100644 --- a/tests/lib/options.js +++ b/tests/lib/options.js @@ -415,4 +415,18 @@ describe("options", () => { }); }); + describe("--no-warn-ignored", () => { + it("should return false when --no-warn-ignored is passed", () => { + const currentOptions = flatOptions.parse("--no-warn-ignored"); + + assert.isFalse(currentOptions.warnIgnored); + }); + + it("should return true when --warn-ignored is passed", () => { + const currentOptions = flatOptions.parse("--warn-ignored"); + + assert.isTrue(currentOptions.warnIgnored); + }); + }); + });