学习NodeJs一讲——模块

来源:互联网 发布:剑三最美喵萝脸型数据 编辑:程序博客网 时间:2024/06/11 11:08

一、写在前面的讨论:

  1. 关于NodeJs是单线程,不能充分利用CPU的说法
    (1) Nodejs 所谓的单线程是指你写的 JS 代码是单线程,事件驱动跑的是 C++ 代码,背后的原生模块及其IO操作,C++ 扩展模块都是运行在线程池当中。
    (2) NodeJs 极大地节省了线程资源和内存资源,从而也大幅降低了 CPU 上下文切换的次数。是并发编程中充分利用CPU性能的典范。(考虑到 IO 操作的比例和 CPU 的主频,线程池的大小是有一个最佳值的,当 CPU 核心数远超这一值的时候,比如16核心28核心等,也可以开启多个 node 进程。但是一个 node 有效利用的 CPU 性能就远超大多数传统服务端在同核心甚至两三倍核心下的性能了。)

  2. Nodejs不支持计算密集型应用
    NodeJs 的 C++ 扩展模块功能完全可以实现多线程计算密集型应用。(只是 JS 本身不行而已,但是不要忘了 node 底层跑的是 C++ !)
    NodeJs 当中的主线程仅仅负责 IO/计算任务的分配和把结果响应给客户端的操作,所有具体的过程都可以用 NodeJs 原生模块或扩展模块实现。

  3. Nodejs比传统服务端有更高的IO性能
    IO 性能的瓶颈从来都不是后端软件,而是硬件 IO,NodeJs 着重于提高 CPU 使用效率来提供更高的并发,同时也提供了更高的 IO 上限,但是对于相同的硬件,只要服务器所需承受的并发请求对应的 IO 操作可以达到硬件 IO 的极限,那么 node 和传统服务端并不会有多少区别。

至于 node 能走到何种地步,很多人认为 node 已经是一个可以完全挖掘JS潜力的存在了。所以 node 的未来,取决于 JS 语言本身的发展情况。

二、基本用例

1.使用 NodeJs 编写 Web 服务器,响应返回 Hello World:

const http = require('http');const hostname = '127.0.0.1';const port = 3000;const server = http.createServer((req, res) => {  res.statusCode = 200;  res.setHeader('Content-Type', 'text/plain');  res.end('Hello World\n');});server.listen(port, hostname, () => {  console.log(`Server running at http://${hostname}:${port}/`);});

2.加载文件模块实现运算
(1)add.js

module.exports = function(a, b){return a + b;}

(2)main.js

var add = require('./add');console.log(add(1, 2));

三、模块系统介绍

模块是 NodeJs 的基本组成部分,同时也是 NodeJs 的强大之处。一个 NodeJs 文件就是一个模块,当然,这个文件可能是 JavaScript 代码、JSON、或者是编译过的 C/C++ 扩展。
NodeJs 提供 exports 和 require 两个对象,其中 exports 是模块公开的接口, require 用于从外部获取模块的接口,即所获取模块的 exports 对象。对于模块的创建也有多种方式,例如:

//hello.jsexports.world = function() {  console.log('Hello World');}

//hello.js function Hello() { var name; this.setName = function(thyName) { name = thyName; }; this.sayHello = function() { console.log('Hello ' + name); }; }; module.exports = Hello;
//main.js var Hello = require('./hello'); hello = new Hello(); hello.setName('BYVoid'); hello.sayHello();
//circle.jsconst PI = Math.PI;exports.area = (r) => PI * r * r;//exports直接导出的函数exports.circumference = (r) => 2 * PI * r;//foo.jsconst circle = require('./circle.js');console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
//square.js//导出一个对象module.exports = (width) => {return {area: () => width * width};}//bar.jsconst square = require('./square.js');var mySquare = square(2);console.log(`The area of my square is ${mySquare.area()}`);
模块的重要概念
1、NodeJs 的 require 方法的文件查找策略,详细的策略如下图:

image

  • 文件模块缓存中加载
    拥有最高的优先级
  • 从原生模块加载
    原生模块的优先级仅次于文件模块缓存的优先级。require 方法在解析文件名后,优先检查模块是否在原生模块列表中。以 http 模块为例,尽管在目录下存在一个 http/http.js/http.node/http.json 文件,require('http') 都不会从这些文件中加载,而是从原生模块中加载。同样,原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。
  • 从文件加载 当文件模块缓存中不存在,而且不是原生模块的时候,NodeJs 会解析 require 方法传入的参数,并从文件系统中加载实际的文件,加载过程中也包括包装、编译等流程。
//require.resolve//the high-level algorithm in pseudocoderequire(X) from module at path Y1. If X is a core module,   a. return the core module   b. STOP2. If X begins with './' or '/' or '../'   a. LOAD_AS_FILE(Y + X)   b. LOAD_AS_DIRECTORY(Y + X)3. LOAD_NODE_MODULES(X, dirname(Y))4. THROW "not found"LOAD_AS_FILE(X)1. If X is a file, load X as JavaScript text.  STOP2. If X.js is a file, load X.js as JavaScript text.  STOP3. If X.json is a file, parse X.json to a JavaScript Object.  STOP4. If X.node is a file, load X.node as binary addon.  STOPLOAD_AS_DIRECTORY(X)1. If X/package.json is a file,   a. Parse X/package.json, and look for "main" field.   b. let M = X + (json main field)   c. LOAD_AS_FILE(M)2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP4. If X/index.node is a file, load X/index.node as binary addon.  STOPLOAD_NODE_MODULES(X, START)1. let DIRS=NODE_MODULES_PATHS(START)2. for each DIR in DIRS:   a. LOAD_AS_FILE(DIR/X)   b. LOAD_AS_DIRECTORY(DIR/X)NODE_MODULES_PATHS(START)1. let PARTS = path split(START)2. let I = count of PARTS - 13. let DIRS = []4. while I >= 0,   a. if PARTS[I] = "node_modules" CONTINUE   b. DIR = path join(PARTS[0 .. I] + "node_modules")   c. DIRS = DIRS + DIR   d. let I = I - 15. return DIRS
2、模块加载缓存

模块第一次加载后就会被缓冲,意味着每次调用 require('foo')都将会解析到同一个文件,返回完全一样的对象。
多次调用并不会引起模块代码的多次执行。

//add.jsmodule.exports = (function(a, b){console.log(123);})();//main.jsrequire('./add');require('./add');require('./add');

这是一个重要的特性,因为有了它, "partially done" objects can be returned,这样,传递依赖也可以被加载了,即使他们产生了循环依赖。如下面的例子:

/*cycles*///a.jsconsole.log('a starting');exports.done = false;const b = require('./b.js');console.log('in a, b.done = %j', b.done);exports.done = true;console.log('a done');//b.jsconsole.log('b starting');exports.done = false;const a = require('./a.js');console.log('in b, a.done = %j', a.done);exports.done = true;console.log('b done');//main.jsconsole.log('main starting');const a = require('./a.js');const b = require('./b.js');console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
3、大小写严格
4、模块打包

在模块执行之前,NodeJs 将会用函数打包模块,例如这样:

(function (exports, require, module, __filename, __dirname) {// Your module code actually lives in here});

require方法接受以下几种参数的传递:

  • http、fs、path等,原生模块。
  • ./mod或../mod,相对路径的文件模块。
  • /pathtomodule/mod,绝对路径的文件模块。
  • mod,非原生模块的文件模块。
example:

在 /home/ry/projects/foo.js 中 require('bar.js'), NodeJs 将会查找这些目录,一直到根目录:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js
    这也就是为什么依赖可以局部化,互相直接没有冲突。

通过包管理器管理模块,require 加载模块也有很多细节,例如在 foo 文件中 require('bar'), 这个操作通常是连接到/usr/lib/node/foo/1.2.3/node_modules/bar, foo后面会有版本号。

四、例子介绍

webpack 配置文件中用到过的一些模块:

var glob = require('glob');// 获取指定路径下的入口文件function getEntries(globPath) {    var files = glob.sync(globPath),        entries = {};    files.forEach(function (filepath) {        var split = filepath.split('/');        var name = split[split.length - 3];        var entity = [];        entity.push('./' + filepath);        entries[name] = entity;    });    return entries;}

node 的 glob 模块允许你使用 * 等符号,来写一个 glob 规则,像在 shell 里一样,获取匹配对应规则的文件。glob 工具是基于 JavaScript 的,它使用了 minimatch 库来进行匹配。

调用格式实例:

var glob = require("glob")// options 是可选的glob("**/*.js", options, function (er, files) {  // files 是匹配到的文件的数组.  // 如果 `nonull` 选项被设置为true, 而且没有找到任何文件,那么files就是glob规则本身,而不是空数组  // er是当寻找的过程中遇的错误})

它可以让我们解析的路径中的某一段使用下面这些字符表示,来实现更加灵活的匹配规则。

//glob.jsmodule.exports = globvar fs = require('fs')var minimatch = require('minimatch')var Minimatch = minimatch.Minimatchvar inherits = require('inherits')var EE = require('events').EventEmittervar path = require('path')var assert = require('assert')var isAbsolute = require('path-is-absolute')var globSync = require('./sync.js')var common = require('./common.js')var alphasort = common.alphasortvar alphasorti = common.alphasortivar setopts = common.setoptsvar ownProp = common.ownPropvar inflight = require('inflight')var util = require('util')var childrenIgnored = common.childrenIgnoredvar isIgnored = common.isIgnoredvar once = require('once')function glob (pattern, options, cb) {  if (typeof options === 'function') cb = options, options = {}  if (!options) options = {}  if (options.sync) {    if (cb)      throw new TypeError('callback provided to sync glob')    return globSync(pattern, options)  }  return new Glob(pattern, options, cb)}glob.sync = globSyncvar GlobSync = glob.GlobSync = globSync.GlobSync// old api surfaceglob.glob = globglob.hasMagic = function (pattern, options_) {  var options = util._extend({}, options_)  options.noprocess = true  var g = new Glob(pattern, options)  var set = g.minimatch.set  if (set.length > 1)    return true  for (var j = 0; j < set[0].length; j++) {    if (typeof set[0][j] !== 'string')      return true  }  return false}glob.Glob = Globinherits(Glob, EE)function Glob (pattern, options, cb) {  if (typeof options === 'function') {    cb = options    options = null  }  if (options && options.sync) {    if (cb)      throw new TypeError('callback provided to sync glob')    return new GlobSync(pattern, options)  }  if (!(this instanceof Glob))    return new Glob(pattern, options, cb)  setopts(this, pattern, options)  this._didRealPath = false  // process each pattern in the minimatch set  var n = this.minimatch.set.length  // The matches are stored as {<filename>: true,...} so that  // duplicates are automagically pruned.  // Later, we do an Object.keys() on these.  // Keep them as a list so we can fill in when nonull is set.  this.matches = new Array(n)  if (typeof cb === 'function') {    cb = once(cb)    this.on('error', cb)    this.on('end', function (matches) {      cb(null, matches)    })  }  var self = this  var n = this.minimatch.set.length  this._processing = 0  this.matches = new Array(n)  this._emitQueue = []  this._processQueue = []  this.paused = false  if (this.noprocess)    return this  if (n === 0)    return done()  for (var i = 0; i < n; i ++) {    this._process(this.minimatch.set[i], i, false, done)  }  function done () {    --self._processing    if (self._processing <= 0)      self._finish()  }}Glob.prototype._finish = function () {  assert(this instanceof Glob)  if (this.aborted)    return  if (this.realpath && !this._didRealpath)    return this._realpath()  common.finish(this)  this.emit('end', this.found)}Glob.prototype._realpath = function () {  if (this._didRealpath)    return  this._didRealpath = true  var n = this.matches.length  if (n === 0)    return this._finish()  var self = this  for (var i = 0; i < this.matches.length; i++)    this._realpathSet(i, next)  function next () {    if (--n === 0)      self._finish()  }}Glob.prototype._realpathSet = function (index, cb) {  var matchset = this.matches[index]  if (!matchset)    return cb()  var found = Object.keys(matchset)  var self = this  var n = found.length  if (n === 0)    return cb()  var set = this.matches[index] = Object.create(null)  found.forEach(function (p, i) {    // If there's a problem with the stat, then it means that    // one or more of the links in the realpath couldn't be    // resolved.  just return the abs value in that case.    p = self._makeAbs(p)    fs.realpath(p, self.realpathCache, function (er, real) {      if (!er)        set[real] = true      else if (er.syscall === 'stat')        set[p] = true      else        self.emit('error', er) // srsly wtf right here      if (--n === 0) {        self.matches[index] = set        cb()      }    })  })}Glob.prototype._mark = function (p) {  return common.mark(this, p)}Glob.prototype._makeAbs = function (f) {  return common.makeAbs(this, f)}Glob.prototype.abort = function () {  this.aborted = true  this.emit('abort')}Glob.prototype.pause = function () {  if (!this.paused) {    this.paused = true    this.emit('pause')  }}Glob.prototype.resume = function () {  if (this.paused) {    this.emit('resume')    this.paused = false    if (this._emitQueue.length) {      var eq = this._emitQueue.slice(0)      this._emitQueue.length = 0      for (var i = 0; i < eq.length; i ++) {        var e = eq[i]        this._emitMatch(e[0], e[1])      }    }    if (this._processQueue.length) {      var pq = this._processQueue.slice(0)      this._processQueue.length = 0      for (var i = 0; i < pq.length; i ++) {        var p = pq[i]        this._processing--        this._process(p[0], p[1], p[2], p[3])      }    }  }}Glob.prototype._process = function (pattern, index, inGlobStar, cb) {  assert(this instanceof Glob)  assert(typeof cb === 'function')  if (this.aborted)    return  this._processing++  if (this.paused) {    this._processQueue.push([pattern, index, inGlobStar, cb])    return  }  //console.error('PROCESS %d', this._processing, pattern)  // 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, cb)      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 cb()  var isGlobStar = remain[0] === minimatch.GLOBSTAR  if (isGlobStar)    this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)  else    this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)}Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {  var self = this  this._readdir(abs, inGlobStar, function (er, entries) {    return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)  })}Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {  // if the abs isn't a dir, then nothing can match!  if (!entries)    return cb()  // 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)    }  }  //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)  var len = matchedEntries.length  // If there are no matched entries, then nothing matches.  if (len === 0)    return cb()  // 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 !== '/')          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 cb()  }  // 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) {      if (prefix !== '/')        e = prefix + '/' + e      else        e = prefix + e    }    this._process([e].concat(remain), index, inGlobStar, cb)  }  cb()}Glob.prototype._emitMatch = function (index, e) {  if (this.aborted)    return  if (this.matches[index][e])    return  if (isIgnored(this, e))    return  if (this.paused) {    this._emitQueue.push([index, e])    return  }  var abs = this._makeAbs(e)  if (this.nodir) {    var c = this.cache[abs]    if (c === 'DIR' || Array.isArray(c))      return  }  if (this.mark)    e = this._mark(e)  this.matches[index][e] = true  var st = this.statCache[abs]  if (st)    this.emit('stat', e, st)  this.emit('match', e)}Glob.prototype._readdirInGlobStar = function (abs, cb) {  if (this.aborted)    return  // follow all symlinked directories forever  // just proceed as if this is a non-globstar situation  if (this.follow)    return this._readdir(abs, false, cb)  var lstatkey = 'lstat\0' + abs  var self = this  var lstatcb = inflight(lstatkey, lstatcb_)  if (lstatcb)    fs.lstat(abs, lstatcb)  function lstatcb_ (er, lstat) {    if (er)      return cb()    var isSym = lstat.isSymbolicLink()    self.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.isDirectory()) {      self.cache[abs] = 'FILE'      cb()    } else      self._readdir(abs, false, cb)  }}Glob.prototype._readdir = function (abs, inGlobStar, cb) {  if (this.aborted)    return  cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)  if (!cb)    return  //console.error('RD %j %j', +inGlobStar, abs)  if (inGlobStar && !ownProp(this.symlinks, abs))    return this._readdirInGlobStar(abs, cb)  if (ownProp(this.cache, abs)) {    var c = this.cache[abs]    if (!c || c === 'FILE')      return cb()    if (Array.isArray(c))      return cb(null, c)  }  var self = this  fs.readdir(abs, readdirCb(this, abs, cb))}function readdirCb (self, abs, cb) {  return function (er, entries) {    if (er)      self._readdirError(abs, er, cb)    else      self._readdirEntries(abs, entries, cb)  }}Glob.prototype._readdirEntries = function (abs, entries, cb) {  if (this.aborted)    return  // 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  return cb(null, entries)}Glob.prototype._readdirError = function (f, er, cb) {  if (this.aborted)    return  // 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.      this.cache[this._makeAbs(f)] = 'FILE'      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) {        this.emit('error', er)        // If the error is handled, then we abort        // if not, we threw out of here        this.abort()      }      if (!this.silent)        console.error('glob error', er)      break  }  return cb()}Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {  var self = this  this._readdir(abs, inGlobStar, function (er, entries) {    self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)  })}Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {  //console.error('pgs2', prefix, remain[0], entries)  // no entries means not a dir, so it can never have matches  // foo.txt/** doesn't match foo.txt  if (!entries)    return cb()  // 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, cb)  var isSym = this.symlinks[abs]  var len = entries.length  // If it's a symlink, and we're in a globstar, then stop  if (isSym && inGlobStar)    return cb()  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, cb)    var below = gspref.concat(entries[i], remain)    this._process(below, index, true, cb)  }  cb()}Glob.prototype._processSimple = function (prefix, index, cb) {  // XXX review this.  Shouldn't it be doing the mounting etc  // before doing stat?  kinda weird?  var self = this  this._stat(prefix, function (er, exists) {    self._processSimple2(prefix, index, er, exists, cb)  })}Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {  //console.error('ps2', prefix, exists)  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 cb()  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)  cb()}// Returns either 'DIR', 'FILE', or falseGlob.prototype._stat = function (f, cb) {  var abs = this._makeAbs(f)  var needDir = f.slice(-1) === '/'  if (f.length > this.maxLength)    return cb()  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 cb(null, c)    if (needDir && c === 'FILE')      return cb()    // 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 !== undefined) {    if (stat === false)      return cb(null, stat)    else {      var type = stat.isDirectory() ? 'DIR' : 'FILE'      if (needDir && type === 'FILE')        return cb()      else        return cb(null, type, stat)    }  }  var self = this  var statcb = inflight('stat\0' + abs, lstatcb_)  if (statcb)    fs.lstat(abs, statcb)  function lstatcb_ (er, lstat) {    if (lstat && lstat.isSymbolicLink()) {      // If it's a symlink, then treat it as the target, unless      // the target does not exist, then treat it as a file.      return fs.stat(abs, function (er, stat) {        if (er)          self._stat2(f, abs, null, lstat, cb)        else          self._stat2(f, abs, er, stat, cb)      })    } else {      self._stat2(f, abs, er, lstat, cb)    }  }}Glob.prototype._stat2 = function (f, abs, er, stat, cb) {  if (er) {    this.statCache[abs] = false    return cb()  }  var needDir = f.slice(-1) === '/'  this.statCache[abs] = stat  if (abs.slice(-1) === '/' && !stat.isDirectory())    return cb(null, false, stat)  var c = stat.isDirectory() ? 'DIR' : 'FILE'  this.cache[abs] = this.cache[abs] || c  if (needDir && c !== 'DIR')    return cb()  return cb(null, c, stat)}


//sync.jsmodule.exports = globSyncglobSync.GlobSync = GlobSyncvar fs = require('fs')var minimatch = require('minimatch')var Minimatch = minimatch.Minimatchvar Glob = require('./glob.js').Globvar 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.alphasortvar alphasorti = common.alphasortivar setopts = common.setoptsvar ownProp = common.ownPropvar childrenIgnored = common.childrenIgnoredfunction 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 = fs.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.matches[index][e] = true    }    // 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) {  var abs = this._makeAbs(e)  if (this.mark)    e = this._mark(e)  if (this.matches[index][e])    return  if (this.nodir) {    var c = this.cache[this._makeAbs(e)]    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) {    // lstat failed, doesn't exist    return null  }  var isSym = 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.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.      this.cache[this._makeAbs(f)] = 'FILE'      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.matches[index][prefix] = true}// Returns either 'DIR', 'FILE', or falseGlobSync.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) {      return false    }    if (lstat.isSymbolicLink()) {      try {        stat = fs.statSync(abs)      } catch (er) {        stat = lstat      }    } else {      stat = lstat    }  }  this.statCache[abs] = stat  var c = stat.isDirectory() ? 'DIR' : 'FILE'  this.cache[abs] = this.cache[abs] || c  if (needDir && c !== 'DIR')    return false  return c}GlobSync.prototype._mark = function (p) {  return common.mark(this, p)}GlobSync.prototype._makeAbs = function (f) {  return common.makeAbs(this, f)}
0 0
原创粉丝点击