1 module.exports = globSync
2 globSync.GlobSync = GlobSync
5 var minimatch = require('minimatch')
6 var Minimatch = minimatch.Minimatch
7 var Glob = require('./glob.js').Glob
8 var util = require('util')
9 var path = require('path')
10 var assert = require('assert')
11 var isAbsolute = require('path-is-absolute')
12 var common = require('./common.js')
13 var alphasort = common.alphasort
14 var alphasorti = common.alphasorti
15 var setopts = common.setopts
16 var ownProp = common.ownProp
17 var childrenIgnored = common.childrenIgnored
19 function globSync (pattern, options) {
20 if (typeof options === 'function' || arguments.length === 3)
21 throw new TypeError('callback provided to sync glob\n'+
22 'See: https://github.com/isaacs/node-glob/issues/167')
24 return new GlobSync(pattern, options).found
27 function GlobSync (pattern, options) {
29 throw new Error('must provide pattern')
31 if (typeof options === 'function' || arguments.length === 3)
32 throw new TypeError('callback provided to sync glob\n'+
33 'See: https://github.com/isaacs/node-glob/issues/167')
35 if (!(this instanceof GlobSync))
36 return new GlobSync(pattern, options)
38 setopts(this, pattern, options)
43 var n = this.minimatch.set.length
44 this.matches = new Array(n)
45 for (var i = 0; i < n; i ++) {
46 this._process(this.minimatch.set[i], i, false)
51 GlobSync.prototype._finish = function () {
52 assert(this instanceof GlobSync)
55 this.matches.forEach(function (matchset, index) {
56 var set = self.matches[index] = Object.create(null)
57 for (var p in matchset) {
60 var real = fs.realpathSync(p, self.realpathCache)
63 if (er.syscall === 'stat')
64 set[self._makeAbs(p)] = true
75 GlobSync.prototype._process = function (pattern, index, inGlobStar) {
76 assert(this instanceof GlobSync)
78 // Get the first [n] parts of pattern that are all strings.
80 while (typeof pattern[n] === 'string') {
83 // now n is the index of the first one that is *not* a string.
85 // See if there's anything else
88 // if not, then this is rather simple
90 this._processSimple(pattern.join('/'), index)
94 // pattern *starts* with some non-trivial item.
95 // going to readdir(cwd), but not include the prefix in matches.
100 // pattern has some string bits in the front.
101 // whatever it starts with, whether that's 'absolute' like /foo/bar,
102 // or 'relative' like '../baz'
103 prefix = pattern.slice(0, n).join('/')
107 var remain = pattern.slice(n)
109 // get the list of entries.
113 else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
114 if (!prefix || !isAbsolute(prefix))
115 prefix = '/' + prefix
120 var abs = this._makeAbs(read)
122 //if ignored, skip processing
123 if (childrenIgnored(this, read))
126 var isGlobStar = remain[0] === minimatch.GLOBSTAR
128 this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
130 this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
134 GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
135 var entries = this._readdir(abs, inGlobStar)
137 // if the abs isn't a dir, then nothing can match!
141 // It will only match dot entries if it starts with a dot, or if
142 // dot is set. Stuff like @(.foo|.bar) isn't allowed.
144 var negate = !!this.minimatch.negate
145 var rawGlob = pn._glob
146 var dotOk = this.dot || rawGlob.charAt(0) === '.'
148 var matchedEntries = []
149 for (var i = 0; i < entries.length; i++) {
151 if (e.charAt(0) !== '.' || dotOk) {
153 if (negate && !prefix) {
159 matchedEntries.push(e)
163 var len = matchedEntries.length
164 // If there are no matched entries, then nothing matches.
168 // if this is the last remaining pattern bit, then no need for
169 // an additional stat *unless* the user has specified mark or
170 // stat explicitly. We know they exist, since readdir returned
173 if (remain.length === 1 && !this.mark && !this.stat) {
174 if (!this.matches[index])
175 this.matches[index] = Object.create(null)
177 for (var i = 0; i < len; i ++) {
178 var e = matchedEntries[i]
180 if (prefix.slice(-1) !== '/')
186 if (e.charAt(0) === '/' && !this.nomount) {
187 e = path.join(this.root, e)
189 this.matches[index][e] = true
191 // This was the last one, and no stats were needed
195 // now test all matched entries as stand-ins for that part
198 for (var i = 0; i < len; i ++) {
199 var e = matchedEntries[i]
202 newPattern = [prefix, e]
205 this._process(newPattern.concat(remain), index, inGlobStar)
210 GlobSync.prototype._emitMatch = function (index, e) {
211 var abs = this._makeAbs(e)
215 if (this.matches[index][e])
219 var c = this.cache[this._makeAbs(e)]
220 if (c === 'DIR' || Array.isArray(c))
224 this.matches[index][e] = true
230 GlobSync.prototype._readdirInGlobStar = function (abs) {
231 // follow all symlinked directories forever
232 // just proceed as if this is a non-globstar situation
234 return this._readdir(abs, false)
240 lstat = fs.lstatSync(abs)
242 // lstat failed, doesn't exist
246 var isSym = lstat.isSymbolicLink()
247 this.symlinks[abs] = isSym
249 // If it's not a symlink or a dir, then it's definitely a regular file.
250 // don't bother doing a readdir in that case.
251 if (!isSym && !lstat.isDirectory())
252 this.cache[abs] = 'FILE'
254 entries = this._readdir(abs, false)
259 GlobSync.prototype._readdir = function (abs, inGlobStar) {
262 if (inGlobStar && !ownProp(this.symlinks, abs))
263 return this._readdirInGlobStar(abs)
265 if (ownProp(this.cache, abs)) {
266 var c = this.cache[abs]
267 if (!c || c === 'FILE')
270 if (Array.isArray(c))
275 return this._readdirEntries(abs, fs.readdirSync(abs))
277 this._readdirError(abs, er)
282 GlobSync.prototype._readdirEntries = function (abs, entries) {
283 // if we haven't asked to stat everything, then just
284 // assume that everything in there exists, so we can avoid
285 // having to stat it a second time.
286 if (!this.mark && !this.stat) {
287 for (var i = 0; i < entries.length; i ++) {
297 this.cache[abs] = entries
299 // mark and cache dir-ness
303 GlobSync.prototype._readdirError = function (f, er) {
304 // handle errors, and cache the information
306 case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
307 case 'ENOTDIR': // totally normal. means it *does* exist.
308 this.cache[this._makeAbs(f)] = 'FILE'
311 case 'ENOENT': // not terribly unusual
315 this.cache[this._makeAbs(f)] = false
318 default: // some unusual error. Treat as failure.
319 this.cache[this._makeAbs(f)] = false
323 console.error('glob error', er)
328 GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
330 var entries = this._readdir(abs, inGlobStar)
332 // no entries means not a dir, so it can never have matches
333 // foo.txt/** doesn't match foo.txt
337 // test without the globstar, and with every child both below
338 // and replacing the globstar.
339 var remainWithoutGlobStar = remain.slice(1)
340 var gspref = prefix ? [ prefix ] : []
341 var noGlobStar = gspref.concat(remainWithoutGlobStar)
343 // the noGlobStar pattern exits the inGlobStar state
344 this._process(noGlobStar, index, false)
346 var len = entries.length
347 var isSym = this.symlinks[abs]
349 // If it's a symlink, and we're in a globstar, then stop
350 if (isSym && inGlobStar)
353 for (var i = 0; i < len; i++) {
355 if (e.charAt(0) === '.' && !this.dot)
358 // these two cases enter the inGlobStar state
359 var instead = gspref.concat(entries[i], remainWithoutGlobStar)
360 this._process(instead, index, true)
362 var below = gspref.concat(entries[i], remain)
363 this._process(below, index, true)
367 GlobSync.prototype._processSimple = function (prefix, index) {
368 // XXX review this. Shouldn't it be doing the mounting etc
369 // before doing stat? kinda weird?
370 var exists = this._stat(prefix)
372 if (!this.matches[index])
373 this.matches[index] = Object.create(null)
375 // If it doesn't exist, then just mark the lack of results
379 if (prefix && isAbsolute(prefix) && !this.nomount) {
380 var trail = /[\/\\]$/.test(prefix)
381 if (prefix.charAt(0) === '/') {
382 prefix = path.join(this.root, prefix)
384 prefix = path.resolve(this.root, prefix)
390 if (process.platform === 'win32')
391 prefix = prefix.replace(/\\/g, '/')
393 // Mark this as a match
394 this.matches[index][prefix] = true
397 // Returns either 'DIR', 'FILE', or false
398 GlobSync.prototype._stat = function (f) {
399 var abs = this._makeAbs(f)
400 var needDir = f.slice(-1) === '/'
402 if (f.length > this.maxLength)
405 if (!this.stat && ownProp(this.cache, abs)) {
406 var c = this.cache[abs]
408 if (Array.isArray(c))
411 // It exists, but maybe not how we need it
412 if (!needDir || c === 'DIR')
415 if (needDir && c === 'FILE')
418 // otherwise we have to stat, because maybe c=true
419 // if we know it exists, but not what it is.
423 var stat = this.statCache[abs]
427 lstat = fs.lstatSync(abs)
432 if (lstat.isSymbolicLink()) {
434 stat = fs.statSync(abs)
443 this.statCache[abs] = stat
445 var c = stat.isDirectory() ? 'DIR' : 'FILE'
446 this.cache[abs] = this.cache[abs] || c
448 if (needDir && c !== 'DIR')
454 GlobSync.prototype._mark = function (p) {
455 return common.mark(this, p)
458 GlobSync.prototype._makeAbs = function (f) {
459 return common.makeAbs(this, f)