mocha-compat 3.6.0 → 3.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,788 @@
1
+ // Approach:
2
+ //
3
+ // 1. Get the minimatch set
4
+ // 2. For each pattern in the set, PROCESS(pattern, false)
5
+ // 3. Store matches per-set, then uniq them
6
+ //
7
+ // PROCESS(pattern, inGlobStar)
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
+ //
13
+ // If inGlobStar and PREFIX is symlink and points to dir
14
+ // set ENTRIES = []
15
+ // else readdir(PREFIX) as ENTRIES
16
+ // If fail, END
17
+ //
18
+ // with ENTRIES
19
+ // If pattern[n] is GLOBSTAR
20
+ // // handle the case where the globstar match is empty
21
+ // // by pruning it out, and testing the resulting pattern
22
+ // PROCESS(pattern[0..n] + pattern[n+1 .. $], false)
23
+ // // handle other cases.
24
+ // for ENTRY in ENTRIES (not dotfiles)
25
+ // // attach globstar + tail onto the entry
26
+ // // Mark that this entry is a globstar match
27
+ // PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true)
28
+ //
29
+ // else // not globstar
30
+ // for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
31
+ // Test ENTRY against pattern[n]
32
+ // If fails, continue
33
+ // If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
34
+ //
35
+ // Caveat:
36
+ // Cache all stats and readdirs results to minimize syscall. Since all
37
+ // we ever care about is existence and directory-ness, we can just keep
38
+ // `true` for files, and [children,...] for directories, or `false` for
39
+ // things that don't exist.
40
+
41
+ module.exports = glob
42
+
43
+ var rp = require('fs.realpath')
44
+ var minimatch = require('minimatch')
45
+ var inherits = require('inherits')
46
+ var EE = require('events').EventEmitter
47
+ var path = require('path')
48
+ var assert = require('assert')
49
+ var isAbsolute = require('is-absolute')
50
+ var globSync = require('./sync.js')
51
+ var common = require('./common.js')
52
+ var setopts = common.setopts
53
+ var ownProp = common.ownProp
54
+ var inflight = require('../inflight/inflight.js')
55
+ var childrenIgnored = common.childrenIgnored
56
+ var isIgnored = common.isIgnored
57
+
58
+ var once = require('once')
59
+
60
+ function glob (pattern, options, cb) {
61
+ if (typeof options === 'function') cb = options, options = {}
62
+ if (!options) options = {}
63
+
64
+ if (options.sync) {
65
+ if (cb)
66
+ throw new TypeError('callback provided to sync glob')
67
+ return globSync(pattern, options)
68
+ }
69
+
70
+ return new Glob(pattern, options, cb)
71
+ }
72
+
73
+ glob.sync = globSync
74
+ var GlobSync = glob.GlobSync = globSync.GlobSync
75
+
76
+ // old api surface
77
+ glob.glob = glob
78
+
79
+ function extend (origin, add) {
80
+ if (add === null || typeof add !== 'object') {
81
+ return origin
82
+ }
83
+
84
+ var keys = Object.keys(add)
85
+ var i = keys.length
86
+ while (i--) {
87
+ origin[keys[i]] = add[keys[i]]
88
+ }
89
+ return origin
90
+ }
91
+
92
+ glob.hasMagic = function (pattern, options_) {
93
+ var options = extend({}, options_)
94
+ options.noprocess = true
95
+
96
+ var g = new Glob(pattern, options)
97
+ var set = g.minimatch.set
98
+
99
+ if (!pattern)
100
+ return false
101
+
102
+ if (set.length > 1)
103
+ return true
104
+
105
+ for (var j = 0; j < set[0].length; j++) {
106
+ if (typeof set[0][j] !== 'string')
107
+ return true
108
+ }
109
+
110
+ return false
111
+ }
112
+
113
+ glob.Glob = Glob
114
+ inherits(Glob, EE)
115
+ function Glob (pattern, options, cb) {
116
+ if (typeof options === 'function') {
117
+ cb = options
118
+ options = null
119
+ }
120
+
121
+ if (options && options.sync) {
122
+ if (cb)
123
+ throw new TypeError('callback provided to sync glob')
124
+ return new GlobSync(pattern, options)
125
+ }
126
+
127
+ if (!(this instanceof Glob))
128
+ return new Glob(pattern, options, cb)
129
+
130
+ setopts(this, pattern, options)
131
+ this._didRealPath = false
132
+
133
+ // process each pattern in the minimatch set
134
+ var n = this.minimatch.set.length
135
+
136
+ // The matches are stored as {<filename>: true,...} so that
137
+ // duplicates are automagically pruned.
138
+ // Later, we do an Object.keys() on these.
139
+ // Keep them as a list so we can fill in when nonull is set.
140
+ this.matches = new Array(n)
141
+
142
+ if (typeof cb === 'function') {
143
+ cb = once(cb)
144
+ this.on('error', cb)
145
+ this.on('end', function (matches) {
146
+ cb(null, matches)
147
+ })
148
+ }
149
+
150
+ var self = this
151
+ this._processing = 0
152
+
153
+ this._emitQueue = []
154
+ this._processQueue = []
155
+ this.paused = false
156
+
157
+ if (this.noprocess)
158
+ return this
159
+
160
+ if (n === 0)
161
+ return done()
162
+
163
+ var sync = true
164
+ for (var i = 0; i < n; i ++) {
165
+ this._process(this.minimatch.set[i], i, false, done)
166
+ }
167
+ sync = false
168
+
169
+ function done () {
170
+ --self._processing
171
+ if (self._processing <= 0) {
172
+ if (sync) {
173
+ process.nextTick(function () {
174
+ self._finish()
175
+ })
176
+ } else {
177
+ self._finish()
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ Glob.prototype._finish = function () {
184
+ assert(this instanceof Glob)
185
+ if (this.aborted)
186
+ return
187
+
188
+ if (this.realpath && !this._didRealpath)
189
+ return this._realpath()
190
+
191
+ common.finish(this)
192
+ this.emit('end', this.found)
193
+ }
194
+
195
+ Glob.prototype._realpath = function () {
196
+ if (this._didRealpath)
197
+ return
198
+
199
+ this._didRealpath = true
200
+
201
+ var n = this.matches.length
202
+ if (n === 0)
203
+ return this._finish()
204
+
205
+ var self = this
206
+ for (var i = 0; i < this.matches.length; i++)
207
+ this._realpathSet(i, next)
208
+
209
+ function next () {
210
+ if (--n === 0)
211
+ self._finish()
212
+ }
213
+ }
214
+
215
+ Glob.prototype._realpathSet = function (index, cb) {
216
+ var matchset = this.matches[index]
217
+ if (!matchset)
218
+ return cb()
219
+
220
+ var found = Object.keys(matchset)
221
+ var self = this
222
+ var n = found.length
223
+
224
+ if (n === 0)
225
+ return cb()
226
+
227
+ var set = this.matches[index] = Object.create(null)
228
+ found.forEach(function (p, i) {
229
+ // If there's a problem with the stat, then it means that
230
+ // one or more of the links in the realpath couldn't be
231
+ // resolved. just return the abs value in that case.
232
+ p = self._makeAbs(p)
233
+ rp.realpath(p, self.realpathCache, function (er, real) {
234
+ if (!er)
235
+ set[real] = true
236
+ else if (er.syscall === 'stat')
237
+ set[p] = true
238
+ else
239
+ self.emit('error', er) // srsly wtf right here
240
+
241
+ if (--n === 0) {
242
+ self.matches[index] = set
243
+ cb()
244
+ }
245
+ })
246
+ })
247
+ }
248
+
249
+ Glob.prototype._mark = function (p) {
250
+ return common.mark(this, p)
251
+ }
252
+
253
+ Glob.prototype._makeAbs = function (f) {
254
+ return common.makeAbs(this, f)
255
+ }
256
+
257
+ Glob.prototype.abort = function () {
258
+ this.aborted = true
259
+ this.emit('abort')
260
+ }
261
+
262
+ Glob.prototype.pause = function () {
263
+ if (!this.paused) {
264
+ this.paused = true
265
+ this.emit('pause')
266
+ }
267
+ }
268
+
269
+ Glob.prototype.resume = function () {
270
+ if (this.paused) {
271
+ this.emit('resume')
272
+ this.paused = false
273
+ if (this._emitQueue.length) {
274
+ var eq = this._emitQueue.slice(0)
275
+ this._emitQueue.length = 0
276
+ for (var i = 0; i < eq.length; i ++) {
277
+ var e = eq[i]
278
+ this._emitMatch(e[0], e[1])
279
+ }
280
+ }
281
+ if (this._processQueue.length) {
282
+ var pq = this._processQueue.slice(0)
283
+ this._processQueue.length = 0
284
+ for (var i = 0; i < pq.length; i ++) {
285
+ var p = pq[i]
286
+ this._processing--
287
+ this._process(p[0], p[1], p[2], p[3])
288
+ }
289
+ }
290
+ }
291
+ }
292
+
293
+ Glob.prototype._process = function (pattern, index, inGlobStar, cb) {
294
+ assert(this instanceof Glob)
295
+ assert(typeof cb === 'function')
296
+
297
+ if (this.aborted)
298
+ return
299
+
300
+ this._processing++
301
+ if (this.paused) {
302
+ this._processQueue.push([pattern, index, inGlobStar, cb])
303
+ return
304
+ }
305
+
306
+ //console.error('PROCESS %d', this._processing, pattern)
307
+
308
+ // Get the first [n] parts of pattern that are all strings.
309
+ var n = 0
310
+ while (typeof pattern[n] === 'string') {
311
+ n ++
312
+ }
313
+ // now n is the index of the first one that is *not* a string.
314
+
315
+ // see if there's anything else
316
+ var prefix
317
+ switch (n) {
318
+ // if not, then this is rather simple
319
+ case pattern.length:
320
+ this._processSimple(pattern.join('/'), index, cb)
321
+ return
322
+
323
+ case 0:
324
+ // pattern *starts* with some non-trivial item.
325
+ // going to readdir(cwd), but not include the prefix in matches.
326
+ prefix = null
327
+ break
328
+
329
+ default:
330
+ // pattern has some string bits in the front.
331
+ // whatever it starts with, whether that's 'absolute' like /foo/bar,
332
+ // or 'relative' like '../baz'
333
+ prefix = pattern.slice(0, n).join('/')
334
+ break
335
+ }
336
+
337
+ var remain = pattern.slice(n)
338
+
339
+ // get the list of entries.
340
+ var read
341
+ if (prefix === null)
342
+ read = '.'
343
+ else if (isAbsolute(prefix) ||
344
+ isAbsolute(pattern.map(function (p) {
345
+ return typeof p === 'string' ? p : '[*]'
346
+ }).join('/'))) {
347
+ if (!prefix || !isAbsolute(prefix))
348
+ prefix = '/' + prefix
349
+ read = prefix
350
+ } else
351
+ read = prefix
352
+
353
+ var abs = this._makeAbs(read)
354
+
355
+ //if ignored, skip _processing
356
+ if (childrenIgnored(this, read))
357
+ return cb()
358
+
359
+ var isGlobStar = remain[0] === minimatch.GLOBSTAR
360
+ if (isGlobStar)
361
+ this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)
362
+ else
363
+ this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)
364
+ }
365
+
366
+ Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {
367
+ var self = this
368
+ this._readdir(abs, inGlobStar, function (er, entries) {
369
+ return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
370
+ })
371
+ }
372
+
373
+ Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
374
+
375
+ // if the abs isn't a dir, then nothing can match!
376
+ if (!entries)
377
+ return cb()
378
+
379
+ // It will only match dot entries if it starts with a dot, or if
380
+ // dot is set. Stuff like @(.foo|.bar) isn't allowed.
381
+ var pn = remain[0]
382
+ var negate = !!this.minimatch.negate
383
+ var rawGlob = pn._glob
384
+ var dotOk = this.dot || rawGlob.charAt(0) === '.'
385
+
386
+ var matchedEntries = []
387
+ for (var i = 0; i < entries.length; i++) {
388
+ var e = entries[i]
389
+ if (e.charAt(0) !== '.' || dotOk) {
390
+ var m
391
+ if (negate && !prefix) {
392
+ m = !e.match(pn)
393
+ } else {
394
+ m = e.match(pn)
395
+ }
396
+ if (m)
397
+ matchedEntries.push(e)
398
+ }
399
+ }
400
+
401
+ //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)
402
+
403
+ var len = matchedEntries.length
404
+ // If there are no matched entries, then nothing matches.
405
+ if (len === 0)
406
+ return cb()
407
+
408
+ // if this is the last remaining pattern bit, then no need for
409
+ // an additional stat *unless* the user has specified mark or
410
+ // stat explicitly. We know they exist, since readdir returned
411
+ // them.
412
+
413
+ if (remain.length === 1 && !this.mark && !this.stat) {
414
+ if (!this.matches[index])
415
+ this.matches[index] = Object.create(null)
416
+
417
+ for (var i = 0; i < len; i ++) {
418
+ var e = matchedEntries[i]
419
+ if (prefix) {
420
+ if (prefix !== '/')
421
+ e = prefix + '/' + e
422
+ else
423
+ e = prefix + e
424
+ }
425
+
426
+ if (e.charAt(0) === '/' && !this.nomount) {
427
+ e = path.join(this.root, e)
428
+ }
429
+ this._emitMatch(index, e)
430
+ }
431
+ // This was the last one, and no stats were needed
432
+ return cb()
433
+ }
434
+
435
+ // now test all matched entries as stand-ins for that part
436
+ // of the pattern.
437
+ remain.shift()
438
+ for (var i = 0; i < len; i ++) {
439
+ var e = matchedEntries[i]
440
+ var newPattern
441
+ if (prefix) {
442
+ if (prefix !== '/')
443
+ e = prefix + '/' + e
444
+ else
445
+ e = prefix + e
446
+ }
447
+ this._process([e].concat(remain), index, inGlobStar, cb)
448
+ }
449
+ cb()
450
+ }
451
+
452
+ Glob.prototype._emitMatch = function (index, e) {
453
+ if (this.aborted)
454
+ return
455
+
456
+ if (isIgnored(this, e))
457
+ return
458
+
459
+ if (this.paused) {
460
+ this._emitQueue.push([index, e])
461
+ return
462
+ }
463
+
464
+ var abs = isAbsolute(e) ? e : this._makeAbs(e)
465
+
466
+ if (this.mark)
467
+ e = this._mark(e)
468
+
469
+ if (this.absolute)
470
+ e = abs
471
+
472
+ if (this.matches[index][e])
473
+ return
474
+
475
+ if (this.nodir) {
476
+ var c = this.cache[abs]
477
+ if (c === 'DIR' || Array.isArray(c))
478
+ return
479
+ }
480
+
481
+ this.matches[index][e] = true
482
+
483
+ var st = this.statCache[abs]
484
+ if (st)
485
+ this.emit('stat', e, st)
486
+
487
+ this.emit('match', e)
488
+ }
489
+
490
+ Glob.prototype._readdirInGlobStar = function (abs, cb) {
491
+ if (this.aborted)
492
+ return
493
+
494
+ // follow all symlinked directories forever
495
+ // just proceed as if this is a non-globstar situation
496
+ if (this.follow)
497
+ return this._readdir(abs, false, cb)
498
+
499
+ var lstatkey = 'lstat\0' + abs
500
+ var self = this
501
+ var lstatcb = inflight(lstatkey, lstatcb_)
502
+
503
+ if (lstatcb)
504
+ self.fs.lstat(abs, lstatcb)
505
+
506
+ function lstatcb_ (er, lstat) {
507
+ if (er && er.code === 'ENOENT')
508
+ return cb()
509
+
510
+ var isSym = lstat && lstat.isSymbolicLink()
511
+ self.symlinks[abs] = isSym
512
+
513
+ // If it's not a symlink or a dir, then it's definitely a regular file.
514
+ // don't bother doing a readdir in that case.
515
+ if (!isSym && lstat && !lstat.isDirectory()) {
516
+ self.cache[abs] = 'FILE'
517
+ cb()
518
+ } else
519
+ self._readdir(abs, false, cb)
520
+ }
521
+ }
522
+
523
+ Glob.prototype._readdir = function (abs, inGlobStar, cb) {
524
+ if (this.aborted)
525
+ return
526
+
527
+ cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)
528
+ if (!cb)
529
+ return
530
+
531
+ //console.error('RD %j %j', +inGlobStar, abs)
532
+ if (inGlobStar && !ownProp(this.symlinks, abs))
533
+ return this._readdirInGlobStar(abs, cb)
534
+
535
+ if (ownProp(this.cache, abs)) {
536
+ var c = this.cache[abs]
537
+ if (!c || c === 'FILE')
538
+ return cb()
539
+
540
+ if (Array.isArray(c))
541
+ return cb(null, c)
542
+ }
543
+
544
+ var self = this
545
+ self.fs.readdir(abs, readdirCb(this, abs, cb))
546
+ }
547
+
548
+ function readdirCb (self, abs, cb) {
549
+ return function (er, entries) {
550
+ if (er)
551
+ self._readdirError(abs, er, cb)
552
+ else
553
+ self._readdirEntries(abs, entries, cb)
554
+ }
555
+ }
556
+
557
+ Glob.prototype._readdirEntries = function (abs, entries, cb) {
558
+ if (this.aborted)
559
+ return
560
+
561
+ // if we haven't asked to stat everything, then just
562
+ // assume that everything in there exists, so we can avoid
563
+ // having to stat it a second time.
564
+ if (!this.mark && !this.stat) {
565
+ for (var i = 0; i < entries.length; i ++) {
566
+ var e = entries[i]
567
+ if (abs === '/')
568
+ e = abs + e
569
+ else
570
+ e = abs + '/' + e
571
+ this.cache[e] = true
572
+ }
573
+ }
574
+
575
+ this.cache[abs] = entries
576
+ return cb(null, entries)
577
+ }
578
+
579
+ Glob.prototype._readdirError = function (f, er, cb) {
580
+ if (this.aborted)
581
+ return
582
+
583
+ // handle errors, and cache the information
584
+ switch (er.code) {
585
+ case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
586
+ case 'ENOTDIR': // totally normal. means it *does* exist.
587
+ var abs = this._makeAbs(f)
588
+ this.cache[abs] = 'FILE'
589
+ if (abs === this.cwdAbs) {
590
+ var error = new Error(er.code + ' invalid cwd ' + this.cwd)
591
+ error.path = this.cwd
592
+ error.code = er.code
593
+ this.emit('error', error)
594
+ this.abort()
595
+ }
596
+ break
597
+
598
+ case 'ENOENT': // not terribly unusual
599
+ case 'ELOOP':
600
+ case 'ENAMETOOLONG':
601
+ case 'UNKNOWN':
602
+ this.cache[this._makeAbs(f)] = false
603
+ break
604
+
605
+ default: // some unusual error. Treat as failure.
606
+ this.cache[this._makeAbs(f)] = false
607
+ if (this.strict) {
608
+ this.emit('error', er)
609
+ // If the error is handled, then we abort
610
+ // if not, we threw out of here
611
+ this.abort()
612
+ }
613
+ if (!this.silent)
614
+ console.error('glob error', er)
615
+ break
616
+ }
617
+
618
+ return cb()
619
+ }
620
+
621
+ Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {
622
+ var self = this
623
+ this._readdir(abs, inGlobStar, function (er, entries) {
624
+ self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
625
+ })
626
+ }
627
+
628
+
629
+ Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
630
+ //console.error('pgs2', prefix, remain[0], entries)
631
+
632
+ // no entries means not a dir, so it can never have matches
633
+ // foo.txt/** doesn't match foo.txt
634
+ if (!entries)
635
+ return cb()
636
+
637
+ // test without the globstar, and with every child both below
638
+ // and replacing the globstar.
639
+ var remainWithoutGlobStar = remain.slice(1)
640
+ var gspref = prefix ? [ prefix ] : []
641
+ var noGlobStar = gspref.concat(remainWithoutGlobStar)
642
+
643
+ // the noGlobStar pattern exits the inGlobStar state
644
+ this._process(noGlobStar, index, false, cb)
645
+
646
+ var isSym = this.symlinks[abs]
647
+ var len = entries.length
648
+
649
+ // If it's a symlink, and we're in a globstar, then stop
650
+ if (isSym && inGlobStar)
651
+ return cb()
652
+
653
+ for (var i = 0; i < len; i++) {
654
+ var e = entries[i]
655
+ if (e.charAt(0) === '.' && !this.dot)
656
+ continue
657
+
658
+ // these two cases enter the inGlobStar state
659
+ var instead = gspref.concat(entries[i], remainWithoutGlobStar)
660
+ this._process(instead, index, true, cb)
661
+
662
+ var below = gspref.concat(entries[i], remain)
663
+ this._process(below, index, true, cb)
664
+ }
665
+
666
+ cb()
667
+ }
668
+
669
+ Glob.prototype._processSimple = function (prefix, index, cb) {
670
+ // XXX review this. Shouldn't it be doing the mounting etc
671
+ // before doing stat? kinda weird?
672
+ var self = this
673
+ this._stat(prefix, function (er, exists) {
674
+ self._processSimple2(prefix, index, er, exists, cb)
675
+ })
676
+ }
677
+ Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {
678
+
679
+ //console.error('ps2', prefix, exists)
680
+
681
+ if (!this.matches[index])
682
+ this.matches[index] = Object.create(null)
683
+
684
+ // If it doesn't exist, then just mark the lack of results
685
+ if (!exists)
686
+ return cb()
687
+
688
+ if (prefix && isAbsolute(prefix) && !this.nomount) {
689
+ var trail = /[\/\\]$/.test(prefix)
690
+ if (prefix.charAt(0) === '/') {
691
+ prefix = path.join(this.root, prefix)
692
+ } else {
693
+ prefix = path.resolve(this.root, prefix)
694
+ if (trail)
695
+ prefix += '/'
696
+ }
697
+ }
698
+
699
+ if (process.platform === 'win32')
700
+ prefix = prefix.replace(/\\/g, '/')
701
+
702
+ // Mark this as a match
703
+ this._emitMatch(index, prefix)
704
+ cb()
705
+ }
706
+
707
+ // Returns either 'DIR', 'FILE', or false
708
+ Glob.prototype._stat = function (f, cb) {
709
+ var abs = this._makeAbs(f)
710
+ var needDir = f.slice(-1) === '/'
711
+
712
+ if (f.length > this.maxLength)
713
+ return cb()
714
+
715
+ if (!this.stat && ownProp(this.cache, abs)) {
716
+ var c = this.cache[abs]
717
+
718
+ if (Array.isArray(c))
719
+ c = 'DIR'
720
+
721
+ // It exists, but maybe not how we need it
722
+ if (!needDir || c === 'DIR')
723
+ return cb(null, c)
724
+
725
+ if (needDir && c === 'FILE')
726
+ return cb()
727
+
728
+ // otherwise we have to stat, because maybe c=true
729
+ // if we know it exists, but not what it is.
730
+ }
731
+
732
+ var exists
733
+ var stat = this.statCache[abs]
734
+ if (stat !== undefined) {
735
+ if (stat === false)
736
+ return cb(null, stat)
737
+ else {
738
+ var type = stat.isDirectory() ? 'DIR' : 'FILE'
739
+ if (needDir && type === 'FILE')
740
+ return cb()
741
+ else
742
+ return cb(null, type, stat)
743
+ }
744
+ }
745
+
746
+ var self = this
747
+ var statcb = inflight('stat\0' + abs, lstatcb_)
748
+ if (statcb)
749
+ self.fs.lstat(abs, statcb)
750
+
751
+ function lstatcb_ (er, lstat) {
752
+ if (lstat && lstat.isSymbolicLink()) {
753
+ // If it's a symlink, then treat it as the target, unless
754
+ // the target does not exist, then treat it as a file.
755
+ return self.fs.stat(abs, function (er, stat) {
756
+ if (er)
757
+ self._stat2(f, abs, null, lstat, cb)
758
+ else
759
+ self._stat2(f, abs, er, stat, cb)
760
+ })
761
+ } else {
762
+ self._stat2(f, abs, er, lstat, cb)
763
+ }
764
+ }
765
+ }
766
+
767
+ Glob.prototype._stat2 = function (f, abs, er, stat, cb) {
768
+ if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
769
+ this.statCache[abs] = false
770
+ return cb()
771
+ }
772
+
773
+ var needDir = f.slice(-1) === '/'
774
+ this.statCache[abs] = stat
775
+
776
+ if (abs.slice(-1) === '/' && stat && !stat.isDirectory())
777
+ return cb(null, false, stat)
778
+
779
+ var c = true
780
+ if (stat)
781
+ c = stat.isDirectory() ? 'DIR' : 'FILE'
782
+ this.cache[abs] = this.cache[abs] || c
783
+
784
+ if (needDir && c === 'FILE')
785
+ return cb()
786
+
787
+ return cb(null, c, stat)
788
+ }