├── examples
├── fixtures
│ ├── bar
│ ├── baz
│ ├── foo
│ ├── fo.o.ba.r
│ ├── foo.bar
│ ├── foobar
│ └── moo.cow
├── sync.js
├── glob-array.js
├── promise.js
├── glob-string.js
├── extglobs.js
├── negation.js
└── events.js
├── test
├── fixtures
│ └── a
│ │ ├── b
│ │ └── c
│ │ │ └── d
│ │ ├── bc
│ │ └── e
│ │ │ └── f
│ │ ├── c
│ │ └── d
│ │ │ └── c
│ │ │ └── b
│ │ ├── cb
│ │ └── e
│ │ │ └── f
│ │ ├── x
│ │ └── .y
│ │ │ └── b
│ │ ├── z
│ │ └── .y
│ │ │ └── b
│ │ ├── abcdef
│ │ └── g
│ │ │ └── h
│ │ ├── abcfed
│ │ └── g
│ │ │ └── h
│ │ ├── symlink
│ │ └── a
│ │ │ └── b
│ │ │ └── c
│ │ └── .abcdef
│ │ └── x
│ │ └── y
│ │ └── z
│ │ └── a
├── support
│ ├── home.js
│ ├── setup.js
│ ├── symlinks.js
│ └── generate.js
├── events.files.js
├── _setup.js
├── empty-set.js
├── events.match.js
├── api.js
├── options.nocase.js
├── options.nonull.js
├── options.cwd.js
├── options.follow.js
├── options.realpath.js
└── broken-symlink.js
├── .travis.yml
├── .gitattributes
├── .editorconfig
├── .gitignore
├── LICENSE
├── .verb.md
├── package.json
├── .github
└── contributing.md
├── .eslintrc.json
├── README.md
└── index.js
/examples/fixtures/bar:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/fixtures/baz:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/fixtures/foo:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/fixtures/fo.o.ba.r:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/fixtures/foo.bar:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/fixtures/foobar:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/fixtures/moo.cow:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/fixtures/a/b/c/d:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/bc/e/f:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/c/d/c/b:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/cb/e/f:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/x/.y/b:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/z/.y/b:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/abcdef/g/h:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/abcfed/g/h:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/test/fixtures/a/symlink/a/b/c:
--------------------------------------------------------------------------------
1 | ../..
--------------------------------------------------------------------------------
/test/fixtures/a/.abcdef/x/y/z/a:
--------------------------------------------------------------------------------
1 | contents
--------------------------------------------------------------------------------
/examples/sync.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var glob = require('..');
4 |
5 | console.log(glob.sync('*.md', {dotglob: true}));
6 | console.log(glob.sync('readme.md'));
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | os:
3 | - linux
4 | - osx
5 | language: node_js
6 | node_js:
7 | - node
8 | - '8'
9 | - '7'
10 | - '6'
11 | - '5'
12 | - '4'
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Enforce Unix newlines
2 | * text eol=lf
3 |
4 | # binaries
5 | *.ai binary
6 | *.psd binary
7 | *.jpg binary
8 | *.gif binary
9 | *.png binary
10 | *.jpeg binary
11 |
--------------------------------------------------------------------------------
/examples/glob-array.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var glob = require('..');
4 |
5 | glob(['**/*.js', '**/*.md'], function(err, files) {
6 | if (err) return console.log(err);
7 | console.log(files);
8 | });
9 |
10 |
--------------------------------------------------------------------------------
/examples/promise.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var glob = require('..');
4 |
5 | glob.on('files', function(files) {
6 | console.log(files);
7 | });
8 |
9 | glob.promise(['**/*.js', '**/*.md'])
10 | .then(null, console.error)
11 |
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org/
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [{**/{actual,fixtures,expected,templates}/**,*.md}]
13 | trim_trailing_whitespace = false
14 | insert_final_newline = false
15 |
--------------------------------------------------------------------------------
/examples/glob-string.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var argv = require('minimist')(process.argv.slice(2));
4 | var glob = argv.glob ? require('glob') : require('..');
5 | console.time(argv.glob ? 'glob' : 'bash-glob');
6 |
7 | glob('**/*.js', function(err, files) {
8 | if (err) return console.log(err);
9 | console.log(files.length);
10 | console.timeEnd(argv.glob ? 'glob' : 'bash-glob');
11 | });
12 |
--------------------------------------------------------------------------------
/test/support/home.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var mkdirp = require('mkdirp');
5 | var each = require('each-parallel-async');
6 |
7 | module.exports = function(names, cwd, cb) {
8 | if (typeof cwd === 'function') {
9 | cb = cwd;
10 | cwd = '/tmp/glob-test';
11 | }
12 |
13 | each(names, function(name, next) {
14 | mkdirp(path.resolve(cwd, name), next);
15 | }, cb);
16 | };
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # always ignore files
2 | *.DS_Store
3 | .idea
4 | *.sublime-*
5 |
6 | # test related, or directories generated by tests
7 | test/actual
8 | actual
9 | coverage
10 | .nyc*
11 |
12 | # npm
13 | node_modules
14 | npm-debug.log
15 |
16 | # yarn
17 | yarn.lock
18 | yarn-error.log
19 |
20 | # misc
21 | _gh_pages
22 | _draft
23 | _drafts
24 | bower_components
25 | vendor
26 | temp
27 | tmp
28 | TODO.md
29 | package-lock.json
--------------------------------------------------------------------------------
/examples/extglobs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var argv = require('minimist')(process.argv.slice(2));
5 | var glob = argv.glob ? require('glob') : require('..');
6 | console.time(argv.glob ? 'glob' : 'bash-glob');
7 |
8 | var files = glob.sync('!(*.*).!(*.*)', {cwd: path.join(__dirname, 'fixtures')});
9 | console.log(files);
10 | console.log(files.length);
11 | console.timeEnd(argv.glob ? 'glob' : 'bash-glob');
12 |
--------------------------------------------------------------------------------
/examples/negation.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var argv = require('minimist')(process.argv.slice(2));
4 | var glob = argv.glob ? require('glob') : require('..');
5 | console.time(argv.glob ? 'glob' : 'bash-glob');
6 |
7 | glob('!(node_modules|vendor)/**/*.js', function(err, files) {
8 | if (err) return console.log(err);
9 | console.log(files);
10 | console.log(files.length);
11 | console.timeEnd(argv.glob ? 'glob' : 'bash-glob');
12 | });
13 |
--------------------------------------------------------------------------------
/test/support/setup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var path = require('path');
5 | var mkdirp = require('mkdirp');
6 | var each = require('each-parallel-async');
7 | var del = require('delete');
8 |
9 | module.exports = function(files, cwd, cb) {
10 | del(cwd, function(err) {
11 | if (err) return cb(err);
12 |
13 | each(files, function(filename, next) {
14 | var filepath = path.resolve(cwd, filename);
15 |
16 | mkdirp(path.dirname(filepath), '0755', function(err) {
17 | if (err) return next(err);
18 |
19 | fs.writeFile(filepath, 'contents', next);
20 | });
21 | }, cb);
22 | });
23 | };
24 |
--------------------------------------------------------------------------------
/test/support/symlinks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var path = require('path');
5 | var mkdirp = require('mkdirp');
6 | var del = require('delete');
7 |
8 | module.exports = function(cwd, cb) {
9 | if (process.platform !== 'win32') {
10 | del(path.join(cwd, 'a/symlink'), function(err) {
11 | if (err) return cb(err);
12 | var symlinkTo = path.resolve(cwd, 'a/symlink/a/b/c');
13 | mkdirp(path.dirname(symlinkTo), '0755', function(err) {
14 | if (err) return cb(err);
15 | fs.symlinkSync('../..', symlinkTo, 'dir');
16 | cb();
17 | });
18 | });
19 | } else {
20 | cb();
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/examples/events.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var glob = require('./');
4 | glob.on('match', function(match, cwd) {
5 | console.log(cwd, match);
6 | });
7 |
8 | // console.log(glob.sync('**/*.md', {dotglob: true}));
9 | console.log(glob.sync('readme.md', {dotglob: true, nocase: true}));
10 |
11 | // glob.sync('**/*.md', {dotglob: true});
12 | // glob.sync('readme.md', {dotglob: true});
13 |
14 | // console.log(glob.cache)
15 |
16 | // glob('slslls', {dot: true, nonull: true}, function(err, files) {
17 | // if (err) return console.log(err);
18 | // console.log(files);
19 | // });
20 |
21 | // glob('*.asfkasjks', {dot: true}, function(err, files) {
22 | // if (err) return console.log(err);
23 | // console.log(files);
24 | // });
25 |
26 |
--------------------------------------------------------------------------------
/test/events.files.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cwd = process.cwd();
4 | var unique = require('array-unique');
5 | var assert = require('assert');
6 | var path = require('path');
7 | var glob = require('..');
8 |
9 | describe('files event', function() {
10 | before(function() {
11 | process.chdir(path.join(__dirname, 'fixtures'));
12 | });
13 |
14 | after(function() {
15 | process.chdir(cwd);
16 | });
17 |
18 | it('should not return duplicate matches', function(cb) {
19 | var pattern = ['a/**/[gh]', 'a/**/[a-z]'];
20 | var matches = [];
21 |
22 | glob.on('files', function(files) {
23 | matches.push.apply(matches, files);
24 | });
25 |
26 | glob.on('end', function(files) {
27 | matches = matches.sort();
28 | files = files.sort();
29 | assert.deepEqual(unique(files), files, 'should have same files of matches');
30 | assert.deepEqual(matches, files, 'should have same files of matches');
31 | cb();
32 | });
33 |
34 | glob.sync(pattern);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/_setup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('mocha');
4 | var del = require('delete');
5 | var path = require('path');
6 | var symlinks = require('./support/symlinks');
7 | var setup = require('./support/setup');
8 | var home = require('./support/home');
9 |
10 | var fixtures = path.join(__dirname, 'fixtures');
11 | var files = [
12 | 'a/.abcdef/x/y/z/a',
13 | 'a/abcdef/g/h',
14 | 'a/abcfed/g/h',
15 | 'a/b/c/d',
16 | 'a/bc/e/f',
17 | 'a/c/d/c/b',
18 | 'a/cb/e/f',
19 | 'a/x/.y/b',
20 | 'a/z/.y/b'
21 | ];
22 |
23 | describe('setup', function() {
24 | it('should remove fixtures', function(cb) {
25 | del([fixtures, '/tmp/glob-test'], {force: true}, cb);
26 | });
27 |
28 | it('should create test fixtures', function(cb) {
29 | setup(files, fixtures, cb);
30 | });
31 |
32 | it('should setup symlinks', function(cb) {
33 | symlinks(fixtures, cb);
34 | });
35 |
36 | it('should setup fixtures in user home', function(cb) {
37 | home(['foo', 'bar', 'baz', 'asdf', 'quux', 'qwer', 'rewq'], cb);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/test/empty-set.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var assert = require('assert');
4 | var glob = require('..');
5 |
6 | // Patterns that cannot match anything
7 | var patterns = [
8 | '# comment',
9 | ' ',
10 | '\n',
11 | 'just doesnt happen to match anything so this is a control'
12 | ];
13 |
14 | describe('empty sets', function() {
15 | patterns.forEach(function(pattern) {
16 | it('async: ' + JSON.stringify(pattern), function(cb) {
17 | glob(pattern, function(err, files) {
18 | if (err) return cb(err);
19 | assert.deepEqual(files, [], 'expected an empty array');
20 | cb();
21 | });
22 | });
23 |
24 | it('promise: ' + JSON.stringify(pattern), function() {
25 | return glob(pattern)
26 | .then(function(files) {
27 | assert.deepEqual(files, [], 'expected an empty array');
28 | });
29 | });
30 |
31 | it('sync: ' + JSON.stringify(pattern), function() {
32 | var files = glob.sync(pattern);
33 | assert.deepEqual(files, [], 'expected an empty array');
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-2017, Jon Schlinkert.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/events.match.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cwd = process.cwd();
4 | var unique = require('array-unique');
5 | var union = require('arr-union');
6 | var assert = require('assert');
7 | var path = require('path');
8 | var glob = require('..');
9 |
10 | describe('match event', function() {
11 | before(function() {
12 | process.chdir(path.join(__dirname, 'fixtures'));
13 | });
14 |
15 | after(function() {
16 | process.chdir(cwd);
17 | });
18 |
19 | it('should not return duplicate matches', function(cb) {
20 | var pattern = ['a/**/[gh]', 'a/**/[a-z]'];
21 | var matches = [];
22 | var count = 0;
23 |
24 | glob.on('match', function(files) {
25 | union(matches, files);
26 | });
27 |
28 | glob.once('end', function(files) {
29 | matches = matches.sort();
30 | files = files.sort();
31 | assert.deepEqual(unique(files), files, 'should have same files of matches');
32 | assert.deepEqual(matches, files, 'should have same files of matches');
33 | count++;
34 | });
35 |
36 | glob(pattern, function(err, files) {
37 | if (err) return cb(err);
38 | assert.equal(count, 1);
39 | assert.deepEqual(unique(files), files, 'should have same files of matches');
40 | assert.deepEqual(matches, files, 'should have same files of matches');
41 | cb();
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/test/support/generate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var path = require('path');
5 | var reduce = require('async-array-reduce');
6 | var glob = require('../../glob');
7 |
8 | function generate(patterns, options, cb) {
9 | reduce(patterns, [], function(acc, pattern, next) {
10 | glob(pattern, options, function(err, files) {
11 | if (err) return next(err);
12 | acc[pattern] = files;
13 | next(null, acc);
14 | });
15 | }, cb);
16 | }
17 |
18 | // var filename = path.resolve(__dirname, 'bash-results.json');
19 | // var data = JSON.stringify(result, null, 2) + '\n';
20 | // fs.writeFile(filename, data, cb);
21 |
22 | var globs = [
23 | ['*/b* */c*'],
24 | '**/*',
25 | 'a/*/+(c|g)/./d',
26 | 'a/**/[cg]/../[cg]',
27 | 'a/{b,c,d,e,f}/**/g',
28 | 'a/b/**',
29 | '**/g',
30 | 'a/abc{fed,def}/g/h',
31 | 'a/abc{fed/g,def}/**/',
32 | 'a/abc{fed/g,def}/**///**/',
33 | '**/a/**/',
34 | '+(a|b|c)/a{/,bc*}/**',
35 | '*/*/*/f',
36 | '**/f',
37 | 'a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**',
38 | '{./*/*,/tmp/glob-test/*}',
39 | '{/tmp/glob-test/*,*}', // evil owl face! how you taunt me!
40 | 'a/!(symlink)/*',
41 | 'a/symlink/a/**/*'
42 | ];
43 |
44 | var options = {
45 | foo: true,
46 | globstar: true,
47 | extglob: false,
48 | cwd: path.join(__dirname, '../fixtures')
49 | };
50 |
51 | generate(globs, options, function() {
52 | console.log(arguments)
53 | });
54 |
--------------------------------------------------------------------------------
/test/api.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('mocha');
4 | var assert = require('assert');
5 | var glob = require('..');
6 |
7 | describe('bash-glob', function() {
8 | describe('API', function() {
9 | it('should export an function', function() {
10 | assert(glob);
11 | assert.equal(typeof glob, 'function');
12 | });
13 |
14 | it('should expose a `.sync` method', function() {
15 | assert.equal(typeof glob.sync, 'function');
16 | });
17 | });
18 |
19 | describe('async: errors', function() {
20 | it('should throw an error when a callback is not passed', function(cb) {
21 | try {
22 | glob('*', {}, null);
23 | cb(new Error('expected an error'));
24 | } catch (err) {
25 | assert(err);
26 | assert.equal(err.message, 'expected callback to be a function');
27 | cb();
28 | }
29 | });
30 |
31 | it('should throw an error when invalid glob is passed', function(cb) {
32 | glob(null, function(err, files) {
33 | assert.equal(err.message, 'expected glob to be a string or array');
34 | cb();
35 | });
36 | });
37 | });
38 |
39 | describe('sync: errors', function() {
40 | it('should throw an error when invalid glob is passed', function(cb) {
41 | try {
42 | glob.sync();
43 | cb(new Error('expected an error'));
44 | } catch (err) {
45 | assert(err);
46 | assert.equal(err.message, 'expected glob to be a string or array');
47 | cb();
48 | }
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/.verb.md:
--------------------------------------------------------------------------------
1 | **Install bash 4.3 or later**
2 |
3 | I recommend using [homebrew](https://github.com/Homebrew/homebrew-core) to install/upgrade bash:
4 |
5 | ```sh
6 | $ brew upgrade bash
7 | ```
8 |
9 | ## Why?
10 |
11 | The initial motivation was to use this for generating the `expected` values for comparisons in tests. But as it turns out, this is faster than node-glob in most cases I've tested.
12 |
13 | Moreover, this supports the majority of the feature-functionaly in node-glob, and it's more Bash-compliant since, well, it **is** Bash.
14 |
15 | **Edge cases**
16 |
17 | Inevitably there will be edge cases. Thus far, however, I've found that many of the edge cases that seem to be problematic are already addressed or not problematic for Bash.
18 |
19 | Please feel free to [create an issue](../../issues) if you find a bug or have a feature request.
20 |
21 | ## Usage
22 |
23 | ```js
24 | var glob = require('{%= name %}');
25 | glob(pattern[, options]);
26 | ```
27 |
28 | ## API
29 | {%= apidocs("index.js") %}
30 |
31 | ## Options
32 |
33 | The following options may be used with the main `glob` function or any other method:
34 |
35 | - `dotglob`: (or `dot`, for [node-glob][] compatibility) Includes filenames beginning with a `.` (dot) in the results of pathname expansion.
36 | - `extglob`: Enable extended [pattern matching](http://wiki.bash-hackers.org/syntax/pattern) features.
37 | - `failglob`: If set, patterns that fail to match filenames during pathname expansion result in an error message.
38 | - `globstar`: Enable recursive globbing with `**`.
39 | - `nocaseglob`: (or `nocase`, for [node-glob][] compatibility) Enable case-insensitive matching in filenames when performing pathname expansion.
40 | - `nullglob`: If set, Bash allows patterns which match no files to expand to a null string, rather than themselves.
41 |
--------------------------------------------------------------------------------
/test/options.nocase.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cwd = process.cwd();
4 | var fs = require('fs');
5 | var path = require('path');
6 | var assert = require('assert');
7 | var mkdirp = require('mkdirp');
8 | var del = require('delete');
9 | var fixtures = path.join.bind(path, __dirname, 'fixtures');
10 | var dir = fixtures('package');
11 | var glob = require('..');
12 |
13 | describe('options.nocase', function() {
14 | before(function(cb) {
15 | process.chdir(fixtures());
16 | mkdirp.sync(dir);
17 | fs.writeFileSync('package/package.json', '{}', 'ascii');
18 | fs.writeFileSync('package/README.md', 'z', 'ascii');
19 | fs.writeFileSync('package/README', 'x', 'ascii');
20 | cb();
21 | });
22 |
23 | after(function(cb) {
24 | process.chdir(cwd);
25 | del(dir, cb);
26 | });
27 |
28 | describe('async', function() {
29 | it('should match a file with no extension', function(cb) {
30 | var options = {cwd: dir, nocase: true};
31 | glob('README?(.*)', options, function(err, files) {
32 | if (err) return cb(err);
33 | assert.deepEqual(files, ['README', 'README.md']);
34 | cb();
35 | });
36 | });
37 | });
38 |
39 | describe('promise', function() {
40 | it('should match a file with no extension', function() {
41 | var options = {cwd: dir, nocase: true};
42 | return glob('README?(.*)', options)
43 | .then(function(files) {
44 | assert.deepEqual(files, ['README', 'README.md']);
45 | });
46 | });
47 |
48 | it('should match a file with no extension (explicit)', function() {
49 | var options = {cwd: dir, nocase: true};
50 | return glob.promise('README?(.*)', options)
51 | .then(function(files) {
52 | assert.deepEqual(files, ['README', 'README.md']);
53 | });
54 | });
55 | });
56 |
57 | describe('sync', function() {
58 | it('should match a file with no extension', function() {
59 | var options = {cwd: dir, nocase: true};
60 | var files = glob.sync('README?(.*)', options);
61 | assert.deepEqual(files, ['README', 'README.md']);
62 | });
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bash-glob",
3 | "description": "Bash-powered globbing for node.js",
4 | "version": "2.0.0",
5 | "homepage": "https://github.com/micromatch/bash-glob",
6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
7 | "repository": "micromatch/bash-glob",
8 | "bugs": {
9 | "url": "https://github.com/micromatch/bash-glob/issues"
10 | },
11 | "license": "MIT",
12 | "files": [
13 | "index.js"
14 | ],
15 | "main": "index.js",
16 | "engines": {
17 | "node": ">=4.0"
18 | },
19 | "scripts": {
20 | "test": "mocha"
21 | },
22 | "dependencies": {
23 | "bash-path": "^1.0.1",
24 | "component-emitter": "^1.2.1",
25 | "cross-spawn": "^5.1.0",
26 | "each-parallel-async": "^1.0.0",
27 | "extend-shallow": "^2.0.1",
28 | "is-extglob": "^2.1.1",
29 | "is-glob": "^4.0.0"
30 | },
31 | "devDependencies": {
32 | "arr-union": "^3.1.0",
33 | "array-unique": "^0.3.2",
34 | "async-array-reduce": "^1.0.0",
35 | "delete": "^1.1.0",
36 | "glob": "^7.1.2",
37 | "gulp-format-md": "^1.0.0",
38 | "minimist": "^1.2.0",
39 | "mkdirp": "^0.5.1",
40 | "mocha": "^3.2.0"
41 | },
42 | "keywords": [
43 | "bash",
44 | "expand",
45 | "expansion",
46 | "expression",
47 | "file",
48 | "files",
49 | "filter",
50 | "find",
51 | "glob",
52 | "globbing",
53 | "globs",
54 | "globstar",
55 | "match",
56 | "matcher",
57 | "matches",
58 | "matching",
59 | "micromatch",
60 | "minimatch",
61 | "multimatch",
62 | "nanomatch",
63 | "path",
64 | "pattern",
65 | "patterns",
66 | "regex",
67 | "regexp",
68 | "regular",
69 | "shell",
70 | "wildcard"
71 | ],
72 | "verb": {
73 | "toc": false,
74 | "layout": "default",
75 | "tasks": [
76 | "readme"
77 | ],
78 | "plugins": [
79 | "gulp-format-md"
80 | ],
81 | "related": {
82 | "list": [
83 | "bash-match",
84 | "braces",
85 | "micromatch",
86 | "nanomatch"
87 | ]
88 | },
89 | "lint": {
90 | "reflinks": true
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/test/options.nonull.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cwd = process.cwd();
4 | var util = require('util');
5 | var assert = require('assert');
6 | var glob = require('..');
7 |
8 | // [pattern, options, expected]
9 | var cases = [
10 | ['a/*NOFILE*/**/', {}, []],
11 | ['a/*NOFILE*/**/', {nonull: true}, ['a/*NOFILE*/**/']],
12 | ['*/*', {cwd: 'NODIR'}, []],
13 | ['*/*', {cwd: 'NODIR', nonull: true}, ['*/*']],
14 | ['NOFILE', {}, []],
15 | ['NOFILE', {nonull: true}, ['NOFILE']],
16 | ['NOFILE', {cwd: 'NODIR'}, []],
17 | ['NOFILE', {cwd: 'NODIR', nonull: true}, ['NOFILE']]
18 | ];
19 |
20 | describe('options.nonull', function() {
21 | before(function() {
22 | process.chdir(__dirname);
23 | });
24 |
25 | after(function() {
26 | process.chdir(cwd);
27 | });
28 |
29 | cases.forEach(function(ele) {
30 | var options = Object.assign({}, ele[1]);
31 | var pattern = ele[0];
32 | var expected = ele[2].sort();
33 |
34 | it('async:' + pattern + ' ' + util.inspect(options), function(cb) {
35 | var files = glob.sync(pattern, options).sort();
36 | assert.deepEqual(files, expected, 'sync results');
37 |
38 | glob(pattern, options, function(err, files) {
39 | if (err) return cb(err);
40 | files = files.sort();
41 | assert.deepEqual(files, expected, 'async results');
42 | cb();
43 | });
44 | });
45 |
46 | it('promise:' + pattern + ' ' + util.inspect(options), function() {
47 | var files = glob.sync(pattern, options).sort();
48 | assert.deepEqual(files, expected, 'sync results');
49 |
50 | return glob(pattern, options)
51 | .then(function(files) {
52 | files = files.sort();
53 | assert.deepEqual(files, expected, 'async results');
54 | });
55 | });
56 |
57 | it('promise explicit:' + pattern + ' ' + util.inspect(options), function() {
58 | var files = glob.sync(pattern, options).sort();
59 | assert.deepEqual(files, expected, 'sync results');
60 |
61 | return glob.promise(pattern, options)
62 | .then(function(files) {
63 | files = files.sort();
64 | assert.deepEqual(files, expected, 'async results');
65 | });
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/test/options.cwd.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cwd = process.cwd();
4 | var path = require('path');
5 | var assert = require('assert');
6 | var fixtures = path.join(__dirname, 'fixtures');
7 | var glob = require('..');
8 |
9 | describe('options.cwd', function() {
10 | before(function(cb) {
11 | process.chdir(fixtures);
12 | cb();
13 | });
14 |
15 | after(function(cb) {
16 | process.chdir(cwd);
17 | cb();
18 | });
19 |
20 | describe('cwd globbing', function() {
21 | it('no matches', function(cb) {
22 | glob('fofofooffo/alalal/**/d', function(err, files) {
23 | if (err) return cb(err);
24 | assert.deepEqual(files, []);
25 | cb();
26 | });
27 | });
28 |
29 | it('.', function(cb) {
30 | glob('**/d', function(err, files) {
31 | if (err) return cb(err);
32 | assert.deepEqual(files, ['a/b/c/d', 'a/c/d']);
33 | cb();
34 | });
35 | });
36 |
37 | it('a', function(cb) {
38 | glob('**/d', {cwd: path.resolve('a')}, function(err, files) {
39 | if (err) return cb(err);
40 | assert.deepEqual(files, ['b/c/d', 'c/d']);
41 | cb();
42 | });
43 | });
44 |
45 | it('a/b', function(cb) {
46 | glob('**/d', {cwd: path.resolve('a/b')}, function(err, files) {
47 | if (err) return cb(err);
48 | assert.deepEqual(files, ['c/d']);
49 | cb();
50 | });
51 | });
52 |
53 | it('a/b/', function(cb) {
54 | glob('**/d', {cwd: path.resolve('a/b/')}, function(err, files) {
55 | if (err) return cb(err);
56 | assert.deepEqual(files, ['c/d']);
57 | cb();
58 | });
59 | });
60 |
61 | it('.', function(cb) {
62 | glob('**/d', {cwd: process.cwd()}, function(err, files) {
63 | if (err) return cb(err);
64 | assert.deepEqual(files, ['a/b/c/d', 'a/c/d']);
65 | cb();
66 | });
67 | });
68 | });
69 |
70 | describe('non-dir cwd should raise error', function() {
71 | var notdir = 'a/b/c/d';
72 | var expected = 'cwd is not a directory: ' + notdir;
73 |
74 | it('sync', function() {
75 | assert.throws(function() {
76 | glob.sync('*', {cwd: notdir});
77 | });
78 | });
79 |
80 | it('async', function(cb) {
81 | glob('*', {cwd: notdir}, function(err, results) {
82 | assert.equal(err.message, expected);
83 | cb();
84 | });
85 | });
86 |
87 | it('promise', function() {
88 | return glob('*', {cwd: notdir})
89 | .then(function() {
90 | return Promise.reject(new Error('expected glob error to be thrown'));
91 | })
92 | .catch(function(err) {
93 | assert.equal(err.message, expected);
94 | });
95 | });
96 | });
97 | });
98 |
99 |
--------------------------------------------------------------------------------
/test/options.follow.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cwd = process.cwd();
4 | var isWindows = process.platform === 'win32';
5 | var assert = require('assert');
6 | var path = require('path');
7 | var glob = require('..');
8 |
9 | describe('options.follow', function() {
10 | before(function() {
11 | process.chdir(path.join(__dirname, 'fixtures'));
12 | });
13 |
14 | after(function() {
15 | process.chdir(cwd);
16 | });
17 |
18 | it('async: should follow symlinks', function(cb) {
19 | if (isWindows) return this.skip();
20 |
21 | var pattern = 'a/symlink/**';
22 | var followSync = glob.sync(pattern, { follow: true }).sort();
23 | var noFollowSync = glob.sync(pattern).sort();
24 |
25 | assert.notDeepEqual(followSync, noFollowSync, 'followSync should not equal noFollowSync');
26 |
27 | glob(pattern, { follow: true }, function(err, files) {
28 | if (err) throw err;
29 | var follow = files.sort();
30 |
31 | glob(pattern, function(err, files) {
32 | if (err) throw err;
33 | var noFollow = files.sort();
34 |
35 | assert.deepEqual(noFollow, noFollowSync, 'sync and async noFollow should match');
36 | assert.notDeepEqual(follow, noFollow, 'follow should not equal noFollow');
37 |
38 | var long = path.resolve('a/symlink/a/b');
39 | assert.deepEqual(follow, followSync, 'sync and async follow should match');
40 | assert.notEqual(follow.indexOf(long), -1, 'follow should have long entry');
41 | assert.notEqual(followSync.indexOf(long), -1, 'followSync should have long entry');
42 | cb();
43 | });
44 | });
45 | });
46 |
47 | it('promise: should follow symlinks', function() {
48 | if (isWindows) return this.skip();
49 |
50 | var pattern = 'a/symlink/**';
51 | var followSync = glob.sync(pattern, { follow: true }).sort();
52 | var noFollowSync = glob.sync(pattern).sort();
53 |
54 | assert.notDeepEqual(followSync, noFollowSync, 'followSync should not equal noFollowSync');
55 |
56 | return glob(pattern, { follow: true })
57 | .then(function(files) {
58 | var follow = files.sort();
59 |
60 | return glob(pattern)
61 | .then(function(files) {
62 | var noFollow = files.sort();
63 |
64 | assert.deepEqual(noFollow, noFollowSync, 'sync and async noFollow should match');
65 | assert.notDeepEqual(follow, noFollow, 'follow should not equal noFollow');
66 |
67 | var long = path.resolve('a/symlink/a/b');
68 | assert.deepEqual(follow, followSync, 'sync and async follow should match');
69 | assert.notEqual(follow.indexOf(long), -1, 'follow should have long entry');
70 | assert.notEqual(followSync.indexOf(long), -1, 'followSync should have long entry');
71 | });
72 | })
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/.github/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to bash-glob
2 |
3 | First and foremost, thank you! We appreciate that you want to contribute to bash-glob, your time is valuable, and your contributions mean a lot to us.
4 |
5 | **What does "contributing" mean?**
6 |
7 | Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following:
8 |
9 | - Updating or correcting documentation
10 | - Feature requests
11 | - Bug reports
12 |
13 | If you'd like to learn more about contributing in general, the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) has a lot of useful information.
14 |
15 | **Showing support for bash-glob**
16 |
17 | Please keep in mind that open source software is built by people like you, who spend their free time creating things the rest the community can use.
18 |
19 | Don't have time to contribute? No worries, here are some other ways to show your support for bash-glob:
20 |
21 | - star the [project](https://github.com/jonschlinkert/bash-glob)
22 | - tweet your support for bash-glob
23 |
24 | ## Issues
25 |
26 | ### Before creating an issue
27 |
28 | Please try to determine if the issue is caused by an underlying library, and if so, create the issue there. Sometimes this is difficult to know. We only ask that you attempt to give a reasonable attempt to find out. Oftentimes the readme will have advice about where to go to create issues.
29 |
30 | Try to follow these guidelines
31 |
32 | - **Investigate the issue**:
33 | - **Check the readme** - oftentimes you will find notes about creating issues, and where to go depending on the type of issue.
34 | - Create the issue in the appropriate repository.
35 |
36 | ### Creating an issue
37 |
38 | Please be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue:
39 |
40 | - **version**: please note the version of bash-glob are you using
41 | - **extensions, plugins, helpers, etc** (if applicable): please list any extensions you're using
42 | - **error messages**: please paste any error messages into the issue, or a [gist](https://gist.github.com/)
43 |
44 | ## Above and beyond
45 |
46 | Here are some tips for creating idiomatic issues. Taking just a little bit extra time will make your issue easier to read, easier to resolve, more likely to be found by others who have the same or similar issue in the future.
47 |
48 | - read the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing)
49 | - take some time to learn basic markdown. This [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601) is super helpful, as is the GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/).
50 | - Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/).
51 | - use backticks to wrap code. This ensures that code will retain its format, making it much more readable to others
52 | - use syntax highlighting by adding the correct language name after the first "code fence"
53 |
54 |
55 | [node-glob]: https://github.com/isaacs/node-glob
56 | [micromatch]: https://github.com/jonschlinkert/micromatch
57 | [so]: http://stackoverflow.com/questions/tagged/bash-glob
--------------------------------------------------------------------------------
/test/options.realpath.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cwd = process.cwd();
4 | var isWindows = process.platform === 'win32';
5 | var argv = require('minimist')(process.argv.slice(2));
6 | var extend = require('extend-shallow');
7 | var assert = require('assert');
8 | var path = require('path');
9 | var glob = argv.glob ? require('glob') : require('..');
10 |
11 | // pattern to find a bunch of duplicates
12 | var fallback = 'a/symlink{,/*,/*/*}';
13 | var fixtures = path.resolve(__dirname, 'fixtures');
14 |
15 | // options, results
16 | // realpath:true set on each option
17 | var cases = [
18 | [{}, ['a/symlink', 'a/symlink/a', 'a/symlink/a/b']],
19 | [{mark: true}, ['a/symlink/', 'a/symlink/a/', 'a/symlink/a/b/']],
20 | [{stat: true}, ['a/symlink', 'a/symlink/a', 'a/symlink/a/b']],
21 | [{follow: true}, ['a/symlink', 'a/symlink/a', 'a/symlink/a/b']],
22 | [{cwd: 'a'}, ['symlink', 'symlink/a', 'symlink/a/b'], fallback.substr(2)],
23 | [{cwd: 'a'}, [], 'no one here but us chickens'],
24 | [{nonull: true},
25 | ['no one here but us {chickens,sheep}'],
26 | 'no one here but us {chickens,sheep}'
27 | ],
28 | [{mark: true},
29 | ['a/symlink/',
30 | 'a/symlink/a/',
31 | 'a/symlink/a/b/'
32 | ]
33 | ],
34 | [{mark: true, follow: true},
35 | ['a/symlink/',
36 | 'a/symlink/a/',
37 | 'a/symlink/a/b/'
38 | ]
39 | ]
40 | ];
41 |
42 | describe('options.realpath', function() {
43 | before(function() {
44 | process.chdir(path.join(__dirname, 'fixtures'));
45 | });
46 |
47 | after(function() {
48 | process.chdir(cwd);
49 | });
50 |
51 | describe('options', function() {
52 | cases.forEach(function(unit) {
53 | var options = extend({}, unit[0]);
54 | var expected = unit[1];
55 |
56 | if (!(options.nonull && expected[0].match(/^no one here/))) {
57 | expected = expected.map(function(fp) {
58 | var cwd = options.cwd ? path.resolve(fixtures, options.cwd) : fixtures;
59 | fp = path.resolve(cwd, fp);
60 | return fp.replace(/\\/g, '/');
61 | });
62 | }
63 |
64 | var pattern = unit[2] || fallback;
65 | options.realpath = true;
66 |
67 | it('sync: ' + JSON.stringify(options), function() {
68 | if (isWindows) return this.skip();
69 | var files = glob.sync(pattern, options);
70 | assert.deepEqual(files, expected, 'sync: ' + expected);
71 | });
72 |
73 | it('async: ' + JSON.stringify(options), function(cb) {
74 | if (isWindows) return this.skip();
75 |
76 | glob(pattern, options, function(err, files) {
77 | if (err) return cb(err);
78 | assert.deepEqual(files, expected, 'async: ' + expected);
79 | cb();
80 | });
81 | });
82 |
83 | it('promise: ' + JSON.stringify(options), function() {
84 | if (isWindows) return this.skip();
85 |
86 | return glob(pattern, options)
87 | .then(function(files) {
88 | assert.deepEqual(files, expected, 'async: ' + expected);
89 | });
90 | });
91 |
92 | it('promise explicit: ' + JSON.stringify(options), function() {
93 | if (isWindows) return this.skip();
94 |
95 | return glob.promise(pattern, options)
96 | .then(function(files) {
97 | assert.deepEqual(files, expected, 'async: ' + expected);
98 | });
99 | });
100 | });
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": false,
4 | "es6": true,
5 | "node": true,
6 | "mocha": true
7 | },
8 |
9 | "globals": {
10 | "document": false,
11 | "navigator": false,
12 | "window": false
13 | },
14 |
15 | "rules": {
16 | "accessor-pairs": 2,
17 | "arrow-spacing": [2, { "before": true, "after": true }],
18 | "block-spacing": [2, "always"],
19 | "brace-style": [2, "1tbs", { "allowSingleLine": true }],
20 | "comma-dangle": [2, "never"],
21 | "comma-spacing": [2, { "before": false, "after": true }],
22 | "comma-style": [2, "last"],
23 | "constructor-super": 2,
24 | "curly": [2, "multi-line"],
25 | "dot-location": [2, "property"],
26 | "eol-last": 2,
27 | "eqeqeq": [2, "allow-null"],
28 | "generator-star-spacing": [2, { "before": true, "after": true }],
29 | "handle-callback-err": [2, "^(err|error)$" ],
30 | "indent": [2, 2, { "SwitchCase": 1 }],
31 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
32 | "keyword-spacing": [2, { "before": true, "after": true }],
33 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }],
34 | "new-parens": 2,
35 | "no-array-constructor": 2,
36 | "no-caller": 2,
37 | "no-class-assign": 2,
38 | "no-cond-assign": 2,
39 | "no-const-assign": 2,
40 | "no-control-regex": 2,
41 | "no-debugger": 2,
42 | "no-delete-var": 2,
43 | "no-dupe-args": 2,
44 | "no-dupe-class-members": 2,
45 | "no-dupe-keys": 2,
46 | "no-duplicate-case": 2,
47 | "no-empty-character-class": 2,
48 | "no-eval": 2,
49 | "no-ex-assign": 2,
50 | "no-extend-native": 2,
51 | "no-extra-bind": 2,
52 | "no-extra-boolean-cast": 2,
53 | "no-extra-parens": [2, "functions"],
54 | "no-fallthrough": 2,
55 | "no-floating-decimal": 2,
56 | "no-func-assign": 2,
57 | "no-implied-eval": 2,
58 | "no-inner-declarations": [2, "functions"],
59 | "no-invalid-regexp": 2,
60 | "no-irregular-whitespace": 2,
61 | "no-iterator": 2,
62 | "no-label-var": 2,
63 | "no-labels": 2,
64 | "no-lone-blocks": 2,
65 | "no-mixed-spaces-and-tabs": 2,
66 | "no-multi-spaces": 2,
67 | "no-multi-str": 2,
68 | "no-multiple-empty-lines": [2, { "max": 1 }],
69 | "no-native-reassign": 0,
70 | "no-negated-in-lhs": 2,
71 | "no-new": 2,
72 | "no-new-func": 2,
73 | "no-new-object": 2,
74 | "no-new-require": 2,
75 | "no-new-wrappers": 2,
76 | "no-obj-calls": 2,
77 | "no-octal": 2,
78 | "no-octal-escape": 2,
79 | "no-proto": 0,
80 | "no-redeclare": 2,
81 | "no-regex-spaces": 2,
82 | "no-return-assign": 2,
83 | "no-self-compare": 2,
84 | "no-sequences": 2,
85 | "no-shadow-restricted-names": 2,
86 | "no-spaced-func": 2,
87 | "no-sparse-arrays": 2,
88 | "no-this-before-super": 2,
89 | "no-throw-literal": 2,
90 | "no-trailing-spaces": 0,
91 | "no-undef": 2,
92 | "no-undef-init": 2,
93 | "no-unexpected-multiline": 2,
94 | "no-unneeded-ternary": [2, { "defaultAssignment": false }],
95 | "no-unreachable": 2,
96 | "no-unused-vars": [2, { "vars": "all", "args": "none" }],
97 | "no-useless-call": 0,
98 | "no-with": 2,
99 | "one-var": [0, { "initialized": "never" }],
100 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
101 | "padded-blocks": [0, "never"],
102 | "quotes": [2, "single", "avoid-escape"],
103 | "radix": 2,
104 | "semi": [2, "always"],
105 | "semi-spacing": [2, { "before": false, "after": true }],
106 | "space-before-blocks": [2, "always"],
107 | "space-before-function-paren": [2, "never"],
108 | "space-in-parens": [2, "never"],
109 | "space-infix-ops": 2,
110 | "space-unary-ops": [2, { "words": true, "nonwords": false }],
111 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
112 | "use-isnan": 2,
113 | "valid-typeof": 2,
114 | "wrap-iife": [2, "any"],
115 | "yoda": [2, "never"]
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/test/broken-symlink.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var isWindows = process.platform === 'win32';
4 | var fs = require('fs');
5 | var each = require('each-parallel-async');
6 | var assert = require('assert');
7 | var mkdirp = require('mkdirp');
8 | var del = require('delete');
9 | var glob = require('..');
10 |
11 | var cwd = process.cwd();
12 |
13 | var link = 'fixtures/a/broken-link/link';
14 | var patterns = [
15 | 'fixtures/a/broken-link/*',
16 | 'fixtures/a/broken-link/**',
17 | 'fixtures/a/broken-link/**/link',
18 | 'fixtures/a/broken-link/**/*',
19 | 'fixtures/a/broken-link/link',
20 | 'fixtures/a/broken-link/{link,asdf}',
21 | 'fixtures/a/broken-link/+(link|asdf)',
22 | 'fixtures/a/broken-link/!(asdf)'
23 | ];
24 |
25 | var options = [
26 | null,
27 | {nonull: true },
28 | {mark: true },
29 | {stat: true },
30 | {follow: true }
31 | ];
32 |
33 | describe('set up broken symlink', function() {
34 | before(function(cb) {
35 | process.chdir(__dirname);
36 | cleanup();
37 | mkdirp.sync('fixtures/a/broken-link');
38 | fs.symlink('this-does-not-exist', 'fixtures/a/broken-link/link', cb);
39 | });
40 |
41 | after(function(cb) {
42 | cleanup();
43 | process.chdir(cwd);
44 | cb();
45 | });
46 |
47 | describe('async', function() {
48 | patterns.forEach(function(pattern) {
49 | it(pattern, function(cb) {
50 | if (isWindows) {
51 | this.skip();
52 | return;
53 | }
54 |
55 | each(options, function(opts, next) {
56 | glob(pattern, opts, function(err, files) {
57 | if (err) {
58 | next(err);
59 | return;
60 | }
61 |
62 | var msg = pattern + ' options=' + JSON.stringify(opts);
63 | if (opts && opts.follow === true) {
64 | assert.equal(files.indexOf(link), -1, msg);
65 | } else if (pattern !== link || (opts && opts.nonull)) {
66 | assert.notEqual(files.indexOf(link), -1, msg);
67 | } else {
68 | assert(!files.length);
69 | }
70 |
71 | setImmediate(next);
72 | });
73 | }, cb);
74 | });
75 | });
76 | });
77 |
78 | describe('promise - implied (no callback)', function() {
79 | patterns.forEach(function(pattern) {
80 | it(pattern, function(cb) {
81 | if (isWindows) {
82 | this.skip();
83 | return;
84 | }
85 |
86 | each(options, function(opts, next) {
87 | glob(pattern, opts)
88 | .then(function(files) {
89 | var msg = pattern + ' options=' + JSON.stringify(opts);
90 | if (opts && opts.follow === true) {
91 | assert.equal(files.indexOf(link), -1, msg);
92 | } else if (pattern !== link || (opts && opts.nonull)) {
93 | assert.notEqual(files.indexOf(link), -1, msg);
94 | } else {
95 | assert(!files.length);
96 | }
97 | setImmediate(next);
98 | })
99 | .catch(next);
100 | }, cb);
101 | });
102 | });
103 | });
104 |
105 | describe('promise - explicit', function() {
106 | patterns.forEach(function(pattern) {
107 | it(pattern, function(cb) {
108 | if (isWindows) {
109 | this.skip();
110 | return;
111 | }
112 |
113 | each(options, function(opts, next) {
114 | glob.promise(pattern, opts)
115 | .then(function(files) {
116 | var msg = pattern + ' options=' + JSON.stringify(opts);
117 | if (opts && opts.follow === true) {
118 | assert.equal(files.indexOf(link), -1, msg);
119 | } else if (pattern !== link || (opts && opts.nonull)) {
120 | assert.notEqual(files.indexOf(link), -1, msg);
121 | } else {
122 | assert(!files.length);
123 | }
124 | setImmediate(next);
125 | })
126 | .catch(next);
127 | }, cb);
128 | });
129 | });
130 | });
131 |
132 | describe('sync', function() {
133 | patterns.forEach(function(pattern) {
134 | it(pattern, function() {
135 | if (isWindows) {
136 | this.skip();
137 | return;
138 | }
139 |
140 | options.forEach(function(opts) {
141 | try {
142 | var files = glob.sync(pattern, opts);
143 | } catch (err) {
144 | console.log(err);
145 | }
146 | var msg = pattern + ' options=' + JSON.stringify(opts);
147 |
148 | if (opts && opts.follow === true) {
149 | assert.equal(files.indexOf(link), -1, msg);
150 | } else if (pattern !== link || (opts && opts.nonull)) {
151 | assert(files.indexOf(link) !== -1, msg);
152 | } else {
153 | assert(!files.length);
154 | }
155 | });
156 | });
157 | });
158 | });
159 | });
160 |
161 | function cleanup() {
162 | del.sync('fixtures/a/broken-link');
163 | }
164 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bash-glob [](https://www.npmjs.com/package/bash-glob) [](https://npmjs.org/package/bash-glob) [](https://npmjs.org/package/bash-glob) [](https://travis-ci.org/micromatch/bash-glob)
2 |
3 | > Bash-powered globbing for node.js
4 |
5 | Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
6 |
7 | ## Install
8 |
9 | Install with [npm](https://www.npmjs.com/):
10 |
11 | ```sh
12 | $ npm install --save bash-glob
13 | ```
14 |
15 | **Install bash 4.3 or later**
16 |
17 | I recommend using [homebrew](https://github.com/Homebrew/homebrew-core) to install/upgrade bash:
18 |
19 | ```sh
20 | $ brew upgrade bash
21 | ```
22 |
23 | ## Why?
24 |
25 | The initial motivation was to use this for generating the `expected` values for comparisons in tests. But as it turns out, this is faster than node-glob in most cases I've tested.
26 |
27 | Moreover, this supports the majority of the feature-functionaly in node-glob, and it's more Bash-compliant since, well, it **is** Bash.
28 |
29 | **Edge cases**
30 |
31 | Inevitably there will be edge cases. Thus far, however, I've found that many of the edge cases that seem to be problematic are already addressed or not problematic for Bash.
32 |
33 | Please feel free to [create an issue](../../issues) if you find a bug or have a feature request.
34 |
35 | ## Usage
36 |
37 | ```js
38 | var glob = require('bash-glob');
39 | glob(pattern[, options]);
40 | ```
41 |
42 | ## API
43 |
44 | ### [glob](index.js#L30)
45 |
46 | Asynchronously returns an array of files that match the given pattern or patterns.
47 |
48 | **Params**
49 |
50 | * `patterns` **{String|Array}**: One or more glob patterns to use for matching.
51 | * `options` **{Object}**: Options to pass to bash. See available [options](#options).
52 | * `cb` **{Function}**: Callback function, with `err` and `files` array.
53 |
54 | **Example**
55 |
56 | ```js
57 | var glob = require('bash-glob');
58 | glob('*.js', function(err, files) {
59 | if (err) return console.log(err);
60 | console.log(files);
61 | });
62 | ```
63 |
64 | ### [.each](index.js#L98)
65 |
66 | Asynchronously glob an array of files that match any of the given `patterns`.
67 |
68 | **Params**
69 |
70 | * `patterns` **{String}**: One or more glob patterns to use for matching.
71 | * `options` **{Object}**: Options to pass to bash. See available [options](#options).
72 | * `cb` **{Function}**: Callback function, with `err` and `files` array.
73 |
74 | **Example**
75 |
76 | ```js
77 | var glob = require('bash-glob');
78 | glob.each(['*.js', '*.md'], {dot: true}, function(err, files) {
79 | if (err) return console.log(err);
80 | console.log(files);
81 | });
82 | ```
83 |
84 | ### [.sync](index.js#L154)
85 |
86 | Returns an array of files that match the given patterns or patterns.
87 |
88 | **Params**
89 |
90 | * `patterns` **{String}**: One or more glob patterns to use for matching.
91 | * `options` **{Object}**: Options to pass to bash. See available [options](#options).
92 | * `returns` **{Array}**: Returns an array of files.
93 |
94 | **Example**
95 |
96 | ```js
97 | var glob = require('bash-glob');
98 | console.log(glob.sync('*.js', {cwd: 'foo'}));
99 | console.log(glob.sync(['*.js'], {cwd: 'bar'}));
100 | ```
101 |
102 | ## Options
103 |
104 | The following options may be used with the main `glob` function or any other method:
105 |
106 | * `dotglob`: (or `dot`, for [node-glob](https://github.com/Crafity/node-glob) compatibility) Includes filenames beginning with a `.` (dot) in the results of pathname expansion.
107 | * `extglob`: Enable extended [pattern matching](http://wiki.bash-hackers.org/syntax/pattern) features.
108 | * `failglob`: If set, patterns that fail to match filenames during pathname expansion result in an error message.
109 | * `globstar`: Enable recursive globbing with `**`.
110 | * `nocaseglob`: (or `nocase`, for [node-glob](https://github.com/Crafity/node-glob) compatibility) Enable case-insensitive matching in filenames when performing pathname expansion.
111 | * `nullglob`: If set, Bash allows patterns which match no files to expand to a null string, rather than themselves.
112 |
113 | ## About
114 |
115 |
116 | Contributing
117 |
118 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
119 |
120 | Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards.
121 |
122 |
123 |
124 |
125 | Running Tests
126 |
127 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
128 |
129 | ```sh
130 | $ npm install && npm test
131 | ```
132 |
133 |
134 |
135 |
136 | Building docs
137 |
138 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
139 |
140 | To generate the readme, run the following command:
141 |
142 | ```sh
143 | $ npm install -g verbose/verb#dev verb-generate-readme && verb
144 | ```
145 |
146 |
147 |
148 | ### Related projects
149 |
150 | You might also be interested in these projects:
151 |
152 | * [bash-match](https://www.npmjs.com/package/bash-match): Match strings using bash. Does not work on windows, and does not read from the… [more](https://github.com/micromatch/bash-match) | [homepage](https://github.com/micromatch/bash-match "Match strings using bash. Does not work on windows, and does not read from the file system. This library requires that Bash 4.3 or higher is installed and is mostly used for checking parity in unit tests.")
153 | * [braces](https://www.npmjs.com/package/braces): Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support… [more](https://github.com/micromatch/braces) | [homepage](https://github.com/micromatch/braces "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.")
154 | * [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/micromatch/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
155 | * [nanomatch](https://www.npmjs.com/package/nanomatch): Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash… [more](https://github.com/micromatch/nanomatch) | [homepage](https://github.com/micromatch/nanomatch "Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces)")
156 |
157 | ### Author
158 |
159 | **Jon Schlinkert**
160 |
161 | * [github/jonschlinkert](https://github.com/jonschlinkert)
162 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
163 |
164 | ### License
165 |
166 | Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
167 | Released under the [MIT License](LICENSE).
168 |
169 | ***
170 |
171 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on October 22, 2017._
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var path = require('path');
5 | var isGlob = require('is-glob');
6 | var each = require('each-parallel-async');
7 | var spawn = require('cross-spawn');
8 | var isExtglob = require('is-extglob');
9 | var extend = require('extend-shallow');
10 | var Emitter = require('component-emitter');
11 | var bashPath = require('bash-path');
12 |
13 | /**
14 | * Asynchronously returns an array of files that match the given pattern
15 | * or patterns.
16 | *
17 | * ```js
18 | * var glob = require('bash-glob');
19 | * glob('*.js', function(err, files) {
20 | * if (err) return console.log(err);
21 | * console.log(files);
22 | * });
23 | * ```
24 | * @param {String|Array} `patterns` One or more glob patterns to use for matching.
25 | * @param {Object} `options` Options to pass to bash. See available [options](#options).
26 | * @param {Function} `cb` Callback function, with `err` and `files` array.
27 | * @api public
28 | */
29 |
30 | function glob(pattern, options, cb) {
31 | if (typeof options === 'function') {
32 | cb = options;
33 | options = {};
34 | }
35 |
36 | if (Array.isArray(pattern)) {
37 | return glob.each.apply(glob, arguments);
38 | }
39 |
40 | if (typeof cb !== 'function') {
41 | if (typeof cb !== 'undefined') {
42 | throw new TypeError('expected callback to be a function');
43 | }
44 | return glob.promise.apply(glob, arguments);
45 | }
46 |
47 | if (typeof pattern !== 'string') {
48 | cb(new TypeError('expected glob to be a string or array'));
49 | return;
50 | }
51 |
52 | var opts = createOptions(pattern, options);
53 | bash(pattern, opts, function(err, files) {
54 | if (err instanceof Error) {
55 | cb(err);
56 | return;
57 | }
58 |
59 | if (!files) {
60 | files = err;
61 | }
62 |
63 | if (opts.nullglob === true && Array.isArray(files) && !files.length) {
64 | files = [pattern];
65 | }
66 |
67 | glob.emit('files', files, opts.cwd);
68 |
69 | if (!opts.each) {
70 | glob.end(files);
71 | }
72 |
73 | cb(null, files);
74 | });
75 |
76 | return glob;
77 | }
78 |
79 | /**
80 | * Mixin `Emitter` methods
81 | */
82 |
83 | Emitter(glob);
84 |
85 | /**
86 | * Asynchronously glob an array of files that match any of the given `patterns`.
87 | *
88 | * ```js
89 | * var glob = require('bash-glob');
90 | * glob.each(['*.js', '*.md'], {dot: true}, function(err, files) {
91 | * if (err) return console.log(err);
92 | * console.log(files);
93 | * });
94 | * ```
95 | * @param {String} `patterns` One or more glob patterns to use for matching.
96 | * @param {Object} `options` Options to pass to bash. See available [options](#options).
97 | * @param {Function} `cb` Callback function, with `err` and `files` array.
98 | * @api public
99 | */
100 |
101 | glob.each = function(patterns, options, cb) {
102 | if (typeof patterns === 'string') {
103 | return glob.apply(glob, arguments);
104 | }
105 |
106 | if (typeof options === 'function') {
107 | cb = options;
108 | options = {};
109 | }
110 |
111 | if (typeof cb !== 'function') {
112 | throw new TypeError('expected callback to be a function');
113 | }
114 |
115 | if (!Array.isArray(patterns)) {
116 | cb(new TypeError('expected patterns to be a string or array'));
117 | return;
118 | }
119 |
120 | var acc = [];
121 | each(patterns, function(pattern, next) {
122 | var opts = extend({}, options, {each: true});
123 | glob(pattern, opts, function(err, files) {
124 | if (err) {
125 | next(err);
126 | return;
127 | }
128 | acc.push.apply(acc, files);
129 | next();
130 | });
131 | }, function(err) {
132 | if (err) {
133 | cb(err, []);
134 | return;
135 | }
136 | glob.end(acc);
137 | cb(null, acc);
138 | });
139 |
140 | return glob;
141 | };
142 |
143 | /**
144 | * Returns an array of files that match the given patterns or patterns.
145 | *
146 | * ```js
147 | * var glob = require('bash-glob');
148 | * console.log(glob.sync('*.js', {cwd: 'foo'}));
149 | * console.log(glob.sync(['*.js'], {cwd: 'bar'}));
150 | * ```
151 | * @param {String} `patterns` One or more glob patterns to use for matching.
152 | * @param {Object} `options` Options to pass to bash. See available [options](#options).
153 | * @return {Array} Returns an array of files.
154 | * @api public
155 | */
156 |
157 | glob.sync = function(pattern, options) {
158 | if (Array.isArray(pattern)) {
159 | return pattern.reduce(function(acc, pattern) {
160 | acc = acc.concat(glob.sync(pattern, options));
161 | return acc;
162 | }, []);
163 | }
164 |
165 | if (typeof pattern !== 'string') {
166 | throw new TypeError('expected glob to be a string or array');
167 | }
168 |
169 | var opts = createOptions(pattern, options);
170 |
171 | try {
172 | var stat = fs.statSync(opts.cwd);
173 | if (!stat.isDirectory()) {
174 | throw new Error('cwd is not a directory: ' + opts.cwd);
175 | }
176 | } catch (error) {
177 | var err = handleError(error, pattern, opts, true);
178 | if (err instanceof Error) {
179 | throw err;
180 | }
181 | return err;
182 | }
183 |
184 | if (!isGlob(pattern)) {
185 | var fp = path.resolve(opts.cwd, pattern);
186 | return (opts.nullglob || fs.existsSync(fp)) ? [pattern] : [];
187 | }
188 |
189 | var cp = spawn.sync(bashPath, cmd(pattern, opts), opts);
190 | var error = cp.stderr ? String(cp.stderr).trim() : null;
191 | if (error) {
192 | err = handleError(error, pattern, opts);
193 | if (err instanceof Error) {
194 | throw err;
195 | }
196 | return err;
197 | }
198 |
199 | if (cp.stdout == null) {
200 | return [];
201 | }
202 |
203 | var files = getFiles(cp.stdout.toString(), pattern, opts);
204 | if (files instanceof Error) {
205 | throw files;
206 | }
207 |
208 | glob.emit('files', files, opts.cwd);
209 | glob.end(files);
210 | return files;
211 | };
212 |
213 | /**
214 | * Emit `end` and remove listeners
215 | */
216 |
217 | glob.promise = function(pattern, options, cb) {
218 | return new Promise(function(resolve, reject) {
219 | glob(pattern, options, function(err, files) {
220 | if (err) {
221 | reject(err);
222 | } else {
223 | resolve(files);
224 | }
225 | });
226 | });
227 | };
228 |
229 | /**
230 | * Emit `end` and remove listeners
231 | */
232 |
233 | glob.end = function(files) {
234 | glob.emit('end', files);
235 | glob.off('match');
236 | glob.off('files');
237 | glob.off('end');
238 | };
239 |
240 | /**
241 | * Base bash function
242 | */
243 |
244 | function bash(pattern, options, cb) {
245 | if (!isGlob(pattern)) {
246 | return nonGlob(pattern, options, cb);
247 | }
248 |
249 | if (typeof options === 'function') {
250 | cb = options;
251 | options = undefined;
252 | }
253 |
254 | var opts = extend({cwd: process.cwd()}, options);
255 |
256 | fs.stat(opts.cwd, function(err, stat) {
257 | if (err) {
258 | cb(handleError(err, pattern, opts));
259 | return;
260 | }
261 |
262 | if (!stat.isDirectory()) {
263 | cb(new Error('cwd is not a directory: ' + opts.cwd));
264 | return;
265 | }
266 |
267 | var cp = spawn(bashPath, cmd(pattern, options), options);
268 | var buf = new Buffer(0);
269 |
270 | cp.stdout.on('data', function(data) {
271 | emitMatches(data.toString(), pattern, options);
272 | buf = Buffer.concat([buf, data]);
273 | });
274 |
275 | cp.stderr.on('data', function(data) {
276 | cb(handleError(data.toString(), pattern, options));
277 | });
278 |
279 | cp.on('close', function(code) {
280 | cb(code, getFiles(buf.toString(), pattern, options));
281 | });
282 | });
283 | }
284 |
285 | /**
286 | * Escape spaces in glob patterns
287 | */
288 |
289 | function normalize(val) {
290 | if (Array.isArray(val)) {
291 | val = val.join(' ');
292 | }
293 | return val.split(' ').join('\\ ');
294 | }
295 |
296 | /**
297 | * Create the command to use
298 | */
299 |
300 | function cmd(patterns, options) {
301 | var str = normalize(patterns);
302 | var keys = Object.keys(options);
303 | var args = [];
304 | var valid = [
305 | 'dotglob',
306 | 'extglob',
307 | 'failglob',
308 | 'globstar',
309 | 'nocaseglob',
310 | 'nullglob'
311 | ];
312 |
313 | for (var i = 0; i < keys.length; i++) {
314 | var key = keys[i];
315 | if (valid.indexOf(key) !== -1) {
316 | args.push('-O', key);
317 | }
318 | }
319 |
320 | args.push('-c', 'for i in ' + str + '; do echo $i; done');
321 | return args;
322 | }
323 |
324 | /**
325 | * Shallow clone and create options
326 | */
327 |
328 | function createOptions(pattern, options) {
329 | if (options && options.normalized === true) return options;
330 | var opts = extend({cwd: process.cwd()}, options);
331 | if (opts.nocase === true) opts.nocaseglob = true;
332 | if (opts.nonull === true) opts.nullglob = true;
333 | if (opts.dot === true) opts.dotglob = true;
334 | if (!opts.hasOwnProperty('globstar') && pattern.indexOf('**') !== -1) {
335 | opts.globstar = true;
336 | }
337 | if (!opts.hasOwnProperty('extglob') && isExtglob(pattern)) {
338 | opts.extglob = true;
339 | }
340 | opts.normalized = true;
341 | return opts;
342 | }
343 |
344 | /**
345 | * Handle errors to ensure the correct value is returned based on options
346 | */
347 |
348 | function handleError(err, pattern, options) {
349 | var message = err;
350 | if (typeof err === 'string') {
351 | err = new Error(message.trim());
352 | err.pattern = pattern;
353 | err.options = options;
354 | if (/invalid shell option/.test(err)) {
355 | err.code = 'INVALID_SHELL_OPTION';
356 | }
357 | if (/no match:/.test(err)) {
358 | err.code = 'NOMATCH';
359 | }
360 | return err;
361 | }
362 |
363 | if (err && (err.code === 'ENOENT' || err.code === 'NOMATCH')) {
364 | if (options.nullglob === true) {
365 | return [pattern];
366 | }
367 | if (options.failglob === true) {
368 | return err;
369 | }
370 | return [];
371 | }
372 | return err;
373 | }
374 |
375 | /**
376 | * Handle files to ensure the correct value is returned based on options
377 | */
378 |
379 | function getFiles(res, pattern, options) {
380 | var files = res.split(/\r?\n/).filter(Boolean);
381 | if (files.length === 1 && files[0] === pattern) {
382 | files = [];
383 | } else if (options.realpath === true || options.follow === true) {
384 | files = toAbsolute(files, options);
385 | }
386 | if (files.length === 0) {
387 | if (options.nullglob === true) {
388 | return [pattern];
389 | }
390 | if (options.failglob === true) {
391 | return new Error('no matches:' + pattern);
392 | }
393 | }
394 |
395 | return files.filter(function(filepath) {
396 | return filepath !== '.' && filepath !== '..';
397 | });
398 | }
399 |
400 | /**
401 | * Make symlinks absolute when `options.follow` is defined.
402 | */
403 |
404 | function toAbsolute(files, options) {
405 | var len = files.length;
406 | var idx = -1;
407 | var arr = [];
408 |
409 | while (++idx < len) {
410 | var file = files[idx];
411 | if (!file.trim()) continue;
412 | if (file && options.cwd) {
413 | file = path.resolve(options.cwd, file);
414 | }
415 | if (file && options.realpath === true) {
416 | file = follow(file);
417 | }
418 | if (file) {
419 | arr.push(file);
420 | }
421 | }
422 | return arr;
423 | }
424 |
425 | /**
426 | * Handle callback
427 | */
428 |
429 | function callback(files, pattern, options, cb) {
430 | return function(err) {
431 | if (err) {
432 | cb(handleError(err, pattern, options), []);
433 | return;
434 | }
435 | cb(null, files || [pattern]);
436 | };
437 | }
438 |
439 | /**
440 | * Follow symlinks
441 | */
442 |
443 | function follow(filepath) {
444 | if (!isSymlink(filepath) && !fs.existsSync(filepath)) {
445 | return false;
446 | }
447 | return filepath;
448 | }
449 |
450 | function isSymlink(filepath) {
451 | try {
452 | return fs.lstatSync(filepath).isSymbolicLink();
453 | } catch (err) {}
454 | return null;
455 | }
456 |
457 | /**
458 | * Handle non-globs
459 | */
460 |
461 | function nonGlob(pattern, options, cb) {
462 | if (options.nullglob) {
463 | cb(null, [pattern]);
464 | return;
465 | }
466 | fs.stat(pattern, callback(null, pattern, options, cb));
467 | return;
468 | }
469 |
470 | /**
471 | * Emit matches for a pattern
472 | */
473 |
474 | function emitMatches(str, pattern, options) {
475 | glob.emit('match', getFiles(str, pattern, options), options.cwd);
476 | }
477 |
478 | /**
479 | * Expose `glob`
480 | */
481 |
482 | module.exports = exports = glob;
483 |
--------------------------------------------------------------------------------