Projekt

Obecné

Profil

Stáhnout (11.7 KB) Statistiky
| Větev: | Revize:
1
module.exports = globSync
2
globSync.GlobSync = GlobSync
3

    
4
var fs = require('fs')
5
var rp = require('fs.realpath')
6
var minimatch = require('minimatch')
7
var Minimatch = minimatch.Minimatch
8
var Glob = require('./glob.js').Glob
9
var util = require('util')
10
var path = require('path')
11
var assert = require('assert')
12
var isAbsolute = require('path-is-absolute')
13
var common = require('./common.js')
14
var alphasort = common.alphasort
15
var alphasorti = common.alphasorti
16
var setopts = common.setopts
17
var ownProp = common.ownProp
18
var childrenIgnored = common.childrenIgnored
19
var isIgnored = common.isIgnored
20

    
21
function globSync (pattern, options) {
22
  if (typeof options === 'function' || arguments.length === 3)
23
    throw new TypeError('callback provided to sync glob\n'+
24
                        'See: https://github.com/isaacs/node-glob/issues/167')
25

    
26
  return new GlobSync(pattern, options).found
27
}
28

    
29
function GlobSync (pattern, options) {
30
  if (!pattern)
31
    throw new Error('must provide pattern')
32

    
33
  if (typeof options === 'function' || arguments.length === 3)
34
    throw new TypeError('callback provided to sync glob\n'+
35
                        'See: https://github.com/isaacs/node-glob/issues/167')
36

    
37
  if (!(this instanceof GlobSync))
38
    return new GlobSync(pattern, options)
39

    
40
  setopts(this, pattern, options)
41

    
42
  if (this.noprocess)
43
    return this
44

    
45
  var n = this.minimatch.set.length
46
  this.matches = new Array(n)
47
  for (var i = 0; i < n; i ++) {
48
    this._process(this.minimatch.set[i], i, false)
49
  }
50
  this._finish()
51
}
52

    
53
GlobSync.prototype._finish = function () {
54
  assert(this instanceof GlobSync)
55
  if (this.realpath) {
56
    var self = this
57
    this.matches.forEach(function (matchset, index) {
58
      var set = self.matches[index] = Object.create(null)
59
      for (var p in matchset) {
60
        try {
61
          p = self._makeAbs(p)
62
          var real = rp.realpathSync(p, self.realpathCache)
63
          set[real] = true
64
        } catch (er) {
65
          if (er.syscall === 'stat')
66
            set[self._makeAbs(p)] = true
67
          else
68
            throw er
69
        }
70
      }
71
    })
72
  }
73
  common.finish(this)
74
}
75

    
76

    
77
GlobSync.prototype._process = function (pattern, index, inGlobStar) {
78
  assert(this instanceof GlobSync)
79

    
80
  // Get the first [n] parts of pattern that are all strings.
81
  var n = 0
82
  while (typeof pattern[n] === 'string') {
83
    n ++
84
  }
85
  // now n is the index of the first one that is *not* a string.
86

    
87
  // See if there's anything else
88
  var prefix
89
  switch (n) {
90
    // if not, then this is rather simple
91
    case pattern.length:
92
      this._processSimple(pattern.join('/'), index)
93
      return
94

    
95
    case 0:
96
      // pattern *starts* with some non-trivial item.
97
      // going to readdir(cwd), but not include the prefix in matches.
98
      prefix = null
99
      break
100

    
101
    default:
102
      // pattern has some string bits in the front.
103
      // whatever it starts with, whether that's 'absolute' like /foo/bar,
104
      // or 'relative' like '../baz'
105
      prefix = pattern.slice(0, n).join('/')
106
      break
107
  }
108

    
109
  var remain = pattern.slice(n)
110

    
111
  // get the list of entries.
112
  var read
113
  if (prefix === null)
114
    read = '.'
115
  else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
116
    if (!prefix || !isAbsolute(prefix))
117
      prefix = '/' + prefix
118
    read = prefix
119
  } else
120
    read = prefix
121

    
122
  var abs = this._makeAbs(read)
123

    
124
  //if ignored, skip processing
125
  if (childrenIgnored(this, read))
126
    return
127

    
128
  var isGlobStar = remain[0] === minimatch.GLOBSTAR
129
  if (isGlobStar)
130
    this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
131
  else
132
    this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
133
}
134

    
135

    
136
GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
137
  var entries = this._readdir(abs, inGlobStar)
138

    
139
  // if the abs isn't a dir, then nothing can match!
140
  if (!entries)
141
    return
142

    
143
  // It will only match dot entries if it starts with a dot, or if
144
  // dot is set.  Stuff like @(.foo|.bar) isn't allowed.
145
  var pn = remain[0]
146
  var negate = !!this.minimatch.negate
147
  var rawGlob = pn._glob
148
  var dotOk = this.dot || rawGlob.charAt(0) === '.'
149

    
150
  var matchedEntries = []
151
  for (var i = 0; i < entries.length; i++) {
152
    var e = entries[i]
153
    if (e.charAt(0) !== '.' || dotOk) {
154
      var m
155
      if (negate && !prefix) {
156
        m = !e.match(pn)
157
      } else {
158
        m = e.match(pn)
159
      }
160
      if (m)
161
        matchedEntries.push(e)
162
    }
163
  }
164

    
165
  var len = matchedEntries.length
166
  // If there are no matched entries, then nothing matches.
167
  if (len === 0)
168
    return
169

    
170
  // if this is the last remaining pattern bit, then no need for
171
  // an additional stat *unless* the user has specified mark or
172
  // stat explicitly.  We know they exist, since readdir returned
173
  // them.
174

    
175
  if (remain.length === 1 && !this.mark && !this.stat) {
176
    if (!this.matches[index])
177
      this.matches[index] = Object.create(null)
178

    
179
    for (var i = 0; i < len; i ++) {
180
      var e = matchedEntries[i]
181
      if (prefix) {
182
        if (prefix.slice(-1) !== '/')
183
          e = prefix + '/' + e
184
        else
185
          e = prefix + e
186
      }
187

    
188
      if (e.charAt(0) === '/' && !this.nomount) {
189
        e = path.join(this.root, e)
190
      }
191
      this._emitMatch(index, e)
192
    }
193
    // This was the last one, and no stats were needed
194
    return
195
  }
196

    
197
  // now test all matched entries as stand-ins for that part
198
  // of the pattern.
199
  remain.shift()
200
  for (var i = 0; i < len; i ++) {
201
    var e = matchedEntries[i]
202
    var newPattern
203
    if (prefix)
204
      newPattern = [prefix, e]
205
    else
206
      newPattern = [e]
207
    this._process(newPattern.concat(remain), index, inGlobStar)
208
  }
209
}
210

    
211

    
212
GlobSync.prototype._emitMatch = function (index, e) {
213
  if (isIgnored(this, e))
214
    return
215

    
216
  var abs = this._makeAbs(e)
217

    
218
  if (this.mark)
219
    e = this._mark(e)
220

    
221
  if (this.absolute) {
222
    e = abs
223
  }
224

    
225
  if (this.matches[index][e])
226
    return
227

    
228
  if (this.nodir) {
229
    var c = this.cache[abs]
230
    if (c === 'DIR' || Array.isArray(c))
231
      return
232
  }
233

    
234
  this.matches[index][e] = true
235

    
236
  if (this.stat)
237
    this._stat(e)
238
}
239

    
240

    
241
GlobSync.prototype._readdirInGlobStar = function (abs) {
242
  // follow all symlinked directories forever
243
  // just proceed as if this is a non-globstar situation
244
  if (this.follow)
245
    return this._readdir(abs, false)
246

    
247
  var entries
248
  var lstat
249
  var stat
250
  try {
251
    lstat = fs.lstatSync(abs)
252
  } catch (er) {
253
    if (er.code === 'ENOENT') {
254
      // lstat failed, doesn't exist
255
      return null
256
    }
257
  }
258

    
259
  var isSym = lstat && lstat.isSymbolicLink()
260
  this.symlinks[abs] = isSym
261

    
262
  // If it's not a symlink or a dir, then it's definitely a regular file.
263
  // don't bother doing a readdir in that case.
264
  if (!isSym && lstat && !lstat.isDirectory())
265
    this.cache[abs] = 'FILE'
266
  else
267
    entries = this._readdir(abs, false)
268

    
269
  return entries
270
}
271

    
272
GlobSync.prototype._readdir = function (abs, inGlobStar) {
273
  var entries
274

    
275
  if (inGlobStar && !ownProp(this.symlinks, abs))
276
    return this._readdirInGlobStar(abs)
277

    
278
  if (ownProp(this.cache, abs)) {
279
    var c = this.cache[abs]
280
    if (!c || c === 'FILE')
281
      return null
282

    
283
    if (Array.isArray(c))
284
      return c
285
  }
286

    
287
  try {
288
    return this._readdirEntries(abs, fs.readdirSync(abs))
289
  } catch (er) {
290
    this._readdirError(abs, er)
291
    return null
292
  }
293
}
294

    
295
GlobSync.prototype._readdirEntries = function (abs, entries) {
296
  // if we haven't asked to stat everything, then just
297
  // assume that everything in there exists, so we can avoid
298
  // having to stat it a second time.
299
  if (!this.mark && !this.stat) {
300
    for (var i = 0; i < entries.length; i ++) {
301
      var e = entries[i]
302
      if (abs === '/')
303
        e = abs + e
304
      else
305
        e = abs + '/' + e
306
      this.cache[e] = true
307
    }
308
  }
309

    
310
  this.cache[abs] = entries
311

    
312
  // mark and cache dir-ness
313
  return entries
314
}
315

    
316
GlobSync.prototype._readdirError = function (f, er) {
317
  // handle errors, and cache the information
318
  switch (er.code) {
319
    case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
320
    case 'ENOTDIR': // totally normal. means it *does* exist.
321
      var abs = this._makeAbs(f)
322
      this.cache[abs] = 'FILE'
323
      if (abs === this.cwdAbs) {
324
        var error = new Error(er.code + ' invalid cwd ' + this.cwd)
325
        error.path = this.cwd
326
        error.code = er.code
327
        throw error
328
      }
329
      break
330

    
331
    case 'ENOENT': // not terribly unusual
332
    case 'ELOOP':
333
    case 'ENAMETOOLONG':
334
    case 'UNKNOWN':
335
      this.cache[this._makeAbs(f)] = false
336
      break
337

    
338
    default: // some unusual error.  Treat as failure.
339
      this.cache[this._makeAbs(f)] = false
340
      if (this.strict)
341
        throw er
342
      if (!this.silent)
343
        console.error('glob error', er)
344
      break
345
  }
346
}
347

    
348
GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
349

    
350
  var entries = this._readdir(abs, inGlobStar)
351

    
352
  // no entries means not a dir, so it can never have matches
353
  // foo.txt/** doesn't match foo.txt
354
  if (!entries)
355
    return
356

    
357
  // test without the globstar, and with every child both below
358
  // and replacing the globstar.
359
  var remainWithoutGlobStar = remain.slice(1)
360
  var gspref = prefix ? [ prefix ] : []
361
  var noGlobStar = gspref.concat(remainWithoutGlobStar)
362

    
363
  // the noGlobStar pattern exits the inGlobStar state
364
  this._process(noGlobStar, index, false)
365

    
366
  var len = entries.length
367
  var isSym = this.symlinks[abs]
368

    
369
  // If it's a symlink, and we're in a globstar, then stop
370
  if (isSym && inGlobStar)
371
    return
372

    
373
  for (var i = 0; i < len; i++) {
374
    var e = entries[i]
375
    if (e.charAt(0) === '.' && !this.dot)
376
      continue
377

    
378
    // these two cases enter the inGlobStar state
379
    var instead = gspref.concat(entries[i], remainWithoutGlobStar)
380
    this._process(instead, index, true)
381

    
382
    var below = gspref.concat(entries[i], remain)
383
    this._process(below, index, true)
384
  }
385
}
386

    
387
GlobSync.prototype._processSimple = function (prefix, index) {
388
  // XXX review this.  Shouldn't it be doing the mounting etc
389
  // before doing stat?  kinda weird?
390
  var exists = this._stat(prefix)
391

    
392
  if (!this.matches[index])
393
    this.matches[index] = Object.create(null)
394

    
395
  // If it doesn't exist, then just mark the lack of results
396
  if (!exists)
397
    return
398

    
399
  if (prefix && isAbsolute(prefix) && !this.nomount) {
400
    var trail = /[\/\\]$/.test(prefix)
401
    if (prefix.charAt(0) === '/') {
402
      prefix = path.join(this.root, prefix)
403
    } else {
404
      prefix = path.resolve(this.root, prefix)
405
      if (trail)
406
        prefix += '/'
407
    }
408
  }
409

    
410
  if (process.platform === 'win32')
411
    prefix = prefix.replace(/\\/g, '/')
412

    
413
  // Mark this as a match
414
  this._emitMatch(index, prefix)
415
}
416

    
417
// Returns either 'DIR', 'FILE', or false
418
GlobSync.prototype._stat = function (f) {
419
  var abs = this._makeAbs(f)
420
  var needDir = f.slice(-1) === '/'
421

    
422
  if (f.length > this.maxLength)
423
    return false
424

    
425
  if (!this.stat && ownProp(this.cache, abs)) {
426
    var c = this.cache[abs]
427

    
428
    if (Array.isArray(c))
429
      c = 'DIR'
430

    
431
    // It exists, but maybe not how we need it
432
    if (!needDir || c === 'DIR')
433
      return c
434

    
435
    if (needDir && c === 'FILE')
436
      return false
437

    
438
    // otherwise we have to stat, because maybe c=true
439
    // if we know it exists, but not what it is.
440
  }
441

    
442
  var exists
443
  var stat = this.statCache[abs]
444
  if (!stat) {
445
    var lstat
446
    try {
447
      lstat = fs.lstatSync(abs)
448
    } catch (er) {
449
      if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
450
        this.statCache[abs] = false
451
        return false
452
      }
453
    }
454

    
455
    if (lstat && lstat.isSymbolicLink()) {
456
      try {
457
        stat = fs.statSync(abs)
458
      } catch (er) {
459
        stat = lstat
460
      }
461
    } else {
462
      stat = lstat
463
    }
464
  }
465

    
466
  this.statCache[abs] = stat
467

    
468
  var c = true
469
  if (stat)
470
    c = stat.isDirectory() ? 'DIR' : 'FILE'
471

    
472
  this.cache[abs] = this.cache[abs] || c
473

    
474
  if (needDir && c === 'FILE')
475
    return false
476

    
477
  return c
478
}
479

    
480
GlobSync.prototype._mark = function (p) {
481
  return common.mark(this, p)
482
}
483

    
484
GlobSync.prototype._makeAbs = function (f) {
485
  return common.makeAbs(this, f)
486
}
(7-7/7)