Comparing version 3.0.1 to 3.1.0
592
glob.js
@@ -0,1 +1,37 @@ | ||
// Approach: | ||
// | ||
// 1. Get the minimatch set | ||
// 2. For each pattern in the set, PROCESS(pattern) | ||
// 3. Store matches per-set, then uniq them | ||
// | ||
// PROCESS(pattern) | ||
// Get the first [n] items from pattern that are all strings | ||
// Join these together. This is PREFIX. | ||
// If there is no more remaining, then stat(PREFIX) and | ||
// add to matches if it succeeds. END. | ||
// readdir(PREFIX) as ENTRIES | ||
// If fails, END | ||
// If pattern[n] is GLOBSTAR | ||
// // handle the case where the globstar match is empty | ||
// // by pruning it out, and testing the resulting pattern | ||
// PROCESS(pattern[0..n] + pattern[n+1 .. $]) | ||
// // handle other cases. | ||
// for ENTRY in ENTRIES (not dotfiles) | ||
// // attach globstar + tail onto the entry | ||
// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $]) | ||
// | ||
// else // not globstar | ||
// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) | ||
// Test ENTRY against pattern[n+1] | ||
// If fails, continue | ||
// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) | ||
// | ||
// Caveat: | ||
// Cache all stats and readdirs results to minimize syscall. Since all | ||
// we ever care about is existence and directory-ness, we can just keep | ||
// `true` for files, and [children,...] for directories, or `false` for | ||
// things that don't exist. | ||
module.exports = glob | ||
@@ -8,24 +44,5 @@ | ||
, EE = require("events").EventEmitter | ||
, FastList = require("fast-list") | ||
, path = require("path") | ||
, isDir = {} | ||
// Globbing is a *little* bit different than just matching, in some | ||
// key ways. | ||
// | ||
// First, and importantly, it matters a great deal whether a pattern | ||
// is "absolute" or "relative". Absolute patterns are patterns that | ||
// start with / on unix, or a full device/unc path on windows. | ||
// | ||
// Second, globs interact with the actual filesystem, so being able | ||
// to stop searching as soon as a match is no longer possible is of | ||
// the utmost importance. It would not do to traverse a large file | ||
// tree, and then eliminate all but one of the options, if it could | ||
// be possible to skip the traversal early. | ||
// Get a Minimatch object from the pattern and options. Then, starting | ||
// from the options.root or the cwd, read the dir, and do a partial | ||
// match on all the files if it's a dir, or a regular match if it's not. | ||
function glob (pattern, options, cb) { | ||
@@ -76,4 +93,8 @@ if (typeof options === "function") cb = options, options = {} | ||
if (typeof cb === "function") { | ||
console.error("cb is function") | ||
this.on("error", cb) | ||
this.on("end", function (matches) { cb(null, matches) }) | ||
this.on("end", function (matches) { | ||
// console.error("cb with matches", matches) | ||
cb(null, matches) | ||
}) | ||
} | ||
@@ -84,10 +105,9 @@ | ||
if (!options.hasOwnProperty("maxDepth")) options.maxDepth = 1000 | ||
if (!options.hasOwnProperty("maxLength")) options.maxLength = 4096 | ||
if (!options.hasOwnProperty("maxLength")) options.maxLength = Infinity | ||
if (!options.hasOwnProperty("statCache")) options.statCache = {} | ||
if (!options.hasOwnProperty("cwd")) options.cwd = process.cwd() | ||
if (!options.hasOwnProperty("root")) { | ||
options.root = path.resolve(options.cwd, "/") | ||
} | ||
var cwd = this.cwd = options.cwd = | ||
options.cwd || process.cwd() | ||
this.root = options.root = | ||
options.root || path.resolve(cwd, "/") | ||
if (!pattern) { | ||
@@ -97,2 +117,10 @@ throw new Error("must provide pattern") | ||
// base-matching: just use globstar for that. | ||
if (options.matchBase && -1 === pattern.indexOf("/")) { | ||
if (options.noglobstar) { | ||
throw new Error("base matching requires globstar") | ||
} | ||
pattern = "**/" + pattern | ||
} | ||
var mm = this.minimatch = new Minimatch(pattern, options) | ||
@@ -105,93 +133,81 @@ options = this.options = mm.options | ||
this.matches = new FastList() | ||
EE.call(this) | ||
var me = this | ||
this._checkedRoot = false | ||
// process each pattern in the minimatch set | ||
var n = this.minimatch.set.length | ||
// if we have any patterns starting with /, then we need to | ||
// start at the root. If we don't, then we can take a short | ||
// cut and just start at the cwd. | ||
var start = this.cwd | ||
for (var i = 0, l = this.minimatch.set.length; i < l; i ++) { | ||
if (this.minimatch.set[i].absolute) { | ||
start = this.root | ||
break | ||
} | ||
} | ||
// The matches are stored as {<filename>: true,...} so that | ||
// duplicates are automagically pruned. | ||
// Later, we do an Object.keys() on these. | ||
// Keep them as a list so we can fill in when nonull is set. | ||
this.matches = new Array(n) | ||
if (me.options.debug) { | ||
console.error("start =", start) | ||
this.minimatch.set.forEach(iterator.bind(this)) | ||
function iterator (pattern, i, set) { | ||
this._process(pattern, 0, i, function (er) { | ||
if (er) this.emit("error", er) | ||
if (-- n <= 0) this._finish() | ||
}.bind(this)) | ||
} | ||
this._process(start, 1, this._finish.bind(this)) | ||
} | ||
Glob.prototype._finish = _finish | ||
function _finish () { | ||
var me = this | ||
if (me.options.debug) { | ||
console.error("!!! GLOB top level cb", me) | ||
} | ||
if (me.options.nonull && me.matches.length === 0) { | ||
return me.emit("end", [pattern]) | ||
} | ||
Glob.prototype._finish = function () { | ||
var found = me.found = me.matches.slice() | ||
var nou = this.options.nounique | ||
, all = nou ? [] : {} | ||
found = me.found = found.map(function (m) { | ||
if (m.indexOf(me.options.cwd) === 0) { | ||
m = m.substr(me.options.cwd.length + 1) | ||
for (var i = 0, l = this.matches.length; i < l; i ++) { | ||
var matches = this.matches[i] | ||
// console.error("matches[%d] =", i, matches) | ||
// do like the shell, and spit out the literal glob | ||
if (!matches) { | ||
if (this.options.nonull) { | ||
var literal = this.minimatch.globSet[i] | ||
if (nou) all.push(literal) | ||
else nou[literal] = true | ||
} | ||
} else { | ||
// had matches | ||
var m = Object.keys(matches) | ||
if (nou) all.push.apply(all, m) | ||
else m.forEach(function (m) { | ||
all[m] = true | ||
}) | ||
} | ||
return m | ||
}) | ||
} | ||
if (!me.options.mark) return next() | ||
if (!nou) all = Object.keys(all) | ||
// mark all directories with a /. | ||
// This may involve some stat calls for things that are unknown. | ||
var needStat = [] | ||
found = me.found = found.map(function (f) { | ||
if (isDir[f] === undefined) needStat.push(f) | ||
else if (isDir[f] && f.slice(-1) !== "/") f += "/" | ||
return f | ||
}) | ||
var c = needStat.length | ||
if (c === 0) return next() | ||
if (!this.options.nosort) { | ||
all = all.sort(this.options.nocase ? alphasorti : alphasort) | ||
} | ||
var stat = me.options.follow ? "stat" : "lstat" | ||
needStat.forEach(function (f) { | ||
if (me.options.sync) { | ||
try { | ||
afterStat(f)(null, fs[stat + "Sync"](f)) | ||
} catch (er) { | ||
afterStat(f)(er) | ||
if (this.options.mark) { | ||
// at *some* point we statted all of these | ||
all = all.map(function (m) { | ||
var sc = this.statCache[m] | ||
if (!sc) return m | ||
if (m.slice(-1) !== "/" && (Array.isArray(sc) || sc === 2)) { | ||
return m + "/" | ||
} | ||
} else fs[stat](f, afterStat(f)) | ||
}) | ||
function afterStat (f) { return function (er, st) { | ||
// ignore errors. if the user only wants to show | ||
// existing files, then set options.stat to exclude anything | ||
// that doesn't exist. | ||
if (st && st.isDirectory() && f.substr(-1) !== "/") { | ||
var i = found.indexOf(f) | ||
if (i !== -1) { | ||
found.splice(i, 1, f + "/") | ||
if (m.slice(-1) === "/") { | ||
return m.replace(/\/$/, "") | ||
} | ||
} | ||
if (-- c <= 0) return next() | ||
}} | ||
return m | ||
}) | ||
} | ||
function next () { | ||
if (!me.options.nosort) { | ||
found = found.sort(alphasort) | ||
} | ||
me.emit("end", found) | ||
} | ||
// console.error("emitting end", all) | ||
this.found = all | ||
this.emit("end", all) | ||
} | ||
function alphasort (a, b) { | ||
function alphasorti (a, b) { | ||
a = a.toLowerCase() | ||
b = b.toLowerCase() | ||
return alphasort(a, b) | ||
} | ||
function alphasort (a, b) { | ||
return a > b ? 1 : a < b ? -1 : 0 | ||
@@ -208,201 +224,269 @@ } | ||
Glob.prototype._process = _process | ||
function _process (f, depth, cb) { | ||
function _process (pattern, depth, index, cb) { | ||
cb = cb.bind(this) | ||
if (this.aborted) return cb() | ||
var me = this | ||
if (depth > this.options.maxDepth) return cb() | ||
// if f matches, then it's a match. emit it, move on. | ||
// if it *partially* matches, then it might be a dir. | ||
// | ||
// possible optimization: don't just minimatch everything | ||
// against the full pattern. if a bit of the pattern is | ||
// not magical, it'd be good to reduce the number of stats | ||
// that had to be made. so, in the pattern: "a/*/b", we could | ||
// readdir a, then stat a/<child>/b in all of them. | ||
// | ||
// however, that'll require a lot of muddying between minimatch | ||
// and glob, and at least for the time being, it's kind of nice to | ||
// keep them a little bit separate. | ||
// if this thing is a match, then add to the matches list. | ||
var match = me.minimatch.match(f) | ||
if (!match) { | ||
if (me.options.debug) { | ||
console.error("not a match", f) | ||
} | ||
return me._processPartial(f, depth, cb) | ||
// Get the first [n] parts of pattern that are all strings. | ||
var n = 0 | ||
while (typeof pattern[n] === "string") { | ||
n ++ | ||
} | ||
// now n is the index of the first one that is *not* a string. | ||
if (match) { | ||
if (me.options.debug) { | ||
console.error(" %s matches %s", f, me.pattern) | ||
} | ||
// make sure it exists if asked. | ||
if (me.options.stat) { | ||
var stat = me.options.follow ? "stat" : "lstat" | ||
if (me.options.sync) { | ||
try { | ||
afterStat(f)(null, fs[stat + "Sync"](f)) | ||
} catch (ex) { | ||
afterStat(f)(ex) | ||
// see if there's anything else | ||
switch (n) { | ||
// if not, then this is rather simple | ||
case pattern.length: | ||
var prefix = pattern.join("/") | ||
this._stat(prefix, function (exists, isDir) { | ||
// either it's there, or it isn't. | ||
// nothing more to do, either way. | ||
if (exists) { | ||
this.matches[index] = this.matches[index] || {} | ||
this.matches[index][prefix] = true | ||
this.emit("match", prefix) | ||
} | ||
} else fs[stat](f, afterStat(f)) | ||
} else if (me.options.sync) { | ||
emitMatch() | ||
} else { | ||
process.nextTick(emitMatch) | ||
} | ||
return cb() | ||
}) | ||
return | ||
return | ||
case 0: | ||
// pattern *starts* with some non-trivial item. | ||
// going to readdir(cwd), but not include the prefix in matches. | ||
var prefix = null | ||
break | ||
function afterStat (f) { return function (er, st) { | ||
if (er) return cb() | ||
isDir[f] = st.isDirectory() | ||
emitMatch() | ||
}} | ||
default: | ||
// pattern has some string bits in the front. | ||
// whatever it starts with, whether that's "absolute" like /foo/bar, | ||
// or "relative" like "../baz" | ||
var prefix = pattern.slice(0, n) | ||
prefix = prefix.join("/") | ||
// console.error("prefix=%s", prefix) | ||
break | ||
} | ||
function emitMatch () { | ||
if (me.options.debug) { | ||
console.error("emitting match", f) | ||
} | ||
me.matches.push(f) | ||
me.emit("match", f) | ||
// move on, since it might also be a partial match | ||
// eg, a/**/c matches both a/c and a/c/d/c | ||
me._processPartial(f, depth, cb) | ||
// get the list of entries. | ||
if (prefix !== null && (prefix.charAt(0) === "/" || prefix === "")) { | ||
prefix = path.join(this.options.root, prefix) | ||
} | ||
var read = prefix || this.options.cwd | ||
return this._readdir(prefix || process.cwd(), function (er, entries) { | ||
// console.error("back from readdir", prefix || process.cwd(), er, entries) | ||
if (er) { | ||
// not a directory! | ||
// this means that, whatever else comes after this, it can never match | ||
return cb() | ||
} | ||
} | ||
} | ||
// globstar is special | ||
if (pattern[n] === minimatch.GLOBSTAR) { | ||
// console.error("globstar!", pattern, n) | ||
// console.error("entries", prefix, entries) | ||
// test without the globstar, and with every child both below | ||
// and replacing the globstar. | ||
var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ] | ||
entries.forEach(function (e) { | ||
if (e.charAt(0) === "." && !this.options.dot) return | ||
// instead of the globstar | ||
s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))) | ||
// below the globstar | ||
s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n))) | ||
}, this) | ||
// now asyncForEach over this | ||
var l = s.length | ||
, errState = null | ||
s.forEach(function (gsPattern) { | ||
this._process(gsPattern, depth + 1, index, function (er) { | ||
if (errState) return | ||
if (er) return cb(errState = er) | ||
if (--l <= 0) return cb() | ||
}) | ||
}, this) | ||
Glob.prototype._processPartial = _processPartial | ||
function _processPartial (f, depth, cb) { | ||
if (this.aborted) return cb() | ||
return | ||
} | ||
var me = this | ||
// not a globstar | ||
// It will only match dot entries if it starts with a dot, or if | ||
// options.dot is set. Stuff like @(.foo|.bar) isn't allowed. | ||
var pn = pattern[n] | ||
if (typeof pn === "string") { | ||
var found = entries.indexOf(pn) !== -1 | ||
entries = found ? entries[pn] : [] | ||
} else { | ||
var rawGlob = pattern[n]._glob | ||
, dotOk = this.options.dot || rawGlob.charAt(0) === "." | ||
var partial = me.minimatch.match(f, true) | ||
if (!partial) { | ||
if (me.options.debug) { | ||
console.error("not a partial", f) | ||
// console.error("pattern", pattern, n, pattern[n]) | ||
entries = entries.filter(function (e) { | ||
return (e.charAt(0) !== "." || dotOk) && | ||
(typeof pattern[n] === "string" && e === pattern[n] || | ||
e.match(pattern[n])) | ||
}) | ||
} | ||
// if not a match or partial match, just move on. | ||
return cb() | ||
} | ||
// If n === pattern.length - 1, then there's no need for the extra stat | ||
// *unless* the user has specified "mark" or "stat" explicitly. | ||
// We know that they exist, since the readdir returned them. | ||
if (n === pattern.length - 1 && | ||
!this.options.mark && | ||
!this.options.stat) { | ||
console.error("skip final stat") | ||
entries.forEach(function (e) { | ||
if (prefix) { | ||
if (prefix !== "/") e = prefix + "/" + e | ||
else e = prefix + e | ||
} | ||
this.matches[index] = this.matches[index] || {} | ||
this.matches[index][e] = true | ||
this.emit("match", e) | ||
}, this) | ||
return cb.call(this) | ||
} | ||
// partial match | ||
// however, this only matters if it's a dir. | ||
//if (me.options.debug) | ||
if (me.options.debug) { | ||
console.error("got a partial", f) | ||
} | ||
me.emit("partial", f) | ||
me._processDir(f, depth, cb) | ||
} | ||
Glob.prototype._processDir = _processDir | ||
function _processDir (f, depth, cb) { | ||
if (this.aborted) return cb() | ||
// console.error("entries", prefix, entries) | ||
// If we're already at the maximum depth, then don't read the dir. | ||
if (depth >= this.options.maxDepth) return cb() | ||
// now test all the remaining entries as stand-ins for that part | ||
// of the pattern. | ||
var l = entries.length | ||
, errState = null | ||
if (l === 0) return cb() // no matches possible | ||
entries.forEach(function (e) { | ||
var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)) | ||
// console.error("new pattern!", p) | ||
this._process(p, depth + 1, index, function (er) { | ||
// console.error("Back from processing", this.matches) | ||
if (errState) return | ||
if (er) return cb(errState = er) | ||
if (--l === 0) return cb.call(this) | ||
}.bind(this)) | ||
}, this) | ||
}) | ||
// if the path is at the maximum length, then don't proceed, either. | ||
if (f.length >= this.options.maxLength) return cb() | ||
// now the fun stuff. | ||
// if it's a dir, then we'll read all the children, and process them. | ||
// if it's not a dir, or we can't access it, then it'll fail. | ||
// We log a warning for EACCES and EPERM, but ENOTDIR and ENOENT are | ||
// expected and fine. | ||
cb = this._afterReaddir(f, depth, cb) | ||
if (this.options.sync) return this._processDirSync(f, depth, cb) | ||
fs.readdir(f, cb) | ||
} | ||
Glob.prototype._processDirSync = _processDirSync | ||
function _processDirSync (f, depth, cb) { | ||
try { | ||
cb(null, fs.readdirSync(f)) | ||
} catch (ex) { | ||
cb(ex) | ||
Glob.prototype._stat = function (f, cb) { | ||
if (f.length > this.options.maxLength) { | ||
var er = new Error("Path name too long") | ||
er.code = "ENAMETOOLONG" | ||
er.path = f | ||
return this._afterStat(f, cb, er) | ||
} | ||
} | ||
Glob.prototype._afterReaddir = _afterReaddir | ||
function _afterReaddir (f, depth, cb) { | ||
var me = this | ||
return function afterReaddir (er, children) { | ||
if (er) switch (er.code) { | ||
case "UNKNOWN": // probably too deep | ||
case "ENOTDIR": // completely expected and normal. | ||
isDir[f] = false | ||
return cb() | ||
case "ENOENT": // should never happen. | ||
default: // some other kind of problem. | ||
if (!me.options.silent) console.error("glob error", er) | ||
if (me.options.strict) return cb(er) | ||
return cb() | ||
if (this.options.statCache.hasOwnProperty(f)) { | ||
var exists = this.options.statCache[f] | ||
, isDir = exists && (Array.isArray(exists) || exists === 2) | ||
if (this.options.sync) return cb.call(this, !!exists, isDir) | ||
return process.nextTick(cb.bind(this, !!exists, isDir)) | ||
} | ||
if (this.options.sync) { | ||
var er, stat | ||
try { | ||
stat = fs.statSync(f) | ||
} catch (e) { | ||
er = e | ||
} | ||
this._afterStat(f, cb, er, stat) | ||
} else { | ||
fs.stat(f, this._afterStat.bind(this, f, cb)) | ||
} | ||
} | ||
// at this point, we know it's a dir, so save a stat later if | ||
// mark is set. | ||
isDir[f] = true | ||
me._processChildren(f, depth, children, cb) | ||
Glob.prototype._afterStat = function (f, cb, er, stat) { | ||
if (er || !stat) { | ||
exists = false | ||
} else { | ||
exists = stat.isDirectory() ? 2 : 1 | ||
} | ||
this.options.statCache[f] = this.options.statCache[f] || exists | ||
cb.call(this, !!exists, exists === 2) | ||
} | ||
Glob.prototype._processChildren = _processChildren | ||
function _processChildren (f, depth, children, cb) { | ||
var me = this | ||
// note: the file ending with / might match, but only if | ||
// it's a directory, which we know it is at this point. | ||
// For example, /a/b/ or /a/b/** would match /a/b/ but not | ||
// /a/b. Note: it'll get the trailing "/" strictly based | ||
// on the "mark" param, but that happens later. | ||
// This is slightly different from bash's glob. | ||
if (!me.minimatch.match(f) && me.minimatch.match(f + "/")) { | ||
me.matches.push(f) | ||
me.emit("match", f) | ||
Glob.prototype._readdir = function (f, cb) { | ||
if (f.length > this.options.maxLength) { | ||
var er = new Error("Path name too long") | ||
er.code = "ENAMETOOLONG" | ||
er.path = f | ||
return this._afterReaddir(f, cb, er) | ||
} | ||
if (-1 === children.indexOf(".")) children.push(".") | ||
if (-1 === children.indexOf("..")) children.push("..") | ||
if (this.options.statCache.hasOwnProperty(f)) { | ||
var c = this.options.statCache[f] | ||
if (Array.isArray(c)) { | ||
if (this.options.sync) return cb.call(this, null, c) | ||
return process.nextTick(cb.bind(this, null, c)) | ||
} | ||
var count = children.length | ||
if (me.options.debug) { | ||
console.error("count=%d %s", count, f, children) | ||
if (!c || c === 1) { | ||
// either ENOENT or ENOTDIR | ||
// console.error("enoent or enotdir?") | ||
var code = c ? "ENOTDIR" : "ENOENT" | ||
, er = new Error((c ? "Not a directory" : "Not found") + ": " + f) | ||
er.path = f | ||
er.code = code | ||
// console.error(f, er) | ||
if (this.options.sync) return cb.call(this, er) | ||
return process.nextTick(cb.bind(this, er)) | ||
} | ||
// at this point, c === 2, meaning it's a dir, but we haven't | ||
// had to read it yet, or c === true, meaning it's *something* | ||
// but we don't have any idea what. Need to read it, either way. | ||
} | ||
if (count === 0) { | ||
if (me.options.debug) { | ||
console.error("no children?", children, f) | ||
if (this.options.sync) { | ||
var er, entries | ||
try { | ||
entries = fs.readdirSync(f) | ||
} catch (e) { | ||
er = e | ||
} | ||
return then() | ||
return this._afterReaddir(f, cb, er, entries) | ||
} | ||
children.forEach(function (c) { | ||
if (f === "/") c = f + c | ||
else c = f + "/" + c | ||
fs.readdir(f, this._afterReaddir.bind(this, f, cb)) | ||
} | ||
if (me.options.debug) { | ||
console.error(" processing", c) | ||
Glob.prototype._afterReaddir = function (f, cb, er, entries) { | ||
if (entries && !er) { | ||
// console.error("has entries, and no er", f, er, entries) | ||
this.options.statCache[f] = entries | ||
// if we haven't asked to stat everything for suresies, then just | ||
// assume that everything in there exists, so we can avoid | ||
// having to stat it a second time. This also gets us one step | ||
// further into ELOOP territory. | ||
if (!this.options.mark && !this.options.stat) { | ||
entries.forEach(function (e) { | ||
if (f === "/") e = f + e | ||
else e = f + "/" + e | ||
this.options.statCache[e] = true | ||
}, this) | ||
} | ||
me._process(c, depth + 1, then) | ||
}) | ||
function then (er) { | ||
count -- | ||
if (me.options.debug) { | ||
console.error("%s THEN %s", f, count, count <= 0 ? "done" : "not done") | ||
} | ||
if (me.error) return | ||
if (er) return me.emit("error", me.error = er) | ||
if (count <= 0) cb() | ||
return cb.call(this, er, entries) | ||
} | ||
// now handle errors, and cache the information | ||
if (er) switch (er.code) { | ||
case "ENOTDIR": // totally normal. means it *does* exist. | ||
this.options.statCache[f] = 1 | ||
return cb.call(this, er) | ||
case "ENOENT": // not terribly unusual | ||
case "ELOOP": | ||
case "ENAMETOOLONG": | ||
case "UNKNOWN": | ||
this.options.statCache[f] = false | ||
return cb.call(this, er) | ||
default: // some unusual error. | ||
if (this.options.strict) this.emit("error", er) | ||
if (!this.options.silent) console.error("glob error", er) | ||
return cb.call(this, er) | ||
} | ||
} |
@@ -5,3 +5,3 @@ { | ||
"description": "a little globber", | ||
"version": "3.0.1", | ||
"version": "3.1.0", | ||
"repository": { | ||
@@ -16,4 +16,3 @@ "type": "git", | ||
"dependencies": { | ||
"fast-list":"1", | ||
"minimatch": "0.1", | ||
"minimatch": "0.2", | ||
"graceful-fs": "~1.1.2", | ||
@@ -20,0 +19,0 @@ "inherits": "1" |
@@ -65,4 +65,2 @@ # Glob | ||
* `options` The options object passed in. | ||
* `matches` A [FastList](https://github.com/isaacs/fast-list) object | ||
containing the matches as they are found. | ||
* `error` The error encountered. When an error is encountered, the | ||
@@ -79,6 +77,5 @@ glob object is in an undefined state, and should be discarded. | ||
are sorted, unless the `nosort` flag is set. | ||
* `match` Every time a match is found, this is emitted with the pattern. | ||
* `partial` Emitted when a directory matches the start of a pattern, and | ||
is then searched for additional matches. | ||
* `error` Emitted when an unexpected error is encountered. | ||
* `match` Every time a match is found, this is emitted with the matched. | ||
* `error` Emitted when an unexpected error is encountered, or whenever | ||
any fs error occurs if `options.strict` is set. | ||
* `abort` When `abort()` is called, this event is raised. | ||
@@ -96,22 +93,25 @@ | ||
All options are false by default. | ||
All options are false by default, unless otherwise noted. | ||
* `cwd` The current working directory in which to search. Since, unlike | ||
Minimatch, Glob requires a working directory to start in, this | ||
defaults to `process.cwd()`. | ||
* `root` Since Glob requires a root setting, this defaults to | ||
`path.resolve(options.cwd, "/")`. | ||
* `mark` Add a `/` character to directory matches. | ||
* `follow` Use `stat` instead of `lstat`. This is only relevant if | ||
`stat` or `mark` are true. | ||
* `cwd` The current working directory in which to search. Defaults | ||
to `process.cwd()`. | ||
* `root` The place where patterns starting with `/` will be mounted | ||
onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix | ||
systems, and `C:\` or some such on Windows.) | ||
* `mark` Add a `/` character to directory matches. Note that this | ||
requires additional stat calls. | ||
* `nosort` Don't sort the results. | ||
* `stat` Set to true to stat/lstat *all* results. This reduces performance | ||
somewhat, but guarantees that the results are files that actually | ||
exist. | ||
* `silent` When an error other than `ENOENT` or `ENOTDIR` is encountered | ||
* `stat` Set to true to stat *all* results. This reduces performance | ||
somewhat. | ||
* `silent` When an unusual error is encountered | ||
when attempting to read a directory, a warning will be printed to | ||
stderr. Set the `silent` option to true to suppress these warnings. | ||
* `strict` When an error other than `ENOENT` or `ENOTDIR` is encountered | ||
* `strict` When an unusual error is encountered | ||
when attempting to read a directory, the process will just continue on | ||
in search of other matches. Set the `strict` option to raise an error | ||
in these cases. | ||
* `statCache` A cache of results of filesystem information, to prevent | ||
unnecessary stat calls. While it should not normally be necessary to | ||
set this, you may pass the statCache from one glob() call to the | ||
options object of another, if you know that the filesystem will not | ||
change between calls. |
@@ -8,3 +8,4 @@ // basic test | ||
, globs = | ||
["test/a/*/+(c|g)/./d" | ||
[ | ||
"test/a/*/+(c|g)/./d" | ||
,"test/a/**/[cg]/../[cg]" | ||
@@ -68,3 +69,3 @@ ,"test/a/{b,c,d,e,f}/**/g" | ||
return set | ||
}, []) | ||
}, []).sort(alphasort) | ||
} | ||
@@ -75,2 +76,10 @@ next() | ||
glob(pattern, function (er, matches) { | ||
// sort and unpark, just to match the shell results | ||
matches = matches.map(function (m) { | ||
return m.replace(/\/+/g, "/").replace(/\/$/, "") | ||
}).sort(alphasort).reduce(function (set, f) { | ||
if (f !== set[set.length - 1]) set.push(f) | ||
return set | ||
}, []).sort(alphasort) | ||
t.ifError(er, pattern + " should not error") | ||
@@ -90,3 +99,10 @@ globResult = matches | ||
tap.test(pattern + " sync", function (t) { | ||
t.deepEqual(glob.sync(pattern), echoOutput, "should match shell") | ||
var matches = glob.sync(pattern).map(function (m) { | ||
return m.replace(/\/+/g, "/").replace(/\/$/, "") | ||
}).sort(alphasort).reduce(function (set, f) { | ||
if (f !== set[set.length - 1]) set.push(f) | ||
return set | ||
}, []).sort(alphasort) | ||
t.deepEqual(matches, echoOutput, "should match shell") | ||
t.end() | ||
@@ -93,0 +109,0 @@ }) |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
23607
3
603
2
+ Addedlru-cache@2.7.3(transitive)
+ Addedminimatch@0.2.14(transitive)
+ Addedsigmund@1.0.1(transitive)
- Removedfast-list@1
- Removedfast-list@1.0.3(transitive)
- Removedlru-cache@1.0.6(transitive)
- Removedminimatch@0.1.5(transitive)
Updatedminimatch@0.2