diff --git a/core/filter-options/__tests__/get-filtered-packages.test.js b/core/filter-options/__tests__/get-filtered-packages.test.js index 32c451d34d..4407e656b7 100644 --- a/core/filter-options/__tests__/get-filtered-packages.test.js +++ b/core/filter-options/__tests__/get-filtered-packages.test.js @@ -90,6 +90,20 @@ test.each` expect.hasAssertions(); }); +test.each` + argv + ${["--scope", "not-a-package", "--continue-if-no-match"]} + ${["--ignore", "package-*", "--continue-if-no-match"]} + ${["--scope", "package-@(1|2)", "--ignore", "package-{1,2}", "--continue-if-no-match"]} +`("no errors and no packages $argv", async ({ argv }) => { + const packageGraph = await buildGraph(cwd); + const execOpts = { cwd }; + const options = parseOptions(...argv); + + const result = await getFilteredPackages(packageGraph, execOpts, options); + expect(result).toHaveLength(0); +}); + test("--since returns all packages if no tag is found", async () => { const packageGraph = await buildGraph(cwd); const execOpts = { cwd }; diff --git a/core/filter-options/index.js b/core/filter-options/index.js index facba7856b..d1c7d016fc 100644 --- a/core/filter-options/index.js +++ b/core/filter-options/index.js @@ -49,6 +49,11 @@ function filterOptions(yargs) { `, type: "boolean", }, + "continue-if-no-match": { + describe: "Don't fail if no package is matched", + hidden: true, + type: "boolean", + }, }; return yargs.options(opts).group(Object.keys(opts), "Filter Options:"); diff --git a/core/filter-options/lib/get-filtered-packages.js b/core/filter-options/lib/get-filtered-packages.js index 79bd3c0579..1e5e961943 100644 --- a/core/filter-options/lib/get-filtered-packages.js +++ b/core/filter-options/lib/get-filtered-packages.js @@ -9,7 +9,13 @@ function getFilteredPackages(packageGraph, execOpts, options) { let chain = Promise.resolve(); chain = chain.then(() => - filterPackages(packageGraph.rawPackageList, options.scope, options.ignore, options.private) + filterPackages( + packageGraph.rawPackageList, + options.scope, + options.ignore, + options.private, + options.continueIfNoMatch + ) ); if (options.since !== undefined) { diff --git a/utils/filter-packages/filter-packages.js b/utils/filter-packages/filter-packages.js index fb7692c8b3..eb2478d283 100644 --- a/utils/filter-packages/filter-packages.js +++ b/utils/filter-packages/filter-packages.js @@ -16,10 +16,11 @@ module.exports = filterPackages; * @param {Array.} include A list of globs to match the package name against * @param {Array.} exclude A list of globs to filter the package name against * @param {Boolean} showPrivate When false, filter out private packages + * @param {Boolean} continueIfNoMatch When true, do not throw if no package is matched * @return {Array.} The packages with a name matching the glob - * @throws when a given glob would produce an empty list of packages + * @throws when a given glob would produce an empty list of packages and `continueIfNoMatch` is not set. */ -function filterPackages(packagesToFilter, include = [], exclude = [], showPrivate) { +function filterPackages(packagesToFilter, include = [], exclude = [], showPrivate, continueIfNoMatch) { const filtered = new Set(packagesToFilter); const patterns = [].concat(arrify(include), negate(exclude)); @@ -49,7 +50,7 @@ function filterPackages(packagesToFilter, include = [], exclude = [], showPrivat } } - if (!filtered.size) { + if (!filtered.size && !continueIfNoMatch) { throw new ValidationError("EFILTER", util.format("No packages remain after filtering", patterns)); } }