From 0c415cda5d76dbe5120ab9f3c4c81320538e35f0 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Tue, 9 May 2023 09:44:17 +0200 Subject: [PATCH] fix: validate `ignorePatterns` constructor option in `FlatESLint` class (#17139) * fix: validate `ignorePatterns` constructor option in `FlatESLint` class * disallow string value --- lib/eslint/eslint-helpers.js | 3 ++ lib/eslint/flat-eslint.js | 50 +++++++++++++++------------------ tests/lib/eslint/flat-eslint.js | 40 ++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/lib/eslint/eslint-helpers.js b/lib/eslint/eslint-helpers.js index 54b0c69d7c8..29a9b4a9b8a 100644 --- a/lib/eslint/eslint-helpers.js +++ b/lib/eslint/eslint-helpers.js @@ -758,6 +758,9 @@ function processOptions({ if (typeof ignore !== "boolean") { errors.push("'ignore' must be a boolean."); } + if (!isArrayOfNonEmptyString(ignorePatterns) && ignorePatterns !== null) { + errors.push("'ignorePatterns' must be an array of non-empty strings or null."); + } if (typeof overrideConfig !== "object") { errors.push("'overrideConfig' must be an object or null."); } diff --git a/lib/eslint/flat-eslint.js b/lib/eslint/flat-eslint.js index 458509567b4..82177fff9d2 100644 --- a/lib/eslint/flat-eslint.js +++ b/lib/eslint/flat-eslint.js @@ -76,7 +76,7 @@ const LintResultCache = require("../cli-engine/lint-result-cache"); * @property {string[]} [fixTypes] Array of rule types to apply fixes for. * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} [ignore] False disables all ignore patterns except for the default ones. - * @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. + * @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`. * @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance * @property {boolean|string} [overrideConfigFile] Searches for default config file when falsy; * doesn't do any config file lookup when `true`; considered to be a config filename @@ -404,45 +404,39 @@ async function calculateConfigArray(eslint, { // add in any configured defaults configs.push(...slots.defaultConfigs); - let allIgnorePatterns = []; - // append command line ignore patterns - if (ignorePatterns) { - if (typeof ignorePatterns === "string") { - allIgnorePatterns.push(ignorePatterns); - } else { - allIgnorePatterns.push(...ignorePatterns); - } - } + if (ignorePatterns && ignorePatterns.length > 0) { - /* - * If the config file basePath is different than the cwd, then - * the ignore patterns won't work correctly. Here, we adjust the - * ignore pattern to include the correct relative path. Patterns - * loaded from ignore files are always relative to the cwd, whereas - * the config file basePath can be an ancestor of the cwd. - */ - if (basePath !== cwd && allIgnorePatterns.length) { + let relativeIgnorePatterns; - const relativeIgnorePath = path.relative(basePath, cwd); + /* + * If the config file basePath is different than the cwd, then + * the ignore patterns won't work correctly. Here, we adjust the + * ignore pattern to include the correct relative path. Patterns + * passed as `ignorePatterns` are relative to the cwd, whereas + * the config file basePath can be an ancestor of the cwd. + */ + if (basePath === cwd) { + relativeIgnorePatterns = ignorePatterns; + } else { - allIgnorePatterns = allIgnorePatterns.map(pattern => { - const negated = pattern.startsWith("!"); - const basePattern = negated ? pattern.slice(1) : pattern; + const relativeIgnorePath = path.relative(basePath, cwd); - return (negated ? "!" : "") + - path.posix.join(relativeIgnorePath, basePattern); - }); - } + relativeIgnorePatterns = ignorePatterns.map(pattern => { + const negated = pattern.startsWith("!"); + const basePattern = negated ? pattern.slice(1) : pattern; - if (allIgnorePatterns.length) { + return (negated ? "!" : "") + + path.posix.join(relativeIgnorePath, basePattern); + }); + } /* * Ignore patterns are added to the end of the config array * so they can override default ignores. */ configs.push({ - ignores: allIgnorePatterns + ignores: relativeIgnorePatterns }); } diff --git a/tests/lib/eslint/flat-eslint.js b/tests/lib/eslint/flat-eslint.js index ce79693af1b..bfccce1aa1e 100644 --- a/tests/lib/eslint/flat-eslint.js +++ b/tests/lib/eslint/flat-eslint.js @@ -182,6 +182,7 @@ describe("FlatESLint", () => { fixTypes: ["xyz"], globInputPaths: "", ignore: "", + ignorePatterns: "", overrideConfig: "", overrideConfigFile: "", plugins: "", @@ -199,6 +200,7 @@ describe("FlatESLint", () => { "- 'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".", "- 'globInputPaths' must be a boolean.", "- 'ignore' must be a boolean.", + "- 'ignorePatterns' must be an array of non-empty strings or null.", "- 'overrideConfig' must be an object or null.", "- 'overrideConfigFile' must be a non-empty string, null, or true.", "- 'plugins' must be an object or null.", @@ -207,6 +209,34 @@ describe("FlatESLint", () => { ); }); + it("should throw readable messages if 'ignorePatterns' is not an array of non-empty strings.", () => { + const invalidIgnorePatterns = [ + () => {}, + false, + {}, + "", + "foo", + [[]], + [() => {}], + [false], + [{}], + [""], + ["foo", ""], + ["foo", "", "bar"], + ["foo", false, "bar"] + ]; + + invalidIgnorePatterns.forEach(ignorePatterns => { + assert.throws( + () => new FlatESLint({ ignorePatterns }), + new RegExp(escapeStringRegExp([ + "Invalid Options:", + "- 'ignorePatterns' must be an array of non-empty strings or null." + ].join("\n")), "u") + ); + }); + }); + it("should throw readable messages if 'plugins' option contains empty key", () => { assert.throws( () => new FlatESLint({ @@ -3549,7 +3579,7 @@ describe("FlatESLint", () => { const engine = new FlatESLint({ cwd, overrideConfigFile: true, - ignorePatterns: "!node_modules/package/**" + ignorePatterns: ["!node_modules/package/**"] }); const result = await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "package", "file.js")); @@ -4080,7 +4110,7 @@ describe("FlatESLint", () => { const engine = new FlatESLint({ overrideConfigFile: true, cwd: path.join(fixtureDir, "foo", "bar"), - ignorePatterns: "*/**", // ignore all subdirectories of `cwd` + ignorePatterns: ["*/**"], // ignore all subdirectories of `cwd` overrideConfig: { rules: { eqeqeq: "warn" @@ -4098,7 +4128,7 @@ describe("FlatESLint", () => { const engine = new FlatESLint({ overrideConfigFile: true, cwd: path.join(fixtureDir, "foo", "bar"), - ignorePatterns: "**" + ignorePatterns: ["**"] }); const results = await engine.lintText("", { warnIgnored: true }); @@ -4111,7 +4141,7 @@ describe("FlatESLint", () => { const engine = new FlatESLint({ overrideConfigFile: true, cwd: getFixturePath(), - ignorePatterns: "passing*", + ignorePatterns: ["passing*"], overrideConfig: { rules: { "no-undef": 2, @@ -4225,7 +4255,7 @@ describe("FlatESLint", () => { it("should ignore messages not related to a rule", async () => { const engine = new FlatESLint({ overrideConfigFile: true, - ignorePatterns: "ignored.js", + ignorePatterns: ["ignored.js"], overrideConfig: { rules: { "no-var": "warn"