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
New: Add overrides
and files
configuration options (refs #3611)
#7177
Conversation
LGTM |
@CrabDude wow, thanks for digging into this. These are some major changes that I don't have enough energy to get to today, but I did want to leave a note to let you know I'll definitely look at this during this week (hopefully Wednesday). I do have a couple of questions in the meantime:
Thanks again! |
@nzakas Thanks for your consideration. I added an expanded example just before you commented, so you may not yet have seen it, but it illustrates that As a result of this implementation, ATM, the same config merging rules apply to sub-configs. So...
Thanks for the feedback. I'll add some more tests for the |
Still working on a fix for nested overrides. Merging it with the fix for extends in overrides. Will push |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again, thanks for doing this. I think overall you're heading in the right direction.
One thing I'd like to explore, though, is if we can pull some of the logic out of config.js
and move it into either config-ops.js
or config-file.js
. The config.js
file is really difficult to unit test, so we started breaking it apart into the smaller files (didn't quite finish).
For instance, it seems like the logic to say "given this config and this filename, give me a config that has overrides
merged in could potentially live in config-ops.js
(as it doesn't rely on the filesystem). And maybe the actual merging could take place in config-file.js
somewhere. I'm open to feedback on this point, I just really want to keep config.js
as small as possible because of the difficulty in unit testing.
@@ -703,6 +703,65 @@ module.exports = { | |||
} | |||
``` | |||
|
|||
## Glob Pattern based Configuration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe "Configuration Based on Glob Patterns"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #8081.
@@ -703,6 +703,65 @@ module.exports = { | |||
} | |||
``` | |||
|
|||
## Glob Pattern based Configuration | |||
|
|||
Sometimes a more fine-controlled configuration is necessary e.g. if the configuration for files within the same directory has to be different. Therefore you can provide configurations that will only apply to files that match a specific glob pattern (based on [minimatch](https://github.com/isaacs/minimatch)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to not use minimatch? I'm concerned that the differences between minimatch and globs that we use on the command line will be confusing for users (why will one thing work on the command line but not in the config?).
See also: https://github.com/eslint/eslint/blob/master/lib/util/glob-util.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a look at using the existing glob code, but it seems to be a different use case (it uses node-glob
, which is the filesystem-aware version of minimatch
). In this case, we've already retrieved a list of candidate files from the filesystem, and we just use minimatch
to filter them. Since node-glob
uses minimatch
under the hood, I think the results should be essentially the same, so I don't think there's too much potential for confusion.
this.ignore = options.ignore; | ||
this.ignorePath = options.ignorePath; | ||
this.cache = {}; | ||
this.mergedCache = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some comments describing what each of these does? Lots of "caches" are hard to distinguish from one another.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many comments added in #8081.
|
||
|
||
/** | ||
* Get the vector of applicable configs from the hierarchy for a given file (glob matching occurs here). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unclear on what "vector" is in this context. Can you use a different word?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to think of a better word for this but came up blank. I fell back to describing it in detail in comments in the JSDoc:
A vector is an array of config file paths, each optionally followed by one or more numbers that correspond to the indices of nested config blocks within the config file's overrides section.
|
||
/** | ||
* Merges all configurations for a given config vector | ||
* @param {Array} vector array of config file paths or relative override indices |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An array of what? If strings, then use string[]
. Otherwise, please use a type-specific array. (And same note about the word "vector", I just don't understand what that means.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #8081.
@@ -54,6 +54,10 @@ | |||
"json-stable-stringify": "^1.0.0", | |||
"levn": "^0.3.0", | |||
"lodash": "^4.0.0", | |||
"lodash.clonedeep": "^3.0.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already include lodash as a whole, so there's no need to include individual methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #8081.
if (this.useSpecificConfig) { | ||
debug("Merging command line config file"); | ||
/** | ||
* Get the local config hierarchy for a given directory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to previous note about adding comments, it's unclear what "local" means here. Can you be more specific in this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSDoc has been rewritten and expanded in #8081.
@@ -48,6 +48,12 @@ | |||
".eslintrc": "{\n \"env\": {\n \"commonjs\": true\n }\n}\n" | |||
} | |||
}, | |||
"overrides": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just double-checking: are you sure this won't interfere with already-existing tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the tests still pass, so this seems to be OK! :)
Should've mentioned I've been OOO since 9/22. Will pick up where we left off this week. |
Just a heads up: we have moved to a new CLA checker on pull requests. Even if you've previously signed our CLA, we will need to you sign the new one. To do so, look at the status checks for licence/cla and click the "Details" link. Sorry for the inconvenience. |
@CrabDude thanks again for all your hard work on this one! Definitely appreciated. This seems like such an awesome change, it would be a shame to let it fall through the cracks. Is there anything that we could do to help move this forward? |
@vitorbal Per #7177 (comment), it's feature complete, but needs to be refactored into more testable files / architecture, with the corresponding tests updated (and added). If you were so motivated, I suppose you could write some tests for the spec defined here, as it would help with the refactor. Also, the encouragement helps. 🙂 |
@CrabDude Friendly ping again. Anything you need from us to help finish this up? |
@kaicataldo While I still intend to get back to this, I'm not sure when that will be. If you or anyone wants to speed this up, please feel free to write tests that cover the implementation as documented above. |
This is on my todo list for when I'm able to spend some extended time at a computer. |
I rebased this onto latest master and fixed the conflicts (plus a couple of the really easy fixes from @nzakas' code review above): https://github.com/smably/eslint/tree/overrides I haven't had a chance to really dig into the logic, and I don't know whether I will have the time to do it, but I hope this is at least a start for whoever takes this on. @nzakas -- do you still intend to come back to this? If not, I might be able to dig a bit deeper into this sometime later this week. |
@smably Thanks so much for wading into this! Definitely motivating to not be working on this alone. I don't believe @nzakas ever had/has any plans to work on this. I plan to finish this, but had to come back to it. I should be able to actively start working on this again. Would you be open to working on it together a bit? We can figure out how specifically you would like to contribute once I dive back in. Off the top of my head there are 2 tasks:
|
Thanks! To clarify re. @nzakas's plans to work on this, I think @smably was referring to #7177 (comment). |
@CrabDude Awesome, glad to hear you're able to pick this up again. I'd love to help, though I think it'll be a few days, at least, before I can dedicate any time to this. Keep me posted on how I can best contribute! |
I had a look into splitting some of the logic out of In any case, I updated my version of the branch with a couple of other fixes and verified that all tests are still passing. Next time I have a chance to look at this, I'll see about adding some more comprehensive tests, though I do wonder whether they'll have to be rewritten anyway to support the refactor... |
@smably The refactor should not affect the tests, as the tests should be written against the spec, as defined in my OP at the top. This is one of the reasons why I pointed to new tests that cover the spec as an easy task for anyone looking to contribute. Thanks for the useful thoughts on the refactor, and proposing a |
Added a bunch of updates and opened a new PR: #8081. |
@alberto Not sure if it's still relevant, but signed. |
Yes, thank you @CrabDude! |
What is the purpose of this pull request? (put an "X" next to item)
[X] Documentation update
[X] Other, please explain:
The ability to override config values based on glob pattern matching relative to the config path.
#3611
Please check each item to ensure your pull request is ready:
What changes did you make? (Give an overview)
This mostly follows @nzakas' micro-proposal, but with grunt-style aggregating glob pattern arrays.
lib/config.js
.Specifically, a vector based caching was implemented, where a vector is an array of absolute config file paths within a hierarchy that apply to a given file, including separate entries for
"overrides"
sub-configs. This hierarchy can then be used to generate a hash for caching, or to merge the config hierarchy and then cached and returned fromConfig.prototype.getConfig(filePath)
.Example
Given the following config hierarchy:
With corresponding configs:
For
project-root/app/lib/fooSpec.js
, where/project-root/
is the absolute path toproject-root
, the corresponding config vector and caching hash (included for illustration, but not important) would be:And the resulting corresponding config value (cached at
config.mergedCache[hash]
):And
getConfig('project-root/app/lib/bazSpec.js')
would load from cache.Details
To maximize the performance benefits of refactoring the config loader caching layer, several other layers of caching (on the config instance) were implemented including config file contents caching, iterative config caching (a merged sub-vector cache), config hierarchy caching by vector and directory-based local config file hierarchy caching.
As a result, from the previous example, all of the files within
project-root
would load from various degrees of partial cache.Results
The net performance gain from these caching improvements is a consistent 15% performance increase when run against the Pinterest web codebase regardless of whether an override config is utilized.
Is there anything you'd like reviewers to focus on?
Edge cases.
@lo1tuma
EDIT: Added an expanded example.