react / react-0.13.3 / examples / basic-commonjs / node_modules / browserify / node_modules / glob / glob.js
80709 views// Approach:1//2// 1. Get the minimatch set3// 2. For each pattern in the set, PROCESS(pattern, false)4// 3. Store matches per-set, then uniq them5//6// PROCESS(pattern, inGlobStar)7// Get the first [n] items from pattern that are all strings8// Join these together. This is PREFIX.9// If there is no more remaining, then stat(PREFIX) and10// add to matches if it succeeds. END.11//12// If inGlobStar and PREFIX is symlink and points to dir13// set ENTRIES = []14// else readdir(PREFIX) as ENTRIES15// If fail, END16//17// with ENTRIES18// If pattern[n] is GLOBSTAR19// // handle the case where the globstar match is empty20// // by pruning it out, and testing the resulting pattern21// PROCESS(pattern[0..n] + pattern[n+1 .. $], false)22// // handle other cases.23// for ENTRY in ENTRIES (not dotfiles)24// // attach globstar + tail onto the entry25// // Mark that this entry is a globstar match26// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true)27//28// else // not globstar29// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)30// Test ENTRY against pattern[n]31// If fails, continue32// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])33//34// Caveat:35// Cache all stats and readdirs results to minimize syscall. Since all36// we ever care about is existence and directory-ness, we can just keep37// `true` for files, and [children,...] for directories, or `false` for38// things that don't exist.3940module.exports = glob4142var fs = require('fs')43var minimatch = require('minimatch')44var Minimatch = minimatch.Minimatch45var inherits = require('inherits')46var EE = require('events').EventEmitter47var path = require('path')48var assert = require('assert')49var globSync = require('./sync.js')50var common = require('./common.js')51var alphasort = common.alphasort52var alphasorti = common.alphasorti53var isAbsolute = common.isAbsolute54var setopts = common.setopts55var ownProp = common.ownProp56var inflight = require('inflight')57var util = require('util')58var childrenIgnored = common.childrenIgnored5960var once = require('once')6162function glob (pattern, options, cb) {63if (typeof options === 'function') cb = options, options = {}64if (!options) options = {}6566if (options.sync) {67if (cb)68throw new TypeError('callback provided to sync glob')69return globSync(pattern, options)70}7172return new Glob(pattern, options, cb)73}7475glob.sync = globSync76var GlobSync = glob.GlobSync = globSync.GlobSync7778// old api surface79glob.glob = glob8081glob.hasMagic = function (pattern, options_) {82var options = util._extend({}, options_)83options.noprocess = true8485var g = new Glob(pattern, options)86var set = g.minimatch.set87if (set.length > 1)88return true8990for (var j = 0; j < set[0].length; j++) {91if (typeof set[0][j] !== 'string')92return true93}9495return false96}9798glob.Glob = Glob99inherits(Glob, EE)100function Glob (pattern, options, cb) {101if (typeof options === 'function') {102cb = options103options = null104}105106if (options && options.sync) {107if (cb)108throw new TypeError('callback provided to sync glob')109return new GlobSync(pattern, options)110}111112if (!(this instanceof Glob))113return new Glob(pattern, options, cb)114115setopts(this, pattern, options)116this._didRealPath = false117118// process each pattern in the minimatch set119var n = this.minimatch.set.length120121// The matches are stored as {<filename>: true,...} so that122// duplicates are automagically pruned.123// Later, we do an Object.keys() on these.124// Keep them as a list so we can fill in when nonull is set.125this.matches = new Array(n)126127if (typeof cb === 'function') {128cb = once(cb)129this.on('error', cb)130this.on('end', function (matches) {131cb(null, matches)132})133}134135var self = this136var n = this.minimatch.set.length137this._processing = 0138this.matches = new Array(n)139140this._emitQueue = []141this._processQueue = []142this.paused = false143144if (this.noprocess)145return this146147if (n === 0)148return done()149150for (var i = 0; i < n; i ++) {151this._process(this.minimatch.set[i], i, false, done)152}153154function done () {155--self._processing156if (self._processing <= 0)157self._finish()158}159}160161Glob.prototype._finish = function () {162assert(this instanceof Glob)163if (this.aborted)164return165166if (this.realpath && !this._didRealpath)167return this._realpath()168169common.finish(this)170this.emit('end', this.found)171}172173Glob.prototype._realpath = function () {174if (this._didRealpath)175return176177this._didRealpath = true178179var n = this.matches.length180if (n === 0)181return this._finish()182183var self = this184for (var i = 0; i < this.matches.length; i++)185this._realpathSet(i, next)186187function next () {188if (--n === 0)189self._finish()190}191}192193Glob.prototype._realpathSet = function (index, cb) {194var matchset = this.matches[index]195if (!matchset)196return cb()197198var found = Object.keys(matchset)199var self = this200var n = found.length201202if (n === 0)203return cb()204205var set = this.matches[index] = Object.create(null)206found.forEach(function (p, i) {207// If there's a problem with the stat, then it means that208// one or more of the links in the realpath couldn't be209// resolved. just return the abs value in that case.210p = self._makeAbs(p)211fs.realpath(p, self.realpathCache, function (er, real) {212if (!er)213set[real] = true214else if (er.syscall === 'stat')215set[p] = true216else217self.emit('error', er) // srsly wtf right here218219if (--n === 0) {220self.matches[index] = set221cb()222}223})224})225}226227Glob.prototype._mark = function (p) {228return common.mark(this, p)229}230231Glob.prototype._makeAbs = function (f) {232return common.makeAbs(this, f)233}234235Glob.prototype.abort = function () {236this.aborted = true237this.emit('abort')238}239240Glob.prototype.pause = function () {241if (!this.paused) {242this.paused = true243this.emit('pause')244}245}246247Glob.prototype.resume = function () {248if (this.paused) {249this.emit('resume')250this.paused = false251if (this._emitQueue.length) {252var eq = this._emitQueue.slice(0)253this._emitQueue.length = 0254for (var i = 0; i < eq.length; i ++) {255var e = eq[i]256this._emitMatch(e[0], e[1])257}258}259if (this._processQueue.length) {260var pq = this._processQueue.slice(0)261this._processQueue.length = 0262for (var i = 0; i < pq.length; i ++) {263var p = pq[i]264this._processing--265this._process(p[0], p[1], p[2], p[3])266}267}268}269}270271Glob.prototype._process = function (pattern, index, inGlobStar, cb) {272assert(this instanceof Glob)273assert(typeof cb === 'function')274275if (this.aborted)276return277278this._processing++279if (this.paused) {280this._processQueue.push([pattern, index, inGlobStar, cb])281return282}283284//console.error('PROCESS %d', this._processing, pattern)285286// Get the first [n] parts of pattern that are all strings.287var n = 0288while (typeof pattern[n] === 'string') {289n ++290}291// now n is the index of the first one that is *not* a string.292293// see if there's anything else294var prefix295switch (n) {296// if not, then this is rather simple297case pattern.length:298this._processSimple(pattern.join('/'), index, cb)299return300301case 0:302// pattern *starts* with some non-trivial item.303// going to readdir(cwd), but not include the prefix in matches.304prefix = null305break306307default:308// pattern has some string bits in the front.309// whatever it starts with, whether that's 'absolute' like /foo/bar,310// or 'relative' like '../baz'311prefix = pattern.slice(0, n).join('/')312break313}314315var remain = pattern.slice(n)316317// get the list of entries.318var read319if (prefix === null)320read = '.'321else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {322if (!prefix || !isAbsolute(prefix))323prefix = '/' + prefix324read = prefix325} else326read = prefix327328var abs = this._makeAbs(read)329330//if ignored, skip _processing331if (childrenIgnored(this, read))332return cb()333334var isGlobStar = remain[0] === minimatch.GLOBSTAR335if (isGlobStar)336this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)337else338this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)339}340341Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {342var self = this343this._readdir(abs, inGlobStar, function (er, entries) {344return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)345})346}347348Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {349350// if the abs isn't a dir, then nothing can match!351if (!entries)352return cb()353354// It will only match dot entries if it starts with a dot, or if355// dot is set. Stuff like @(.foo|.bar) isn't allowed.356var pn = remain[0]357var negate = !!this.minimatch.negate358var rawGlob = pn._glob359var dotOk = this.dot || rawGlob.charAt(0) === '.'360361var matchedEntries = []362for (var i = 0; i < entries.length; i++) {363var e = entries[i]364if (e.charAt(0) !== '.' || dotOk) {365var m366if (negate && !prefix) {367m = !e.match(pn)368} else {369m = e.match(pn)370}371if (m)372matchedEntries.push(e)373}374}375376//console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)377378var len = matchedEntries.length379// If there are no matched entries, then nothing matches.380if (len === 0)381return cb()382383// if this is the last remaining pattern bit, then no need for384// an additional stat *unless* the user has specified mark or385// stat explicitly. We know they exist, since readdir returned386// them.387388if (remain.length === 1 && !this.mark && !this.stat) {389if (!this.matches[index])390this.matches[index] = Object.create(null)391392for (var i = 0; i < len; i ++) {393var e = matchedEntries[i]394if (prefix) {395if (prefix !== '/')396e = prefix + '/' + e397else398e = prefix + e399}400401if (e.charAt(0) === '/' && !this.nomount) {402e = path.join(this.root, e)403}404this._emitMatch(index, e)405}406// This was the last one, and no stats were needed407return cb()408}409410// now test all matched entries as stand-ins for that part411// of the pattern.412remain.shift()413for (var i = 0; i < len; i ++) {414var e = matchedEntries[i]415var newPattern416if (prefix) {417if (prefix !== '/')418e = prefix + '/' + e419else420e = prefix + e421}422this._process([e].concat(remain), index, inGlobStar, cb)423}424cb()425}426427Glob.prototype._emitMatch = function (index, e) {428if (this.aborted)429return430431if (this.matches[index][e])432return433434if (this.paused) {435this._emitQueue.push([index, e])436return437}438439var abs = this._makeAbs(e)440441if (this.nodir) {442var c = this.cache[abs]443if (c === 'DIR' || Array.isArray(c))444return445}446447if (this.mark)448e = this._mark(e)449450this.matches[index][e] = true451452var st = this.statCache[abs]453if (st)454this.emit('stat', e, st)455456this.emit('match', e)457}458459Glob.prototype._readdirInGlobStar = function (abs, cb) {460if (this.aborted)461return462463// follow all symlinked directories forever464// just proceed as if this is a non-globstar situation465if (this.follow)466return this._readdir(abs, false, cb)467468var lstatkey = 'lstat\0' + abs469var self = this470var lstatcb = inflight(lstatkey, lstatcb_)471472if (lstatcb)473fs.lstat(abs, lstatcb)474475function lstatcb_ (er, lstat) {476if (er)477return cb()478479var isSym = lstat.isSymbolicLink()480self.symlinks[abs] = isSym481482// If it's not a symlink or a dir, then it's definitely a regular file.483// don't bother doing a readdir in that case.484if (!isSym && !lstat.isDirectory()) {485self.cache[abs] = 'FILE'486cb()487} else488self._readdir(abs, false, cb)489}490}491492Glob.prototype._readdir = function (abs, inGlobStar, cb) {493if (this.aborted)494return495496cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)497if (!cb)498return499500//console.error('RD %j %j', +inGlobStar, abs)501if (inGlobStar && !ownProp(this.symlinks, abs))502return this._readdirInGlobStar(abs, cb)503504if (ownProp(this.cache, abs)) {505var c = this.cache[abs]506if (!c || c === 'FILE')507return cb()508509if (Array.isArray(c))510return cb(null, c)511}512513var self = this514fs.readdir(abs, readdirCb(this, abs, cb))515}516517function readdirCb (self, abs, cb) {518return function (er, entries) {519if (er)520self._readdirError(abs, er, cb)521else522self._readdirEntries(abs, entries, cb)523}524}525526Glob.prototype._readdirEntries = function (abs, entries, cb) {527if (this.aborted)528return529530// if we haven't asked to stat everything, then just531// assume that everything in there exists, so we can avoid532// having to stat it a second time.533if (!this.mark && !this.stat) {534for (var i = 0; i < entries.length; i ++) {535var e = entries[i]536if (abs === '/')537e = abs + e538else539e = abs + '/' + e540this.cache[e] = true541}542}543544this.cache[abs] = entries545return cb(null, entries)546}547548Glob.prototype._readdirError = function (f, er, cb) {549if (this.aborted)550return551552// handle errors, and cache the information553switch (er.code) {554case 'ENOTDIR': // totally normal. means it *does* exist.555this.cache[this._makeAbs(f)] = 'FILE'556break557558case 'ENOENT': // not terribly unusual559case 'ELOOP':560case 'ENAMETOOLONG':561case 'UNKNOWN':562this.cache[this._makeAbs(f)] = false563break564565default: // some unusual error. Treat as failure.566this.cache[this._makeAbs(f)] = false567if (this.strict) return this.emit('error', er)568if (!this.silent) console.error('glob error', er)569break570}571return cb()572}573574Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {575var self = this576this._readdir(abs, inGlobStar, function (er, entries) {577self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)578})579}580581582Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {583//console.error('pgs2', prefix, remain[0], entries)584585// no entries means not a dir, so it can never have matches586// foo.txt/** doesn't match foo.txt587if (!entries)588return cb()589590// test without the globstar, and with every child both below591// and replacing the globstar.592var remainWithoutGlobStar = remain.slice(1)593var gspref = prefix ? [ prefix ] : []594var noGlobStar = gspref.concat(remainWithoutGlobStar)595596// the noGlobStar pattern exits the inGlobStar state597this._process(noGlobStar, index, false, cb)598599var isSym = this.symlinks[abs]600var len = entries.length601602// If it's a symlink, and we're in a globstar, then stop603if (isSym && inGlobStar)604return cb()605606for (var i = 0; i < len; i++) {607var e = entries[i]608if (e.charAt(0) === '.' && !this.dot)609continue610611// these two cases enter the inGlobStar state612var instead = gspref.concat(entries[i], remainWithoutGlobStar)613this._process(instead, index, true, cb)614615var below = gspref.concat(entries[i], remain)616this._process(below, index, true, cb)617}618619cb()620}621622Glob.prototype._processSimple = function (prefix, index, cb) {623// XXX review this. Shouldn't it be doing the mounting etc624// before doing stat? kinda weird?625var self = this626this._stat(prefix, function (er, exists) {627self._processSimple2(prefix, index, er, exists, cb)628})629}630Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {631632//console.error('ps2', prefix, exists)633634if (!this.matches[index])635this.matches[index] = Object.create(null)636637// If it doesn't exist, then just mark the lack of results638if (!exists)639return cb()640641if (prefix && isAbsolute(prefix) && !this.nomount) {642var trail = /[\/\\]$/.test(prefix)643if (prefix.charAt(0) === '/') {644prefix = path.join(this.root, prefix)645} else {646prefix = path.resolve(this.root, prefix)647if (trail)648prefix += '/'649}650}651652if (process.platform === 'win32')653prefix = prefix.replace(/\\/g, '/')654655// Mark this as a match656this._emitMatch(index, prefix)657cb()658}659660// Returns either 'DIR', 'FILE', or false661Glob.prototype._stat = function (f, cb) {662var abs = this._makeAbs(f)663var needDir = f.slice(-1) === '/'664665if (f.length > this.maxLength)666return cb()667668if (!this.stat && ownProp(this.cache, abs)) {669var c = this.cache[abs]670671if (Array.isArray(c))672c = 'DIR'673674// It exists, but maybe not how we need it675if (!needDir || c === 'DIR')676return cb(null, c)677678if (needDir && c === 'FILE')679return cb()680681// otherwise we have to stat, because maybe c=true682// if we know it exists, but not what it is.683}684685var exists686var stat = this.statCache[abs]687if (stat !== undefined) {688if (stat === false)689return cb(null, stat)690else {691var type = stat.isDirectory() ? 'DIR' : 'FILE'692if (needDir && type === 'FILE')693return cb()694else695return cb(null, type, stat)696}697}698699var self = this700var statcb = inflight('stat\0' + abs, lstatcb_)701if (statcb)702fs.lstat(abs, statcb)703704function lstatcb_ (er, lstat) {705if (lstat && lstat.isSymbolicLink()) {706// If it's a symlink, then treat it as the target, unless707// the target does not exist, then treat it as a file.708return fs.stat(abs, function (er, stat) {709if (er)710self._stat2(f, abs, null, lstat, cb)711else712self._stat2(f, abs, er, stat, cb)713})714} else {715self._stat2(f, abs, er, lstat, cb)716}717}718}719720Glob.prototype._stat2 = function (f, abs, er, stat, cb) {721if (er) {722this.statCache[abs] = false723return cb()724}725726var needDir = f.slice(-1) === '/'727this.statCache[abs] = stat728729if (abs.slice(-1) === '/' && !stat.isDirectory())730return cb(null, false, stat)731732var c = stat.isDirectory() ? 'DIR' : 'FILE'733this.cache[abs] = this.cache[abs] || c734735if (needDir && c !== 'DIR')736return cb()737738return cb(null, c, stat)739}740741742