@eggjs/router
Advanced tools
+10
| 'use strict'; | ||
| const KoaRouter = require('./lib/router'); | ||
| const EggRouter = require('./lib/egg_router'); | ||
| // for compact | ||
| module.exports = KoaRouter; | ||
| module.exports.KoaRouter = KoaRouter; | ||
| module.exports.EggRouter = EggRouter; | ||
| 'use strict'; | ||
| const is = require('is-type-of'); | ||
| const Router = require('./router'); | ||
| const utility = require('utility'); | ||
| const inflection = require('inflection'); | ||
| const assert = require('assert'); | ||
| const utils = require('./utils'); | ||
| const METHODS = [ 'head', 'options', 'get', 'put', 'patch', 'post', 'delete' ]; | ||
| const REST_MAP = { | ||
| index: { | ||
| suffix: '', | ||
| method: 'GET', | ||
| }, | ||
| new: { | ||
| namePrefix: 'new_', | ||
| member: true, | ||
| suffix: 'new', | ||
| method: 'GET', | ||
| }, | ||
| create: { | ||
| suffix: '', | ||
| method: 'POST', | ||
| }, | ||
| show: { | ||
| member: true, | ||
| suffix: ':id', | ||
| method: 'GET', | ||
| }, | ||
| edit: { | ||
| member: true, | ||
| namePrefix: 'edit_', | ||
| suffix: ':id/edit', | ||
| method: 'GET', | ||
| }, | ||
| update: { | ||
| member: true, | ||
| namePrefix: '', | ||
| suffix: ':id', | ||
| method: ['PATCH', 'PUT'], | ||
| }, | ||
| destroy: { | ||
| member: true, | ||
| namePrefix: 'destroy_', | ||
| suffix: ':id', | ||
| method: 'DELETE', | ||
| }, | ||
| }; | ||
| /** | ||
| * FIXME: move these patch into @eggjs/router | ||
| */ | ||
| class EggRouter extends Router { | ||
| /** | ||
| * @constructor | ||
| * @param {Object} opts - Router options. | ||
| * @param {Application} app - Application object. | ||
| */ | ||
| constructor(opts, app) { | ||
| super(opts); | ||
| this.app = app; | ||
| this.patchRouterMethod(); | ||
| } | ||
| patchRouterMethod() { | ||
| // patch router methods to support generator function middleware and string controller | ||
| METHODS.concat(['all']).forEach(method => { | ||
| this[method] = (...args) => { | ||
| const splited = spliteAndResolveRouterParams({ args, app: this.app }); | ||
| // format and rebuild params | ||
| args = splited.prefix.concat(splited.middlewares); | ||
| return super[method](...args); | ||
| }; | ||
| }); | ||
| } | ||
| /** | ||
| * Create and register a route. | ||
| * @param {String} path - url path | ||
| * @param {Array} methods - Array of HTTP verbs | ||
| * @param {Array} middlewares - | ||
| * @param {Object} opts - | ||
| * @return {Route} this | ||
| */ | ||
| register(path, methods, middlewares, opts) { | ||
| // patch register to support generator function middleware and string controller | ||
| middlewares = Array.isArray(middlewares) ? middlewares : [middlewares]; | ||
| middlewares = convertMiddlewares(middlewares, this.app); | ||
| path = Array.isArray(path) ? path : [path]; | ||
| path.forEach(p => super.register(p, methods, middlewares, opts)); | ||
| return this; | ||
| } | ||
| /** | ||
| * restful router api | ||
| * @param {String} name - Router name | ||
| * @param {String} prefix - url prefix | ||
| * @param {Function} middleware - middleware or controller | ||
| * @example | ||
| * ```js | ||
| * app.resources('/posts', 'posts') | ||
| * app.resources('posts', '/posts', 'posts') | ||
| * app.resources('posts', '/posts', app.role.can('user'), app.controller.posts) | ||
| * ``` | ||
| * | ||
| * Examples: | ||
| * | ||
| * ```js | ||
| * app.resources('/posts', 'posts') | ||
| * ``` | ||
| * | ||
| * yield router mapping | ||
| * | ||
| * Method | Path | Route Name | Controller.Action | ||
| * -------|-----------------|----------------|----------------------------- | ||
| * GET | /posts | posts | app.controller.posts.index | ||
| * GET | /posts/new | new_post | app.controller.posts.new | ||
| * GET | /posts/:id | post | app.controller.posts.show | ||
| * GET | /posts/:id/edit | edit_post | app.controller.posts.edit | ||
| * POST | /posts | posts | app.controller.posts.create | ||
| * PATCH | /posts/:id | post | app.controller.posts.update | ||
| * DELETE | /posts/:id | post | app.controller.posts.destroy | ||
| * | ||
| * app.router.url can generate url based on arguments | ||
| * ```js | ||
| * app.router.url('posts') | ||
| * => /posts | ||
| * app.router.url('post', { id: 1 }) | ||
| * => /posts/1 | ||
| * app.router.url('new_post') | ||
| * => /posts/new | ||
| * app.router.url('edit_post', { id: 1 }) | ||
| * => /posts/1/edit | ||
| * ``` | ||
| * @return {Router} return route object. | ||
| * @since 1.0.0 | ||
| */ | ||
| resources(...args) { | ||
| const splited = spliteAndResolveRouterParams({ args, app: this.app }); | ||
| const middlewares = splited.middlewares; | ||
| // last argument is Controller object | ||
| const controller = splited.middlewares.pop(); | ||
| let name = ''; | ||
| let prefix = ''; | ||
| if (splited.prefix.length === 2) { | ||
| // router.get('users', '/users') | ||
| name = splited.prefix[0]; | ||
| prefix = splited.prefix[1]; | ||
| } else { | ||
| // router.get('/users') | ||
| prefix = splited.prefix[0]; | ||
| } | ||
| for (const key in REST_MAP) { | ||
| const action = controller[key]; | ||
| if (!action) continue; | ||
| const opts = REST_MAP[key]; | ||
| let formatedName; | ||
| if (opts.member) { | ||
| formatedName = inflection.singularize(name); | ||
| } else { | ||
| formatedName = inflection.pluralize(name); | ||
| } | ||
| if (opts.namePrefix) { | ||
| formatedName = opts.namePrefix + formatedName; | ||
| } | ||
| prefix = prefix.replace(/\/$/, ''); | ||
| const path = opts.suffix ? `${prefix}/${opts.suffix}` : prefix; | ||
| const method = Array.isArray(opts.method) ? opts.method : [opts.method]; | ||
| this.register(path, method, middlewares.concat(action), { name: formatedName }); | ||
| } | ||
| return this; | ||
| } | ||
| /** | ||
| * @param {String} name - Router name | ||
| * @param {Object} params - more parameters | ||
| * @example | ||
| * ```js | ||
| * router.url('edit_post', { id: 1, name: 'foo', page: 2 }) | ||
| * => /posts/1/edit?name=foo&page=2 | ||
| * router.url('posts', { name: 'foo&1', page: 2 }) | ||
| * => /posts?name=foo%261&page=2 | ||
| * ``` | ||
| * @return {String} url by path name and query params. | ||
| * @since 1.0.0 | ||
| */ | ||
| url(name, params) { | ||
| const route = this.route(name); | ||
| if (!route) return ''; | ||
| const args = params; | ||
| let url = route.path; | ||
| assert(!is.regExp(url), `Can't get the url for regExp ${url} for by name '${name}'`); | ||
| const queries = []; | ||
| if (typeof args === 'object' && args !== null) { | ||
| const replacedParams = []; | ||
| url = url.replace(/:([a-zA-Z_]\w*)/g, function ($0, key) { | ||
| if (utility.has(args, key)) { | ||
| const values = args[key]; | ||
| replacedParams.push(key); | ||
| return utility.encodeURIComponent(Array.isArray(values) ? values[0] : values); | ||
| } | ||
| return $0; | ||
| }); | ||
| for (const key in args) { | ||
| if (replacedParams.includes(key)) { | ||
| continue; | ||
| } | ||
| const values = args[key]; | ||
| const encodedKey = utility.encodeURIComponent(key); | ||
| if (Array.isArray(values)) { | ||
| for (const val of values) { | ||
| queries.push(`${encodedKey}=${utility.encodeURIComponent(val)}`); | ||
| } | ||
| } else { | ||
| queries.push(`${encodedKey}=${utility.encodeURIComponent(values)}`); | ||
| } | ||
| } | ||
| } | ||
| if (queries.length > 0) { | ||
| const queryStr = queries.join('&'); | ||
| if (!url.includes('?')) { | ||
| url = `${url}?${queryStr}`; | ||
| } else { | ||
| url = `${url}&${queryStr}`; | ||
| } | ||
| } | ||
| return url; | ||
| } | ||
| pathFor(name, params) { | ||
| return this.url(name, params); | ||
| } | ||
| } | ||
| /** | ||
| * 1. split (name, url, ...middleware, controller) to | ||
| * { | ||
| * prefix: [name, url] | ||
| * middlewares [...middleware, controller] | ||
| * } | ||
| * | ||
| * 2. resolve controller from string to function | ||
| * | ||
| * @param {Object} options inputs | ||
| * @param {Object} options.args router params | ||
| * @param {Object} options.app egg application instance | ||
| * @return {Object} prefix and middlewares | ||
| */ | ||
| function spliteAndResolveRouterParams({ args, app }) { | ||
| let prefix; | ||
| let middlewares; | ||
| if (args.length >= 3 && (is.string(args[1]) || is.regExp(args[1]))) { | ||
| // app.get(name, url, [...middleware], controller) | ||
| prefix = args.slice(0, 2); | ||
| middlewares = args.slice(2); | ||
| } else { | ||
| // app.get(url, [...middleware], controller) | ||
| prefix = args.slice(0, 1); | ||
| middlewares = args.slice(1); | ||
| } | ||
| // resolve controller | ||
| const controller = middlewares.pop(); | ||
| middlewares.push(resolveController(controller, app)); | ||
| return { prefix, middlewares }; | ||
| } | ||
| /** | ||
| * resolve controller from string to function | ||
| * @param {String|Function} controller input controller | ||
| * @param {Application} app egg application instance | ||
| * @return {Function} controller function | ||
| */ | ||
| function resolveController(controller, app) { | ||
| if (is.string(controller)) { | ||
| const actions = controller.split('.'); | ||
| let obj = app.controller; | ||
| actions.forEach(key => { | ||
| obj = obj[key]; | ||
| if (!obj) throw new Error(`controller '${controller}' not exists`); | ||
| }); | ||
| controller = obj; | ||
| } | ||
| // ensure controller is exists | ||
| if (!controller) throw new Error('controller not exists'); | ||
| return controller; | ||
| } | ||
| /** | ||
| * 1. ensure controller(last argument) support string | ||
| * - [url, controller]: app.get('/home', 'home'); | ||
| * - [name, url, controller(string)]: app.get('posts', '/posts', 'posts.list'); | ||
| * - [name, url, controller]: app.get('posts', '/posts', app.controller.posts.list); | ||
| * - [name, url(regexp), controller]: app.get('regRouter', /\/home\/index/, 'home.index'); | ||
| * - [name, url, middleware, [...], controller]: `app.get(/user/:id', hasLogin, canGetUser, 'user.show');` | ||
| * | ||
| * 2. make middleware support generator function | ||
| * | ||
| * @param {Array} middlewares middlewares and controller(last middleware) | ||
| * @param {Application} app egg application instance | ||
| * @return {Array} middlewares | ||
| */ | ||
| function convertMiddlewares(middlewares, app) { | ||
| // ensure controller is resolved | ||
| const controller = resolveController(middlewares.pop(), app); | ||
| // make middleware support generator function | ||
| middlewares = middlewares.map(utils.middleware); | ||
| const wrappedController = (ctx, next) => { | ||
| return utils.callFn(controller, [ctx, next], ctx); | ||
| }; | ||
| return middlewares.concat([wrappedController]); | ||
| } | ||
| module.exports = EggRouter; |
+18
| 'use strict'; | ||
| const convert = require('koa-convert'); | ||
| const is = require('is-type-of'); | ||
| const co = require('co'); | ||
| module.exports = { | ||
| async callFn(fn, args, ctx) { | ||
| args = args || []; | ||
| if (!is.function(fn)) return; | ||
| if (is.generatorFunction(fn)) fn = co.wrap(fn); | ||
| return ctx ? fn.call(ctx, ...args) : fn(...args); | ||
| }, | ||
| middleware(fn) { | ||
| return is.generatorFunction(fn) ? convert(fn) : fn; | ||
| }, | ||
| }; |
+11
-2
| 1.0.0 / 2019-01-30 | ||
| 1.1.0 / 2019-01-30 | ||
| ================== | ||
| **features** | ||
| * [[`b318dd5`](http://github.com/eggjs/egg-router/commit/b318dd5ff2eea60013ed62b2a435f803c12bf20f)] - feat: add egg-router (dead-horse <<dead_horse@qq.com>>) | ||
| **fixes** | ||
| * [[`b3db7b4`](http://github.com/eggjs/egg-router/commit/b3db7b41988d3bf1b4e885ed76b3a8165c1d3b1d)] - fix: add missing dependencies koa-convert (dead-horse <<dead_horse@qq.com>>) | ||
| * [[`c280336`](http://github.com/eggjs/egg-router/commit/c2803368a8256bc9504ae3c821eefeb9d1fcbc4d)] - fix: only support node@8 (dead-horse <<dead_horse@qq.com>>) | ||
| * [[`7a887a2`](http://github.com/eggjs/egg-router/commit/7a887a252445e602c2ea7e1c6f4cde52a433644d)] - fix: update license (dead-horse <<dead_horse@qq.com>>) | ||
| **others** | ||
| * [[`ae40168`](http://github.com/eggjs/egg-router/commit/ae4016862fb8bdaf30e730a9278fbb1455d8b75d)] - docs: clean doc (dead-horse <<dead_horse@qq.com>>) | ||
| * [[`e4f21a8`](http://github.com/eggjs/egg-router/commit/e4f21a8ed6dfd72c4d6f4a13bbda9a4e90b0401c)] - chore: fix history (dead-horse <<dead_horse@qq.com>>) | ||
| 1.0.0 / 2019-01-30 | ||
@@ -9,0 +18,0 @@ ================== |
+1
-1
@@ -1,2 +0,2 @@ | ||
| var debug = require('debug')('koa-router'); | ||
| var debug = require('debug')('egg-router'); | ||
| var pathToRegExp = require('path-to-regexp'); | ||
@@ -3,0 +3,0 @@ var uri = require('urijs'); |
+2
-5
| /** | ||
| * RESTful resource routing middleware for koa. | ||
| * | ||
| * @author Alex Mingoia <talk@alexmingoia.com> | ||
| * @link https://github.com/alexmingoia/koa-router | ||
| * RESTful resource routing middleware for eggjs. | ||
| */ | ||
| var debug = require('debug')('koa-router'); | ||
| var debug = require('debug')('egg-router'); | ||
| var compose = require('koa-compose'); | ||
@@ -10,0 +7,0 @@ var HttpError = require('http-errors'); |
+1
-1
| The MIT License (MIT) | ||
| Copyright (c) 2015 Alexander C. Mingoia | ||
| Copyright (c) 2019 eggjs | ||
@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy |
+17
-8
@@ -8,8 +8,8 @@ { | ||
| }, | ||
| "main": "lib/router.js", | ||
| "files": [ | ||
| "lib" | ||
| "lib", | ||
| "index.js" | ||
| ], | ||
| "author": "eggjs", | ||
| "version": "1.0.0", | ||
| "version": "1.1.0", | ||
| "keywords": [ | ||
@@ -22,12 +22,18 @@ "koa", | ||
| "dependencies": { | ||
| "co": "^4.6.0", | ||
| "debug": "^3.1.0", | ||
| "http-errors": "^1.3.1", | ||
| "inflection": "^1.12.0", | ||
| "is-type-of": "^1.2.1", | ||
| "koa-compose": "^3.0.0", | ||
| "koa-convert": "^1.2.0", | ||
| "methods": "^1.0.1", | ||
| "path-to-regexp": "^1.1.1", | ||
| "urijs": "^1.19.0" | ||
| "urijs": "^1.19.0", | ||
| "utility": "^1.15.0" | ||
| }, | ||
| "devDependencies": { | ||
| "egg-bin": "^4.10.0", | ||
| "egg-ci": "^1.11.0", | ||
| "expect.js": "^0.3.1", | ||
| "jsdoc-to-markdown": "^1.1.1", | ||
| "koa": "2.2.0", | ||
@@ -39,9 +45,12 @@ "mocha": "^2.0.1", | ||
| "scripts": { | ||
| "test": "NODE_ENV=test mocha test/**/*.js", | ||
| "docs": "NODE_ENV=test jsdoc2md -t ./lib/README_tpl.hbs --src ./lib/*.js >| README.md" | ||
| "test": "egg-bin test", | ||
| "ci": "egg-bin cov" | ||
| }, | ||
| "ci": { | ||
| "version": "8, 10" | ||
| }, | ||
| "engines": { | ||
| "node": ">= 4" | ||
| "node": ">= 8" | ||
| }, | ||
| "license": "MIT" | ||
| } |
+0
-7
@@ -411,12 +411,5 @@ # @eggjs/router | ||
| ``` | ||
| ## Contributing | ||
| Please submit all issues and pull requests to the [alexmingoia/egg-router](http://github.com/alexmingoia/egg-router) repository! | ||
| ## Tests | ||
| Run tests using `npm test`. | ||
| ## Support | ||
| If you have any problem or suggestion please open an issue [here](https://github.com/alexmingoia/egg-router/issues). |
| # koa-router | ||
| [](https://npmjs.org/package/koa-router) [](https://npmjs.org/package/koa-router) [](http://nodejs.org/download/) [](http://travis-ci.org/alexmingoia/koa-router) [](https://www.gratipay.com/alexmingoia/) [](https://gitter.im/alexmingoia/koa-router/) | ||
| > Router middleware for [koa](https://github.com/koajs/koa) | ||
| * Express-style routing using `app.get`, `app.put`, `app.post`, etc. | ||
| * Named URL parameters. | ||
| * Named routes with URL generation. | ||
| * Responds to `OPTIONS` requests with allowed methods. | ||
| * Support for `405 Method Not Allowed` and `501 Not Implemented`. | ||
| * Multiple route middleware. | ||
| * Multiple routers. | ||
| * Nestable routers. | ||
| * ES7 async/await support. | ||
| {{#module name="koa-router"}}{{>body}}{{/module}}## Migrating to 7 / Koa 2 | ||
| - The API has changed to match the new promise-based middleware | ||
| signature of koa 2. See the | ||
| [koa 2.x readme](https://github.com/koajs/koa/tree/2.0.0-alpha.3) for more | ||
| information. | ||
| - Middleware is now always run in the order declared by `.use()` (or `.get()`, | ||
| etc.), which matches Express 4 API. | ||
| ## Installation | ||
| Install using [npm](https://www.npmjs.org/): | ||
| ```sh | ||
| npm install koa-router | ||
| ``` | ||
| ## API Reference | ||
| {{#module name="koa-router"~}} | ||
| {{>body~}} | ||
| {{>member-index~}} | ||
| {{>members~}} | ||
| {{/module~}} | ||
| ## Contributing | ||
| Please submit all issues and pull requests to the [alexmingoia/koa-router](http://github.com/alexmingoia/koa-router) repository! | ||
| ## Tests | ||
| Run tests using `npm test`. | ||
| ## Support | ||
| If you have any problem or suggestion please open an issue [here](https://github.com/alexmingoia/koa-router/issues). |
54309
19.53%9
28.57%1179
37.41%11
83.33%7
16.67%415
-1.66%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added