|
|
module.exports = globSync globSync.GlobSync = GlobSync
var fs = require('fs') var rp = require('fs.realpath') var minimatch = require('minimatch') var Minimatch = minimatch.Minimatch var Glob = require('./glob.js').Glob var util = require('util') var path = require('path') var assert = require('assert') var isAbsolute = require('path-is-absolute') var common = require('./common.js') var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts var ownProp = common.ownProp var childrenIgnored = common.childrenIgnored var isIgnored = common.isIgnored
function globSync (pattern, options) { if (typeof options === 'function' || arguments.length === 3) throw new TypeError('callback provided to sync glob\n'+ 'See: https://github.com/isaacs/node-glob/issues/167')
return new GlobSync(pattern, options).found }
function GlobSync (pattern, options) { if (!pattern) throw new Error('must provide pattern')
if (typeof options === 'function' || arguments.length === 3) throw new TypeError('callback provided to sync glob\n'+ 'See: https://github.com/isaacs/node-glob/issues/167')
if (!(this instanceof GlobSync)) return new GlobSync(pattern, options)
setopts(this, pattern, options)
if (this.noprocess) return this
var n = this.minimatch.set.length this.matches = new Array(n) for (var i = 0; i < n; i ++) { this._process(this.minimatch.set[i], i, false) } this._finish() }
GlobSync.prototype._finish = function () { assert(this instanceof GlobSync) if (this.realpath) { var self = this this.matches.forEach(function (matchset, index) { var set = self.matches[index] = Object.create(null) for (var p in matchset) { try { p = self._makeAbs(p) var real = rp.realpathSync(p, self.realpathCache) set[real] = true } catch (er) { if (er.syscall === 'stat') set[self._makeAbs(p)] = true else throw er } } }) } common.finish(this) }
GlobSync.prototype._process = function (pattern, index, inGlobStar) { assert(this instanceof GlobSync)
// Get the first [n] parts of pattern that are all strings.
var n = 0 while (typeof pattern[n] === 'string') { n ++ } // now n is the index of the first one that is *not* a string.
// See if there's anything else
var prefix switch (n) { // if not, then this is rather simple
case pattern.length: this._processSimple(pattern.join('/'), index) return
case 0: // pattern *starts* with some non-trivial item.
// going to readdir(cwd), but not include the prefix in matches.
prefix = null break
default: // pattern has some string bits in the front.
// whatever it starts with, whether that's 'absolute' like /foo/bar,
// or 'relative' like '../baz'
prefix = pattern.slice(0, n).join('/') break }
var remain = pattern.slice(n)
// get the list of entries.
var read if (prefix === null) read = '.' else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { if (!prefix || !isAbsolute(prefix)) prefix = '/' + prefix read = prefix } else read = prefix
var abs = this._makeAbs(read)
//if ignored, skip processing
if (childrenIgnored(this, read)) return
var isGlobStar = remain[0] === minimatch.GLOBSTAR if (isGlobStar) this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) else this._processReaddir(prefix, read, abs, remain, index, inGlobStar) }
GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { var entries = this._readdir(abs, inGlobStar)
// if the abs isn't a dir, then nothing can match!
if (!entries) return
// It will only match dot entries if it starts with a dot, or if
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
var pn = remain[0] var negate = !!this.minimatch.negate var rawGlob = pn._glob var dotOk = this.dot || rawGlob.charAt(0) === '.'
var matchedEntries = [] for (var i = 0; i < entries.length; i++) { var e = entries[i] if (e.charAt(0) !== '.' || dotOk) { var m if (negate && !prefix) { m = !e.match(pn) } else { m = e.match(pn) } if (m) matchedEntries.push(e) } }
var len = matchedEntries.length // If there are no matched entries, then nothing matches.
if (len === 0) return
// if this is the last remaining pattern bit, then no need for
// an additional stat *unless* the user has specified mark or
// stat explicitly. We know they exist, since readdir returned
// them.
if (remain.length === 1 && !this.mark && !this.stat) { if (!this.matches[index]) this.matches[index] = Object.create(null)
for (var i = 0; i < len; i ++) { var e = matchedEntries[i] if (prefix) { if (prefix.slice(-1) !== '/') e = prefix + '/' + e else e = prefix + e }
if (e.charAt(0) === '/' && !this.nomount) { e = path.join(this.root, e) } this._emitMatch(index, e) } // This was the last one, and no stats were needed
return }
// now test all matched entries as stand-ins for that part
// of the pattern.
remain.shift() for (var i = 0; i < len; i ++) { var e = matchedEntries[i] var newPattern if (prefix) newPattern = [prefix, e] else newPattern = [e] this._process(newPattern.concat(remain), index, inGlobStar) } }
GlobSync.prototype._emitMatch = function (index, e) { if (isIgnored(this, e)) return
var abs = this._makeAbs(e)
if (this.mark) e = this._mark(e)
if (this.absolute) { e = abs }
if (this.matches[index][e]) return
if (this.nodir) { var c = this.cache[abs] if (c === 'DIR' || Array.isArray(c)) return }
this.matches[index][e] = true
if (this.stat) this._stat(e) }
GlobSync.prototype._readdirInGlobStar = function (abs) { // follow all symlinked directories forever
// just proceed as if this is a non-globstar situation
if (this.follow) return this._readdir(abs, false)
var entries var lstat var stat try { lstat = fs.lstatSync(abs) } catch (er) { if (er.code === 'ENOENT') { // lstat failed, doesn't exist
return null } }
var isSym = lstat && lstat.isSymbolicLink() this.symlinks[abs] = isSym
// If it's not a symlink or a dir, then it's definitely a regular file.
// don't bother doing a readdir in that case.
if (!isSym && lstat && !lstat.isDirectory()) this.cache[abs] = 'FILE' else entries = this._readdir(abs, false)
return entries }
GlobSync.prototype._readdir = function (abs, inGlobStar) { var entries
if (inGlobStar && !ownProp(this.symlinks, abs)) return this._readdirInGlobStar(abs)
if (ownProp(this.cache, abs)) { var c = this.cache[abs] if (!c || c === 'FILE') return null
if (Array.isArray(c)) return c }
try { return this._readdirEntries(abs, fs.readdirSync(abs)) } catch (er) { this._readdirError(abs, er) return null } }
GlobSync.prototype._readdirEntries = function (abs, entries) { // if we haven't asked to stat everything, then just
// assume that everything in there exists, so we can avoid
// having to stat it a second time.
if (!this.mark && !this.stat) { for (var i = 0; i < entries.length; i ++) { var e = entries[i] if (abs === '/') e = abs + e else e = abs + '/' + e this.cache[e] = true } }
this.cache[abs] = entries
// mark and cache dir-ness
return entries }
GlobSync.prototype._readdirError = function (f, er) { // handle errors, and cache the information
switch (er.code) { case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
case 'ENOTDIR': // totally normal. means it *does* exist.
var abs = this._makeAbs(f) this.cache[abs] = 'FILE' if (abs === this.cwdAbs) { var error = new Error(er.code + ' invalid cwd ' + this.cwd) error.path = this.cwd error.code = er.code throw error } break
case 'ENOENT': // not terribly unusual
case 'ELOOP': case 'ENAMETOOLONG': case 'UNKNOWN': this.cache[this._makeAbs(f)] = false break
default: // some unusual error. Treat as failure.
this.cache[this._makeAbs(f)] = false if (this.strict) throw er if (!this.silent) console.error('glob error', er) break } }
GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
var entries = this._readdir(abs, inGlobStar)
// no entries means not a dir, so it can never have matches
// foo.txt/** doesn't match foo.txt
if (!entries) return
// test without the globstar, and with every child both below
// and replacing the globstar.
var remainWithoutGlobStar = remain.slice(1) var gspref = prefix ? [ prefix ] : [] var noGlobStar = gspref.concat(remainWithoutGlobStar)
// the noGlobStar pattern exits the inGlobStar state
this._process(noGlobStar, index, false)
var len = entries.length var isSym = this.symlinks[abs]
// If it's a symlink, and we're in a globstar, then stop
if (isSym && inGlobStar) return
for (var i = 0; i < len; i++) { var e = entries[i] if (e.charAt(0) === '.' && !this.dot) continue
// these two cases enter the inGlobStar state
var instead = gspref.concat(entries[i], remainWithoutGlobStar) this._process(instead, index, true)
var below = gspref.concat(entries[i], remain) this._process(below, index, true) } }
GlobSync.prototype._processSimple = function (prefix, index) { // XXX review this. Shouldn't it be doing the mounting etc
// before doing stat? kinda weird?
var exists = this._stat(prefix)
if (!this.matches[index]) this.matches[index] = Object.create(null)
// If it doesn't exist, then just mark the lack of results
if (!exists) return
if (prefix && isAbsolute(prefix) && !this.nomount) { var trail = /[\/\\]$/.test(prefix) if (prefix.charAt(0) === '/') { prefix = path.join(this.root, prefix) } else { prefix = path.resolve(this.root, prefix) if (trail) prefix += '/' } }
if (process.platform === 'win32') prefix = prefix.replace(/\\/g, '/')
// Mark this as a match
this._emitMatch(index, prefix) }
// Returns either 'DIR', 'FILE', or false
GlobSync.prototype._stat = function (f) { var abs = this._makeAbs(f) var needDir = f.slice(-1) === '/'
if (f.length > this.maxLength) return false
if (!this.stat && ownProp(this.cache, abs)) { var c = this.cache[abs]
if (Array.isArray(c)) c = 'DIR'
// It exists, but maybe not how we need it
if (!needDir || c === 'DIR') return c
if (needDir && c === 'FILE') return false
// otherwise we have to stat, because maybe c=true
// if we know it exists, but not what it is.
}
var exists var stat = this.statCache[abs] if (!stat) { var lstat try { lstat = fs.lstatSync(abs) } catch (er) { if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { this.statCache[abs] = false return false } }
if (lstat && lstat.isSymbolicLink()) { try { stat = fs.statSync(abs) } catch (er) { stat = lstat } } else { stat = lstat } }
this.statCache[abs] = stat
var c = true if (stat) c = stat.isDirectory() ? 'DIR' : 'FILE'
this.cache[abs] = this.cache[abs] || c
if (needDir && c === 'FILE') return false
return c }
GlobSync.prototype._mark = function (p) { return common.mark(this, p) }
GlobSync.prototype._makeAbs = function (f) { return common.makeAbs(this, f) }
|