react / wstein / node_modules / jest-cli / node_modules / istanbul / node_modules / fileset / node_modules / glob / glob.js
80711 views// Approach:1//2// 1. Get the minimatch set3// 2. For each pattern in the set, PROCESS(pattern)4// 3. Store matches per-set, then uniq them5//6// PROCESS(pattern)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// readdir(PREFIX) as ENTRIES12// If fails, END13// If pattern[n] is GLOBSTAR14// // handle the case where the globstar match is empty15// // by pruning it out, and testing the resulting pattern16// PROCESS(pattern[0..n] + pattern[n+1 .. $])17// // handle other cases.18// for ENTRY in ENTRIES (not dotfiles)19// // attach globstar + tail onto the entry20// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $])21//22// else // not globstar23// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)24// Test ENTRY against pattern[n]25// If fails, continue26// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])27//28// Caveat:29// Cache all stats and readdirs results to minimize syscall. Since all30// we ever care about is existence and directory-ness, we can just keep31// `true` for files, and [children,...] for directories, or `false` for32// things that don't exist.33343536module.exports = glob3738var fs = require("fs")39, minimatch = require("minimatch")40, Minimatch = minimatch.Minimatch41, inherits = require("inherits")42, EE = require("events").EventEmitter43, path = require("path")44, isDir = {}45, assert = require("assert").ok4647function glob (pattern, options, cb) {48if (typeof options === "function") cb = options, options = {}49if (!options) options = {}5051if (typeof options === "number") {52deprecated()53return54}5556var g = new Glob(pattern, options, cb)57return g.sync ? g.found : g58}5960glob.fnmatch = deprecated6162function deprecated () {63throw new Error("glob's interface has changed. Please see the docs.")64}6566glob.sync = globSync67function globSync (pattern, options) {68if (typeof options === "number") {69deprecated()70return71}7273options = options || {}74options.sync = true75return glob(pattern, options)76}7778this._processingEmitQueue = false7980glob.Glob = Glob81inherits(Glob, EE)82function Glob (pattern, options, cb) {83if (!(this instanceof Glob)) {84return new Glob(pattern, options, cb)85}8687if (typeof options === "function") {88cb = options89options = null90}9192if (typeof cb === "function") {93this.on("error", cb)94this.on("end", function (matches) {95cb(null, matches)96})97}9899options = options || {}100101this._endEmitted = false102this.EOF = {}103this._emitQueue = []104105this.paused = false106this._processingEmitQueue = false107108this.maxDepth = options.maxDepth || 1000109this.maxLength = options.maxLength || Infinity110this.cache = options.cache || {}111this.statCache = options.statCache || {}112113this.changedCwd = false114var cwd = process.cwd()115if (!options.hasOwnProperty("cwd")) this.cwd = cwd116else {117this.cwd = options.cwd118this.changedCwd = path.resolve(options.cwd) !== cwd119}120121this.root = options.root || path.resolve(this.cwd, "/")122this.root = path.resolve(this.root)123if (process.platform === "win32")124this.root = this.root.replace(/\\/g, "/")125126this.nomount = !!options.nomount127128if (!pattern) {129throw new Error("must provide pattern")130}131132// base-matching: just use globstar for that.133if (options.matchBase && -1 === pattern.indexOf("/")) {134if (options.noglobstar) {135throw new Error("base matching requires globstar")136}137pattern = "**/" + pattern138}139140this.strict = options.strict !== false141this.dot = !!options.dot142this.mark = !!options.mark143this.sync = !!options.sync144this.nounique = !!options.nounique145this.nonull = !!options.nonull146this.nosort = !!options.nosort147this.nocase = !!options.nocase148this.stat = !!options.stat149150this.debug = !!options.debug || !!options.globDebug151if (this.debug)152this.log = console.error153154this.silent = !!options.silent155156var mm = this.minimatch = new Minimatch(pattern, options)157this.options = mm.options158pattern = this.pattern = mm.pattern159160this.error = null161this.aborted = false162163// list of all the patterns that ** has resolved do, so164// we can avoid visiting multiple times.165this._globstars = {}166167EE.call(this)168169// process each pattern in the minimatch set170var n = this.minimatch.set.length171172// The matches are stored as {<filename>: true,...} so that173// duplicates are automagically pruned.174// Later, we do an Object.keys() on these.175// Keep them as a list so we can fill in when nonull is set.176this.matches = new Array(n)177178this.minimatch.set.forEach(iterator.bind(this))179function iterator (pattern, i, set) {180this._process(pattern, 0, i, function (er) {181if (er) this.emit("error", er)182if (-- n <= 0) this._finish()183})184}185}186187Glob.prototype.log = function () {}188189Glob.prototype._finish = function () {190assert(this instanceof Glob)191192var nou = this.nounique193, all = nou ? [] : {}194195for (var i = 0, l = this.matches.length; i < l; i ++) {196var matches = this.matches[i]197this.log("matches[%d] =", i, matches)198// do like the shell, and spit out the literal glob199if (!matches) {200if (this.nonull) {201var literal = this.minimatch.globSet[i]202if (nou) all.push(literal)203else all[literal] = true204}205} else {206// had matches207var m = Object.keys(matches)208if (nou) all.push.apply(all, m)209else m.forEach(function (m) {210all[m] = true211})212}213}214215if (!nou) all = Object.keys(all)216217if (!this.nosort) {218all = all.sort(this.nocase ? alphasorti : alphasort)219}220221if (this.mark) {222// at *some* point we statted all of these223all = all.map(this._mark, this)224}225226this.log("emitting end", all)227228this.EOF = this.found = all229this.emitMatch(this.EOF)230}231232function alphasorti (a, b) {233a = a.toLowerCase()234b = b.toLowerCase()235return alphasort(a, b)236}237238function alphasort (a, b) {239return a > b ? 1 : a < b ? -1 : 0240}241242Glob.prototype._mark = function (p) {243var c = this.cache[p]244var m = p245if (c) {246var isDir = c === 2 || Array.isArray(c)247var slash = p.slice(-1) === '/'248249if (isDir && !slash)250m += '/'251else if (!isDir && slash)252m = m.slice(0, -1)253254if (m !== p) {255this.statCache[m] = this.statCache[p]256this.cache[m] = this.cache[p]257}258}259260return m261}262263Glob.prototype.abort = function () {264this.aborted = true265this.emit("abort")266}267268Glob.prototype.pause = function () {269if (this.paused) return270if (this.sync)271this.emit("error", new Error("Can't pause/resume sync glob"))272this.paused = true273this.emit("pause")274}275276Glob.prototype.resume = function () {277if (!this.paused) return278if (this.sync)279this.emit("error", new Error("Can't pause/resume sync glob"))280this.paused = false281this.emit("resume")282this._processEmitQueue()283//process.nextTick(this.emit.bind(this, "resume"))284}285286Glob.prototype.emitMatch = function (m) {287this.log('emitMatch', m)288this._emitQueue.push(m)289this._processEmitQueue()290}291292Glob.prototype._processEmitQueue = function (m) {293this.log("pEQ paused=%j processing=%j m=%j", this.paused,294this._processingEmitQueue, m)295var done = false296while (!this._processingEmitQueue &&297!this.paused) {298this._processingEmitQueue = true299var m = this._emitQueue.shift()300this.log(">processEmitQueue", m === this.EOF ? ":EOF:" : m)301if (!m) {302this.log(">processEmitQueue, falsey m")303this._processingEmitQueue = false304break305}306307if (m === this.EOF || !(this.mark && !this.stat)) {308this.log("peq: unmarked, or eof")309next.call(this, 0, false)310} else if (this.statCache[m]) {311var sc = this.statCache[m]312var exists313if (sc)314exists = sc.isDirectory() ? 2 : 1315this.log("peq: stat cached")316next.call(this, exists, exists === 2)317} else {318this.log("peq: _stat, then next")319this._stat(m, next)320}321322function next(exists, isDir) {323this.log("next", m, exists, isDir)324var ev = m === this.EOF ? "end" : "match"325326// "end" can only happen once.327assert(!this._endEmitted)328if (ev === "end")329this._endEmitted = true330331if (exists) {332// Doesn't mean it necessarily doesn't exist, it's possible333// we just didn't check because we don't care that much, or334// this is EOF anyway.335if (isDir && !m.match(/\/$/)) {336m = m + "/"337} else if (!isDir && m.match(/\/$/)) {338m = m.replace(/\/+$/, "")339}340}341this.log("emit", ev, m)342this.emit(ev, m)343this._processingEmitQueue = false344if (done && m !== this.EOF && !this.paused)345this._processEmitQueue()346}347}348done = true349}350351Glob.prototype._process = function (pattern, depth, index, cb_) {352assert(this instanceof Glob)353354var cb = function cb (er, res) {355assert(this instanceof Glob)356if (this.paused) {357if (!this._processQueue) {358this._processQueue = []359this.once("resume", function () {360var q = this._processQueue361this._processQueue = null362q.forEach(function (cb) { cb() })363})364}365this._processQueue.push(cb_.bind(this, er, res))366} else {367cb_.call(this, er, res)368}369}.bind(this)370371if (this.aborted) return cb()372373if (depth > this.maxDepth) return cb()374375// Get the first [n] parts of pattern that are all strings.376var n = 0377while (typeof pattern[n] === "string") {378n ++379}380// now n is the index of the first one that is *not* a string.381382// see if there's anything else383var prefix384switch (n) {385// if not, then this is rather simple386case pattern.length:387prefix = pattern.join("/")388this._stat(prefix, function (exists, isDir) {389// either it's there, or it isn't.390// nothing more to do, either way.391if (exists) {392if (prefix && isAbsolute(prefix) && !this.nomount) {393if (prefix.charAt(0) === "/") {394prefix = path.join(this.root, prefix)395} else {396prefix = path.resolve(this.root, prefix)397}398}399400if (process.platform === "win32")401prefix = prefix.replace(/\\/g, "/")402403this.matches[index] = this.matches[index] || {}404this.matches[index][prefix] = true405this.emitMatch(prefix)406}407return cb()408})409return410411case 0:412// pattern *starts* with some non-trivial item.413// going to readdir(cwd), but not include the prefix in matches.414prefix = null415break416417default:418// pattern has some string bits in the front.419// whatever it starts with, whether that's "absolute" like /foo/bar,420// or "relative" like "../baz"421prefix = pattern.slice(0, n)422prefix = prefix.join("/")423break424}425426// get the list of entries.427var read428if (prefix === null) read = "."429else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) {430if (!prefix || !isAbsolute(prefix)) {431prefix = path.join("/", prefix)432}433read = prefix = path.resolve(prefix)434435// if (process.platform === "win32")436// read = prefix = prefix.replace(/^[a-zA-Z]:|\\/g, "/")437438this.log('absolute: ', prefix, this.root, pattern, read)439} else {440read = prefix441}442443this.log('readdir(%j)', read, this.cwd, this.root)444445return this._readdir(read, function (er, entries) {446if (er) {447// not a directory!448// this means that, whatever else comes after this, it can never match449return cb()450}451452// globstar is special453if (pattern[n] === minimatch.GLOBSTAR) {454// test without the globstar, and with every child both below455// and replacing the globstar.456var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ]457entries.forEach(function (e) {458if (e.charAt(0) === "." && !this.dot) return459// instead of the globstar460s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)))461// below the globstar462s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))463}, this)464465s = s.filter(function (pattern) {466var key = gsKey(pattern)467var seen = !this._globstars[key]468this._globstars[key] = true469return seen470}, this)471472if (!s.length)473return cb()474475// now asyncForEach over this476var l = s.length477, errState = null478s.forEach(function (gsPattern) {479this._process(gsPattern, depth + 1, index, function (er) {480if (errState) return481if (er) return cb(errState = er)482if (--l <= 0) return cb()483})484}, this)485486return487}488489// not a globstar490// It will only match dot entries if it starts with a dot, or if491// dot is set. Stuff like @(.foo|.bar) isn't allowed.492var pn = pattern[n]493var rawGlob = pattern[n]._glob494, dotOk = this.dot || rawGlob.charAt(0) === "."495496entries = entries.filter(function (e) {497return (e.charAt(0) !== "." || dotOk) &&498e.match(pattern[n])499})500501// If n === pattern.length - 1, then there's no need for the extra stat502// *unless* the user has specified "mark" or "stat" explicitly.503// We know that they exist, since the readdir returned them.504if (n === pattern.length - 1 &&505!this.mark &&506!this.stat) {507entries.forEach(function (e) {508if (prefix) {509if (prefix !== "/") e = prefix + "/" + e510else e = prefix + e511}512if (e.charAt(0) === "/" && !this.nomount) {513e = path.join(this.root, e)514}515516if (process.platform === "win32")517e = e.replace(/\\/g, "/")518519this.matches[index] = this.matches[index] || {}520this.matches[index][e] = true521this.emitMatch(e)522}, this)523return cb.call(this)524}525526527// now test all the remaining entries as stand-ins for that part528// of the pattern.529var l = entries.length530, errState = null531if (l === 0) return cb() // no matches possible532entries.forEach(function (e) {533var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))534this._process(p, depth + 1, index, function (er) {535if (errState) return536if (er) return cb(errState = er)537if (--l === 0) return cb.call(this)538})539}, this)540})541542}543544function gsKey (pattern) {545return '**' + pattern.map(function (p) {546return (p === minimatch.GLOBSTAR) ? '**' : (''+p)547}).join('/')548}549550Glob.prototype._stat = function (f, cb) {551assert(this instanceof Glob)552var abs = f553if (f.charAt(0) === "/") {554abs = path.join(this.root, f)555} else if (this.changedCwd) {556abs = path.resolve(this.cwd, f)557}558559if (f.length > this.maxLength) {560var er = new Error("Path name too long")561er.code = "ENAMETOOLONG"562er.path = f563return this._afterStat(f, abs, cb, er)564}565566this.log('stat', [this.cwd, f, '=', abs])567568if (!this.stat && this.cache.hasOwnProperty(f)) {569var exists = this.cache[f]570, isDir = exists && (Array.isArray(exists) || exists === 2)571if (this.sync) return cb.call(this, !!exists, isDir)572return process.nextTick(cb.bind(this, !!exists, isDir))573}574575var stat = this.statCache[abs]576if (this.sync || stat) {577var er578try {579stat = fs.statSync(abs)580} catch (e) {581er = e582}583this._afterStat(f, abs, cb, er, stat)584} else {585fs.stat(abs, this._afterStat.bind(this, f, abs, cb))586}587}588589Glob.prototype._afterStat = function (f, abs, cb, er, stat) {590var exists591assert(this instanceof Glob)592593if (abs.slice(-1) === "/" && stat && !stat.isDirectory()) {594this.log("should be ENOTDIR, fake it")595596er = new Error("ENOTDIR, not a directory '" + abs + "'")597er.path = abs598er.code = "ENOTDIR"599stat = null600}601602var emit = !this.statCache[abs]603this.statCache[abs] = stat604605if (er || !stat) {606exists = false607} else {608exists = stat.isDirectory() ? 2 : 1609if (emit)610this.emit('stat', f, stat)611}612this.cache[f] = this.cache[f] || exists613cb.call(this, !!exists, exists === 2)614}615616Glob.prototype._readdir = function (f, cb) {617assert(this instanceof Glob)618var abs = f619if (f.charAt(0) === "/") {620abs = path.join(this.root, f)621} else if (isAbsolute(f)) {622abs = f623} else if (this.changedCwd) {624abs = path.resolve(this.cwd, f)625}626627if (f.length > this.maxLength) {628var er = new Error("Path name too long")629er.code = "ENAMETOOLONG"630er.path = f631return this._afterReaddir(f, abs, cb, er)632}633634this.log('readdir', [this.cwd, f, abs])635if (this.cache.hasOwnProperty(f)) {636var c = this.cache[f]637if (Array.isArray(c)) {638if (this.sync) return cb.call(this, null, c)639return process.nextTick(cb.bind(this, null, c))640}641642if (!c || c === 1) {643// either ENOENT or ENOTDIR644var code = c ? "ENOTDIR" : "ENOENT"645, er = new Error((c ? "Not a directory" : "Not found") + ": " + f)646er.path = f647er.code = code648this.log(f, er)649if (this.sync) return cb.call(this, er)650return process.nextTick(cb.bind(this, er))651}652653// at this point, c === 2, meaning it's a dir, but we haven't654// had to read it yet, or c === true, meaning it's *something*655// but we don't have any idea what. Need to read it, either way.656}657658if (this.sync) {659var er, entries660try {661entries = fs.readdirSync(abs)662} catch (e) {663er = e664}665return this._afterReaddir(f, abs, cb, er, entries)666}667668fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb))669}670671Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {672assert(this instanceof Glob)673if (entries && !er) {674this.cache[f] = entries675// if we haven't asked to stat everything for suresies, then just676// assume that everything in there exists, so we can avoid677// having to stat it a second time. This also gets us one step678// further into ELOOP territory.679if (!this.mark && !this.stat) {680entries.forEach(function (e) {681if (f === "/") e = f + e682else e = f + "/" + e683this.cache[e] = true684}, this)685}686687return cb.call(this, er, entries)688}689690// now handle errors, and cache the information691if (er) switch (er.code) {692case "ENOTDIR": // totally normal. means it *does* exist.693this.cache[f] = 1694return cb.call(this, er)695case "ENOENT": // not terribly unusual696case "ELOOP":697case "ENAMETOOLONG":698case "UNKNOWN":699this.cache[f] = false700return cb.call(this, er)701default: // some unusual error. Treat as failure.702this.cache[f] = false703if (this.strict) this.emit("error", er)704if (!this.silent) console.error("glob error", er)705return cb.call(this, er)706}707}708709var isAbsolute = process.platform === "win32" ? absWin : absUnix710711function absWin (p) {712if (absUnix(p)) return true713// pull off the device/UNC bit from a windows path.714// from node's lib/path.js715var splitDeviceRe =716/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/717, result = splitDeviceRe.exec(p)718, device = result[1] || ''719, isUnc = device && device.charAt(1) !== ':'720, isAbsolute = !!result[2] || isUnc // UNC paths are always absolute721722return isAbsolute723}724725function absUnix (p) {726return p.charAt(0) === "/" || p === ""727}728729730