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

feat: Implement alternate config lookup #18742

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open

feat: Implement alternate config lookup #18742

wants to merge 26 commits into from

Conversation

nzakas
Copy link
Member

@nzakas nzakas commented Aug 1, 2024

Prerequisites checklist

What is the purpose of this pull request? (put an "X" next to an item)

[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofix to a rule
[ ] Add a CLI option
[x] Add something to the core
[ ] Other, please explain:

What changes did you make? (Give an overview)

Implemented the design from eslint/rfcs#120:

  • New unstable_config_lookup_from_file flag
  • Refactored config lookup into ConfigLoader and LegacyConfigLoader
  • Refactored findFiles() to use new config scheme.
  • Updated documentation
  • Updated tests

Fixes #18385

Is there anything you'd like reviewers to focus on?

Marking as a draft because I'd like us to merge #18134 first.

Note that I did not remove the config loading logic from the ESLint class, I just duplicated it in the config-loader.js file in order to have an easier merge once #18134 is merged. (This accounts for the linting error in eslint.js)

@eslint-github-bot eslint-github-bot bot added the feature This change adds a new feature to ESLint label Aug 1, 2024
@github-actions github-actions bot added cli Relates to ESLint's command-line interface core Relates to ESLint's core APIs and features labels Aug 1, 2024
Copy link

netlify bot commented Aug 1, 2024

Deploy Preview for docs-eslint canceled.

Name Link
🔨 Latest commit e16493b
🔍 Latest deploy log https://app.netlify.com/sites/docs-eslint/deploys/66e1dc315768ad000857ec8b

@nzakas nzakas marked this pull request as ready for review August 9, 2024 19:08
@nzakas nzakas requested a review from a team as a code owner August 9, 2024 19:08
@fasttime fasttime added the accepted There is consensus among the team that this change meets the criteria for inclusion label Aug 20, 2024
@fasttime
Copy link
Member

There are merge conflicts now.

@nzakas
Copy link
Member Author

nzakas commented Aug 21, 2024

I'll look at those, but if you can please review ASAP I'd appreciate it. This is a complicated change and there will continue to be more conflicts until we get this merged. It's already been three weeks since I posted it.

docs/src/use/configure/configuration-files.md Outdated Show resolved Hide resolved
lib/cli.js Show resolved Hide resolved
lib/config/config-loader.js Outdated Show resolved Hide resolved
lib/config/config-loader.js Outdated Show resolved Hide resolved
lib/config/config-loader.js Show resolved Hide resolved
tests/lib/eslint/eslint.js Show resolved Hide resolved
Comment on lines 303 to 297
const configs = rawPatterns[matchIndex] !== "." && !entry.path.includes("/")
? void 0
: await configLoader.loadConfigArrayForDirectory(absolutePath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Special-casing only immediate children would cause inconsistent behavior, in my opinion. For example, if the root config file ignores foo and bar/baz directories, and both of them have their own config files, eslint foo would work, while eslint bar/baz wouldn't.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm having trouble following what it is you're describing. Can you put together a test case that will fail?

And do you have other suggestions for how you might approach this? It turns out getting the exact behavior in the RFC is pretty difficult and this was the only approach I found that passed all of the tests we had described there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a test case: bb439b1

Copy link
Member

@mdjermanovic mdjermanovic Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One approach could be to search per each pattern and start loading configs from globParent() of the pattern. That's how eslintrc's FileEnumerator works. A downside is performance as the same directories might be traversed multiple times.

Another approach could be to keep searching with multiple patterns at once but check if the directory path is a part of the globParent() of at least one of the patterns, and in that case enter the directory even if it's ignored by a config in a parent directory. Though, this seems complicated as only those patterns should match in the directory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's another test that I think should pass but is failing with the current iteration of this PR: 7e21937

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another possible approach: perhaps we could always group patterns by their globParent() and start loading configs from that path.

@fasttime
Copy link
Member

There is a new merge conflict now and the tests were not run.

@nzakas
Copy link
Member Author

nzakas commented Aug 28, 2024

Added in @mdjermanovic's test and removed the check for immediate children. Somehow, this caused all tests to pass. 😄 Maybe I'm missing something, though.

@fasttime
Copy link
Member

fasttime commented Sep 2, 2024

LGTM, thanks! Leaving open for @mdjermanovic to complete his review.

fasttime
fasttime previously approved these changes Sep 2, 2024
Comment on lines 295 to 357
const configs = rawPatterns[matchIndex] !== "."
? void 0
: await configLoader.loadConfigArrayForDirectory(absolutePath);

return !configs?.isDirectoryIgnored(absolutePath);
Copy link
Member

@mdjermanovic mdjermanovic Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, on this branch, a pattern like **/*.js would traverse all directories, which is not what we want, I believe.

To reproduce, add console.log(entry.path); at the beginning of async directoryFilter(entry) and run npx eslint "**/*.js".

.cache
.git
.git/hooks
.git/info
.git/logs
.git/logs/refs
.git/logs/refs/heads
.git/logs/refs/remotes
.git/logs/refs/remotes/origin
.git/logs/refs/remotes/origin/chore
.git/logs/refs/remotes/origin/docs
.git/logs/refs/remotes/origin/fix
.git/logs/refs/remotes/origin/renovate
.git/objects
.git/objects/00
.git/objects/01
.git/objects/02
.git/objects/03
.git/objects/04
.git/objects/05
.git/objects/06
.git/objects/07
.git/objects/08
.git/objects/09
.git/objects/0a
.git/objects/0b
.git/objects/0c
.git/objects/0d
.git/objects/0e

... (~10,000 directories)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm yeah, that's not right. I'll take a look.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not able to reproduce this (Windows 10). Are you sure you're using the right command (npx eslint would download ESLint if you don't have it locally?). Here's what I'm getting:

$ node ./bin/eslint.js "**/*.js"
(node:5064) ESLintIgnoreWarning: The ".eslintignore" file is no longer supported. 
Switch to using the "ignores" property in "eslint.config.js": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files
(Use `node --trace-warnings ...` to show 
where the warning was created)
.cache
.git
.github
.nyc_output
.trunk
.vscode
bin
build
conf
coverage
docs
lib
messages
node_modules
packages
templates
tests
tmp
tools

Oops! Something went wrong! :(

ESLint: 9.9.1

No files matching the pattern "**C:/Program Files/Git/*.js" were found.
Please check for typing mistakes in the pattern.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Something went wrong! :(

ESLint: 9.9.1

No files matching the pattern "**C:/Program Files/Git/*.js" were found.
Please check for typing mistakes in the pattern.

That "**C:/Program Files/Git/*.js" looks weird, I'm not sure where it did come from when running $ node ./bin/eslint.js "**/*.js" (maybe your shell is doing some expansion despite the quotes?).

I tried again, on this branch, with a clean npm install, and in cmd.exe which doesn't expand globs. I also added console.log({ patterns }); at the beginning of async function globSearch to make sure that the pattern is passed through.

C:\projects\eslint>node ./bin/eslint.js "**/*.js"
(node:7136) ESLintIgnoreWarning: The ".eslintignore" file is no longer supported. Switch to using the "ignores" property in "eslint.config.js": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files
(Use `node --trace-warnings ...` to show where the warning was created)

{ patterns: [ 'C:\\projects\\eslint\\**\\*.js' ] }
.cache
.git
.git/hooks
.git/info
.git/logs
.git/logs/refs
.git/logs/refs/heads
.git/logs/refs/remotes
.git/logs/refs/remotes/origin
.git/logs/refs/remotes/origin/chore
.git/logs/refs/remotes/origin/docs
.git/logs/refs/remotes/origin/fix
.git/logs/refs/remotes/origin/renovate
.git/objects
.git/objects/00
.git/objects/01
.git/objects/02
.git/objects/03
.git/objects/04
.git/objects/05
.git/objects/06
.git/objects/07
.git/objects/08
.git/objects/09
.git/objects/0a
.git/objects/0b
.git/objects/0c

...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I can see this in cmd but not in Git Bash. It looks like Git Bash is representing the path in Windows differently:

  patterns: [
    'C:\\Users\\nzaka\\projects\\eslint\\eslint\\**C:\\Program Files\\Git\\*.js'  
  ]

I'll take a look at that, too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it looks like Git for Windows is expanding the pattern even if it's quoted.
git-for-windows/git#5131

}
}
});
const failFilePath = fs.realpathSync(ge
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected behavior in this case when cwd has some files in it? When I add a file (any file, e.g.. file with no extension) directly into subdir-only-config, the test fails with "Error: Could not find config file.".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we explicitly defined this behavior. Technically, throwing an error is correct because our logic says "for a given file, if you can't find a config file that tells you what to do with it, throw an error."

I'm not sure how common it would be for someone to run eslint . in a directory without a config file. This might be a case where we leave this behavior as-is and see what feedback we get. Otherwise, I'd be back to checking "if pattern is . and file is an immediate child of ., then ignore it".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that throwing an error when a pattern matches a file for which a config file cannot be found is the correct behavior.

@nzakas
Copy link
Member Author

nzakas commented Sep 10, 2024

@mdjermanovic I've been fighting with this for the last three days and have made zero progress. Do you want to take a stab at it?

@nzakas
Copy link
Member Author

nzakas commented Sep 11, 2024

Hold that thought! I might have figured it out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion cli Relates to ESLint's command-line interface core Relates to ESLint's core APIs and features feature This change adds a new feature to ESLint
Projects
Status: Second Review Needed
Development

Successfully merging this pull request may close these issues.

Change Request: Make it easier to inherit flat configs from the repo root
4 participants