koa源碼閱讀之context.js/koa-convert/is-generator-function

hey 老夥計

今天咱們來說一下koa的context.js還有一些tj本身寫的庫吧。


context.js

'use strict';
const createError = require('http-errors');
const httpAssert = require('http-assert');
const delegate = require('delegates');
const statuses = require('statuses');

const proto = module.exports = {

    //前面文章有講過這個了,這裏不描述了
  inspect() {
    if (this === proto) return this;
    return this.toJSON();
  },

  toJSON() {
    return {
      request: this.request.toJSON(),//實際上調用request.js的toJSON(下同)
      response: this.response.toJSON(),
      app: this.app.toJSON(),
      originalUrl: this.originalUrl,
      req: '<original node req>',
      res: '<original node res>',
      socket: '<original node socket>'
    };
  },

  /**
   * Similar to .throw(), adds assertion.
   *
   *    this.assert(this.user, 401, 'Please login!');
   *
   * See: https://github.com/jshttp/http-assert
   *
   * @param {Mixed} test
   * @param {Number} status
   * @param {String} message
   * @api public
   */

  assert: httpAssert,

  /**
   * Throw an error with `msg` and optional `status`
   * defaulting to 500. Note that these are user-level
   * errors, and the message may be exposed to the client.
   *
   *    this.throw(403)
   *    this.throw('name required', 400)
   *    this.throw(400, 'name required')
   *    this.throw('something exploded')
   *    this.throw(new Error('invalid'), 400);
   *    this.throw(400, new Error('invalid'));
   *
   * See: https://github.com/jshttp/http-errors
   *
   * @param {String|Number|Error} err, msg or status
   * @param {String|Number|Error} [err, msg or status]
   * @param {Object} [props]
   * @api public
   */
    //throw方法。上面是使用的方法。咱們 經常使用來在中間件throw發出一些錯誤狀態碼。
    //從而使得上級中間件能夠try catch這個錯誤從而響應
    //createError([status], [message], [properties])
    //properties - custom properties to attach to the object
  throw(...args) {
    throw createError(...args);
  },
    //默認的一個錯誤處理
  onerror(err) {
    // don't do anything if there is no error.
    // this allows you to pass `this.onerror`
    // to node-style callbacks.
    if (null == err) return;
    // 若是error不是Error實例。此時生成一個錯誤實例給下文處理
    if (!(err instanceof Error)) err = new Error(`non-error thrown: ${err}`);

    let headerSent = false;
    //固然須要可寫且沒有被髮送
    if (this.headerSent || !this.writable) {
      headerSent = err.headerSent = true;
    }

    //觸發事件
    this.app.emit('error', err, this);

    //發送了確定啥都不能幹了
    if (headerSent) {
      return;
    }

    const { res } = this;
    //解構一下得到response
    
    //兼容咯
    //首次清除全部的headers
    if (typeof res.getHeaderNames === 'function') {
      res.getHeaderNames().forEach(name => res.removeHeader(name));
    } else {
      res._headers = {}; // Node < 7.7
    }

    // 而後設置爲錯誤的headers標識
    this.set(err.headers);

    //強制text/plain
    this.type = 'text';

    // 支持ENOENT 
    if ('ENOENT' == err.code) err.status = 404;

    // 默認轉換成500狀態碼
    if ('number' != typeof err.status || !statuses[err.status]) err.status = 500;

    //響應
    const code = statuses[err.status];
    const msg = err.expose ? err.message : code;
    this.status = err.status;
    this.length = Buffer.byteLength(msg);
    this.res.end(msg);
    //跟原生的同樣嘛。
    //給咱們一個提示。咱們要使一個鏈接關閉。那麼ctx.res.end(msg);
  }
};

/**
 * Response delegation.
 */
//委託到這個上下文對象裏
//委託方法 與屬性的getter或者setter
delegate(proto, 'response')
  .method('attachment')
  .method('redirect')
  .method('remove')
  .method('vary')
  .method('set')
  .method('append')
  .method('flushHeaders')
  .access('status')
  .access('message')
  .access('body')
  .access('length')
  .access('type')
  .access('lastModified')
  .access('etag')
  .getter('headerSent')
  .getter('writable');

/**
 * Request delegation.
 */

delegate(proto, 'request')
  .method('acceptsLanguages')
  .method('acceptsEncodings')
  .method('acceptsCharsets')
  .method('accepts')
  .method('get')
  .method('is')
  .access('querystring')
  .access('idempotent')
  .access('socket')
  .access('search')
  .access('method')
  .access('query')
  .access('path')
  .access('url')
  .getter('origin')
  .getter('href')
  .getter('subdomains')
  .getter('protocol')
  .getter('host')
  .getter('hostname')
  .getter('URL')
  .getter('header')
  .getter('headers')
  .getter('secure')
  .getter('stale')
  .getter('fresh')
  .getter('ips')
  .getter('ip');

由於下一個篇幅準備將最重要的application.js
因此這個接下來準備說幾個引入的庫源碼node

koa convert

用於什麼?用於將koa1的中間件轉化爲promise

看到co就應該有這麼個想法了= =

'use strict'

const co = require('co')
//引入co
const compose = require('koa-compose')

module.exports = convert

function convert (mw) {
    //進行判斷
  if (typeof mw !== 'function') {
    throw new TypeError('middleware must be a function')
  }
  if (mw.constructor.name !== 'GeneratorFunction') {
    // assume it's Promise-based middleware
    return mw
  }
  const converted = function (ctx, next) {
    return co.call(ctx, mw.call(ctx, createGenerator(next)))
  }
  converted._name = mw._name || mw.name
  return converted
}

function * createGenerator (next) {
  return yield next()
}

// convert.compose(mw, mw, mw)
// convert.compose([mw, mw, mw])
// koa-compose 往後再說嘻嘻^v^
convert.compose = function (arr) {
  if (!Array.isArray(arr)) {
    arr = Array.from(arguments)
  }
  return compose(arr.map(convert))
}
//個人天啊。這個瘋子還支持回退
//回退方法非常精妙啊
convert.back = function (mw) {
  if (typeof mw !== 'function') {
    throw new TypeError('middleware must be a function')
  }
  if (mw.constructor.name === 'GeneratorFunction') {
    // assume it's generator middleware
    return mw
  }
  const converted = function * (next) {
    let ctx = this
    let called = false
    // no need try...catch here, it's ok even `mw()` throw exception
    yield Promise.resolve(mw(ctx, function () {
        //使得next僅僅被調用一次
      if (called) {
        // guard against multiple next() calls
        // https://github.com/koajs/compose/blob/4e3e96baf58b817d71bd44a8c0d78bb42623aa95/index.js#L36
        return Promise.reject(new Error('next() called multiple times'))
      }
      called = true
      return co.call(ctx, next)
    }))
  }
  converted._name = mw._name || mw.name
  return converted
}

is-generator-function

'use strict';
//減小查找引用,常見的優化方法
var toStr = Object.prototype.toString;
var fnToStr = Function.prototype.toString;
//這個正則匹配function *可是好像有點bug
//* function(){}也會受到斷定true
var isFnRegex = /^\s*(?:function)?\*/;

var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
var getProto = Object.getPrototypeOf;
var getGeneratorFunc = function () { // eslint-disable-line consistent-return
    if (!hasToStringTag) {
        return false;
    }
    try {
        return Function('return function*() {}')();
    } catch (e) {
    }
};
var generatorFunc = getGeneratorFunc();
var GeneratorFunction = generatorFunc ? getProto(generatorFunc) : {};

//主要從三點看。
//一點是function toString
//一點是[object GeneratorFunction] Object toString
//一點是從原型看(內部[[Prototype]]屬性的值)
module.exports = function isGeneratorFunction(fn) {
    if (typeof fn !== 'function') {
        return false;
    }
    if (isFnRegex.test(fnToStr.call(fn))) {
        return true;
    }
    if (!hasToStringTag) {
        var str = toStr.call(fn);
        return str === '[object GeneratorFunction]';
    }
    return getProto(fn) === GeneratorFunction;
};

此次寫的好粗糙啊= =
但願在結尾部分能寫好。
sry sry srygit

看徹底文都是好樣的!!


相關文章
相關標籤/搜索