react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / node_modules / commoner / node_modules / glob / glob.js
81164 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 isAbsolute = common.isAbsolute53var setopts = common.setopts54var ownProp = common.ownProp55var inflight = require("inflight")56var util = require("util")5758var once = require("once")5960function glob (pattern, options, cb) {61if (typeof options === "function") cb = options, options = {}62if (!options) options = {}6364if (options.sync) {65if (cb)66throw new TypeError('callback provided to sync glob')67return globSync(pattern, options)68}6970return new Glob(pattern, options, cb)71}7273glob.sync = globSync74var GlobSync = glob.GlobSync = globSync.GlobSync7576// old api surface77glob.glob = glob7879glob.hasMagic = function (pattern, options_) {80var options = util._extend({}, options_)81options.noprocess = true8283var g = new Glob(pattern, options)84var set = g.minimatch.set85if (set.length > 1)86return true8788for (var j = 0; j < set[0].length; j++) {89if (typeof set[0][j] !== 'string')90return true91}9293return false94}9596glob.Glob = Glob97inherits(Glob, EE)98function Glob (pattern, options, cb) {99if (typeof options === "function") {100cb = options101options = null102}103104if (options && options.sync) {105if (cb)106throw new TypeError('callback provided to sync glob')107return new GlobSync(pattern, options)108}109110if (!(this instanceof Glob))111return new Glob(pattern, options, cb)112113setopts(this, pattern, options)114115// process each pattern in the minimatch set116var n = this.minimatch.set.length117118// The matches are stored as {<filename>: true,...} so that119// duplicates are automagically pruned.120// Later, we do an Object.keys() on these.121// Keep them as a list so we can fill in when nonull is set.122this.matches = new Array(n)123124if (typeof cb === "function") {125cb = once(cb)126this.on("error", cb)127this.on("end", function (matches) {128cb(null, matches)129})130}131132var self = this133var n = this.minimatch.set.length134this._processing = 0135this.matches = new Array(n)136137this._emitQueue = []138this._processQueue = []139this.paused = false140141if (this.noprocess)142return this143144if (n === 0)145return done()146147for (var i = 0; i < n; i ++) {148this._process(this.minimatch.set[i], i, false, done)149}150151function done () {152--self._processing153if (self._processing <= 0)154self._finish()155}156}157158Glob.prototype._finish = function () {159assert(this instanceof Glob)160if (this.aborted)161return162163//console.error('FINISH', this.matches)164common.finish(this)165this.emit("end", this.found)166}167168Glob.prototype._mark = function (p) {169return common.mark(this, p)170}171172Glob.prototype._makeAbs = function (f) {173return common.makeAbs(this, f)174}175176Glob.prototype.abort = function () {177this.aborted = true178this.emit("abort")179}180181Glob.prototype.pause = function () {182if (!this.paused) {183this.paused = true184this.emit("pause")185}186}187188Glob.prototype.resume = function () {189if (this.paused) {190this.emit("resume")191this.paused = false192if (this._emitQueue.length) {193var eq = this._emitQueue.slice(0)194this._emitQueue.length = 0195for (var i = 0; i < eq.length; i ++) {196var e = eq[i]197this._emitMatch(e[0], e[1])198}199}200if (this._processQueue.length) {201var pq = this._processQueue.slice(0)202this._processQueue.length = 0203for (var i = 0; i < pq.length; i ++) {204var p = pq[i]205this._processing--206this._process(p[0], p[1], p[2], p[3])207}208}209}210}211212Glob.prototype._process = function (pattern, index, inGlobStar, cb) {213assert(this instanceof Glob)214assert(typeof cb === 'function')215216if (this.aborted)217return218219this._processing++220if (this.paused) {221this._processQueue.push([pattern, index, inGlobStar, cb])222return223}224225//console.error("PROCESS %d", this._processing, pattern)226227// Get the first [n] parts of pattern that are all strings.228var n = 0229while (typeof pattern[n] === "string") {230n ++231}232// now n is the index of the first one that is *not* a string.233234// see if there's anything else235var prefix236switch (n) {237// if not, then this is rather simple238case pattern.length:239this._processSimple(pattern.join('/'), index, cb)240return241242case 0:243// pattern *starts* with some non-trivial item.244// going to readdir(cwd), but not include the prefix in matches.245prefix = null246break247248default:249// pattern has some string bits in the front.250// whatever it starts with, whether that's "absolute" like /foo/bar,251// or "relative" like "../baz"252prefix = pattern.slice(0, n).join("/")253break254}255256var remain = pattern.slice(n)257258// get the list of entries.259var read260if (prefix === null)261read = "."262else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {263if (!prefix || !isAbsolute(prefix))264prefix = "/" + prefix265read = prefix266} else267read = prefix268269var abs = this._makeAbs(read)270271var isGlobStar = remain[0] === minimatch.GLOBSTAR272if (isGlobStar)273this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)274else275this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)276}277278279Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {280var self = this281this._readdir(abs, inGlobStar, function (er, entries) {282return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)283})284}285286Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {287288// if the abs isn't a dir, then nothing can match!289if (!entries)290return cb()291292// It will only match dot entries if it starts with a dot, or if293// dot is set. Stuff like @(.foo|.bar) isn't allowed.294var pn = remain[0]295var negate = !!this.minimatch.negate296var rawGlob = pn._glob297var dotOk = this.dot || rawGlob.charAt(0) === "."298299var matchedEntries = []300for (var i = 0; i < entries.length; i++) {301var e = entries[i]302if (e.charAt(0) !== "." || dotOk) {303var m304if (negate && !prefix) {305m = !e.match(pn)306} else {307m = e.match(pn)308}309if (m)310matchedEntries.push(e)311}312}313314//console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)315316var len = matchedEntries.length317// If there are no matched entries, then nothing matches.318if (len === 0)319return cb()320321// if this is the last remaining pattern bit, then no need for322// an additional stat *unless* the user has specified mark or323// stat explicitly. We know they exist, since readdir returned324// them.325326if (remain.length === 1 && !this.mark && !this.stat) {327if (!this.matches[index])328this.matches[index] = Object.create(null)329330for (var i = 0; i < len; i ++) {331var e = matchedEntries[i]332if (prefix) {333if (prefix !== "/")334e = prefix + "/" + e335else336e = prefix + e337}338339if (e.charAt(0) === "/" && !this.nomount) {340e = path.join(this.root, e)341}342this._emitMatch(index, e)343}344// This was the last one, and no stats were needed345return cb()346}347348// now test all matched entries as stand-ins for that part349// of the pattern.350remain.shift()351for (var i = 0; i < len; i ++) {352var e = matchedEntries[i]353var newPattern354if (prefix) {355if (prefix !== "/")356e = prefix + "/" + e357else358e = prefix + e359}360this._process([e].concat(remain), index, inGlobStar, cb)361}362cb()363}364365Glob.prototype._emitMatch = function (index, e) {366if (this.aborted)367return368369if (!this.matches[index][e]) {370if (this.paused) {371this._emitQueue.push([index, e])372return373}374375if (this.nodir) {376var c = this.cache[this._makeAbs(e)]377if (c === 'DIR' || Array.isArray(c))378return379}380381this.matches[index][e] = true382if (!this.stat && !this.mark)383return this.emit("match", e)384385var self = this386this._stat(this._makeAbs(e), function (er, c, st) {387self.emit("stat", e, st)388self.emit("match", e)389})390}391}392393Glob.prototype._readdirInGlobStar = function (abs, cb) {394if (this.aborted)395return396397var lstatkey = "lstat\0" + abs398var self = this399var lstatcb = inflight(lstatkey, lstatcb_)400401if (lstatcb)402fs.lstat(abs, lstatcb)403404function lstatcb_ (er, lstat) {405if (er)406return cb()407408var isSym = lstat.isSymbolicLink()409self.symlinks[abs] = isSym410411// If it's not a symlink or a dir, then it's definitely a regular file.412// don't bother doing a readdir in that case.413if (!isSym && !lstat.isDirectory()) {414self.cache[abs] = 'FILE'415cb()416} else417self._readdir(abs, false, cb)418}419}420421Glob.prototype._readdir = function (abs, inGlobStar, cb) {422if (this.aborted)423return424425cb = inflight("readdir\0"+abs+"\0"+inGlobStar, cb)426if (!cb)427return428429//console.error("RD %j %j", +inGlobStar, abs)430if (inGlobStar && !ownProp(this.symlinks, abs))431return this._readdirInGlobStar(abs, cb)432433if (ownProp(this.cache, abs)) {434var c = this.cache[abs]435if (!c || c === 'FILE')436return cb()437438if (Array.isArray(c))439return cb(null, c)440}441442var self = this443fs.readdir(abs, readdirCb(this, abs, cb))444}445446function readdirCb (self, abs, cb) {447return function (er, entries) {448if (er)449self._readdirError(abs, er, cb)450else451self._readdirEntries(abs, entries.sort(alphasort), cb)452}453}454455Glob.prototype._readdirEntries = function (abs, entries, cb) {456if (this.aborted)457return458459// if we haven't asked to stat everything, then just460// assume that everything in there exists, so we can avoid461// having to stat it a second time.462if (!this.mark && !this.stat) {463for (var i = 0; i < entries.length; i ++) {464var e = entries[i]465if (abs === "/")466e = abs + e467else468e = abs + "/" + e469this.cache[e] = true470}471}472473this.cache[abs] = entries474return cb(null, entries)475}476477Glob.prototype._readdirError = function (f, er, cb) {478if (this.aborted)479return480481// handle errors, and cache the information482switch (er.code) {483case "ENOTDIR": // totally normal. means it *does* exist.484this.cache[f] = 'FILE'485break486487case "ENOENT": // not terribly unusual488case "ELOOP":489case "ENAMETOOLONG":490case "UNKNOWN":491this.cache[f] = false492break493494default: // some unusual error. Treat as failure.495this.cache[f] = false496if (this.strict) return this.emit("error", er)497if (!this.silent) console.error("glob error", er)498break499}500return cb()501}502503Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {504var self = this505this._readdir(abs, inGlobStar, function (er, entries) {506self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)507})508}509510511Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {512//console.error("pgs2", prefix, remain[0], entries)513514// no entries means not a dir, so it can never have matches515// foo.txt/** doesn't match foo.txt516if (!entries)517return cb()518519// test without the globstar, and with every child both below520// and replacing the globstar.521var remainWithoutGlobStar = remain.slice(1)522var gspref = prefix ? [ prefix ] : []523var noGlobStar = gspref.concat(remainWithoutGlobStar)524525// the noGlobStar pattern exits the inGlobStar state526this._process(noGlobStar, index, false, cb)527528var isSym = this.symlinks[abs]529var len = entries.length530531// If it's a symlink, and we're in a globstar, then stop532if (isSym && inGlobStar)533return cb()534535for (var i = 0; i < len; i++) {536var e = entries[i]537if (e.charAt(0) === "." && !this.dot)538continue539540// these two cases enter the inGlobStar state541var instead = gspref.concat(entries[i], remainWithoutGlobStar)542this._process(instead, index, true, cb)543544var below = gspref.concat(entries[i], remain)545this._process(below, index, true, cb)546}547548cb()549}550551Glob.prototype._processSimple = function (prefix, index, cb) {552// XXX review this. Shouldn't it be doing the mounting etc553// before doing stat? kinda weird?554var self = this555this._stat(prefix, function (er, exists) {556self._processSimple2(prefix, index, er, exists, cb)557})558}559Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {560561//console.error("ps2", prefix, exists)562563if (!this.matches[index])564this.matches[index] = Object.create(null)565566// If it doesn't exist, then just mark the lack of results567if (!exists)568return cb()569570if (prefix && isAbsolute(prefix) && !this.nomount) {571if (prefix.charAt(0) === "/") {572prefix = path.join(this.root, prefix)573} else {574prefix = path.resolve(this.root, prefix)575}576}577578if (process.platform === "win32")579prefix = prefix.replace(/\\/g, "/")580581// Mark this as a match582this._emitMatch(index, prefix)583cb()584}585586// Returns either 'DIR', 'FILE', or false587Glob.prototype._stat = function (f, cb) {588var abs = f589if (f.charAt(0) === "/")590abs = path.join(this.root, f)591else if (this.changedCwd)592abs = path.resolve(this.cwd, f)593594595if (f.length > this.maxLength)596return cb()597598if (!this.stat && ownProp(this.cache, f)) {599var c = this.cache[f]600601if (Array.isArray(c))602c = 'DIR'603604// It exists, but not how we need it605if (abs.slice(-1) === "/" && c !== 'DIR')606return cb()607608return cb(null, c)609}610611var exists612var stat = this.statCache[abs]613if (stat !== undefined) {614if (stat === false)615return cb(null, stat)616else617return cb(null, stat.isDirectory() ? 'DIR' : 'FILE', stat)618}619620var self = this621var statcb = inflight("stat\0" + abs, statcb_)622if (statcb)623fs.stat(abs, statcb)624625function statcb_ (er, stat) {626self._stat2(f, abs, er, stat, cb)627}628}629630Glob.prototype._stat2 = function (f, abs, er, stat, cb) {631if (er) {632this.statCache[abs] = false633return cb()634}635636this.statCache[abs] = stat637638if (abs.slice(-1) === "/" && !stat.isDirectory())639return cb(null, false, stat)640641var c = stat.isDirectory() ? 'DIR' : 'FILE'642this.cache[f] = this.cache[f] || c643return cb(null, c, stat)644}645646647