text: 'use strict';
  
  const is = require('is-type-of');
  const assert = require('assert');
  const methods = [ 'head', 'options', 'get', 'put', 'patch', 'post', 'delete', 'del', 'all', 'resources' ];
  
  class RouterPlus {
    constructor(app) {
      this.app = app;
    }
  
    /**
     * get sub router
     *
     * @method Router#namespace
     * @param {String} prefix - sub router prefix
     * @param {...Function} [middlewares] - optional middlewares
     * @return {Router} Return sub router with special prefix
     */
    namespace(prefix, ...middlewares) {
      assert(is.string(prefix), `only support prefix with string, but got ${prefix}`);
      assert(prefix !== '/', 'namespace / is not supported');
  
      const fnCache = {};
      // mock router
      const proxy = new Proxy(this.app.router, {
        get(target, property) {
          if (methods.includes(property)) {
            if (!fnCache[property]) {
              fnCache[property] = proxyFn(target, property, prefix, middlewares, proxy);
            }
            return fnCache[property];
          }
          return target[property];
        },
      });
  
      return proxy;
    }
  }
  
  module.exports = RouterPlus;
  
  function proxyFn(target, property, prefix, middlewares, routerProxy) {
    const fn = target[property];
    const proxy = new Proxy(fn, {
      apply(targetFn, ctx, args) {
        if (args.length >= 3 && (is.string(args[1]) || is.regExp(args[1]))) {
          // app.get(name, url, [...middleware], controller)
          args[1] = addPrefix(prefix, args[1]);
          args.splice(2, 0, ...middlewares);
        } else {
          // app.get(url, [...middleware], controller)
          args[0] = addPrefix(prefix, args[0]);
          args.splice(1, 0, ...middlewares);
        }
        Reflect.apply(targetFn, ctx, args);
        return routerProxy;
      },
    });
    return proxy;
  }
  
  function addPrefix(prefix, path) {
    assert(is.string(path), `only support path with string, but got ${path}`);
    return prefix + path;
  }
  