diff --git a/lib/eslint/eslint-helpers.js b/lib/eslint/eslint-helpers.js index 442686e56bf..8001ddd32ee 100644 --- a/lib/eslint/eslint-helpers.js +++ b/lib/eslint/eslint-helpers.js @@ -15,6 +15,7 @@ const fsp = fs.promises; const isGlob = require("is-glob"); const globby = require("globby"); const hash = require("../cli-engine/hash"); +const minimatch = require("minimatch"); //----------------------------------------------------------------------------- // Errors @@ -126,7 +127,7 @@ async function findFiles({ const filePaths = patterns.map(filePath => path.resolve(cwd, filePath)); const stats = await Promise.all( filePaths.map( - filePath => fsp.stat(filePath).catch(() => {}) + filePath => fsp.stat(filePath).catch(() => { }) ) ); @@ -157,6 +158,11 @@ async function findFiles({ return false; } + // patterns starting with ** always apply + if (filePattern.startsWith("**")) { + return true; + } + // patterns ending with * are not used for file search if (filePattern.endsWith("*")) { return false; @@ -167,11 +173,27 @@ async function findFiles({ return false; } - // check if the pattern would be inside the cwd or not + // check if the pattern would be inside the config base path or not const fullFilePattern = path.join(cwd, filePattern); - const relativeFilePattern = path.relative(configs.basePath, fullFilePattern); + const patternRelativeToConfigBasePath = path.relative(configs.basePath, fullFilePattern); + + if (patternRelativeToConfigBasePath.startsWith("..")) { + return false; + } + + // check if the pattern matches + if (minimatch(filePath, path.dirname(fullFilePattern), { partial: true })) { + return true; + } + + // check if the pattern is inside the directory or not + const patternRelativeToFilePath = path.relative(filePath, fullFilePattern); + + if (patternRelativeToFilePath.startsWith("..")) { + return false; + } - return !relativeFilePattern.startsWith(".."); + return true; }) .map(filePattern => { if (filePattern.startsWith("**")) { diff --git a/tests/fixtures/shallow-glob/eslint.config.js b/tests/fixtures/shallow-glob/eslint.config.js new file mode 100644 index 00000000000..211719ddb90 --- /dev/null +++ b/tests/fixtures/shallow-glob/eslint.config.js @@ -0,0 +1,5 @@ +module.exports = [ + { + files: ["subdir/*.js"] + } +]; diff --git a/tests/fixtures/shallow-glob/subdir/broken.js b/tests/fixtures/shallow-glob/subdir/broken.js new file mode 100644 index 00000000000..d280fee2fdc --- /dev/null +++ b/tests/fixtures/shallow-glob/subdir/broken.js @@ -0,0 +1 @@ +module.exports = /* intentional syntax error */ diff --git a/tests/fixtures/shallow-glob/subdir/subsubdir/broken.js b/tests/fixtures/shallow-glob/subdir/subsubdir/broken.js new file mode 100644 index 00000000000..2e1fe2e76a2 --- /dev/null +++ b/tests/fixtures/shallow-glob/subdir/subsubdir/broken.js @@ -0,0 +1 @@ +function( {} // intentional syntax error diff --git a/tests/fixtures/shallow-glob/subdir/subsubdir/plain.jsx b/tests/fixtures/shallow-glob/subdir/subsubdir/plain.jsx new file mode 100644 index 00000000000..e901f01b487 --- /dev/null +++ b/tests/fixtures/shallow-glob/subdir/subsubdir/plain.jsx @@ -0,0 +1 @@ +foo; diff --git a/tests/fixtures/shallow-glob/target-dir/passing.js b/tests/fixtures/shallow-glob/target-dir/passing.js new file mode 100644 index 00000000000..ec01c2c1416 --- /dev/null +++ b/tests/fixtures/shallow-glob/target-dir/passing.js @@ -0,0 +1 @@ +module.exports = true; diff --git a/tests/lib/eslint/flat-eslint.js b/tests/lib/eslint/flat-eslint.js index 1af6c2e1d8b..47d8cbbfbd8 100644 --- a/tests/lib/eslint/flat-eslint.js +++ b/tests/lib/eslint/flat-eslint.js @@ -784,6 +784,72 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].suppressedMessages.length, 0); }); + // https://github.com/eslint/eslint/issues/16260 + describe("Globbing based on configs", () => { + it("should report zero messages when given a directory with a .js and config file specifying a subdirectory", async () => { + eslint = new FlatESLint({ + ignore: false, + cwd: getFixturePath("shallow-glob") + }); + const results = await eslint.lintFiles(["target-dir"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].suppressedMessages.length, 0); + }); + + it("should glob for .jsx file in a subdirectory of the passed-in directory and not glob for any other patterns", async () => { + eslint = new FlatESLint({ + ignore: false, + overrideConfigFile: true, + overrideConfig: { + files: ["subdir/**/*.jsx", "target-dir/*.js"], + languageOptions: { + parserOptions: { + jsx: true + } + } + }, + cwd: getFixturePath("shallow-glob") + }); + const results = await eslint.lintFiles(["subdir/subsubdir"]); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].messages.length, 1); + assert(results[0].messages[0].fatal, "Fatal error expected."); + assert.strictEqual(results[0].suppressedMessages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[1].suppressedMessages.length, 0); + }); + + it("should glob for all files in subdir when passed-in on the command line with a partial matching glob", async () => { + eslint = new FlatESLint({ + ignore: false, + overrideConfigFile: true, + overrideConfig: { + files: ["s*/subsubdir/*.jsx", "target-dir/*.js"], + languageOptions: { + parserOptions: { + jsx: true + } + } + }, + cwd: getFixturePath("shallow-glob") + }); + const results = await eslint.lintFiles(["subdir"]); + + assert.strictEqual(results.length, 3); + assert.strictEqual(results[0].messages.length, 1); + assert(results[0].messages[0].fatal, "Fatal error expected."); + assert.strictEqual(results[0].suppressedMessages.length, 0); + assert.strictEqual(results[1].messages.length, 1); + assert(results[0].messages[0].fatal, "Fatal error expected."); + assert.strictEqual(results[1].suppressedMessages.length, 0); + assert.strictEqual(results[2].messages.length, 0); + assert.strictEqual(results[2].suppressedMessages.length, 0); + }); + }); + it("should report zero messages when given a '**' pattern with a .js and a .js2 file", async () => { eslint = new FlatESLint({ ignore: false,