3 // 1. Get the minimatch set
4 // 2. For each pattern in the set, PROCESS(pattern)
5 // 3. Store matches per-set, then uniq them
8 // Get the first [n] items from pattern that are all strings
9 // Join these together. This is PREFIX.
10 // If there is no more remaining, then stat(PREFIX) and
11 // add to matches if it succeeds. END.
12 // readdir(PREFIX) as ENTRIES
14 // If pattern[n] is GLOBSTAR
15 // // handle the case where the globstar match is empty
16 // // by pruning it out, and testing the resulting pattern
17 // PROCESS(pattern[0..n] + pattern[n+1 .. $])
18 // // handle other cases.
19 // for ENTRY in ENTRIES (not dotfiles)
20 // // attach globstar + tail onto the entry
21 // PROCESS(pattern[0..n] + ENTRY + pattern[n .. $])
23 // else // not globstar
24 // for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
25 // Test ENTRY against pattern[n+1]
27 // If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
30 // Cache all stats and readdirs results to minimize syscall. Since all
31 // we ever care about is existence and directory-ness, we can just keep
32 // `true` for files, and [children,...] for directories, or `false` for
33 // things that don't exist.
39 var fs = require("graceful-fs")
40 , minimatch = require("minimatch")
41 , Minimatch = minimatch.Minimatch
42 , inherits = require("inherits")
43 , EE = require("events").EventEmitter
44 , path = require("path")
46 , assert = require("assert").ok
48 function glob (pattern, options, cb) {
49 if (typeof options === "function") cb = options, options = {}
50 if (!options) options = {}
52 if (typeof options === "number") {
57 var g = new Glob(pattern, options, cb)
58 return g.sync ? g.found : g
61 glob.fnmatch = deprecated
63 function deprecated () {
64 throw new Error("glob's interface has changed. Please see the docs.")
68 function globSync (pattern, options) {
69 if (typeof options === "number") {
74 options = options || {}
76 return glob(pattern, options)
82 function Glob (pattern, options, cb) {
83 if (!(this instanceof Glob)) {
84 return new Glob(pattern, options, cb)
87 if (typeof cb === "function") {
89 this.on("end", function (matches) {
90 // console.error("cb with matches", matches)
95 options = options || {}
100 this.maxDepth = options.maxDepth || 1000
101 this.maxLength = options.maxLength || Infinity
102 this.statCache = options.statCache || {}
104 this.changedCwd = false
105 var cwd = process.cwd()
106 if (!options.hasOwnProperty("cwd")) this.cwd = cwd
108 this.cwd = options.cwd
109 this.changedCwd = path.resolve(options.cwd) !== cwd
112 this.root = options.root || path.resolve(this.cwd, "/")
113 this.root = path.resolve(this.root)
115 this.nomount = !!options.nomount
118 throw new Error("must provide pattern")
121 // base-matching: just use globstar for that.
122 if (options.matchBase && -1 === pattern.indexOf("/")) {
123 if (options.noglobstar) {
124 throw new Error("base matching requires globstar")
126 pattern = "**/" + pattern
129 this.dot = !!options.dot
130 this.mark = !!options.mark
131 this.sync = !!options.sync
132 this.nounique = !!options.nounique
133 this.nonull = !!options.nonull
134 this.nosort = !!options.nosort
135 this.nocase = !!options.nocase
136 this.stat = !!options.stat
137 this.debug = !!options.debug || !!options.globDebug
138 this.silent = !!options.silent
140 var mm = this.minimatch = new Minimatch(pattern, options)
141 this.options = mm.options
142 pattern = this.pattern = mm.pattern
149 // process each pattern in the minimatch set
150 var n = this.minimatch.set.length
152 // The matches are stored as {<filename>: true,...} so that
153 // duplicates are automagically pruned.
154 // Later, we do an Object.keys() on these.
155 // Keep them as a list so we can fill in when nonull is set.
156 this.matches = new Array(n)
158 this.minimatch.set.forEach(iterator.bind(this))
159 function iterator (pattern, i, set) {
160 this._process(pattern, 0, i, function (er) {
161 if (er) this.emit("error", er)
162 if (-- n <= 0) this._finish()
167 Glob.prototype._finish = function () {
168 assert(this instanceof Glob)
170 var nou = this.nounique
171 , all = nou ? [] : {}
173 for (var i = 0, l = this.matches.length; i < l; i ++) {
174 var matches = this.matches[i]
175 if (this.debug) console.error("matches[%d] =", i, matches)
176 // do like the shell, and spit out the literal glob
179 var literal = this.minimatch.globSet[i]
180 if (nou) all.push(literal)
181 else all[literal] = true
185 var m = Object.keys(matches)
186 if (nou) all.push.apply(all, m)
187 else m.forEach(function (m) {
193 if (!nou) all = Object.keys(all)
196 all = all.sort(this.nocase ? alphasorti : alphasort)
200 // at *some* point we statted all of these
201 all = all.map(function (m) {
202 var sc = this.statCache[m]
205 var isDir = (Array.isArray(sc) || sc === 2)
206 if (isDir && m.slice(-1) !== "/") {
209 if (!isDir && m.slice(-1) === "/") {
210 return m.replace(/\/+$/, "")
216 if (this.debug) console.error("emitting end", all)
218 this.EOF = this.found = all
219 this.emitMatch(this.EOF)
222 function alphasorti (a, b) {
225 return alphasort(a, b)
228 function alphasort (a, b) {
229 return a > b ? 1 : a < b ? -1 : 0
232 Glob.prototype.abort = function () {
237 Glob.prototype.pause = function () {
238 if (this.paused) return
240 this.emit("error", new Error("Can't pause/resume sync glob"))
245 Glob.prototype.resume = function () {
246 if (!this.paused) return
248 this.emit("error", new Error("Can't pause/resume sync glob"))
251 this._processEmitQueue()
252 //process.nextTick(this.emit.bind(this, "resume"))
255 Glob.prototype.emitMatch = function (m) {
256 this._emitQueue.push(m)
257 this._processEmitQueue()
260 Glob.prototype._processEmitQueue = function (m) {
261 while (!this._processingEmitQueue &&
263 this._processingEmitQueue = true
264 var m = this._emitQueue.shift()
266 this._processingEmitQueue = false
270 console.error('emit!', m === this.EOF ? "end" : "match")
272 this.emit(m === this.EOF ? "end" : "match", m)
273 this._processingEmitQueue = false
277 Glob.prototype._process = function (pattern, depth, index, cb_) {
278 assert(this instanceof Glob)
280 var cb = function cb (er, res) {
281 assert(this instanceof Glob)
283 if (!this._processQueue) {
284 this._processQueue = []
285 this.once("resume", function () {
286 var q = this._processQueue
287 this._processQueue = null
288 q.forEach(function (cb) { cb() })
291 this._processQueue.push(cb_.bind(this, er, res))
293 cb_.call(this, er, res)
297 if (this.aborted) return cb()
299 if (depth > this.maxDepth) return cb()
301 // Get the first [n] parts of pattern that are all strings.
303 while (typeof pattern[n] === "string") {
306 // now n is the index of the first one that is *not* a string.
308 // see if there's anything else
311 // if not, then this is rather simple
313 prefix = pattern.join("/")
314 this._stat(prefix, function (exists, isDir) {
315 // either it's there, or it isn't.
316 // nothing more to do, either way.
318 if (prefix.charAt(0) === "/" && !this.nomount) {
319 prefix = path.join(this.root, prefix)
321 this.matches[index] = this.matches[index] || {}
322 this.matches[index][prefix] = true
323 this.emitMatch(prefix)
330 // pattern *starts* with some non-trivial item.
331 // going to readdir(cwd), but not include the prefix in matches.
336 // pattern has some string bits in the front.
337 // whatever it starts with, whether that's "absolute" like /foo/bar,
338 // or "relative" like "../baz"
339 prefix = pattern.slice(0, n)
340 prefix = prefix.join("/")
344 // get the list of entries.
346 if (prefix === null) read = "."
347 else if (isAbsolute(prefix)) {
348 read = prefix = path.resolve("/", prefix)
349 if (this.debug) console.error('absolute: ', prefix, this.root, pattern)
352 if (this.debug) console.error('readdir(%j)', read, this.cwd, this.root)
353 return this._readdir(read, function (er, entries) {
356 // this means that, whatever else comes after this, it can never match
360 // globstar is special
361 if (pattern[n] === minimatch.GLOBSTAR) {
362 // test without the globstar, and with every child both below
363 // and replacing the globstar.
364 var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ]
365 entries.forEach(function (e) {
366 if (e.charAt(0) === "." && !this.dot) return
367 // instead of the globstar
368 s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)))
369 // below the globstar
370 s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))
373 // now asyncForEach over this
376 s.forEach(function (gsPattern) {
377 this._process(gsPattern, depth + 1, index, function (er) {
379 if (er) return cb(errState = er)
380 if (--l <= 0) return cb()
388 // It will only match dot entries if it starts with a dot, or if
389 // dot is set. Stuff like @(.foo|.bar) isn't allowed.
391 if (typeof pn === "string") {
392 var found = entries.indexOf(pn) !== -1
393 entries = found ? entries[pn] : []
395 var rawGlob = pattern[n]._glob
396 , dotOk = this.dot || rawGlob.charAt(0) === "."
398 entries = entries.filter(function (e) {
399 return (e.charAt(0) !== "." || dotOk) &&
400 (typeof pattern[n] === "string" && e === pattern[n] ||
405 // If n === pattern.length - 1, then there's no need for the extra stat
406 // *unless* the user has specified "mark" or "stat" explicitly.
407 // We know that they exist, since the readdir returned them.
408 if (n === pattern.length - 1 &&
411 entries.forEach(function (e) {
413 if (prefix !== "/") e = prefix + "/" + e
416 if (e.charAt(0) === "/" && !this.nomount) {
417 e = path.join(this.root, e)
420 this.matches[index] = this.matches[index] || {}
421 this.matches[index][e] = true
428 // now test all the remaining entries as stand-ins for that part
430 var l = entries.length
432 if (l === 0) return cb() // no matches possible
433 entries.forEach(function (e) {
434 var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))
435 this._process(p, depth + 1, index, function (er) {
437 if (er) return cb(errState = er)
438 if (--l === 0) return cb.call(this)
445 Glob.prototype._stat = function (f, cb) {
446 assert(this instanceof Glob)
448 if (f.charAt(0) === "/") {
449 abs = path.join(this.root, f)
450 } else if (this.changedCwd) {
451 abs = path.resolve(this.cwd, f)
453 if (this.debug) console.error('stat', [this.cwd, f, '=', abs])
454 if (f.length > this.maxLength) {
455 var er = new Error("Path name too long")
456 er.code = "ENAMETOOLONG"
458 return this._afterStat(f, abs, cb, er)
461 if (this.statCache.hasOwnProperty(f)) {
462 var exists = this.statCache[f]
463 , isDir = exists && (Array.isArray(exists) || exists === 2)
464 if (this.sync) return cb.call(this, !!exists, isDir)
465 return process.nextTick(cb.bind(this, !!exists, isDir))
471 stat = fs.statSync(abs)
475 this._afterStat(f, abs, cb, er, stat)
477 fs.stat(abs, this._afterStat.bind(this, f, abs, cb))
481 Glob.prototype._afterStat = function (f, abs, cb, er, stat) {
483 assert(this instanceof Glob)
487 exists = stat.isDirectory() ? 2 : 1
489 this.statCache[f] = this.statCache[f] || exists
490 cb.call(this, !!exists, exists === 2)
493 Glob.prototype._readdir = function (f, cb) {
494 assert(this instanceof Glob)
496 if (f.charAt(0) === "/") {
497 abs = path.join(this.root, f)
498 } else if (isAbsolute(f)) {
500 } else if (this.changedCwd) {
501 abs = path.resolve(this.cwd, f)
504 if (this.debug) console.error('readdir', [this.cwd, f, abs])
505 if (f.length > this.maxLength) {
506 var er = new Error("Path name too long")
507 er.code = "ENAMETOOLONG"
509 return this._afterReaddir(f, abs, cb, er)
512 if (this.statCache.hasOwnProperty(f)) {
513 var c = this.statCache[f]
514 if (Array.isArray(c)) {
515 if (this.sync) return cb.call(this, null, c)
516 return process.nextTick(cb.bind(this, null, c))
520 // either ENOENT or ENOTDIR
521 var code = c ? "ENOTDIR" : "ENOENT"
522 , er = new Error((c ? "Not a directory" : "Not found") + ": " + f)
525 if (this.debug) console.error(f, er)
526 if (this.sync) return cb.call(this, er)
527 return process.nextTick(cb.bind(this, er))
530 // at this point, c === 2, meaning it's a dir, but we haven't
531 // had to read it yet, or c === true, meaning it's *something*
532 // but we don't have any idea what. Need to read it, either way.
538 entries = fs.readdirSync(abs)
542 return this._afterReaddir(f, abs, cb, er, entries)
545 fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb))
548 Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {
549 assert(this instanceof Glob)
550 if (entries && !er) {
551 this.statCache[f] = entries
552 // if we haven't asked to stat everything for suresies, then just
553 // assume that everything in there exists, so we can avoid
554 // having to stat it a second time. This also gets us one step
555 // further into ELOOP territory.
556 if (!this.mark && !this.stat) {
557 entries.forEach(function (e) {
558 if (f === "/") e = f + e
560 this.statCache[e] = true
564 return cb.call(this, er, entries)
567 // now handle errors, and cache the information
568 if (er) switch (er.code) {
569 case "ENOTDIR": // totally normal. means it *does* exist.
570 this.statCache[f] = 1
571 return cb.call(this, er)
572 case "ENOENT": // not terribly unusual
576 this.statCache[f] = false
577 return cb.call(this, er)
578 default: // some unusual error. Treat as failure.
579 this.statCache[f] = false
580 if (this.strict) this.emit("error", er)
581 if (!this.silent) console.error("glob error", er)
582 return cb.call(this, er)
586 var isAbsolute = process.platform === "win32" ? absWin : absUnix
588 function absWin (p) {
589 if (absUnix(p)) return true
590 // pull off the device/UNC bit from a windows path.
591 // from node's lib/path.js
593 /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?/
594 , result = splitDeviceRe.exec(p)
595 , device = result[1] || ''
596 , isUnc = device && device.charAt(1) !== ':'
597 , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
602 function absUnix (p) {
603 return p.charAt(0) === "/" || p === ""