프로젝트

일반

사용자정보

통계
| 개정판:

root / HServer / 00.Server / 00.Program / node_modules / glob / glob.js

이력 | 보기 | 이력해설 | 다운로드 (18.4 KB)

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 fs = require('fs')
44
var minimatch = require('minimatch')
45
var Minimatch = minimatch.Minimatch
46
var inherits = require('inherits')
47
var EE = require('events').EventEmitter
48
var path = require('path')
49
var assert = require('assert')
50
var isAbsolute = require('path-is-absolute')
51
var globSync = require('./sync.js')
52
var common = require('./common.js')
53
var alphasort = common.alphasort
54
var alphasorti = common.alphasorti
55
var setopts = common.setopts
56
var ownProp = common.ownProp
57
var inflight = require('inflight')
58
var util = require('util')
59
var childrenIgnored = common.childrenIgnored
60
var isIgnored = common.isIgnored
61

    
62
var once = require('once')
63

    
64
function glob (pattern, options, cb) {
65
  if (typeof options === 'function') cb = options, options = {}
66
  if (!options) options = {}
67

    
68
  if (options.sync) {
69
    if (cb)
70
      throw new TypeError('callback provided to sync glob')
71
    return globSync(pattern, options)
72
  }
73

    
74
  return new Glob(pattern, options, cb)
75
}
76

    
77
glob.sync = globSync
78
var GlobSync = glob.GlobSync = globSync.GlobSync
79

    
80
// old api surface
81
glob.glob = glob
82

    
83
function extend (origin, add) {
84
  if (add === null || typeof add !== 'object') {
85
    return origin
86
  }
87

    
88
  var keys = Object.keys(add)
89
  var i = keys.length
90
  while (i--) {
91
    origin[keys[i]] = add[keys[i]]
92
  }
93
  return origin
94
}
95

    
96
glob.hasMagic = function (pattern, options_) {
97
  var options = extend({}, options_)
98
  options.noprocess = true
99

    
100
  var g = new Glob(pattern, options)
101
  var set = g.minimatch.set
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
  var n = this.minimatch.set.length
152
  this._processing = 0
153
  this.matches = new Array(n)
154

    
155
  this._emitQueue = []
156
  this._processQueue = []
157
  this.paused = false
158

    
159
  if (this.noprocess)
160
    return this
161

    
162
  if (n === 0)
163
    return done()
164

    
165
  for (var i = 0; i < n; i ++) {
166
    this._process(this.minimatch.set[i], i, false, done)
167
  }
168

    
169
  function done () {
170
    --self._processing
171
    if (self._processing <= 0)
172
      self._finish()
173
  }
174
}
175

    
176
Glob.prototype._finish = function () {
177
  assert(this instanceof Glob)
178
  if (this.aborted)
179
    return
180

    
181
  if (this.realpath && !this._didRealpath)
182
    return this._realpath()
183

    
184
  common.finish(this)
185
  this.emit('end', this.found)
186
}
187

    
188
Glob.prototype._realpath = function () {
189
  if (this._didRealpath)
190
    return
191

    
192
  this._didRealpath = true
193

    
194
  var n = this.matches.length
195
  if (n === 0)
196
    return this._finish()
197

    
198
  var self = this
199
  for (var i = 0; i < this.matches.length; i++)
200
    this._realpathSet(i, next)
201

    
202
  function next () {
203
    if (--n === 0)
204
      self._finish()
205
  }
206
}
207

    
208
Glob.prototype._realpathSet = function (index, cb) {
209
  var matchset = this.matches[index]
210
  if (!matchset)
211
    return cb()
212

    
213
  var found = Object.keys(matchset)
214
  var self = this
215
  var n = found.length
216

    
217
  if (n === 0)
218
    return cb()
219

    
220
  var set = this.matches[index] = Object.create(null)
221
  found.forEach(function (p, i) {
222
    // If there's a problem with the stat, then it means that
223
    // one or more of the links in the realpath couldn't be
224
    // resolved.  just return the abs value in that case.
225
    p = self._makeAbs(p)
226
    fs.realpath(p, self.realpathCache, function (er, real) {
227
      if (!er)
228
        set[real] = true
229
      else if (er.syscall === 'stat')
230
        set[p] = true
231
      else
232
        self.emit('error', er) // srsly wtf right here
233

    
234
      if (--n === 0) {
235
        self.matches[index] = set
236
        cb()
237
      }
238
    })
239
  })
240
}
241

    
242
Glob.prototype._mark = function (p) {
243
  return common.mark(this, p)
244
}
245

    
246
Glob.prototype._makeAbs = function (f) {
247
  return common.makeAbs(this, f)
248
}
249

    
250
Glob.prototype.abort = function () {
251
  this.aborted = true
252
  this.emit('abort')
253
}
254

    
255
Glob.prototype.pause = function () {
256
  if (!this.paused) {
257
    this.paused = true
258
    this.emit('pause')
259
  }
260
}
261

    
262
Glob.prototype.resume = function () {
263
  if (this.paused) {
264
    this.emit('resume')
265
    this.paused = false
266
    if (this._emitQueue.length) {
267
      var eq = this._emitQueue.slice(0)
268
      this._emitQueue.length = 0
269
      for (var i = 0; i < eq.length; i ++) {
270
        var e = eq[i]
271
        this._emitMatch(e[0], e[1])
272
      }
273
    }
274
    if (this._processQueue.length) {
275
      var pq = this._processQueue.slice(0)
276
      this._processQueue.length = 0
277
      for (var i = 0; i < pq.length; i ++) {
278
        var p = pq[i]
279
        this._processing--
280
        this._process(p[0], p[1], p[2], p[3])
281
      }
282
    }
283
  }
284
}
285

    
286
Glob.prototype._process = function (pattern, index, inGlobStar, cb) {
287
  assert(this instanceof Glob)
288
  assert(typeof cb === 'function')
289

    
290
  if (this.aborted)
291
    return
292

    
293
  this._processing++
294
  if (this.paused) {
295
    this._processQueue.push([pattern, index, inGlobStar, cb])
296
    return
297
  }
298

    
299
  //console.error('PROCESS %d', this._processing, pattern)
300

    
301
  // Get the first [n] parts of pattern that are all strings.
302
  var n = 0
303
  while (typeof pattern[n] === 'string') {
304
    n ++
305
  }
306
  // now n is the index of the first one that is *not* a string.
307

    
308
  // see if there's anything else
309
  var prefix
310
  switch (n) {
311
    // if not, then this is rather simple
312
    case pattern.length:
313
      this._processSimple(pattern.join('/'), index, cb)
314
      return
315

    
316
    case 0:
317
      // pattern *starts* with some non-trivial item.
318
      // going to readdir(cwd), but not include the prefix in matches.
319
      prefix = null
320
      break
321

    
322
    default:
323
      // pattern has some string bits in the front.
324
      // whatever it starts with, whether that's 'absolute' like /foo/bar,
325
      // or 'relative' like '../baz'
326
      prefix = pattern.slice(0, n).join('/')
327
      break
328
  }
329

    
330
  var remain = pattern.slice(n)
331

    
332
  // get the list of entries.
333
  var read
334
  if (prefix === null)
335
    read = '.'
336
  else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
337
    if (!prefix || !isAbsolute(prefix))
338
      prefix = '/' + prefix
339
    read = prefix
340
  } else
341
    read = prefix
342

    
343
  var abs = this._makeAbs(read)
344

    
345
  //if ignored, skip _processing
346
  if (childrenIgnored(this, read))
347
    return cb()
348

    
349
  var isGlobStar = remain[0] === minimatch.GLOBSTAR
350
  if (isGlobStar)
351
    this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)
352
  else
353
    this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)
354
}
355

    
356
Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {
357
  var self = this
358
  this._readdir(abs, inGlobStar, function (er, entries) {
359
    return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
360
  })
361
}
362

    
363
Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
364

    
365
  // if the abs isn't a dir, then nothing can match!
366
  if (!entries)
367
    return cb()
368

    
369
  // It will only match dot entries if it starts with a dot, or if
370
  // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
371
  var pn = remain[0]
372
  var negate = !!this.minimatch.negate
373
  var rawGlob = pn._glob
374
  var dotOk = this.dot || rawGlob.charAt(0) === '.'
375

    
376
  var matchedEntries = []
377
  for (var i = 0; i < entries.length; i++) {
378
    var e = entries[i]
379
    if (e.charAt(0) !== '.' || dotOk) {
380
      var m
381
      if (negate && !prefix) {
382
        m = !e.match(pn)
383
      } else {
384
        m = e.match(pn)
385
      }
386
      if (m)
387
        matchedEntries.push(e)
388
    }
389
  }
390

    
391
  //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)
392

    
393
  var len = matchedEntries.length
394
  // If there are no matched entries, then nothing matches.
395
  if (len === 0)
396
    return cb()
397

    
398
  // if this is the last remaining pattern bit, then no need for
399
  // an additional stat *unless* the user has specified mark or
400
  // stat explicitly.  We know they exist, since readdir returned
401
  // them.
402

    
403
  if (remain.length === 1 && !this.mark && !this.stat) {
404
    if (!this.matches[index])
405
      this.matches[index] = Object.create(null)
406

    
407
    for (var i = 0; i < len; i ++) {
408
      var e = matchedEntries[i]
409
      if (prefix) {
410
        if (prefix !== '/')
411
          e = prefix + '/' + e
412
        else
413
          e = prefix + e
414
      }
415

    
416
      if (e.charAt(0) === '/' && !this.nomount) {
417
        e = path.join(this.root, e)
418
      }
419
      this._emitMatch(index, e)
420
    }
421
    // This was the last one, and no stats were needed
422
    return cb()
423
  }
424

    
425
  // now test all matched entries as stand-ins for that part
426
  // of the pattern.
427
  remain.shift()
428
  for (var i = 0; i < len; i ++) {
429
    var e = matchedEntries[i]
430
    var newPattern
431
    if (prefix) {
432
      if (prefix !== '/')
433
        e = prefix + '/' + e
434
      else
435
        e = prefix + e
436
    }
437
    this._process([e].concat(remain), index, inGlobStar, cb)
438
  }
439
  cb()
440
}
441

    
442
Glob.prototype._emitMatch = function (index, e) {
443
  if (this.aborted)
444
    return
445

    
446
  if (this.matches[index][e])
447
    return
448

    
449
  if (isIgnored(this, e))
450
    return
451

    
452
  if (this.paused) {
453
    this._emitQueue.push([index, e])
454
    return
455
  }
456

    
457
  var abs = this._makeAbs(e)
458

    
459
  if (this.nodir) {
460
    var c = this.cache[abs]
461
    if (c === 'DIR' || Array.isArray(c))
462
      return
463
  }
464

    
465
  if (this.mark)
466
    e = this._mark(e)
467

    
468
  this.matches[index][e] = true
469

    
470
  var st = this.statCache[abs]
471
  if (st)
472
    this.emit('stat', e, st)
473

    
474
  this.emit('match', e)
475
}
476

    
477
Glob.prototype._readdirInGlobStar = function (abs, cb) {
478
  if (this.aborted)
479
    return
480

    
481
  // follow all symlinked directories forever
482
  // just proceed as if this is a non-globstar situation
483
  if (this.follow)
484
    return this._readdir(abs, false, cb)
485

    
486
  var lstatkey = 'lstat\0' + abs
487
  var self = this
488
  var lstatcb = inflight(lstatkey, lstatcb_)
489

    
490
  if (lstatcb)
491
    fs.lstat(abs, lstatcb)
492

    
493
  function lstatcb_ (er, lstat) {
494
    if (er)
495
      return cb()
496

    
497
    var isSym = lstat.isSymbolicLink()
498
    self.symlinks[abs] = isSym
499

    
500
    // If it's not a symlink or a dir, then it's definitely a regular file.
501
    // don't bother doing a readdir in that case.
502
    if (!isSym && !lstat.isDirectory()) {
503
      self.cache[abs] = 'FILE'
504
      cb()
505
    } else
506
      self._readdir(abs, false, cb)
507
  }
508
}
509

    
510
Glob.prototype._readdir = function (abs, inGlobStar, cb) {
511
  if (this.aborted)
512
    return
513

    
514
  cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)
515
  if (!cb)
516
    return
517

    
518
  //console.error('RD %j %j', +inGlobStar, abs)
519
  if (inGlobStar && !ownProp(this.symlinks, abs))
520
    return this._readdirInGlobStar(abs, cb)
521

    
522
  if (ownProp(this.cache, abs)) {
523
    var c = this.cache[abs]
524
    if (!c || c === 'FILE')
525
      return cb()
526

    
527
    if (Array.isArray(c))
528
      return cb(null, c)
529
  }
530

    
531
  var self = this
532
  fs.readdir(abs, readdirCb(this, abs, cb))
533
}
534

    
535
function readdirCb (self, abs, cb) {
536
  return function (er, entries) {
537
    if (er)
538
      self._readdirError(abs, er, cb)
539
    else
540
      self._readdirEntries(abs, entries, cb)
541
  }
542
}
543

    
544
Glob.prototype._readdirEntries = function (abs, entries, cb) {
545
  if (this.aborted)
546
    return
547

    
548
  // if we haven't asked to stat everything, then just
549
  // assume that everything in there exists, so we can avoid
550
  // having to stat it a second time.
551
  if (!this.mark && !this.stat) {
552
    for (var i = 0; i < entries.length; i ++) {
553
      var e = entries[i]
554
      if (abs === '/')
555
        e = abs + e
556
      else
557
        e = abs + '/' + e
558
      this.cache[e] = true
559
    }
560
  }
561

    
562
  this.cache[abs] = entries
563
  return cb(null, entries)
564
}
565

    
566
Glob.prototype._readdirError = function (f, er, cb) {
567
  if (this.aborted)
568
    return
569

    
570
  // handle errors, and cache the information
571
  switch (er.code) {
572
    case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
573
    case 'ENOTDIR': // totally normal. means it *does* exist.
574
      this.cache[this._makeAbs(f)] = 'FILE'
575
      break
576

    
577
    case 'ENOENT': // not terribly unusual
578
    case 'ELOOP':
579
    case 'ENAMETOOLONG':
580
    case 'UNKNOWN':
581
      this.cache[this._makeAbs(f)] = false
582
      break
583

    
584
    default: // some unusual error.  Treat as failure.
585
      this.cache[this._makeAbs(f)] = false
586
      if (this.strict) {
587
        this.emit('error', er)
588
        // If the error is handled, then we abort
589
        // if not, we threw out of here
590
        this.abort()
591
      }
592
      if (!this.silent)
593
        console.error('glob error', er)
594
      break
595
  }
596

    
597
  return cb()
598
}
599

    
600
Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {
601
  var self = this
602
  this._readdir(abs, inGlobStar, function (er, entries) {
603
    self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
604
  })
605
}
606

    
607

    
608
Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
609
  //console.error('pgs2', prefix, remain[0], entries)
610

    
611
  // no entries means not a dir, so it can never have matches
612
  // foo.txt/** doesn't match foo.txt
613
  if (!entries)
614
    return cb()
615

    
616
  // test without the globstar, and with every child both below
617
  // and replacing the globstar.
618
  var remainWithoutGlobStar = remain.slice(1)
619
  var gspref = prefix ? [ prefix ] : []
620
  var noGlobStar = gspref.concat(remainWithoutGlobStar)
621

    
622
  // the noGlobStar pattern exits the inGlobStar state
623
  this._process(noGlobStar, index, false, cb)
624

    
625
  var isSym = this.symlinks[abs]
626
  var len = entries.length
627

    
628
  // If it's a symlink, and we're in a globstar, then stop
629
  if (isSym && inGlobStar)
630
    return cb()
631

    
632
  for (var i = 0; i < len; i++) {
633
    var e = entries[i]
634
    if (e.charAt(0) === '.' && !this.dot)
635
      continue
636

    
637
    // these two cases enter the inGlobStar state
638
    var instead = gspref.concat(entries[i], remainWithoutGlobStar)
639
    this._process(instead, index, true, cb)
640

    
641
    var below = gspref.concat(entries[i], remain)
642
    this._process(below, index, true, cb)
643
  }
644

    
645
  cb()
646
}
647

    
648
Glob.prototype._processSimple = function (prefix, index, cb) {
649
  // XXX review this.  Shouldn't it be doing the mounting etc
650
  // before doing stat?  kinda weird?
651
  var self = this
652
  this._stat(prefix, function (er, exists) {
653
    self._processSimple2(prefix, index, er, exists, cb)
654
  })
655
}
656
Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {
657

    
658
  //console.error('ps2', prefix, exists)
659

    
660
  if (!this.matches[index])
661
    this.matches[index] = Object.create(null)
662

    
663
  // If it doesn't exist, then just mark the lack of results
664
  if (!exists)
665
    return cb()
666

    
667
  if (prefix && isAbsolute(prefix) && !this.nomount) {
668
    var trail = /[\/\\]$/.test(prefix)
669
    if (prefix.charAt(0) === '/') {
670
      prefix = path.join(this.root, prefix)
671
    } else {
672
      prefix = path.resolve(this.root, prefix)
673
      if (trail)
674
        prefix += '/'
675
    }
676
  }
677

    
678
  if (process.platform === 'win32')
679
    prefix = prefix.replace(/\\/g, '/')
680

    
681
  // Mark this as a match
682
  this._emitMatch(index, prefix)
683
  cb()
684
}
685

    
686
// Returns either 'DIR', 'FILE', or false
687
Glob.prototype._stat = function (f, cb) {
688
  var abs = this._makeAbs(f)
689
  var needDir = f.slice(-1) === '/'
690

    
691
  if (f.length > this.maxLength)
692
    return cb()
693

    
694
  if (!this.stat && ownProp(this.cache, abs)) {
695
    var c = this.cache[abs]
696

    
697
    if (Array.isArray(c))
698
      c = 'DIR'
699

    
700
    // It exists, but maybe not how we need it
701
    if (!needDir || c === 'DIR')
702
      return cb(null, c)
703

    
704
    if (needDir && c === 'FILE')
705
      return cb()
706

    
707
    // otherwise we have to stat, because maybe c=true
708
    // if we know it exists, but not what it is.
709
  }
710

    
711
  var exists
712
  var stat = this.statCache[abs]
713
  if (stat !== undefined) {
714
    if (stat === false)
715
      return cb(null, stat)
716
    else {
717
      var type = stat.isDirectory() ? 'DIR' : 'FILE'
718
      if (needDir && type === 'FILE')
719
        return cb()
720
      else
721
        return cb(null, type, stat)
722
    }
723
  }
724

    
725
  var self = this
726
  var statcb = inflight('stat\0' + abs, lstatcb_)
727
  if (statcb)
728
    fs.lstat(abs, statcb)
729

    
730
  function lstatcb_ (er, lstat) {
731
    if (lstat && lstat.isSymbolicLink()) {
732
      // If it's a symlink, then treat it as the target, unless
733
      // the target does not exist, then treat it as a file.
734
      return fs.stat(abs, function (er, stat) {
735
        if (er)
736
          self._stat2(f, abs, null, lstat, cb)
737
        else
738
          self._stat2(f, abs, er, stat, cb)
739
      })
740
    } else {
741
      self._stat2(f, abs, er, lstat, cb)
742
    }
743
  }
744
}
745

    
746
Glob.prototype._stat2 = function (f, abs, er, stat, cb) {
747
  if (er) {
748
    this.statCache[abs] = false
749
    return cb()
750
  }
751

    
752
  var needDir = f.slice(-1) === '/'
753
  this.statCache[abs] = stat
754

    
755
  if (abs.slice(-1) === '/' && !stat.isDirectory())
756
    return cb(null, false, stat)
757

    
758
  var c = stat.isDirectory() ? 'DIR' : 'FILE'
759
  this.cache[abs] = this.cache[abs] || c
760

    
761
  if (needDir && c !== 'DIR')
762
    return cb()
763

    
764
  return cb(null, c, stat)
765
}