Eslint: Glob based configuration

Created on 31 Aug 2015  Β·  87Comments  Β·  Source: eslint/eslint

Glob based configuration

Goal

Currently ESLint allows you to cascade the configurations based on the directory structure of your project. This approach is not flexible enough if you have files that don’t share the same parent directory but you still want to have a specific ESLint configuration just for those files. This could be solved by specifying a configuration that applies to all files which match a given glob pattern.

How it works

  • The glob patterns can be configured within .eslintrc files
  • glob patterns are relative to the location of the .eslintrc in which they are defined
  • if an already resolved file matches a glob pattern in one of its corresponding .eslintrc files, the glob pattern specific configuration will be applied
  • a glob pattern based configuration has a higher precedence than the regular configuration in the same .eslintrc file
  • A glob specific configuration works exactly the same as the regular configuration in your .eslintrc, except that you can’t define nested glob based configurations. That means you can configure globals, env, ecmaFeatures, rules, plugins, extends and parser.

Note: The glob patterns are NOT used for file resolving / directory traversal.

Example for relative glob patterns

Given the following directory tree:

β”œβ”€β”€ app
β”‚   β”œβ”€β”€ lib
β”‚   β”‚   β”œβ”€β”€ foo.js
β”‚   β”‚   β”œβ”€β”€ fooSpec.js
β”‚   β”œβ”€β”€ components
β”‚   β”‚   β”œβ”€β”€ bar.js
β”‚   β”‚   β”œβ”€β”€ barSpec.js
β”‚   β”œβ”€β”€ .eslintrc
β”œβ”€β”€ server
β”‚   β”œβ”€β”€ server.js
β”‚   β”œβ”€β”€ serverSpec.js
β”œβ”€β”€ .eslintrc

The config in app/.eslintrc defines the glob pattern **/*Spec.js. This pattern is relative to the base directory of app/.eslintrc. So, this pattern would match app/lib/fooSpec.js and app/components/barSpec.js but NOT server/serverSpec.js.
If you would define the same pattern in the .eslintrc file within in the project-root folder, it would match all three of the *Spec files.

Configuration Examples

Single pattern

{
  "files": [
    {
      "patterns": "**/*Spec.js",
      "rules": { "no-unused-expression": 0 },
      "env": { "mocha": true } 
    }
  ] 
} 

Multiple patterns

{
  "files": [
    {
      "patterns": [ "app/components/**/*.js", "test/unit/app/components/**/*.js" ],
      "rules": { "react/display-name": 2 },
      "ecmaFeatures": { "jsx": true },
      "plugins": [ "react" ] 
    }
  ] 
} 

As you can see patterns could be a string for a single glob pattern or an array if you like to define multiple patterns.

Open Questions / Possible Problems

  • Should we allow patterns in shareable configs? To which base path should the relative glob pattern be resolved? I think we should not allow this. Extending from a shareable config should mean that you unconditionally get the rules from this shareable configuration for all the files that you configure it in your project
  • Should this feature also work somehow for the CLIEngine?

_PS: I’m not happy with the current naming of "files" and "patterns". Suggestions welcome_

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

accepted archived due to age core feature

Most helpful comment

Can't speak to timeframe, but work is progressing in #8081.

All 87 comments

I agree with not allowing this in shareable configs, but I think we should support extends in each pattern. This way you can extend one config for your code, and another for tests.
I would imagine that has to work for CLIEngine, since CLIEngine now can accept a directory.

As we discussed I really like this @lo1tuma!

If I got you right @ilyavolodin you suggested that this should work:

{
  "files": [
    {
      "patterns": "**/*Spec.js",
      "extends": "someBodysShareableConfig/test-rules"
    }
  ] 
} 

This is exactly what I was already discussing with @lo1tuma. So 2/2 people who commented on this proposal thought this is an important point. Maybe you should add some information in your proposal about this extends part. I think its important.

Ah and one very small idea (just to think about it): Maybe it would be better if the configuration enforced an array for patterns. This way at least the name would match and it would be clear that you can use multiple or one.

Apart from that I will just repeat what I said to you in personal: The files and patterns reads not that bad if you think of it as this configuration should be in place for all files that match specific patterns. Only allowing one pattern would make it better but would mean code duplication in your configuration which is definitely not what one would want.

Maybe someone else has a great idea on it :).

I’ve updated the description to clarify that a glob specific configuration works the same as the regular configuration in an .eslintrc file:

A glob specific configuration works exactly the same as the regular configuration in your .eslintrc. That means you can configure globals, env, ecmaFeatures, rules, plugins, extends and parser.

I'm uncomfortable with having a difference between shareable and non-shareable configs, as the whole point is to be able to share all your settings. I think configs must all be the same regardless of being shareable or not.

@nzakas I don't think I agree. Or rather, I understand exactly where you coming from in terms of keeping shareable and non-shareable configs the same, but in my mind, there is a philosophical difference between them. Shareable configs are generic, they are there to setup a skeleton. Personal configs are specific to a project. And file name patterns/folder layout are very specific to projects.

I agree with @nzakas. Can't it just be bad practice or an issue that developers who write shareable configs be aware of?

As in - convention over configuration? Honestly I was always against that in OSS. People don't read docs:-)

Yeah, I mean it seems pretty reasonable to assume that most developers distributing shareable configs will see those potential issues. And honestly they might want that to force directory structure - not saying it's a good idea but it's a potential option that would be taken away otherwise

I like configuration over convention for some things :+1:

@lo1tuma potential name include(s)?

Ok, I guess I can buy "taken away" argument. In that case @lo1tuma I would imagine root would be the point extends is called from (as in directory of .eslintrc file that has extends in it).

My point is that you should be able to take a personal config, wrap it in an npm package, and make it a shareable config. That's always been the vision for shareable configs, so anything that gets in the way of that vision is :-1:.

Also, let's keep the scope of this fairly small for now. Let's just start with specifying rules, globals, and environments for glob patterns. We can always add more capabilities later, but I think this scope provides most of what people are looking for.

@lo1tuma, I suddenly have a pressing need for this kind of functionality. Is it something you're still planning to work on?

@IanVS yes, I’ve already started working on this. The hard thing about this is that I don’t want to break the current in-memory caching during resolving the config for a file. Currently the config for multiple files in the same directory is always the same, that’s why it is cached per directory, but this isn’t true anymore if start merging the glob-pattern based configuration during the resolving phase.

Can we simplify this to apply to extensions only, rather than full glob patterns? Would that make it simpler?

@nzakas I can’t really see how that would make it simpler. The configurations for different files in the same directory would still differ.

Only applying to extensions would not help me. My use case is I have __spec directories spread throughout the code base, and files in those directories need to have a different environment set from the rest of the code. Currently, each __spec directory needs to contain an eslint config file.

Same for me. I have my tests alongside the production code with the pattern _*_spec.js. And it would be really helpful to apply the mocha env only to files that match this pattern. Just the other day I had the situation that a fellow developer used an undefined context variable in the production code that ESLint was unable to pick up because the mocha env (which defines context as a global) is applied to every file.

This came up as a possible solution to eslint/eslint-plugin-markdown#48. @lo1tuma are you still working on an answer to the caching issue? Is there anything I can do to help?

@btmills pretty sure no one is working on this right now.

I got stuck with the caching problem. Maybe we can apply the glob patterns later, after the configuration for the file has been resolved. I will take another look at it this weekend.

I have a use case which I think this issue would solve. I recently joined a project with an existing codebase. We are migrating/refactoring the JavaScript incrementally from one folder tree to another. The legacy code uses JSHint. The new code will use ESLint. During the transition (which may be months) we need the two to coexist. I've never had to deal with this before. If I could tell .eslintrc to target a glob the way we can on the CLI e.g. $ eslint client/**/*.js, and if tooling like houndci and Atom ESLint plugin honoured this field, we would be in a smooth tooling situation. I presume others have been, are, or will be in a similar situation.

I have the same case (coexisting jshint and eslint), and there are other ways to solve this (at least for me). The trick is only running ESLint on the folders you want, combined with adding the legacy folder to your .eslintignore file. So, for example, your npm run lint script could be something like eslint client, and you can put your .eslintrc file within client/, then enable the option in Atom's linter-eslint: "Disable when no ESLint config is found", and uncheck "Disable using .eslintignore files". I'm not familiar with houndci, but I assume there's a way to have it target only specific folders as well.

Bottom line, your use case should be achievable without this feature. If you're having trouble, jump into the Gitter chat and we'll see what we can do.

@IanVS Thanks, I believe we have what we want now. Cheers.

Here's what we ended up doing in XO: https://github.com/sindresorhus/xo#config-overrides

FWIW: I’ve pushed a WiP commit, but there is still some work to do.

@lo1tuma how much work is left? I.e. when do you think it could be released into the wild?

I am currently using eslint 2.2.0 with babel-eslint, and I want to have some different rules for my tests.
For instance, tests have the 'mocha' environment and in tests I allow deep nesting of callbacks (because of nested test suites). However my tests are not in one seperate folder but structured like this:

- app/
    - __tests__
    - components
        - __tests__
        - shared
            - __tests__

This structure works really good for me. However, this means I either would have to put an .eslint file in each single __tests__ folder. Or use a separate config like I currently do.

Currently I do the following:

{
  "lint": "npm run lint:base && npm run lint:tests",
  "lint:base": "eslint .",
  "lint:tests": "eslint . -c .eslintrc.tests.js --ignore-path .eslintignore.tests"
}

This means that lint:base lints all files but ignores the ones in __tests__. lint:test lints ALL files with the less strict test rules (but this relies on test rules actually being less strict for all rules, which is not a given). Also this means my IDE will give errors in the test because it can only use one .eslintrc file at a time.

For my case it would be really nice to be able to specify different rules for different globs.

Sorry for the late response, I haven’t found time to continue working on this recently. Next week I take a few days off from work, so I can probably continue on this.

No problem, already awesome work you are doing. My current solution is just to use the same rules for the test, but just making those rules less strict. But still would be awesome to have this!

Yeah it would be awesome to configure custom environment like mocha for all files named *.spec.js.

so was the glob based configuration ever merged into eslint? I'm also looking for a ways to specify an environment just for *.spec.js files.

No, glob based configuration has never been fully implemented (that's why this issue is still opened), as such it was never merged in.

Is there any ETA on this feature?

@panrafal no. Everyone works in their spare time so we have no real schedule. It's basically when someone decides they want to work on it.

I'd like to help but I'm not confident with the code base. Would it be possible to point to the functions that need to be edited?

@FezVrasta Unfortunately it's not quite as easy as changing a function.There's a branch https://github.com/eslint/eslint/tree/glob-pattern-based-config that has some changes to allow for glob based configs, but it's incomplete and hasn't been updated in a long while. You can take a look at those changes and pick it up from there.

When this is fixed, please consider making a note in StackOverflow

@lo1tuma What still needs to be done? I'm looking to take this on.

I rebased on master, and all tests pass (after updating some minor lint errors), and the base functionality is working (mapping of pattern+configs to the correct files).

EDIT: It succeeded both with a simple base config, and a nested config with a very very complex base config. Perhaps, all it needs is more test suites to verify in combination with existing functionality?

UPDATE: Looks like friendly error reporting needs to be implemented. E.g.,

Configuration for files "patterns" is invalid

Hi @CrabDude. Thanks for considering to continue the work on this.

IMHO the following things needs to be done:

  • Think about what implications the exceptions, I mentioned in the documentation, have:

A glob specific configuration works almost the same as the regular configuration in your .eslintrc, except that you can’t define nested glob based configurations or using extends.

Or if we can/should possibly support extends.

  • I’m not really happy with the configuration field files but I couldn’t find any better name.
  • Check if the relative path resolution works correctly. A glob based configuration should never apply to a file that is not in the same folder or in a sub-folder of the .eslintrc file where the glob pattern was defined.
  • Implementing tests:

    • AFAIR there are currently no unit tests for getConfigForFile

    • Apart from unit tests I think we should also implement some integration tests to verify the correct behavior. IIRC we always introduced a lot of bugs whenever we changed the file resolution or config merging algorithm.

I solved similar issue for JSHint https://github.com/ikokostya/jshint-groups#configuration-file

  • Common options section is very useful for sharing configurations between file sets.
  • I think glob based configurations should support only one file in the root project (i.e. should not support nesting), because it's very simple to read and understand all rules in one place.
  • I propose rename "patterns" to includes and excludes. Note that includes ignores negation patterns (https://github.com/isaacs/node-glob#comments-and-negation). For this purpose excludes is used.

Common options section is very useful for sharing configurations between file sets.

That’s already possible. Glob based configuration doesn’t break the current configuration feature of ESLint. So you can simply define common options/config as you would define them today (glob based configurations will be merged into that).

I think glob based configurations should support only one file in the root project

I’m not sure if I understand you correctly. Do mean nesting .eslintrc files? I’m not sure if this is possible because ESLint doesn’t have a concept of project-root or root .eslintrc file. Actually you can even have multiple root .eslintrc files, see documentation.

I propose rename "patterns" to includes and excludes.

What kind of issue would the excludes feature solve and would be worth the additional complexity?

ESLint doesn’t have a concept of project-root or root .eslintrc file.

ESLint won't determine an .eslintrc to be a root file on its own, but you _can_ specify root: true to keep ESLint from merging any further config files in any ancestor directories. But, I think the point of @ikokostya's comment is to avoid merging glob configurations from different config files in a more general sense.

It makes sense to me that if you are using globbing to configure rules for different parts of your project, to put them all in one file. I imagine that would reduce some of the technical complexity of implementing glob-based configuration as well.

@IanVS I’m not sure if it would reduce complexity. You still need to merge the glob-based configuration with the non-glob-based configuration in order to not break how eslint configuration cascading currently works.

You still need to merge the glob-based configuration with the non-glob-based configuration in order to not break how eslint configuration cascading currently works.

Yes, unless we decide that using a glob-based configuration will override any configs in child directories, which seems like a reasonable limitation/constraint to me personally. Could something like that work?

@lo1tuma

What kind of issue would the excludes feature solve?

For example you need check src directory, but exclude src/vendor directory. Instead of verbose enumeration all files except vendor

{
   "includes": ["src/{a,b,c,d}"]
}

you can write

{
  "includes": ["src/**"],
  "excludes": ["src/vendor/**"]
}

would be worth the additional complexity?

excludes is optional. Algorithm is also simple: get list of files which match to includes and then remove from this list all files which match to excludes.

I started looking into this myself the other day. Can I propose what I think may be a simpler way to handle this? It would require 2 changes:

  1. Allow the excludes and includes properties (or whatever naming convention is agreed on) on _any_ configuration object at the base level (so no additional files property with nested rules, or similar, needed).
  2. Instead of a requiring a single configuration object, allow an array of configuration objects to be specified. Those would simply cascade in the same way they currently do as if they were at different levels in the file system hierarchy.

So that would allow a configuration like this:

[
  {
    "extends": "airbnb",
    "env": {
      "browser": true
    }
  },
  {
    "includes": ["**/*.spec.js"],
    "env": {
      "mocha": true
    }
  }
]

In this example the first configuration would be applied to all files, while the second configuration would be used only for those files matching the includes glob (while being merged into the first, plus any configs higher up in the directory tree).

It looks like implementing this would only require some changes to lib/config.js. Any thoughts on this approach as an option (which I'd be happy to take on right away if @CrabDude wasn't interested)?

@eslint/eslint-tsc Have we settled on a design/intended implementation for this? If not, should this issue be "accepted" or should we go back to "evaluating" and work on a design?

Setting back to "evaluating" since it appears there's still some design considerations.

If I could ask a favor: this thread is already insanely long and it's hard to tell when a comment is building on another or conflicting. If you'd like to make a full proposal for how this should work, can you please put it into a gist and link to it from this issue? That way, we can clearly see the extent of the entire proposal and be able to evaluate it properly.

@nzakas Sounds good. Continuing non-proposal discussion...

Based on the feedback here, more discussion seems beneficial. @joekrill's proposal of specifying an array of configs seems rather elegant. The configs in .eslintrc can extend a base config, leaving behind primarily platform-specific configurations. While, Nesting of configs within "files" creates an almost secondary "extends" functionality, and raises the obvious concerns regarding the interplay of "files" & "extends" and the implications of nested/hierarchical "files" (allow/disallow, "root", overwrite, etc...).

I need to think about this some more.

@joekrill Actually, I don't think the top-level array approach will work. Otherwise, it'll get confusing b/c we'll have to require an "includes"/"excludes" in each config in the array except optionally the last, but at that point, the "files" approach is more explicit.

To add my unrequested two cents to the discussion... I'm not sure if this would address the concerns that @CrabDude has, but I had a similar idea to @joekrill awhile ago, but using a children attribute. I put together a gist with the proposal: https://gist.github.com/JustinLivi/a5f87a8d9288b618c09c97da8f988ff3

Just an alternative take on the structure. My main concern with it would be that it might create more complexity than simplicity.

@CrabDude The includes/excludes would be completely optional in all cases. If one isn't specified in any configuration block, then all files would implicitly be included.

The array of configs could be interpreted and cascaded in exactly the same way they currently are -- it would really just act as if each element in the array created a new directory level.

I'll write up a gist with more details shortly.

I'd just like to guide this discussion a bit more. It seems like each addition makes configuration more complicated. We should be targeting the least-complex solution for adding glob-based configuration as a starting point (for instance, cascading globbing seems two steps beyond what is necessary to solve the problem).

Configuration is the most complicated part of ESLint, so we really want to answer the question, "what is the minimum we can do to support this?"

@nzakas Agreed, which is why I'm proposing splitting this up into two smaller distinct features that can be used in conjunction to create the desired functionality: https://gist.github.com/joekrill/886f9d07b64ac1c98f280fd6cd915004

I don't believe @joekrill's proposal is best considered as 2 distinct features, since neither is particularly useful in isolation.

  1. With only includes, configs are awkwardly coupled to a directory (by virtue of location); This would not solve my use case, and I think most people looking to utilize include would find themselves very awkwardly needing to put their targeted configs in the parent directory.
  2. With only arrays of configs, there's literally no new functionality.

ATM, I believe @lo1tuma's "files" approach would be better because file matching configs don't need to occupy the location within a directory intended for a directory config, while still coupling the patterns to a config hierarchy (mirroring directory config hierarchy). This approach has the same file:config combinatorial explosion issue and consequently would likely add a non-trivial support burden. The combination of not matching on files outside the config's directory, and the explicitly additive or subtractive ("off", "warn" or "error") nature of rules means it would work quite well, not accounting for effects on performance or implementation complexity. I'll spend some time seeing if I can think of a better approach.

@CrabDude I'm suggesting splitting this into 2 distinct features not because either is necessarily useful in isolation (although excluding files at the configuration level _would_ be useful, I think) -- but the point is really just to simplify the implementation work needed and keep any added complexity at a minimum.

Each of these features on their own would be fairly simple to implement and easier to test than a single larger feature.

From a documentation and configuration standpoint, this would only add 2 very straight-forward additional configuration options -- both simple glob patterns -- whereas the nested files config approach creates a more complex hierarchy which itself would only support of subset of the existing config options, which will likely create confusion. Frankly, I think we could get by with _only_ the includes option if we really wanted to simplify this to a single config setting. Handling arrays instead of a single object is pretty straight-forward from that perspective, too -- there's no real big change there at a documentation level, and the documentation and workflow around cascading those configurations is already well defined.

So I went ahead and did some work on this based on my proposal and submitted a PR (#7073) for the first feature (includes/excludes configuration options). If it's decided that this is the way to move forward with this I'll move on to the second part of my proposal.

1.Regarding Caching...

I'm putting together a PR with cacheing support by config vector (See https://github.com/eslint/eslint/issues/3611#issuecomment-161701313).

Since the configs hierarchy for all proposals (files, children or top-level array) is a topologically sorted (by subdirectory) directed acyclic graph, we can cache by config vector (ordered combination):

Config.prototype.getConfig = function(filePath) {
    // Assuming we have a config hierarchy (e.g., 3 separate .eslintrc files)
    // Where each config is an array of subconfigs (e.g,. top-level, `files`, `children` or top-level array)
    // Note: Each config is the shallow config, not the historically merged/cached directory config
    let configs = config1.concat(config2).concat(config3)

    // And their associated pattern hierarchy
    let patterns = patterns1.concat(patterns2).concat(patterns3)

    // Get the config vector (file-specific config hierarchy)
    let configVector = this.getConfigVector(configs, patterns, filePath)
    // E.g., ['/rootDir:0', '/rootDir:2', '/some/sub/dir:0', '/some/sub/dir:3']
    return this.getConfigByVector(configVector) // merged config
}

Config.prototype.getConfigByVector = function(configVector) {
    let config = this.cache[configVector.join(',')]
    if (!config) {
        // incrementally merge and cache configs from configVector[0] => configVector.last
    }
    return config
}

_Side Note:_
Since we now have a config graph, instead of tree, depending on V8 (de-)optimizations, it's worth considering Object.create over Object.assign (ConfigOps.merge), potentially allowing for some fancy cacheing optimizations via either ad-hoc prototype reassignment or Object.create per vector (instead of Object.assign per vector).

2.Regarding pattern matching...

  1. Subdirectory configs (patterned on unpatterned) will override parent patterned configs.
  2. With globs, there's no way to avoid matching every file path with the full pattern hierarchy, except...

    1. Cache (and inherit) includes matches by parent directory. (Must still run corresponding excludes)

    2. Cache (and omit) excludes matches by parent directory (when parent directory is excluded, child files will always be excluded).

Also, independent of the outcome, thanks @joekrill for putting together both a proposal and PR.

Summary of the current state of this issue

All current (3) glob proposals support the same functionality through differing APIs.

All existing proposals allow the following features:

  1. Specifying an ordered array of sub-configs via globs that cascade from preceding configs (other sub-configs or the same file's top-level config), with normal hierarchy rules applying.
  2. An array of globs, with support for both "inclusion" and "exclusion" patterns
  3. Glob paths are relative to the config file (.eslintrc), not the file they're declared in (e.g., shareable config).

_NOTE:_ While this allows shareable configs to enforce a file naming convention / directory structure, this seems acceptable since the current plugin/config dichotomy has declarations in plugins and how they're applied in configs. Globs merely empower greater config granularity. We could instead treat globs as a project-level construct. For an idea on this, see .eslintmap at the bottom of this comment.

Examples of these features in existing proposals:

  1. files & patterns (Original proposal):

js { // ... parent config here // array of sub-configs: "files": [{ "patterns": [globA, globB, etc...], // ... sub-config here }] }

  1. children:

js { // ... parent config here // array of sub-configs: "children": [{ "includes": [globA, globB, etc...], // ... sub-config here }] }

  1. includes, excludes and top-level array of configs (This PR):

js [ { // ... parent config here }, // array of sub-configs: { "includes": [globA, globB, etc...], // ... sub-config here }, // ... }

Differences in proposals:

The various proposals differ primarily in how sub-configs and their patterns are specified, not functionality. The proposals are also in consensus on config precedence (caveating that children allows for a matrix of sub-configs).

Problems with all approaches

From what I can tell, all the problems in the OP (https://github.com/eslint/eslint/issues/3611) revolved around complexity and performance concerns (e.g., nesting, caching) that are addressable with the approaches mentioned in this thread (e.g., top-level arrays avoid nesting, and caching by config vector).

Shareable configs

Presently, targeting is external to configuration files via config location (directory, file comments, ~/.eslintrc). Globs in configs would change this, so it would be nice if there was an equally external method for globs. However, without creating the concept of a project-level config (or treating root configs as de facto project configs, with allowances for extra rules), .eslintrc will continue to be the target location for this, but not the ideal location.

.eslintmap

In addition to the current declaration/configuration (plugin/config) dichotomy, we can add a 3rd type, .eslintmap, for mapping that only allows the includes, excludes and configs (or extends) functionality:

// .eslintmap
[
    {
        "includes": ["**/*.test.js"],
        "configs": [
            "eslint:all",
            "my-locally-installed-config",
            "./config/eslint/tests.json"
        ]
    }
]

And/or, in lieu of the other proposals, instead allow this stripped functionality in config files:

// .eslintrc
{
    "mappings": [
        {
            "includes": ["**/*.test.js"],
            "extends": [
                "eslint:all",
                "my-locally-installed-config",
                "./config/eslint/tests.json"
            ]
        }
    ]
}

Or possibly a project-level config like .eslint.project that would house all project-level configs (.eslintmap, .eslintignore) and presume "root": true where it is located. Later, non-content configs (everything except rules, globals, plugins, extends, etc...) can be moved into this file (e.g,. parser, ???)

I suggest to use Grunt globbing patterns. It's simple from one side, from another side it's much more powerful than regular glob or include/exclude pair, since it allows to include and exclude multiple times in any desired order.

For example: you want to match all js files under src/ directory, but from src/test/ directory you want only match files from src/test/utils/. With include/exclude it's hard/impossible to achieve, but with grunt globbing it's pretty easy:
['src/**/*.js', '!src/test/**/*', 'src/test/utils/**/*.js']

Also grunt is very popular in js world, so it's well known and it's easy to get help/advice when needed.

FWIW, It appears that grunt uses node-glob and minimatch for glob matching.

@IanVS right, grunt just specify simple convention how array of patterns modifies files set

Also, since the OP is accepted and road-mapped, I'm still planning to submit a files PR with config-vector caching.

[Copied from the mailing list, in case it's best discussed here.]

I noticed config files are cached in config.localConfigFinder / FileFinder, but not the config contents. This makes a little sense since merged directory configs are cached, but it's also a weird since incremental cacheing is not utilized (i.e., traverse up config hierarchy to nearest cache and merge from there; e.g., a/b is cached, a/b/c is not, but config hierarchy is merged from scratch instead of from a/b's cache).

Is there a reason not to do this? I see none. The seemingly negligible memory overhead of cacheing config files (since we're already cacheing merged directory configs), seems more than worth the performance gains to be had by not having to reload from fs and recompute (extends, plugins, etc...) config files.

EDIT: I do see the explicit avoidance of cacheing via require-uncached, so it seems there is definitely some deliberate motivation for not cacheing config files...

EDIT 2: Looks like incremental cacheing (merging from parent config) is not utilized b/c of how config hierarchies are currently ordered (base, local/personal, specific, CLI, parser, env), which doesn't make it impossible, just morecomplex... Still doesn't explain the reason for using require-uncached.

What is the reasoning for this test?

// tests/lib/config.js
describe("getConfig()", function() {
    it("should return the project config when called in current working directory")

Following up on my require-uncached comment stating concerns about adding caching to Config.

Looks like require-uncached is necessary to avoid process-level caching via require.cache (https://github.com/eslint/eslint/issues/5067, https://github.com/eslint/eslint/commit/29772486, https://github.com/eslint/eslint/commit/f912e217).

However, since each "run" instantiates a new Config, caching at the Config instance level (e.g., config.cache) is perfectly fine; we're already doing it. Adding the equivalent of require.cache into Config makes perfect sense, and works fine (all tests passing).

I had mentioned on a PR somewhere (too tired to look it up now), that I'd prefer this approach instead:

{
    rules: {
        semi: "error"
    },
    overrides: [
        {
            files: "foo.js; bar/*.js",
            extends: "eslint:recommended",
            rules: {
                semi: "warn"
            }
        }

    ]
}

As I see it, this has several advantages:

  1. It's not file-pattern specific, so we have the option of having other overrides in the future (maybe we want to add an override based on an environment variable value, or some other condition).
  2. Avoids the confusion between .eslintignore and the excludes proposal while leaving open the possibility that we could move ignores into the config file in the future.
  3. Better indicates what the expected behavior of the overrides are. I feel like patterns, files, and children are a bit opaque as to the intended consequence of their definition, whereas overrides is quite clear.

Some of the complexities that still need to be answered:

  • Is only the first matching override applied? Or are all matching overrides applied in the order in which they appear?
  • What if you extend a config with overrides? Are those overrides applied first? And what if an override extends a config with an override? What's the order in which those overrides are applied?

@CrabDude I'm a bit unclear on why the caching conversation is part of this issue. Can I ask that you open a separate issue around that topic? This thread is already very long and has a bunch of tangents, and I'd rather try to keep conversations a bit more focused.

@nzakas The caching came up because it was the primary blocker for this issue since, it was unlikely this change would land if it broke config caching and/or had a significant performance impact. FWICT, the only viable caching strategy for patterns is the vector-based approach I proposed. As a result, config loading needed to be refactored to allow for vector caching, which required knowledge of what can and cannot be cached or refactored.

To illustrate this, appreciate the appearance of contradiction from the current caching strategies:

  • explicitly don't cache config files (require-uncached)
  • cache a cumulative list of config files per-directory (FileFinder)
  • cache the merged configs for a filePath's directory
  • don't cache the iterative configs even though most won't/cannot change (e.g., config.options), and are usually identical to their parent directory (unless there's a config file in the current dir)

I'll submit a WIP PR (needs new tests) for the above within the next hour soon. I have to do some commit cleanup.

I'm looking forward to this feature, as it will allow me to specify that every file ending in .spec.js, containing tests, should permit a different set of globals for writing Jasmine tests. This is a pretty common pattern for AngularJS code.

I have a use case currently blocking on this in github.com/marcuswestin/store.js (current v2-dev branch).

All of the library addons and storages have a _test.js fileΒ in the same directory (e.g cookieStorage.js has cookieStorage_test.js), and all test files depend on three global functions (test, assert and print).

The most sensical way to configure this project structure would be to declare those three globals but only for files matching *_test.js.

Looking forward to this landing!

Cheers,
Marcus

Workaround is simple:

  • Create directory with eslint configs:

    eslint-configs/
    common.js
    tests.js
    
    // tests.js
    
    module.exports = {
     extends: './common',
     env: {
         mocha: true
     }
    };
    
  • Add target in your Makefile (or whatever):

    test:
        node_modules/.bin/eslint $(shell find src -name "*.js" -and ! -name "*.test.js") --config eslint-configs/common.js
        node_modules/.bin/eslint $(shell find src -name "*.test.js) --config eslint-configs/tests.js
    

There is only one drawback: two eslint calls.

@ikokostya And you lose all editor support which is a major drawback. I want to see problems with my code while I'm typing, not only in a separate test run.

@hpurmann this is workaround. If you want editor support - make this issue or write plugin.

Thanks for taking the time to write up the workaround. Its overhead and
shortcomings are too great for my taste. I settle with declaring those
globals for all files, and waiting for the correct solution :)

On Thu, Dec 22, 2016 at 12:55 PM ikokostya notifications@github.com wrote:

@hpurmann https://github.com/hpurmann this is workaround. If you want
editor support - make this issue or write plugin.

β€”
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/eslint/eslint/issues/3611#issuecomment-268854193, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAIDfzPvPFPOKtIpNVZOraGvBEdGGA5tks5rKrmDgaJpZM4F1B1j
.

--
-- while mobile

This feature would be required for automatic linting of a complex project directory from an editor like Atom or VSCode, since they don't scan all .eslintrc.js files in each directory. For example, if you have a server and client directory in your project, having the .eslintrc.js in the root folder, will please one side or the other.

Currently you need to run npm scripts to watch each directory, and you lose the in-editor linting errors.

Basically what @hpurmann said here: https://github.com/eslint/eslint/issues/3611#issuecomment-268853157

@knownasilya true but you could argue that that should be fixed in the IDE plugin and not in ESLint.

@knownasilya Your best bet for now is to put the shared configuration into the config file at the root, and then put separate config files into the subfolders which override as needed. This will work with all editor integrations that I know of.

Yeah, maybe. I mean the other thing I'd want to see is proxying ESLint rules through plugins, so you can configure the same ESLint rules for plugins, which work on the same files (so globs don't help here), but different parts of the file, usecase: https://github.com/knownasilya/eslint-plugin-doc-code-blocks

Current workaround is to run ESLint twice, with a different config.

Hi there, any idea if the feature is still being worked on and if there is a rough estimate about the timeframe?

Can't speak to timeframe, but work is progressing in #8081.

@not-an-aardvark πŸ‘
Massive patch! Nice work man

This will be especially useful for configuring .spec.js files for Jasmine tests separately from other code. Thank you for your work on this. :+1:

WOOHOO! one of the longest watched issues I had :) great work everyone !

@marcuswestin Thanks, but all the credit goes to @smably and @CrabDude for working on this. πŸ˜ƒ

@smably & @CrabDude thank you! Impressive patch :+1

πŸŽ‰

Was this page helpful?
0 / 5 - 0 ratings

Related issues

medikoo picture medikoo  Β·  55Comments

okonet picture okonet  Β·  76Comments

nzakas picture nzakas  Β·  65Comments

nzakas picture nzakas  Β·  50Comments

LinusU picture LinusU  Β·  59Comments