├── .gitignore ├── .travis.yml ├── AUTHORS ├── LICENSE ├── Readme.md ├── appveyor.yml ├── autolint.js ├── lib └── multi-glob.js ├── package-lock.json ├── package.json └── test └── multi-glob-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | .idea 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "6" 5 | - "8" 6 | - "10" 7 | - "11" 8 | before_install: 9 | - npm i -g npm 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Christian Johansen (christian@cjohansen.no) 2 | Stein Magnus Jodal stein.magnus@jodal.no 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The BSD License) 2 | 3 | Copyright (c) 2011-2012, Christian Johansen, christian@cjohansen.no. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of Christian Johansen nor the names of his contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # multi-glob 2 | 3 | [![Build status](https://secure.travis-ci.org/busterjs/multi-glob.svg?branch=master)](http://travis-ci.org/busterjs/multi-glob) 4 | 5 | > Glob all the patterns! 6 | 7 | `multi-glob` is a small wrapper around [node-glob](https://github.com/isaacs/node-glob) 8 | that allows you to glob multiple patterns in one go, and optionally treat it as a failure 9 | if any one pattern matches no files. 10 | 11 | If you just need to glob multiple patterns in one go, you can simply do: 12 | 13 | ```javascript 14 | var glob = require("multi-glob").glob; 15 | glob(["all", "the", "patterns"], cb); 16 | ``` 17 | 18 | However, with `multi-glob`, you can do a "strict" glob, which will cause an 19 | error if e.g. the pattern `"the"` in the previous example matched no files. 20 | 21 | 22 | ## API 23 | 24 | ### `multiGlob.glob(patterns[, options], callback);` 25 | 26 | Works like [node-glob's glob](https://github.com/isaacs/node-glob>), with the 27 | following two exceptions: 28 | 29 | * `patterns` may be either a string pattern or an array of string patterns 30 | * `options` may include `strict`. When set to `true`, `glob` will yield 31 | an error if either one of `patterns` matches no files. 32 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "6" 4 | - nodejs_version: "8" 5 | - nodejs_version: "10" 6 | - nodejs_version: "11" 7 | 8 | install: 9 | - ps: Install-Product node $env:nodejs_version 10 | - npm install -g npm 11 | - npm install 12 | 13 | test_script: 14 | - node --version 15 | - npm --version 16 | - npm test 17 | 18 | build: off 19 | -------------------------------------------------------------------------------- /autolint.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | paths: [ 3 | "lib/*.js", 4 | "test/*.js" 5 | ], 6 | linterOptions: { 7 | node: true, 8 | browser: true, 9 | plusplus: true, 10 | sloppy: true, 11 | vars: true, 12 | nomen: true, 13 | predef: [ 14 | "define", 15 | "assert", 16 | "refute", 17 | "buster" 18 | ] 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /lib/multi-glob.js: -------------------------------------------------------------------------------- 1 | var glob = require("glob"); 2 | 3 | exports._glob = glob; 4 | 5 | function uniq(xs) { 6 | return xs.reduce((unique, x) => { 7 | return unique.indexOf(x) < 0 ? unique.concat(x) : unique; 8 | }, []); 9 | } 10 | 11 | function flatten(xs) { 12 | return xs.reduce((flattened, x) => { 13 | return flattened.concat(Array.isArray(x) ? flatten(x) : x); 14 | }, []); 15 | } 16 | 17 | function array(arr) { 18 | return Array.isArray(arr) ? arr : [arr]; 19 | } 20 | 21 | function resolveGlobs(patterns, options) { 22 | options = options || {}; 23 | return Promise.all( 24 | array(patterns).map(pattern => new Promise((resolve, reject) => { 25 | exports._glob(pattern, options, (err, matches) => { 26 | if (!err && options.strict && matches.length === 0) { 27 | reject(new Error("'" + pattern + "' matched no files")); 28 | } else if (err) { 29 | reject(err); 30 | } else { 31 | resolve(matches); 32 | } 33 | }); 34 | })) 35 | ); 36 | } 37 | 38 | exports.glob = function (patterns, options, cb) { 39 | if (typeof options === "function") { 40 | cb = options; 41 | options = null; 42 | } 43 | 44 | resolveGlobs(patterns, options) 45 | .then(matches => cb(null, uniq(flatten(array(matches))))) 46 | .catch(err => cb(err)); 47 | }; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multi-glob", 3 | "version": "1.0.2", 4 | "description": "Small wrapper around the glob module that allows globbing for multiple patterns at once", 5 | "homepage": "http://busterjs.org/docs/modules/multi-glob", 6 | "author": "Christian Johansen", 7 | "contributors": [ 8 | { 9 | "name": "Christian Johansen", 10 | "email": "christian@cjohansen.no", 11 | "url": "http://cjohansen.no" 12 | }, 13 | { 14 | "name": "Stein Magnus Jodal", 15 | "email": "stein.magnus@jodal.no", 16 | "url": "http://jodal.no" 17 | } 18 | ], 19 | "license": "BSD-3-Clause", 20 | "main": "./lib/multi-glob", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/busterjs/multi-glob.git" 24 | }, 25 | "scripts": { 26 | "test": "jest", 27 | "autotest": "jest --watch --verbose", 28 | "test-debug": "node --debug-brk node_modules/.bin/jest" 29 | }, 30 | "engines": { 31 | "node": ">= 6" 32 | }, 33 | "dependencies": { 34 | "glob": "5.x" 35 | }, 36 | "devDependencies": { 37 | "@sinonjs/referee-sinon": "^4.1.0", 38 | "jest": "23.6.0" 39 | }, 40 | "jest": { 41 | "testRegex": "test/*\\.js" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/multi-glob-test.js: -------------------------------------------------------------------------------- 1 | /*global describe, beforeEach, afterEach, it*/ 2 | const {sinon, assert} = require("@sinonjs/referee-sinon"); 3 | const multiGlob = require("../lib/multi-glob.js"); 4 | 5 | describe("Multi-glob", function () { 6 | var nodeGlobStub; 7 | 8 | beforeEach(function () { 9 | nodeGlobStub = sinon.stub(multiGlob, "_glob"); 10 | }); 11 | 12 | afterEach(function () { 13 | multiGlob._glob.restore(); 14 | }); 15 | 16 | it("calls glob with pattern", function () { 17 | multiGlob.glob("lib/buster.js", () => {}); 18 | 19 | assert.calledOnceWith(nodeGlobStub, "lib/buster.js"); 20 | }); 21 | 22 | it("calls glob with provided options", function () { 23 | var args = { silent: true }; 24 | multiGlob.glob("lib/buster.js", args, () => {}); 25 | 26 | assert.calledOnceWith(nodeGlobStub, "lib/buster.js", args); 27 | }); 28 | 29 | it("calls glob with empty options when none are provided", function () { 30 | multiGlob.glob("lib/buster.js", () => {}); 31 | assert.equals(nodeGlobStub.args[0].length, 3); 32 | assert.isFunction(nodeGlobStub.args[0][2]); 33 | }); 34 | 35 | it("calls glob once with each pattern", function () { 36 | multiGlob.glob(["lib/buster.js", "src/buster.js"], () => {}); 37 | 38 | assert.calledTwice(nodeGlobStub); 39 | assert.calledWith(nodeGlobStub, "lib/buster.js"); 40 | assert.calledWith(nodeGlobStub, "src/buster.js"); 41 | }); 42 | 43 | it("calls callback with result from glob", function (done) { 44 | nodeGlobStub.yields(null, ["lib/buster.js"]); 45 | 46 | multiGlob.glob("lib/buster.js", function (err, res) { 47 | assert.isNull(err); 48 | assert.equals(res, ["lib/buster.js"]); 49 | done(); 50 | }); 51 | }); 52 | 53 | it("calls callback with combined results from glob", function (done) { 54 | nodeGlobStub.withArgs("lib/buster.js").yields(null, ["lib/buster.js"]); 55 | var files = ["src/buster.js", "src/stuff.js"]; 56 | nodeGlobStub.withArgs("src/*.js").yields(null, files); 57 | 58 | multiGlob.glob(["lib/buster.js", "src/*.js"], function (err, res) { 59 | assert.isNull(err); 60 | assert.equals(res, ["lib/buster.js", "src/buster.js", "src/stuff.js"]); 61 | done(); 62 | }); 63 | }); 64 | 65 | it("calls callback once with glob error", function (done) { 66 | nodeGlobStub.withArgs("lib/buster.js").yields({ message: "Oh no" }); 67 | var files = ["src/buster.js", "src/stuff.js"]; 68 | nodeGlobStub.withArgs("src/*.js").yields(null, files); 69 | 70 | multiGlob.glob(["lib/buster.js", "src/*.js"], function (err) { 71 | assert.equals(err, { message: "Oh no" }); 72 | done(); 73 | }); 74 | }); 75 | 76 | it("ignore duplicated items from glob", function (done) { 77 | nodeGlobStub.withArgs("src/foo.js").yields(null, ["src/foo.js"]); 78 | var files = ["src/foo.js", "src/bar.js"]; 79 | nodeGlobStub.withArgs("src/*.js").yields(null, files); 80 | 81 | multiGlob.glob(["src/foo.js", "src/*.js"], function (err, res) { 82 | assert.isNull(err); 83 | assert.equals(res, ["src/foo.js", "src/bar.js"]); 84 | done(); 85 | }); 86 | }); 87 | 88 | it("fails on glob that matches no patterns when strict", function (done) { 89 | nodeGlobStub.withArgs("src/foo.js").yields(null, []); 90 | 91 | multiGlob.glob(["src/foo.js"], { strict: true }, function (err) { 92 | assert.match(err, { 93 | message: "'src/foo.js' matched no files" 94 | }); 95 | done(); 96 | }); 97 | }); 98 | }); 99 | --------------------------------------------------------------------------------