Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: correctly iterate files matched by glob patterns #16831

Merged
merged 10 commits into from Feb 28, 2023
10 changes: 6 additions & 4 deletions lib/cli-engine/file-enumerator.js
Expand Up @@ -349,7 +349,7 @@ class FileEnumerator {
return this._iterateFilesWithFile(absolutePath);
}
if (globInputPaths && isGlobPattern(pattern)) {
return this._iterateFilesWithGlob(absolutePath, isDot);
return this._iterateFilesWithGlob(pattern, isDot);
}

return [];
Expand Down Expand Up @@ -398,15 +398,17 @@ class FileEnumerator {
_iterateFilesWithGlob(pattern, dotfiles) {
debug(`Glob: ${pattern}`);

const directoryPath = path.resolve(getGlobParent(pattern));
const globPart = pattern.slice(directoryPath.length + 1);
const { cwd } = internalSlotsMap.get(this);
const directoryPath = path.resolve(cwd, getGlobParent(pattern));
const absolutePath = path.resolve(cwd, pattern);
const globPart = absolutePath.slice(directoryPath.length + 1);

/*
* recursive if there are `**` or path separators in the glob part.
* Otherwise, patterns such as `src/*.js`, it doesn't need recursive.
*/
const recursive = /\*\*|\/|\\/u.test(globPart);
const selector = new Minimatch(pattern, minimatchOpts);
const selector = new Minimatch(absolutePath, minimatchOpts);

debug(`recursive? ${recursive}`);

Expand Down
68 changes: 68 additions & 0 deletions tests/lib/cli-engine/file-enumerator.js
Expand Up @@ -13,6 +13,7 @@ const path = require("path");
const os = require("os");
const { assert } = require("chai");
const sh = require("shelljs");
const sinon = require("sinon");
const {
Legacy: {
CascadingConfigArrayFactory
Expand Down Expand Up @@ -182,6 +183,73 @@ describe("FileEnumerator", () => {
});
});

// https://github.com/eslint/eslint/issues/14742
describe("with 5 directories ('{lib}', '{lib}/client', '{lib}/client/src', '{lib}/server', '{lib}/server/src') that contains two files '{lib}/client/src/one.js' and '{lib}/server/src/two.js'", () => {
const root = path.join(os.tmpdir(), "eslint/file-enumerator");
const files = {
"{lib}/client/src/one.js": "console.log('one.js');",
"{lib}/server/src/two.js": "console.log('two.js');",
"{lib}/client/.eslintrc.json": JSON.stringify({
rules: {
"no-console": "error"
},
env: {
mocha: true
}
}),
"{lib}/server/.eslintrc.json": JSON.stringify({
rules: {
"no-console": "off"
},
env: {
mocha: true
}
})
};
const { prepare, cleanup, getPath } = createCustomTeardown({
cwd: root,
files
});

/** @type {FileEnumerator} */
let enumerator;

beforeEach(async () => {
await prepare();
enumerator = new FileEnumerator({
cwd: path.resolve(getPath("{lib}/server"))
});
});

afterEach(cleanup);

describe("when running eslint in the server directory", () => {
it("should use the config '{lib}/server/.eslintrc.json' for '{lib}/server/src/two.js'.", () => {
const spy = sinon.spy(fs, "readdirSync");

const list = [
...enumerator.iterateFiles(["src/**/*.{js,json}"])
];

// should enter the directory '{lib}/server/src' directly
assert.strictEqual(spy.getCall(0).firstArg, path.join(root, "{lib}/server/src"));
assert.strictEqual(list.length, 1);
assert.strictEqual(list[0].config.length, 2);
assert.strictEqual(list[0].config[0].name, "DefaultIgnorePattern");
assert.strictEqual(list[0].config[1].filePath, getPath("{lib}/server/.eslintrc.json"));
assert.deepStrictEqual(
list.map(entry => entry.filePath),
[
path.join(root, "{lib}/server/src/two.js")
]
);

// destroy the spy
sinon.restore();
});
});
});

// This group moved from 'tests/lib/util/glob-utils.js' when refactoring to keep the cumulated test cases.
describe("with 'tests/fixtures/glob-utils' files", () => {
let fixtureDir;
Expand Down