diff --git a/docs/user-guide/command-line-interface.md b/docs/user-guide/command-line-interface.md index 8938bc91f246..b290bc50be9c 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 e768da837d05..0fdd2e29a874 100644 --- a/lib/cli-engine/cli-engine.js +++ b/lib/cli-engine/cli-engine.js @@ -563,7 +563,8 @@ class CLIEngine { extensions: options.extensions, globInputPaths: options.globInputPaths, ignore: options.ignore, - ignoredPaths + ignoredPaths, + errorOnUnmatchedPattern: options.errorOnUnmatchedPattern }); const lintResultCache = options.cache ? new LintResultCache(cacheFilePath) : null; diff --git a/lib/cli-engine/file-enumerator.js b/lib/cli-engine/file-enumerator.js index 59083fc4636b..fc2368421167 100644 --- a/lib/cli-engine/file-enumerator.js +++ b/lib/cli-engine/file-enumerator.js @@ -191,7 +191,8 @@ class FileEnumerator { extensions = [".js"], globInputPaths = true, ignore = true, - ignoredPaths = new IgnoredPaths({ cwd, ignore }) + ignoredPaths = new IgnoredPaths({ cwd, ignore }), + errorOnUnmatchedPattern = true } = {}) { internalSlotsMap.set(this, { configArrayFactory, @@ -213,7 +214,8 @@ class FileEnumerator { ignoredPathsWithDotfiles: new IgnoredPaths({ ...ignoredPaths.options, dotfiles: true - }) + }), + errorOnUnmatchedPattern }); } @@ -231,7 +233,7 @@ class FileEnumerator { * @returns {IterableIterator} The found files. */ *iterateFiles(patternOrPatterns) { - const { globInputPaths } = internalSlotsMap.get(this); + const { globInputPaths, errorOnUnmatchedPattern } = internalSlotsMap.get(this); const patterns = Array.isArray(patternOrPatterns) ? patternOrPatterns : [patternOrPatterns]; @@ -270,14 +272,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", 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);