Initial commit
[yaffs-website] / node_modules / globule / node_modules / glob / glob.js
1 // Approach:
2 //
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
6 //
7 // PROCESS(pattern)
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
13 //   If fails, END
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 .. $])
22 //
23 //   else // not globstar
24 //     for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
25 //       Test ENTRY against pattern[n]
26 //       If fails, continue
27 //       If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
28 //
29 // Caveat:
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.
34
35
36
37 module.exports = glob
38
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")
45 , isDir = {}
46 , assert = require("assert").ok
47
48 function glob (pattern, options, cb) {
49   if (typeof options === "function") cb = options, options = {}
50   if (!options) options = {}
51
52   if (typeof options === "number") {
53     deprecated()
54     return
55   }
56
57   var g = new Glob(pattern, options, cb)
58   return g.sync ? g.found : g
59 }
60
61 glob.fnmatch = deprecated
62
63 function deprecated () {
64   throw new Error("glob's interface has changed. Please see the docs.")
65 }
66
67 glob.sync = globSync
68 function globSync (pattern, options) {
69   if (typeof options === "number") {
70     deprecated()
71     return
72   }
73
74   options = options || {}
75   options.sync = true
76   return glob(pattern, options)
77 }
78
79
80 glob.Glob = Glob
81 inherits(Glob, EE)
82 function Glob (pattern, options, cb) {
83   if (!(this instanceof Glob)) {
84     return new Glob(pattern, options, cb)
85   }
86
87   if (typeof cb === "function") {
88     this.on("error", cb)
89     this.on("end", function (matches) {
90       cb(null, matches)
91     })
92   }
93
94   options = options || {}
95
96   this.EOF = {}
97   this._emitQueue = []
98
99   this.maxDepth = options.maxDepth || 1000
100   this.maxLength = options.maxLength || Infinity
101   this.statCache = options.statCache || {}
102
103   this.changedCwd = false
104   var cwd = process.cwd()
105   if (!options.hasOwnProperty("cwd")) this.cwd = cwd
106   else {
107     this.cwd = options.cwd
108     this.changedCwd = path.resolve(options.cwd) !== cwd
109   }
110
111   this.root = options.root || path.resolve(this.cwd, "/")
112   this.root = path.resolve(this.root)
113   if (process.platform === "win32")
114     this.root = this.root.replace(/\\/g, "/")
115
116   this.nomount = !!options.nomount
117
118   if (!pattern) {
119     throw new Error("must provide pattern")
120   }
121
122   // base-matching: just use globstar for that.
123   if (options.matchBase && -1 === pattern.indexOf("/")) {
124     if (options.noglobstar) {
125       throw new Error("base matching requires globstar")
126     }
127     pattern = "**/" + pattern
128   }
129
130   this.strict = options.strict !== false
131   this.dot = !!options.dot
132   this.mark = !!options.mark
133   this.sync = !!options.sync
134   this.nounique = !!options.nounique
135   this.nonull = !!options.nonull
136   this.nosort = !!options.nosort
137   this.nocase = !!options.nocase
138   this.stat = !!options.stat
139
140   this.debug = !!options.debug || !!options.globDebug
141   if (this.debug)
142     this.log = console.error
143
144   this.silent = !!options.silent
145
146   var mm = this.minimatch = new Minimatch(pattern, options)
147   this.options = mm.options
148   pattern = this.pattern = mm.pattern
149
150   this.error = null
151   this.aborted = false
152
153   EE.call(this)
154
155   // process each pattern in the minimatch set
156   var n = this.minimatch.set.length
157
158   // The matches are stored as {<filename>: true,...} so that
159   // duplicates are automagically pruned.
160   // Later, we do an Object.keys() on these.
161   // Keep them as a list so we can fill in when nonull is set.
162   this.matches = new Array(n)
163
164   this.minimatch.set.forEach(iterator.bind(this))
165   function iterator (pattern, i, set) {
166     this._process(pattern, 0, i, function (er) {
167       if (er) this.emit("error", er)
168       if (-- n <= 0) this._finish()
169     })
170   }
171 }
172
173 Glob.prototype.log = function () {}
174
175 Glob.prototype._finish = function () {
176   assert(this instanceof Glob)
177
178   var nou = this.nounique
179   , all = nou ? [] : {}
180
181   for (var i = 0, l = this.matches.length; i < l; i ++) {
182     var matches = this.matches[i]
183     this.log("matches[%d] =", i, matches)
184     // do like the shell, and spit out the literal glob
185     if (!matches) {
186       if (this.nonull) {
187         var literal = this.minimatch.globSet[i]
188         if (nou) all.push(literal)
189         else all[literal] = true
190       }
191     } else {
192       // had matches
193       var m = Object.keys(matches)
194       if (nou) all.push.apply(all, m)
195       else m.forEach(function (m) {
196         all[m] = true
197       })
198     }
199   }
200
201   if (!nou) all = Object.keys(all)
202
203   if (!this.nosort) {
204     all = all.sort(this.nocase ? alphasorti : alphasort)
205   }
206
207   if (this.mark) {
208     // at *some* point we statted all of these
209     all = all.map(function (m) {
210       var sc = this.statCache[m]
211       if (!sc)
212         return m
213       var isDir = (Array.isArray(sc) || sc === 2)
214       if (isDir && m.slice(-1) !== "/") {
215         return m + "/"
216       }
217       if (!isDir && m.slice(-1) === "/") {
218         return m.replace(/\/+$/, "")
219       }
220       return m
221     }, this)
222   }
223
224   this.log("emitting end", all)
225
226   this.EOF = this.found = all
227   this.emitMatch(this.EOF)
228 }
229
230 function alphasorti (a, b) {
231   a = a.toLowerCase()
232   b = b.toLowerCase()
233   return alphasort(a, b)
234 }
235
236 function alphasort (a, b) {
237   return a > b ? 1 : a < b ? -1 : 0
238 }
239
240 Glob.prototype.abort = function () {
241   this.aborted = true
242   this.emit("abort")
243 }
244
245 Glob.prototype.pause = function () {
246   if (this.paused) return
247   if (this.sync)
248     this.emit("error", new Error("Can't pause/resume sync glob"))
249   this.paused = true
250   this.emit("pause")
251 }
252
253 Glob.prototype.resume = function () {
254   if (!this.paused) return
255   if (this.sync)
256     this.emit("error", new Error("Can't pause/resume sync glob"))
257   this.paused = false
258   this.emit("resume")
259   this._processEmitQueue()
260   //process.nextTick(this.emit.bind(this, "resume"))
261 }
262
263 Glob.prototype.emitMatch = function (m) {
264   this._emitQueue.push(m)
265   this._processEmitQueue()
266 }
267
268 Glob.prototype._processEmitQueue = function (m) {
269   while (!this._processingEmitQueue &&
270          !this.paused) {
271     this._processingEmitQueue = true
272     var m = this._emitQueue.shift()
273     if (!m) {
274       this._processingEmitQueue = false
275       break
276     }
277
278     this.log('emit!', m === this.EOF ? "end" : "match")
279
280     this.emit(m === this.EOF ? "end" : "match", m)
281     this._processingEmitQueue = false
282   }
283 }
284
285 Glob.prototype._process = function (pattern, depth, index, cb_) {
286   assert(this instanceof Glob)
287
288   var cb = function cb (er, res) {
289     assert(this instanceof Glob)
290     if (this.paused) {
291       if (!this._processQueue) {
292         this._processQueue = []
293         this.once("resume", function () {
294           var q = this._processQueue
295           this._processQueue = null
296           q.forEach(function (cb) { cb() })
297         })
298       }
299       this._processQueue.push(cb_.bind(this, er, res))
300     } else {
301       cb_.call(this, er, res)
302     }
303   }.bind(this)
304
305   if (this.aborted) return cb()
306
307   if (depth > this.maxDepth) return cb()
308
309   // Get the first [n] parts of pattern that are all strings.
310   var n = 0
311   while (typeof pattern[n] === "string") {
312     n ++
313   }
314   // now n is the index of the first one that is *not* a string.
315
316   // see if there's anything else
317   var prefix
318   switch (n) {
319     // if not, then this is rather simple
320     case pattern.length:
321       prefix = pattern.join("/")
322       this._stat(prefix, function (exists, isDir) {
323         // either it's there, or it isn't.
324         // nothing more to do, either way.
325         if (exists) {
326           if (prefix && isAbsolute(prefix) && !this.nomount) {
327             if (prefix.charAt(0) === "/") {
328               prefix = path.join(this.root, prefix)
329             } else {
330               prefix = path.resolve(this.root, prefix)
331             }
332           }
333
334           if (process.platform === "win32")
335             prefix = prefix.replace(/\\/g, "/")
336
337           this.matches[index] = this.matches[index] || {}
338           this.matches[index][prefix] = true
339           this.emitMatch(prefix)
340         }
341         return cb()
342       })
343       return
344
345     case 0:
346       // pattern *starts* with some non-trivial item.
347       // going to readdir(cwd), but not include the prefix in matches.
348       prefix = null
349       break
350
351     default:
352       // pattern has some string bits in the front.
353       // whatever it starts with, whether that's "absolute" like /foo/bar,
354       // or "relative" like "../baz"
355       prefix = pattern.slice(0, n)
356       prefix = prefix.join("/")
357       break
358   }
359
360   // get the list of entries.
361   var read
362   if (prefix === null) read = "."
363   else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {
364     if (!prefix || !isAbsolute(prefix)) {
365       prefix = path.join("/", prefix)
366     }
367     read = prefix = path.resolve(prefix)
368
369     // if (process.platform === "win32")
370     //   read = prefix = prefix.replace(/^[a-zA-Z]:|\\/g, "/")
371
372     this.log('absolute: ', prefix, this.root, pattern, read)
373   } else {
374     read = prefix
375   }
376
377   this.log('readdir(%j)', read, this.cwd, this.root)
378
379   return this._readdir(read, function (er, entries) {
380     if (er) {
381       // not a directory!
382       // this means that, whatever else comes after this, it can never match
383       return cb()
384     }
385
386     // globstar is special
387     if (pattern[n] === minimatch.GLOBSTAR) {
388       // test without the globstar, and with every child both below
389       // and replacing the globstar.
390       var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ]
391       entries.forEach(function (e) {
392         if (e.charAt(0) === "." && !this.dot) return
393         // instead of the globstar
394         s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)))
395         // below the globstar
396         s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))
397       }, this)
398
399       // now asyncForEach over this
400       var l = s.length
401       , errState = null
402       s.forEach(function (gsPattern) {
403         this._process(gsPattern, depth + 1, index, function (er) {
404           if (errState) return
405           if (er) return cb(errState = er)
406           if (--l <= 0) return cb()
407         })
408       }, this)
409
410       return
411     }
412
413     // not a globstar
414     // It will only match dot entries if it starts with a dot, or if
415     // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
416     var pn = pattern[n]
417     if (typeof pn === "string") {
418       var found = entries.indexOf(pn) !== -1
419       entries = found ? entries[pn] : []
420     } else {
421       var rawGlob = pattern[n]._glob
422       , dotOk = this.dot || rawGlob.charAt(0) === "."
423
424       entries = entries.filter(function (e) {
425         return (e.charAt(0) !== "." || dotOk) &&
426                (typeof pattern[n] === "string" && e === pattern[n] ||
427                 e.match(pattern[n]))
428       })
429     }
430
431     // If n === pattern.length - 1, then there's no need for the extra stat
432     // *unless* the user has specified "mark" or "stat" explicitly.
433     // We know that they exist, since the readdir returned them.
434     if (n === pattern.length - 1 &&
435         !this.mark &&
436         !this.stat) {
437       entries.forEach(function (e) {
438         if (prefix) {
439           if (prefix !== "/") e = prefix + "/" + e
440           else e = prefix + e
441         }
442         if (e.charAt(0) === "/" && !this.nomount) {
443           e = path.join(this.root, e)
444         }
445
446         if (process.platform === "win32")
447           e = e.replace(/\\/g, "/")
448
449         this.matches[index] = this.matches[index] || {}
450         this.matches[index][e] = true
451         this.emitMatch(e)
452       }, this)
453       return cb.call(this)
454     }
455
456
457     // now test all the remaining entries as stand-ins for that part
458     // of the pattern.
459     var l = entries.length
460     , errState = null
461     if (l === 0) return cb() // no matches possible
462     entries.forEach(function (e) {
463       var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))
464       this._process(p, depth + 1, index, function (er) {
465         if (errState) return
466         if (er) return cb(errState = er)
467         if (--l === 0) return cb.call(this)
468       })
469     }, this)
470   })
471
472 }
473
474 Glob.prototype._stat = function (f, cb) {
475   assert(this instanceof Glob)
476   var abs = f
477   if (f.charAt(0) === "/") {
478     abs = path.join(this.root, f)
479   } else if (this.changedCwd) {
480     abs = path.resolve(this.cwd, f)
481   }
482   this.log('stat', [this.cwd, f, '=', abs])
483   if (f.length > this.maxLength) {
484     var er = new Error("Path name too long")
485     er.code = "ENAMETOOLONG"
486     er.path = f
487     return this._afterStat(f, abs, cb, er)
488   }
489
490   if (this.statCache.hasOwnProperty(f)) {
491     var exists = this.statCache[f]
492     , isDir = exists && (Array.isArray(exists) || exists === 2)
493     if (this.sync) return cb.call(this, !!exists, isDir)
494     return process.nextTick(cb.bind(this, !!exists, isDir))
495   }
496
497   if (this.sync) {
498     var er, stat
499     try {
500       stat = fs.statSync(abs)
501     } catch (e) {
502       er = e
503     }
504     this._afterStat(f, abs, cb, er, stat)
505   } else {
506     fs.stat(abs, this._afterStat.bind(this, f, abs, cb))
507   }
508 }
509
510 Glob.prototype._afterStat = function (f, abs, cb, er, stat) {
511   var exists
512   assert(this instanceof Glob)
513
514   if (abs.slice(-1) === "/" && stat && !stat.isDirectory()) {
515     this.log("should be ENOTDIR, fake it")
516
517     er = new Error("ENOTDIR, not a directory '" + abs + "'")
518     er.path = abs
519     er.code = "ENOTDIR"
520     stat = null
521   }
522
523   if (er || !stat) {
524     exists = false
525   } else {
526     exists = stat.isDirectory() ? 2 : 1
527   }
528   this.statCache[f] = this.statCache[f] || exists
529   cb.call(this, !!exists, exists === 2)
530 }
531
532 Glob.prototype._readdir = function (f, cb) {
533   assert(this instanceof Glob)
534   var abs = f
535   if (f.charAt(0) === "/") {
536     abs = path.join(this.root, f)
537   } else if (isAbsolute(f)) {
538     abs = f
539   } else if (this.changedCwd) {
540     abs = path.resolve(this.cwd, f)
541   }
542
543   this.log('readdir', [this.cwd, f, abs])
544   if (f.length > this.maxLength) {
545     var er = new Error("Path name too long")
546     er.code = "ENAMETOOLONG"
547     er.path = f
548     return this._afterReaddir(f, abs, cb, er)
549   }
550
551   if (this.statCache.hasOwnProperty(f)) {
552     var c = this.statCache[f]
553     if (Array.isArray(c)) {
554       if (this.sync) return cb.call(this, null, c)
555       return process.nextTick(cb.bind(this, null, c))
556     }
557
558     if (!c || c === 1) {
559       // either ENOENT or ENOTDIR
560       var code = c ? "ENOTDIR" : "ENOENT"
561       , er = new Error((c ? "Not a directory" : "Not found") + ": " + f)
562       er.path = f
563       er.code = code
564       this.log(f, er)
565       if (this.sync) return cb.call(this, er)
566       return process.nextTick(cb.bind(this, er))
567     }
568
569     // at this point, c === 2, meaning it's a dir, but we haven't
570     // had to read it yet, or c === true, meaning it's *something*
571     // but we don't have any idea what.  Need to read it, either way.
572   }
573
574   if (this.sync) {
575     var er, entries
576     try {
577       entries = fs.readdirSync(abs)
578     } catch (e) {
579       er = e
580     }
581     return this._afterReaddir(f, abs, cb, er, entries)
582   }
583
584   fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb))
585 }
586
587 Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {
588   assert(this instanceof Glob)
589   if (entries && !er) {
590     this.statCache[f] = entries
591     // if we haven't asked to stat everything for suresies, then just
592     // assume that everything in there exists, so we can avoid
593     // having to stat it a second time.  This also gets us one step
594     // further into ELOOP territory.
595     if (!this.mark && !this.stat) {
596       entries.forEach(function (e) {
597         if (f === "/") e = f + e
598         else e = f + "/" + e
599         this.statCache[e] = true
600       }, this)
601     }
602
603     return cb.call(this, er, entries)
604   }
605
606   // now handle errors, and cache the information
607   if (er) switch (er.code) {
608     case "ENOTDIR": // totally normal. means it *does* exist.
609       this.statCache[f] = 1
610       return cb.call(this, er)
611     case "ENOENT": // not terribly unusual
612     case "ELOOP":
613     case "ENAMETOOLONG":
614     case "UNKNOWN":
615       this.statCache[f] = false
616       return cb.call(this, er)
617     default: // some unusual error.  Treat as failure.
618       this.statCache[f] = false
619       if (this.strict) this.emit("error", er)
620       if (!this.silent) console.error("glob error", er)
621       return cb.call(this, er)
622   }
623 }
624
625 var isAbsolute = process.platform === "win32" ? absWin : absUnix
626
627 function absWin (p) {
628   if (absUnix(p)) return true
629   // pull off the device/UNC bit from a windows path.
630   // from node's lib/path.js
631   var splitDeviceRe =
632       /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/
633     , result = splitDeviceRe.exec(p)
634     , device = result[1] || ''
635     , isUnc = device && device.charAt(1) !== ':'
636     , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
637
638   return isAbsolute
639 }
640
641 function absUnix (p) {
642   return p.charAt(0) === "/" || p === ""
643 }