Repository: eslint/eslint (JavaScript)

Issue:
Add an option to not throw an error on an unmatched pattern
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for requesting a change that is not a bug fix, rule change, or new rule. If you are here for another reason, please see below:

    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
    2. To request a rule change: https://eslint.org/docs/developer-guide/contributing/rule-changes
    3. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
    4. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**The version of ESLint you are using.**

```
ESLint: 5.1.0.
```

**The problem you want to solve.**

Since breaking behaviour change 8b7c6eaed39e8506dba1aa6e57b1d0e2fdc351c3 #7390 #10143 bundled in ESLint 5, a fatal error is thrown if any file pattern return no files.

My company is using a generic ESLint configuration/runner for all our projects, and we are pretty happy with that:

```bash
eslint \
  --ext .js --ext .jsx --ext .mjs \
  -- src test "packages/*/src" "package/*/test"
```

The problem is that not ALL of our projects matches the same patterns, and we got this error since we upgraded to `eslint@5`

```
No files matching the pattern "test" were found.
Please check for typing mistakes in the pattern.
```

We don't want to implement custom code to check for pattern existence before appending them to eslint CLI parameters (and we where really happy with eslint@4 behaviour on this particular case).

NOTE: This issue is a bit related to https://github.com/eslint/eslint/issues/9977


**Your take on the correct solution to problem.**

Add a CLI option (exactly like [how proposed here](https://github.com/eslint/eslint/issues/9977#issuecomment-402269256)) not to throw errors on unmatched patterns.

Something like `--no-error-on-missing-files` or `--no-error-on-unmatched-pattern`




Discussion:
This seems reasonable to me as long as the default behavior remains unchanged. Let's see what others say.

Edit: typo
Agreed, it would be better to opt-in to this so that by default we are helping users avoid typos in their patterns.
Ignore my comment. Thanks @not-an-aardvark , I was doing `eslint ./*/ --ext .js --ignore-path .gitignore` which worked in 4.19 but was breaking with a similar looking error. Changing it to `eslint . --ext .js --ignore-path .gitignore` fixed it.
I don't think an error is thrown for nonexistent paths in `.gitignore` or `.eslintignore`. Are you sure that's creating an error?
I think there are relatively few use cases for this option (one of which might be running eslint in an integration), but I don't object to adding it. That said, if you're encountering an error like this when running ESLint from the command line, the solution should usually be to stop trying to lint non-existing files, not to add a flag to make ESLint ignore your errors. This issue has a lot of :+1:s in comparison with the narrowness of the use case, which makes me suspect that some people are misunderstanding what the error message is saying.
Any update on this? I would like to describe my case:
I have a monorepo:
```
packages
  /api
  /app
  /shared
  /web
```

My `web` project runs in docker. And I don't add `app` code inside it. But my only script to lint all projects contains a path to `app` too. So, in v5 this command is broken inside docker.
I would like to see a parameter to not throw an error about unmatched paths.
@isnifer Thanks for explaining your use case. I'm a bit confused -- if you don't have any `app` code, couldn't you simply remove the path to `app/` from your script that lints all projects?
@not-an-aardvark no, I have a code. But `app` and `web` are independent projects that use both `shared` and `api`. I don't use different configs and linter commands. I have the only config, the only lint command, and many projects.
```
.eslintrc
.eslintignore
packages/
package.json
lerna.json
...etc
```

Actually, I have found a solution for this:
```
# my v4 command to lint js
"lint": "eslint -c .eslintrc.js 'packages/api/**/*.js' 'packages/app/src/**/*.js' 'packages/app/__tests__/**/*.js' 'packages/app/scripts/**/*.js' 'packages/shared/**/*.js' 'packages/web/src/**/*.js' 'packages/web/__tests__/**/*.js' --cache"

# my v5 command to lint js
"lint": "eslint -c .eslintrc.js 'packages/**/*.js' --cache",
```
It works little bit longer when cold start, but then there is no problem.
@isnifer I'm still not sure I understand -- what is the error message when you use your "v4 command" with ESLint v5? Is there a particular pattern that ESLint says isn't matching any files?
@not-an-aardvark 
```
> eslint -c .eslintrc.js 'packages/api/**/*.js' 'packages/app/src/**/*.js' 'packages/app/__tests__/**/*.js' 'packages/app/scripts/**/*.js' 'packages/shared/**/*.js' 'packages/web/src/**/*.js' 'packages/web/__tests__/**/*.js' --cache


Oops! Something went wrong! :(

ESLint: 5.2.0.
No files matching the pattern "packages/app/__tests__/**/*.js" were found.
Please check for typing mistakes in the pattern.
```
Because I don't need my RN `app` code inside docker for `web`
So to make sure I understand, you want to use the same lint script in multiple environments, but in certain environments some of the files are absent?
@not-an-aardvark correct. And I did it before v5.
I've just upgraded to latest eslint@5.*, and this check is causing troubles to me as well.

I usually use eslint as: `eslint app/** --ext .js`.
Then in the *.eslintignore* file I exclude the undesired directory (eg. app/dist)...

In this case I get the following error:

```
No files matching the pattern "app/dist" were found.
Please check for typing mistakes in the pattern.
```

IMHO eslint shouldn't trigger the error for folders which are explicitly ignored. What do you think about this?


Sorry for the double post.

I've also tried to run eslint as `eslint app/folder1/** app/folder2/** app/file1 --ext .js`.

That's not optimal cause if I add a folder, or file I've to update the build script as well, but I need a workaround for now.

However the above causes troubles as well, because I've some folders (eg. in app/folder2) which don't contain javascript file, but only json (eg. app/folder2/data/foo.json).

So I would really need a way to opt-out from this stricter check.

Hi @brunoscopelliti, have you tried quoting the globs like `eslint "app/**" --ext .js`? It's possible that your shell is expanding the globs to a list of files before passing the files to ESLint, and the expanded list of files includes some folders that don't contain any lintable files.
Hi @not-an-aardvark, I didn't know that was possible; quoting the globs worked for me. Thank you for the help.
I've created a bash script as a workaround for this issue:

```bash
#!/bin/bash

GLOB_PATTERN=${1:-"."}

OUTPUT="$(script --quiet --return --command "eslint ${GLOB_PATTERN}" /dev/null)"
EXIT_CODE=${?}

if [[ "${EXIT_CODE}" == "2" ]]; then
  exit 0
fi

echo "${OUTPUT}"
exit "${EXIT_CODE}"
```

Just call it passing the desired pattern as an argument.
Hope it helps!

Unfortunately, it looks like there wasn't enough interest from the team
or community to implement this change. While we wish we'd be able to
accommodate everyone's requests, we do need to prioritize. We've found
that issues failing to reach accepted status after 21 days tend to
never be accepted, and as such, we [close those issues](https://eslint.org/docs/maintainer-guide/issues#when-to-close-an-issue).
This doesn't mean the idea isn't interesting or useful, just that it's
not something the team can commit to.

Thanks for contributing to ESLint and we appreciate your understanding.

[//]: # (auto-close)

I think this issue was closed prematurely. Many people (including me) want to be able to save time and share ESLint scripts across different projects (with sometimes non-existing paths in the glob).

It could be easily solved by adding a new CLI flag to skip glob entries that result in no paths.

If somebody from admin team are still reading this, please consider reopening this issue.
I'd like to second that. We're working on legacy code which is close to impossible to refactor to our eslint rules, so we use `git log` to generate a list of changed files that eslint should run on. But sometimes this will included deleted files, breaking our workflow. Adding an option to just keep going would be extremely helpful.
Anyone has some workaround for this issue. I'm migrating from js to typescript and eslint not support `as` operator then I disable to lint `ts` files but doing this brake the linting of the rest of `js` files. :(
@nathanredblur I ended up using a bit convoluted set of ignores on top of `--fix`, here's my npm `script`:

```
...
"lint": "./node_modules/.bin/eslint \"**/*.js\" --fix --ignore-pattern \"dist/*\" --ignore-pattern \"rollup.config.js\"",
...
```

Every time something breaks in my universal eslint script above, I add that path to every script on every package from the set. Thinking about your case, you could maybe add `--ignore-pattern \"**/*.ts\"`?
I just tried to upgrade to eslint@5 and i ran into the same issue.

I have a "starter-kit" project with all the backbone code that I then fork to create other projects.

I have an eslint command that is trigged by the ci to lint my sources files, as well as my test files.
The starter-kit project doesn't have any test files since it has nothing to test, but i don't think the linting script should fail because of it...

Here are the scripts

```json
{
  "eslint:source": "eslint . --ignore-pattern=*.spec.js --ext=.jsx --ext=.js",
  "eslint:test": "eslint . -c=.eslintrc.test --ext=.spec.jsx --ext=.spec.js",
  "eslint": "npm run eslint:source && npm run eslint:test"
}
```

And the Error

```No files matching the pattern "." were found.```

Thanks for fixing this, or at least provide an option to not fail on empty test suites.
Otherwise I have to add a dummy empty test file in the starter-kit project, which is lame.
This definitely should be re-opened and fixed. Just faced with this strange behaviour on version v5.15.0. tested with Node 6 & 8.

Eslint should **NOT** fail if it can't find some files from a pattern.
Tooling should exit silently if it doesn't apply to any files.
Please, reopen this issue as we should not get an error if eslint can't find files from a pattern.

`eslint . --ext .graphql` with a custom extension I would assume it would exit silently if no files were found matching that extension in my project.  I do plan to eventually add some `.graphql` files to this project but my CI tooling runs all lint tasks.
I'll reopen this. We need to at least evaluate this as a TSC for sure.
**TSC Summary:**

In ESLint 5.0.0, we started throwing errors when nonexistent directories or files, or glob patterns that don't match any files, were passed to ESLint. The assumption was that users would want to know when a file wasn't being linted as it could be due to a misspelling. However, this has caused unforeseen side-effects for some tooling, where the intention is to create lint scripts that will lint certain files as they are created in a new project.

Numerous members of the community are asking that we add a CLI option to enable the old behavior on an opt-in basis. Example option names: `--no-error-on-missing-files`, `--no-error-on-unmatched-pattern`

**TSC Question**:

Should we invite contributions for a new CLI option to suppress errors when lint target files/directories are not found, or globs match no patterns?
I don't know what my schedule will be this afternoon, so in case I don't make it to the TSC meeting, I left a :+1: on the TSC summary above. It seems like the new default behavior works for most users, but the use cases described here justify an opt-out.

If you'll allow me to bike-shed a bit, I have a slight preference for option names like unmatched glob or unmatched pattern, since "missing files" sounds ambiguous to me. Having a long option name shouldn't be an issue since the use case here is a write-once shared script, and users wouldn't be typing it on every invocation.
Per discussion in the [2019/04/11 TSC meeting](https://github.com/eslint/tsc-meetings/blob/master/notes/2019/2019-04-11.md), this is now accepted. (Apologies for not following up here.)
Hi guys, just in case is useful, I'm in the same issue as you but with a bit different approach. 
Used command: `git diff --name-only --relative develop... src/ | grep -E \"\\.(js|jsx)$\" | xargs eslint -f node_modules/eslint-friendly-formatter --color`
What is doing, `git diff` to check for changed files between my branch and develop in this case in src/ folder then pass it through grep to only get .js and .jsx files then let's say normalize them and finally run eslint.

Is working fine but when there is at least one removed file is throwing an error:
`ESLint: 5.15.1.
No files matching the pattern "web/src/pathToTheFile/FileName.jsx" were found.
Please check for typing mistakes in the pattern.`
@ozaytsev86 In your case, you might be able to use `--diff-filter=d` to remove the deleted paths from consideration and avoid the problem.
thanks a lot @platinumazure , I checked again git diff documentation and exactly is saying if u use mayus arguments is to include and minus to exclude. Now it's working as a charm. :)
I would like this too as some of my projects dont have certain files. I make lots of projects a day so its hard to constantly adjust the command.
A solution is to create all needed files by patterns in separated directory
It's too silly
I ended using :disappointed: 
```
"npx eslint ./crehana/frontdev/jsx/**/*.js ./crehana/frontdev/jsx/**/*.ts ./crehana/frontdev/jsx/**/*.tsx --ignore-pattern /node_modules/",
```
I'm working with a monorepo and just wasted 3 hours with this dumb error.
This proposal has been accepted. PRs welcome!
I'm gonna take a look at this over the weekend and see if it's something I can tackle



Patch:
diff --git a/docs/user-guide/command-line-interface.md b/docs/user-guide/command-line-interface.md
index c8871b07d2ff..89f42744eb3b 100644
--- a/docs/user-guide/command-line-interface.md
+++ b/docs/user-guide/command-line-interface.md
@@ -79,6 +79,7 @@ Caching:
 Miscellaneous:
   --init                         Run config initialization wizard - default: false
   --env-info                     Output execution environment information - default: false
+  --no-error-on-unmatched-pattern  Prevent errors when pattern is unmatched - default: false
   --debug                        Output debugging information
   -h, --help                     Show help
   -v, --version                  Output the version number
@@ -451,6 +452,10 @@ The resulting configuration file will be created in the current directory.
 
 This option outputs information about the execution environment, including the version of Node, npm, and local and global installations of ESLint. The ESLint team may ask for this information to help solve bugs.
 
+#### `--no-error-on-unmatched-pattern`
+
+This option prevents errors when a quoted glob pattern or `--ext` is unmatched. This will not prevent errors when your shell can't match a glob.
+
 #### `--debug`
 
 This option outputs debugging information to the console. This information is useful when you're seeing a problem and having a hard time pinpointing it. The ESLint team may ask for this debugging information to help solve bugs.
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index 0b1c76bac686..22336a91de29 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -576,6 +576,7 @@ class CLIEngine {
             cwd: options.cwd,
             extensions: options.extensions,
             globInputPaths: options.globInputPaths,
+            errorOnUnmatchedPattern: options.errorOnUnmatchedPattern,
             ignore: options.ignore
         });
         const lintResultCache =
diff --git a/lib/cli-engine/file-enumerator.js b/lib/cli-engine/file-enumerator.js
index 700f8009cf88..a231c8db9f86 100644
--- a/lib/cli-engine/file-enumerator.js
+++ b/lib/cli-engine/file-enumerator.js
@@ -190,6 +190,7 @@ class FileEnumerator {
         configArrayFactory = new CascadingConfigArrayFactory({ cwd }),
         extensions = [".js"],
         globInputPaths = true,
+        errorOnUnmatchedPattern = true,
         ignore = true
     } = {}) {
         internalSlotsMap.set(this, {
@@ -208,6 +209,7 @@ class FileEnumerator {
                 "u"
             ),
             globInputPaths,
+            errorOnUnmatchedPattern,
             ignoreFlag: ignore
         });
     }
@@ -226,7 +228,7 @@ class FileEnumerator {
      * @returns {IterableIterator<FileAndConfig>} The found files.
      */
     *iterateFiles(patternOrPatterns) {
-        const { globInputPaths } = internalSlotsMap.get(this);
+        const { globInputPaths, errorOnUnmatchedPattern } = internalSlotsMap.get(this);
         const patterns = Array.isArray(patternOrPatterns)
             ? patternOrPatterns
             : [patternOrPatterns];
@@ -265,14 +267,16 @@ class FileEnumerator {
             }
 
             // Raise an error if any files were not found.
-            if (!foundRegardlessOfIgnored) {
-                throw new NoFilesFoundError(
-                    pattern,
-                    !globInputPaths && isGlob(pattern)
-                );
-            }
-            if (!found) {
-                throw new AllFilesIgnoredError(pattern);
+            if (errorOnUnmatchedPattern) {
+                if (!foundRegardlessOfIgnored) {
+                    throw new NoFilesFoundError(
+                        pattern,
+                        !globInputPaths && isGlob(pattern)
+                    );
+                }
+                if (!found) {
+                    throw new AllFilesIgnoredError(pattern);
+                }
             }
         }
 
diff --git a/lib/cli.js b/lib/cli.js
index 18a917cf0b0a..944b4b79353b 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -68,7 +68,8 @@ function translateOptions(cliOptions) {
         fixTypes: cliOptions.fixType,
         allowInlineConfig: cliOptions.inlineConfig,
         reportUnusedDisableDirectives: cliOptions.reportUnusedDisableDirectives,
-        resolvePluginsRelativeTo: cliOptions.resolvePluginsRelativeTo
+        resolvePluginsRelativeTo: cliOptions.resolvePluginsRelativeTo,
+        errorOnUnmatchedPattern: cliOptions.errorOnUnmatchedPattern
     };
 }
 
diff --git a/lib/options.js b/lib/options.js
index 83bf9afc22c9..98dc04b6eb39 100644
--- a/lib/options.js
+++ b/lib/options.js
@@ -230,6 +230,12 @@ module.exports = optionator({
             default: "false",
             description: "Output execution environment information"
         },
+        {
+            option: "error-on-unmatched-pattern",
+            type: "Boolean",
+            default: "true",
+            description: "Prevent errors when pattern is unmatched"
+        },
         {
             option: "debug",
             type: "Boolean",


Test patch:
diff --git a/tests/fixtures/unmatched-patterns/failing.js b/tests/fixtures/unmatched-patterns/failing.js
new file mode 100644
index 000000000000..e24993ff6f61
--- /dev/null
+++ b/tests/fixtures/unmatched-patterns/failing.js
@@ -0,0 +1,3 @@
+var foo = "bar";
+if (foo) {
+    foo = "bar2";
diff --git a/tests/fixtures/unmatched-patterns/passing.js2 b/tests/fixtures/unmatched-patterns/passing.js2
new file mode 100644
index 000000000000..425be7cea23c
--- /dev/null
+++ b/tests/fixtures/unmatched-patterns/passing.js2
@@ -0,0 +1,5 @@
+let foo = "bar";
+
+if (foo) {
+    foo = "bar2";
+}
diff --git a/tests/lib/cli.js b/tests/lib/cli.js
index e0f10d5ae4dc..6af3b5c838e4 100644
--- a/tests/lib/cli.js
+++ b/tests/lib/cli.js
@@ -347,6 +347,74 @@ describe("cli", () => {
         });
     });
 
+    describe("when executing without no-error-on-unmatched-pattern flag", () => {
+        it("should throw an error on unmatched glob pattern", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const globPattern = "*.js3";
+
+            assert.throws(() => {
+                cli.execute(`"${filePath}/${globPattern}"`);
+            }, `No files matching '${filePath}/${globPattern}' were found.`);
+        });
+
+        it("should throw an error on unmatched --ext", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const extension = ".js3";
+
+            assert.throws(() => {
+                cli.execute(`--ext ${extension} ${filePath}`);
+            }, `No files matching '${filePath}' were found`);
+        });
+    });
+
+    describe("when executing with no-error-on-unmatched-pattern flag", () => {
+        it("should not throw an error on unmatched node glob syntax patterns", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const exit = cli.execute(`--no-error-on-unmatched-pattern "${filePath}/*.js3"`);
+
+            assert.strictEqual(exit, 0);
+        });
+
+        it("should not throw an error on unmatched --ext", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const exit = cli.execute(`--no-error-on-unmatched-pattern --ext .js3 ${filePath}`);
+
+            assert.strictEqual(exit, 0);
+        });
+    });
+
+    describe("when executing with no-error-on-unmatched-pattern flag and multiple patterns", () => {
+        it("should not throw an error on multiple unmatched node glob syntax patterns", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const exit = cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js4`);
+
+            assert.strictEqual(exit, 0);
+        });
+
+        it("should still throw an error on when a matched pattern has lint errors", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const exit = cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js`);
+
+            assert.strictEqual(exit, 1);
+        });
+    });
+
+    describe("when executing with no-error-on-unmatched-pattern flag and multiple --ext arguments", () => {
+        it("should not throw an error on multiple unmatched --ext arguments", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const exit = cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js4 ${filePath}`);
+
+            assert.strictEqual(exit, 0);
+        });
+
+        it("should still throw an error on when a matched pattern has lint errors", () => {
+            const filePath = getFixturePath("unmatched-patterns");
+            const exit = cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js ${filePath}`);
+
+            assert.strictEqual(exit, 1);
+        });
+    });
+
     describe("when executing with help flag", () => {
         it("should print out help", () => {
             assert.strictEqual(cli.execute("-h"), 0);