Comparing version
117
index.js
@@ -9,10 +9,11 @@ 'use strict'; | ||
var visit = require('object-visit'); | ||
var extend = require('extend-shallow'); | ||
var Emitter = require('component-emitter'); | ||
var middleware = require('./lib/middleware'); | ||
var exclude = require('./middleware/exclude'); | ||
var include = require('./middleware/include'); | ||
var npm = require('./middleware/npm'); | ||
var symlinks = require('./lib/symlinks'); | ||
var iterators = require('./lib/iterators'); | ||
var Handler = require('./lib/handler'); | ||
var Pattern = require('./lib/pattern'); | ||
var options = require('./lib/options'); | ||
var readers = require('./lib/readers'); | ||
@@ -23,2 +24,10 @@ var utils = require('./lib/utils'); | ||
/** | ||
* Lazily required module dependencies | ||
*/ | ||
var lazy = require('lazy-cache')(require); | ||
var gitignore = lazy('glob-fs-gitignore'); | ||
var dotfiles = lazy('glob-fs-dotfiles'); | ||
/** | ||
* Optionally create an instance of `Glob` with the given `options`. | ||
@@ -39,5 +48,6 @@ * | ||
} | ||
Emitter.call(this); | ||
this.options = options || {}; | ||
this.init(this.options); | ||
this.handler = new Handler(this); | ||
this.init(options); | ||
} | ||
@@ -55,3 +65,5 @@ | ||
init: function (options) { | ||
init: function (opts) { | ||
this.options = opts || {}; | ||
this.pattern = null; | ||
this.middleware = {}; | ||
@@ -63,2 +75,3 @@ this.includes = {}; | ||
options(this); | ||
iterators(this); | ||
@@ -83,7 +96,19 @@ symlinks(this); | ||
} | ||
middleware(this, glob, opts); | ||
// if not disabled by the user, run the built-ins | ||
if (!this.disabled('builtins')) { | ||
if (!this.disabled('npm')) { | ||
this.use(npm(opts)); | ||
} | ||
if (!this.disabled('dotfiles')) { | ||
this.use(dotfiles()(opts)); | ||
} | ||
if (!this.disabled('gitignore')) { | ||
this.use(gitignore()(opts)); | ||
} | ||
} | ||
}, | ||
/** | ||
* Create an instance of `Pattern` for the current glob pattern. | ||
* Create an instance of `Pattern` from the current glob pattern. | ||
* | ||
@@ -105,2 +130,3 @@ * @param {String} `pattern` | ||
this.defaults(glob, options); | ||
this.include(glob, options); | ||
return this; | ||
@@ -110,12 +136,9 @@ }, | ||
/** | ||
* Create a file object with the given properties. | ||
* Create a file object with properties that will be used | ||
* by middleware. | ||
* | ||
* @param {String} `dir` | ||
* @param {String} `segment` | ||
* @param {String} `fp` | ||
* @param {Object} `stat` | ||
* @param {String} `file` | ||
* @return {Object} | ||
*/ | ||
// createFile: function (dir, segment, fp, stat) { | ||
createFile: function (file) { | ||
@@ -140,3 +163,3 @@ return new File({ | ||
shouldRecurse: function(pattern, options) { | ||
var opts = extend({}, this.options, options); | ||
var opts = this.setDefaults(options); | ||
if (typeof opts.recurse === 'boolean') { | ||
@@ -149,4 +172,3 @@ return opts.recurse; | ||
/** | ||
* Thin wrapper around `.use()` for easily excluding files or | ||
* directories that match the given `pattern`. | ||
* Add a middleware to be called in the order defined. | ||
* | ||
@@ -157,5 +179,4 @@ * ```js | ||
* var glob = require('glob-fs')({ foo: true }) | ||
* .exclude(/\.foo$/) | ||
* .exclude('*.bar') | ||
* .exclude('*.baz'); | ||
* .use(gitignore()) | ||
* .use(dotfiles()); | ||
* | ||
@@ -165,11 +186,10 @@ * var files = glob.readdirSync('**'); | ||
* | ||
* @name .exclude | ||
* @param {String} `pattern` | ||
* @param {Object} `options` | ||
* @name .use | ||
* @param {Function} `fn` | ||
* @return {Object} Returns the `Glob` instance, for chaining. | ||
* @api public | ||
*/ | ||
exclude: function(pattern, options) { | ||
var opts = extend({}, this.options, options); | ||
this.use(exclude(pattern, opts)); | ||
use: function(fn) { | ||
this.handler.use(fn); | ||
return this; | ||
@@ -187,3 +207,3 @@ }, | ||
include: function(pattern, options) { | ||
var opts = extend({}, this.options, options); | ||
var opts = this.setDefaults(options); | ||
this.use(include(pattern, opts)); | ||
@@ -194,3 +214,4 @@ return this; | ||
/** | ||
* Add a middleware to be called in the order defined. | ||
* Thin wrapper around `.use()` for easily excluding files or | ||
* directories that match the given `pattern`. | ||
* | ||
@@ -200,17 +221,20 @@ * ```js | ||
* var dotfiles = require('glob-fs-dotfiles'); | ||
* var glob = require('glob-fs')({ foo: true }) | ||
* .use(gitignore()) | ||
* .use(dotfiles()); | ||
* var glob = require('glob-fs')() | ||
* .exclude(/\.foo$/) | ||
* .exclude('*.bar') | ||
* .exclude('*.baz'); | ||
* | ||
* var files = glob.readdirSync('*.js'); | ||
* var files = glob.readdirSync('**'); | ||
* //=> ['index.js', 'README.md', ...] | ||
* ``` | ||
* | ||
* @name .use | ||
* @param {Function} `fn` | ||
* @return {Object} Returns the `Glob` instance, for chaining. | ||
* @name .exclude | ||
* @param {String} `pattern` | ||
* @param {Object} `options` | ||
* @api public | ||
*/ | ||
use: function(fn) { | ||
this.fns.push(fn); | ||
exclude: function(pattern, options) { | ||
var opts = this.setDefaults(options); | ||
this.use(exclude(pattern, opts)); | ||
return this; | ||
@@ -240,14 +264,4 @@ }, | ||
handle: function(file) { | ||
this.fns = this.fns.filter(Boolean); | ||
var len = this.fns.length, i = -1; | ||
this.track(file); | ||
while (++i < len) { | ||
this.fns[i].call(this, file); | ||
this.track(file); | ||
if (file.include === true || file.exclude === true) { | ||
break; | ||
} | ||
} | ||
this.handler.handle(file); | ||
return this; | ||
}, | ||
@@ -263,5 +277,6 @@ | ||
map: function(method, arr, options) { | ||
utils.arrayify(arr || []).forEach(function (ele) { | ||
this[method](ele, options); | ||
map: function(method, arr/*, arguments*/) { | ||
var args = [].slice.call(arguments, 2); | ||
utils.arrayify(arr || []).forEach(function (obj) { | ||
this[method](obj, args); | ||
}.bind(this)); | ||
@@ -268,0 +283,0 @@ return this; |
145
lib/file.js
@@ -6,19 +6,144 @@ 'use strict'; | ||
function File(obj) { | ||
/** | ||
* Lazily required modules | ||
*/ | ||
var lazy = require('lazy-cache')(require); | ||
var parsePath = lazy('parse-filepath'); | ||
var startsWith = lazy('starts-with'); | ||
var endsWith = lazy('ends-with'); | ||
var isDotfile = lazy('is-dotfile'); | ||
var isDotdir = lazy('is-dotdir'); | ||
/** | ||
* Create a new `File` from the given `object`. | ||
* | ||
* @param {Object} `object` | ||
* @api public | ||
*/ | ||
function File(file) { | ||
this.cache = new Map(); | ||
this.history = []; | ||
this.pattern = obj.pattern; | ||
this.recurse = obj.recurse; | ||
this.dirname = obj.dirname; | ||
this.segment = obj.segment; | ||
this.path = obj.path; | ||
this.pattern = file.pattern; | ||
this.recurse = file.recurse; | ||
this.dirname = file.dirname; | ||
this.segment = file.segment; | ||
this.path = file.path; | ||
this.orig = file.path; | ||
} | ||
File.prototype.parse = function(dir) { | ||
dir = dir || process.cwd(); | ||
this.absolute = path.resolve(this.path); | ||
this.relative = relative(dir, this.path); | ||
/** | ||
* Parse the `file.path` to add additional path properties | ||
* to the `file` object. This is used in the iterators | ||
* before the middleware handler is called. | ||
* | ||
* @param {String} `cwd` | ||
*/ | ||
File.prototype.parse = function(cwd) { | ||
cwd = cwd || process.cwd(); | ||
this.relative = relative(cwd, this.path); | ||
var parsed = parsePath()(this.path); | ||
for (var key in parsed) { | ||
if (parsed.hasOwnProperty(key)) { | ||
this[key] = parsed[key]; | ||
} | ||
} | ||
if (this.isDirectory()) { | ||
if (this.pattern.endsWith('/') && !this.endsWith('/')) { | ||
this.relative += '/'; | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns `true if the file give filepath or `file.path` looks like | ||
* a dotfile. | ||
* | ||
* @param {String} `fp` | ||
* @return {Boolean} | ||
*/ | ||
File.prototype.isDotfile = function(fp) { | ||
return isDotfile()(fp || this.path); | ||
}; | ||
/** | ||
* Returns `true if the file give filepath or `file.path` looks like | ||
* a dot-directory. | ||
* | ||
* @param {String} `fp` | ||
* @return {Boolean} | ||
*/ | ||
File.prototype.isDotdir = function(fp) { | ||
return isDotdir()(fp || this.path); | ||
}; | ||
/** | ||
* Return the absolute filepath based on the file's root path. | ||
* | ||
* @param {String} `fp` | ||
* @return {String} | ||
*/ | ||
File.prototype.toAbsolute = function(fp) { | ||
if (typeof fp === 'undefined') { | ||
fp = this.path; | ||
} | ||
if (this.startsWith('/', this.original)) { | ||
return path.join(this.root, fp); | ||
} | ||
if (this.isAbsolute || fp === '') { | ||
return fp; | ||
} | ||
return path.resolve(fp); | ||
}; | ||
/** | ||
* Return `true` if the given `file.path` ends with the specified | ||
* `character` | ||
* | ||
* @param {String} `character` | ||
* @param {String} `fp` If no filepath is passed, the cached `file.path` is used. | ||
*/ | ||
File.prototype.endsWith = function(ch, fp) { | ||
var key = 'endsWith:' + ch; | ||
if (this.cache.has(key)) return this.cache.get(key); | ||
if (typeof fp === 'undefined') { | ||
fp = this.relative || this.path; | ||
} | ||
var res = endsWith()(fp, ch); | ||
this.cache.set(key, res); | ||
return res; | ||
}; | ||
/** | ||
* Return `true` if the given `filepath` starts with the specified | ||
* `character` | ||
* | ||
* @param {String} `character` | ||
* @param {String} `fp` If no filepath is passed, the cached `file.path` is used. | ||
*/ | ||
File.prototype.startsWith = function(ch, fp) { | ||
var key = 'startsWith:' + ch; | ||
if (this.cache.has(key)) return this.cache.get(key); | ||
if (typeof fp === 'undefined') { | ||
fp = this.relative || this.path; | ||
} | ||
var res = startsWith()(fp, ch); | ||
this.cache.set(key, res); | ||
return res; | ||
}; | ||
/** | ||
* Expose `File` | ||
@@ -25,0 +150,0 @@ */ |
@@ -10,2 +10,3 @@ 'use strict'; | ||
var promise = lazy('bluebird'); | ||
var filter = require('./filter'); | ||
@@ -18,7 +19,9 @@ module.exports = function (app) { | ||
fs.exists(dir, function(exists) { | ||
if (!exists) { | ||
return cb(null, []); | ||
} | ||
return walk(dir, cb); | ||
setImmediate(function () { | ||
fs.exists(dir, function(exists) { | ||
if (!exists) { | ||
return cb(null, []); | ||
} | ||
return walk(dir, cb); | ||
}); | ||
}); | ||
@@ -110,2 +113,3 @@ | ||
// console.log(file.path) | ||
// emit included file | ||
@@ -129,11 +133,13 @@ if (file.include === true) { | ||
fs.exists(dir, function(exists) { | ||
if (!exists) return; | ||
setImmediate(function () { | ||
fs.exists(dir, function(exists) { | ||
if (!exists) return; | ||
walk(dir, function (err) { | ||
if (err) { | ||
stream.emit('error', err); | ||
return; | ||
} | ||
stream.end(); | ||
walk(dir, function (err) { | ||
if (err) { | ||
stream.emit('error', err); | ||
return; | ||
} | ||
stream.end(); | ||
}); | ||
}); | ||
@@ -176,2 +182,3 @@ }); | ||
self.emit('include', file); | ||
self.files.push(file.relative); | ||
stream.write(file); | ||
@@ -230,3 +237,3 @@ } | ||
self.emit('include', file); | ||
self.files.push(file.path); | ||
self.files.push(file.relative); | ||
} | ||
@@ -233,0 +240,0 @@ |
'use strict'; | ||
// require('time-require') | ||
var path = require('path'); | ||
var mm = require('micromatch'); | ||
var typeOf = require('kind-of'); | ||
var isGlob = require('is-glob'); | ||
var parent = require('glob-parent'); | ||
var utils = require('./utils'); | ||
var isWindows = require('is-windows'); | ||
/** | ||
* Lazily required modules | ||
*/ | ||
var lazy = require('lazy-cache')(require); | ||
var startsWith = lazy('starts-with'); | ||
var endsWith = lazy('ends-with'); | ||
var parent = lazy('glob-parent'); | ||
var mm = lazy('micromatch'); | ||
/** | ||
* Create an instance of `Pattern` with the given `options`. | ||
@@ -18,2 +31,3 @@ * | ||
function Pattern(glob, options, isNegated) { | ||
utils.defineProp(this, 'cache', new Map()); | ||
this.negated = !!isNegated; | ||
@@ -38,3 +52,14 @@ this.options = options || {}; | ||
this.isGlobstar = /(?:[*]{2}|\/\*\/\*\/)/.test(pattern); | ||
// this.isGlobstar = pattern.indexOf('**') !== -1; | ||
if (this.endsWith('/', pattern)) { | ||
this.hasTrailingSlash = true; | ||
pattern = pattern.substr(0, pattern.length - 1); | ||
if (this.isGlobstar) { | ||
pattern += '*/*'; | ||
} | ||
} | ||
if (!isGlob(pattern)) { | ||
@@ -46,13 +71,11 @@ this.parent = '.'; | ||
} else { | ||
this.isGlobstar = pattern.indexOf('**') !== -1; | ||
if (pattern.charAt(0) === '!') { | ||
if (this.startsWith('!', pattern)) { | ||
pattern = pattern.slice(1); | ||
this.negated = true; | ||
} | ||
this.parent = parent(pattern); | ||
this.parent = parent()(pattern); | ||
this.base = path.join(this.cwd, this.parent); | ||
pattern = this.normalizePattern(pattern); | ||
this.normalizePattern(pattern); | ||
} | ||
this.toRegex(pattern); | ||
this.toRegex(this.glob); | ||
return this; | ||
@@ -70,2 +93,3 @@ }; | ||
var sep = this.parent; | ||
if (sep === '.') sep = ''; | ||
@@ -75,6 +99,6 @@ sep = new RegExp('^' + sep); | ||
pattern = pattern.replace(sep, ''); | ||
if (pattern.charAt(0) === '/') { | ||
if (this.startsWith('/', pattern)) { | ||
pattern = pattern.slice(1); | ||
this.root = '/'; | ||
} | ||
this.glob = pattern; | ||
@@ -84,4 +108,45 @@ return pattern; | ||
/** | ||
* Return `true` if the given `pattern` ends with the given | ||
* `character` | ||
* | ||
* @param {String} `character` | ||
* @param {String} `pattern` If no `pattern` is passed, the cached value is used. | ||
*/ | ||
Pattern.prototype.endsWith = function(ch, pattern) { | ||
var key = 'endsWith:' + ch; | ||
if (this.cache.has(key)) return this.cache.get(key); | ||
if (typeof pattern === 'undefined') { | ||
pattern = this.glob || this.original; | ||
} | ||
var res = endsWith()(pattern, ch); | ||
this.cache.set(key, res); | ||
return res; | ||
}; | ||
/** | ||
* Return `true` if the given `pattern` starts with the given | ||
* `character` | ||
* | ||
* @param {String} `character` | ||
* @param {String} `pattern` If no `pattern` is passed, the cached value is used. | ||
*/ | ||
Pattern.prototype.startsWith = function(ch, pattern) { | ||
var key = 'startsWith:' + ch; | ||
if (this.cache.has(key)) return this.cache.get(key); | ||
if (typeof pattern === 'undefined') { | ||
pattern = this.glob || this.original; | ||
} | ||
var res = startsWith()(pattern, ch); | ||
this.cache.set(key, res); | ||
return res; | ||
}; | ||
/** | ||
* Return `true` if an actual parent was extracted from | ||
@@ -103,3 +168,16 @@ * the glob pattern. e.g. not `.` | ||
/** | ||
* Resolve the root directory. | ||
* | ||
* @param {String} `fp` | ||
* @return {String} | ||
*/ | ||
Pattern.prototype.resolveRoot = function(dir) { | ||
this.root = path.resolve(this.root || path.resolve((dir || this.cwd), '/')); | ||
if (isWindows()) { | ||
this.root = this.root.split('\\').join('/'); | ||
} | ||
}; | ||
/** | ||
@@ -113,12 +191,29 @@ * Convert `pattern` to regex. | ||
Pattern.prototype.toRegex = function(pattern) { | ||
if (typeOf(this.regex) === 'regexp') { | ||
return this.regex; | ||
} | ||
if (!pattern && this.negated) { | ||
this.re = new RegExp(this.parent); | ||
this.regex = new RegExp(this.parent); | ||
} else { | ||
this.re = mm.makeRe(pattern); | ||
if (this.hasTrailingSlash) { | ||
pattern += '{,/}'; | ||
} | ||
this.regex = mm().makeRe(pattern); | ||
} | ||
if (!this.re) { | ||
this.re = new RegExp(this.original); | ||
if (typeOf(this.regex) !== 'regexp') { | ||
this.regex = new RegExp(this.original); | ||
} | ||
}; | ||
Pattern.prototype.test = function(fp) { | ||
if (typeOf(this.regex) === 'regexp') { | ||
return this.regex.test(fp); | ||
} | ||
this.toRegex(this.glob); | ||
return this.regex.test(fp); | ||
}; | ||
/** | ||
@@ -125,0 +220,0 @@ * Expose `Pattern` |
@@ -93,3 +93,5 @@ 'use strict'; | ||
this.setPattern(pattern, options); | ||
return this.iteratorStream(this.pattern.base); | ||
var res = this.iteratorStream(this.pattern.base); | ||
this.emit('end', this.files); | ||
return res; | ||
}, | ||
@@ -100,5 +102,7 @@ | ||
this.setPattern(pattern, options); | ||
return this.iteratorPromise(this.pattern.base); | ||
var res = this.iteratorPromise(this.pattern.base); | ||
this.emit('end', this.files); | ||
return res; | ||
} | ||
}); | ||
}; |
'use strict'; | ||
var fs = require('fs'); | ||
/** | ||
@@ -36,35 +34,16 @@ * Utils | ||
/** | ||
* File system utils. | ||
* Add a non-enumerable property to `receiver` | ||
* | ||
* @param {Object} `obj` | ||
* @param {String} `name` | ||
* @param {Function} `val` | ||
*/ | ||
utils.tryRead = function tryRead(fp) { | ||
try { | ||
return fs.readFileSync(fp, 'utf8'); | ||
} catch (err) { | ||
return null; | ||
} | ||
utils.defineProp = function defineProp(receiver, key, value) { | ||
return Object.defineProperty(receiver, key, { | ||
configurable: true, | ||
enumerable: false, | ||
writable: true, | ||
value: value | ||
}); | ||
}; | ||
utils.tryReaddir = function tryReaddir(dir) { | ||
try { | ||
return fs.readdirSync(dir); | ||
} catch(err) {} | ||
return []; | ||
}; | ||
utils.tryStat = function tryStat(fp) { | ||
try { | ||
return fs.statSync(fp); | ||
} catch(err) {} | ||
return null; | ||
}; | ||
utils.reduce = function reduce(arr, cb, thisArg) { | ||
var len = arr.length; | ||
var res = []; | ||
if (!len) return []; | ||
for (var i = 0; i < len; i++) { | ||
res = cb.call(thisArg, res, arr[i], i, arr); | ||
} | ||
return res || []; | ||
}; |
@@ -7,2 +7,8 @@ 'use strict'; | ||
function testPattern(pattern) { | ||
return function (fp) { | ||
return pattern.test(fp); | ||
} | ||
} | ||
module.exports = function (pattern, options) { | ||
@@ -12,9 +18,11 @@ var opts = extend({}, options); | ||
var isMatch = type === 'regexp' | ||
? function (fp) { | ||
return pattern.test(fp); | ||
var isMatch = type !== 'regexp' | ||
? mm.matcher(pattern, opts) | ||
: testPattern(pattern); | ||
return function exclude(file) { | ||
if (file.pattern.hasTrailingSlash && file.isFile()) { | ||
return file; | ||
} | ||
: mm.matcher(pattern, opts); | ||
return function exclude(file) { | ||
if (isMatch(file.path)) { | ||
@@ -26,6 +34,11 @@ file.exclude = true; | ||
if (file.pattern.hasParent()) { | ||
if (isMatch(file.relative) || file.pattern.re.test(file.segment)) { | ||
if (isMatch(file.relative)) { | ||
file.exclude = true; | ||
return file; | ||
} | ||
if (file.pattern.test(file.segment) || file.pattern.test(file.relative)) { | ||
file.exclude = true; | ||
return file; | ||
} | ||
} | ||
@@ -32,0 +45,0 @@ return file; |
'use strict'; | ||
var path = require('path'); | ||
var mm = require('micromatch'); | ||
@@ -7,2 +8,8 @@ var typeOf = require('kind-of'); | ||
function testPattern(pattern) { | ||
return function (fp) { | ||
return pattern.test(fp); | ||
} | ||
} | ||
module.exports = function (pattern, options) { | ||
@@ -12,9 +19,12 @@ var opts = extend({}, options); | ||
var isMatch = type === 'regexp' | ||
? function (fp) { | ||
return pattern.test(fp); | ||
var isMatch = type !== 'regexp' | ||
? mm.matcher(pattern, opts) | ||
: testPattern(pattern); | ||
return function include(file) { | ||
if (this.pattern.hasTrailingSlash && file.isFile()) { | ||
return file; | ||
} | ||
: mm.matcher(pattern, opts); | ||
return function include(file) { | ||
if (isMatch(file.path)) { | ||
@@ -25,7 +35,21 @@ file.include = true; | ||
if (file.pattern.hasParent()) { | ||
if (isMatch(file.relative) || file.pattern.re.test(file.segment)) { | ||
if (this.pattern.hasParent()) { | ||
if (isMatch(file.relative)) { | ||
file.include = true; | ||
return file; | ||
} | ||
var cwd = this.pattern.options.cwd || '.'; | ||
var re = this.pattern.regex; | ||
if (re.test(path.join(cwd, file.relative))) { | ||
file.include = true; | ||
return file; | ||
} | ||
if (re.test(file.segment) || re.test(file.relative)) { | ||
file.include = true; | ||
return file; | ||
} | ||
} | ||
@@ -32,0 +56,0 @@ return file; |
{ | ||
"name": "glob-fs", | ||
"description": "file globbing for node.js. speedy and powerful alternative to node-glob.", | ||
"version": "0.1.5", | ||
"version": "0.1.6", | ||
"homepage": "https://github.com/jonschlinkert/glob-fs", | ||
@@ -28,9 +28,14 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)", | ||
"component-emitter": "^1.2.0", | ||
"ends-with": "^0.2.0", | ||
"export-files": "^2.0.1", | ||
"extend-shallow": "^2.0.0", | ||
"glob-fs-dotfiles": "^0.1.5", | ||
"glob-fs-gitignore": "^0.1.3", | ||
"get-value": "^1.1.5", | ||
"glob-fs-dotfiles": "^0.1.6", | ||
"glob-fs-gitignore": "^0.1.5", | ||
"glob-parent": "^1.2.0", | ||
"graceful-fs": "^4.1.2", | ||
"is-dotdir": "^0.1.0", | ||
"is-dotfile": "^1.0.1", | ||
"is-glob": "^2.0.0", | ||
"is-windows": "^0.1.0", | ||
"kind-of": "^2.0.0", | ||
@@ -42,3 +47,6 @@ "lazy-cache": "^0.1.0", | ||
"object.omit": "^1.1.0", | ||
"parse-filepath": "^0.6.1", | ||
"relative": "^3.0.1", | ||
"set-value": "^0.2.0", | ||
"starts-with": "^1.0.2", | ||
"through2": "^2.0.0" | ||
@@ -45,0 +53,0 @@ }, |
177
README.md
@@ -31,2 +31,3 @@ # glob-fs [](http://badge.fury.io/js/glob-fs) | ||
- [Middleware conventions](#middleware-conventions) | ||
- [Advice for middleware authors](#advice-for-middleware-authors) | ||
* [Globbing examples](#globbing-examples) | ||
@@ -41,2 +42,3 @@ - [async](#async) | ||
* [TODO](#todo) | ||
* [Community middleware](#community-middleware) | ||
* [Related projects](#related-projects) | ||
@@ -64,4 +66,9 @@ * [Running tests](#running-tests) | ||
All "read" methods take a glob pattern and an `options` object. Examples: | ||
All "read" methods take a glob pattern and an `options` object. | ||
* `pattern` **{String}**: Glob pattern to use for matching. (multiple pattern support is planned) | ||
* `options` **{Object}**: Options for `glob-fs` or middleware. | ||
**Examples:** | ||
```js | ||
@@ -91,3 +98,3 @@ // sync | ||
### [.readdir](lib/readers.js#L27) | ||
### [.readdir](lib/readers.js#L25) | ||
@@ -112,3 +119,3 @@ Asynchronously glob files or directories that match the given `pattern`. | ||
### [.readdirSync](lib/readers.js#L60) | ||
### [.readdirSync](lib/readers.js#L59) | ||
@@ -132,3 +139,3 @@ Synchronously glob files or directories that match the given `pattern`. | ||
### [.readdirStream](lib/readers.js#L91) | ||
### [.readdirStream](lib/readers.js#L90) | ||
@@ -158,3 +165,3 @@ Stream files or directories that match the given glob `pattern`. | ||
### [Glob](index.js#L35) | ||
### [Glob](index.js#L42) | ||
@@ -174,10 +181,10 @@ Optionally create an instance of `Glob` with the given `options`. | ||
### [.exclude](index.js#L172) | ||
### [.use](index.js#L178) | ||
Thin wrapper around `.use()` for easily excluding files or directories that match the given `pattern`. | ||
Add a middleware to be called in the order defined. | ||
**Params** | ||
* `pattern` **{String}** | ||
* `options` **{Object}** | ||
* `fn` **{Function}** | ||
* `returns` **{Object}**: Returns the `Glob` instance, for chaining. | ||
@@ -190,5 +197,4 @@ **Example** | ||
var glob = require('glob-fs')({ foo: true }) | ||
.exclude(/\.foo$/) | ||
.exclude('*.bar') | ||
.exclude('*.baz'); | ||
.use(gitignore()) | ||
.use(dotfiles()); | ||
@@ -198,10 +204,10 @@ var files = glob.readdirSync('**'); | ||
### [.use](index.js#L211) | ||
### [.exclude](index.js#L219) | ||
Add a middleware to be called in the order defined. | ||
Thin wrapper around `.use()` for easily excluding files or directories that match the given `pattern`. | ||
**Params** | ||
* `fn` **{Function}** | ||
* `returns` **{Object}**: Returns the `Glob` instance, for chaining. | ||
* `pattern` **{String}** | ||
* `options` **{Object}** | ||
@@ -213,7 +219,9 @@ **Example** | ||
var dotfiles = require('glob-fs-dotfiles'); | ||
var glob = require('glob-fs')({ foo: true }) | ||
.use(gitignore()) | ||
.use(dotfiles()); | ||
var glob = require('glob-fs')() | ||
.exclude(/\.foo$/) | ||
.exclude('*.bar') | ||
.exclude('*.baz'); | ||
var files = glob.readdirSync('*.js'); | ||
var files = glob.readdirSync('**'); | ||
//=> ['index.js', 'README.md', ...] | ||
``` | ||
@@ -229,63 +237,93 @@ | ||
**What does "process" mean?** | ||
Additionally, middleware can: | ||
Typically, it means one of the following: | ||
* be chained | ||
* `include` or `exclude` a file based on some condition, like whether or not one of its properties matches a regex or glob pattern. | ||
* determine whether or not to continue recursing in a specific directory | ||
* modifying an existing property to the `file` object | ||
* add a new property to the `file` object | ||
1. matching a `file.path`, or | ||
2. modifying a property on the `file` object, or | ||
3. determining whether or not to continue recursing | ||
### Middleware examples | ||
**recursing** | ||
**Ignoring files** | ||
Here is how a middleware might determine whether or not to recurse based on a glob pattern: | ||
In the following example, `notemp` is a complete and functional middleware for excluding any filepath that has the substring `temp`: | ||
```js | ||
var glob = require('glob-fs'); | ||
var glob = require('glob-fs')(); | ||
// this is already handled by glob-fs, but it | ||
// makes a good example | ||
function recurse() { | ||
return function(file) { | ||
// `file.pattern` is an object with a `glob` (string) property | ||
file.recurse = file.pattern.glob.indexOf('**') !== -1; | ||
return file; | ||
function notemp(file) { | ||
if (/temp/.test(file.path)) { | ||
file.exclude = true; | ||
} | ||
return file; | ||
} | ||
// use the middleware | ||
glob() | ||
.use(recurse()) | ||
.readdir('**/*.js', function(err, files) { | ||
console.log(files); | ||
glob.use(notemp) | ||
.readdirStream('**/*.js') | ||
.on('data', function(file) { | ||
console.log(file.relative); | ||
}); | ||
``` | ||
**exclusion** | ||
**Matching** | ||
Middleware for excluding file paths: | ||
Pattern matching is done by default in glob-fs, but you get disable the built-in matchers or get more specific by adding a middleware that uses [micromatch][] or [minimatch](https://github.com/isaacs/minimatch#readme) for matching files. | ||
```js | ||
// `notests` middleware to exclude any file in the `test` directory | ||
function tests(options) { | ||
return function(file) { | ||
if (/^test\//.test(file.dirname)) { | ||
file.exclude = true; | ||
} | ||
var glob = require('glob-fs')({ gitignore: true }); | ||
var mm = require('micromatch'); | ||
glob.use(function(file) { | ||
if (mm.isMatch(file.relative, 'vendor/**')) file.exclude = true; | ||
return file; | ||
}; | ||
}) | ||
.readdirStream('**/*.js') | ||
.on('data', function(file) { | ||
console.log(file.relative); | ||
}); | ||
``` | ||
**recursion** | ||
Here is how a middleware might determine whether or not to recurse based on a certain pattern: | ||
```js | ||
var glob = require('glob-fs')(); | ||
// this specific check is already done by glob-fs, it's just used here as an example | ||
function recurse(file) { | ||
// `file.pattern` is an object with a `glob` (string) property | ||
file.recurse = file.pattern.glob.indexOf('**') !== -1; | ||
return file; | ||
} | ||
// usage | ||
var glob = glob({ gitignore: true }) | ||
.use(tests()) | ||
// use the middleware | ||
glob.use(recurse) | ||
.readdir('**/*.js', function(err, files) { | ||
console.log(files); | ||
}); | ||
``` | ||
// get files | ||
glob.readdirStream('**/*') | ||
.on('data', function (file) { | ||
console.log(file.path); | ||
}) | ||
**Built-in middleware** | ||
Currently glob-fs includes and runs the following middleware automatically: | ||
<!-- list automatically generated from deps. see .verb.md --> | ||
* [glob-fs-dotfiles](https://github.com/jonschlinkert/glob-fs-dotfiles): glob-fs middleware for automatically ignoring dotfiles. | ||
* [glob-fs-gitignore](https://github.com/jonschlinkert/glob-fs-gitignore): glob-fs middleware for automatically ignoring files specified in `.gitignore` | ||
**Disabling built-ins** | ||
To disable built-in middleware and prevent them from running, pass `builtins: false` on the global options. This will disable **all built-in middleware**. | ||
Example: | ||
```js | ||
var glob = require('glob-fs')({builtins: false}); | ||
``` | ||
To disable a specific middleware from running, you can usually pass the name of the middleware on the options, like `dotfiles: false`, but it's best to check the readme of that middleware for specifics. | ||
### Middleware conventions | ||
@@ -298,2 +336,12 @@ | ||
### Advice for middleware authors | ||
* A middleware should only do one specific thing. | ||
* Multiple middleware libs can be bundled together to create a single middleware. | ||
* Pattern matching should be extremely specific. Don't force downstream middleware to reverse your mistakes. | ||
* As mentioned in the [middleware conventions](#middleware-conventions) section, **always return the `file` object**. | ||
* A single conditional should only set `file.exclude` to `true`, or `file.include` to `true`, never both. | ||
* It's completely okay to check `this.options` | ||
* Middleware modules should be fully documented. | ||
## Globbing examples | ||
@@ -486,4 +534,13 @@ | ||
* [ ] clean up `./lib` | ||
* [ ](https://github.com/isaacs/node-glob/)[] comparison | ||
* [ ] comparsion to [node-glob][] | ||
## Community middleware | ||
_(Add your project to the [.verb.md](./.verb.md) template do a PR!)_ | ||
<!-- remove these after we get some community middleware libs listed --> | ||
* [glob-fs-dotfiles](https://github.com/jonschlinkert/glob-fs-dotfiles): glob-fs middleware for automatically ignoring dotfiles. | ||
* [glob-fs-gitignore](https://github.com/jonschlinkert/glob-fs-gitignore): glob-fs middleware for automatically ignoring files specified in `.gitignore` | ||
## Related projects | ||
@@ -522,2 +579,2 @@ | ||
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on July 09, 2015._ | ||
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on July 11, 2015._ |
51729
35.46%22
22.22%1316
44.62%565
11.22%2
-33.33%26
44.44%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
Updated
Updated