koa-router
Advanced tools
Comparing version 10.1.1 to 11.0.0
@@ -0,3 +1,3 @@ | ||
const { parse: parseUrl, format: formatUrl } = require('url'); | ||
const { pathToRegexp, compile, parse } = require('path-to-regexp'); | ||
const { parse: parseUrl, format: formatUrl } = require('url'); | ||
@@ -20,4 +20,4 @@ module.exports = Layer; | ||
function Layer(path, methods, middleware, opts) { | ||
this.opts = opts || {}; | ||
function Layer(path, methods, middleware, opts = {}) { | ||
this.opts = opts; | ||
this.name = this.opts.name || null; | ||
@@ -28,4 +28,4 @@ this.methods = []; | ||
for (let i = 0; i < methods.length; i++) { | ||
const l = this.methods.push(methods[i].toUpperCase()); | ||
for (const method of methods) { | ||
const l = this.methods.push(method.toUpperCase()); | ||
if (this.methods[l - 1] === 'GET') this.methods.unshift('HEAD'); | ||
@@ -37,6 +37,8 @@ } | ||
const fn = this.stack[i]; | ||
const type = (typeof fn); | ||
const type = typeof fn; | ||
if (type !== 'function') | ||
throw new Error( | ||
`${methods.toString()} \`${this.opts.name || path}\`: \`middleware\` must be a function, not \`${type}\`` | ||
`${methods.toString()} \`${ | ||
this.opts.name || path | ||
}\`: \`middleware\` must be a function, not \`${type}\`` | ||
); | ||
@@ -47,3 +49,3 @@ } | ||
this.regexp = pathToRegexp(path, this.paramNames, this.opts); | ||
}; | ||
} | ||
@@ -67,3 +69,3 @@ /** | ||
* @param {Array.<String>} captures | ||
* @param {Object=} existingParams | ||
* @param {Object=} params | ||
* @returns {Object} | ||
@@ -73,9 +75,8 @@ * @private | ||
Layer.prototype.params = function (path, captures, existingParams) { | ||
const params = existingParams || {}; | ||
Layer.prototype.params = function (path, captures, params = {}) { | ||
for (let len = captures.length, i = 0; i < len; i++) { | ||
if (this.paramNames[i]) { | ||
const c = captures[i]; | ||
if (c && c.length !== 0) params[this.paramNames[i].name] = c ? safeDecodeURIComponent(c) : c; | ||
if (c && c.length > 0) | ||
params[this.paramNames[i].name] = c ? safeDecodeURIComponent(c) : c; | ||
} | ||
@@ -119,7 +120,7 @@ } | ||
if (typeof params != 'object') { | ||
if (typeof params !== 'object') { | ||
args = Array.prototype.slice.call(arguments); | ||
if (typeof args[args.length - 1] == 'object') { | ||
if (typeof args[args.length - 1] === 'object') { | ||
options = args[args.length - 1]; | ||
args = args.slice(0, args.length - 1); | ||
args = args.slice(0, -1); | ||
} | ||
@@ -134,7 +135,7 @@ } | ||
if (args instanceof Array) { | ||
if (Array.isArray(args)) { | ||
for (let len = tokens.length, i = 0, j = 0; i < len; i++) { | ||
if (tokens[i].name) replace[tokens[i].name] = args[j++]; | ||
} | ||
} else if (tokens.some(token => token.name)) { | ||
} else if (tokens.some((token) => token.name)) { | ||
replace = params; | ||
@@ -155,2 +156,3 @@ } else if (!options) { | ||
} | ||
return formatUrl(replaced); | ||
@@ -171,3 +173,3 @@ } | ||
* ctx.user = users[id]; | ||
* if (!user) return ctx.status = 404; | ||
* if (!ctx.user) return ctx.status = 404; | ||
* next(); | ||
@@ -187,3 +189,3 @@ * }) | ||
Layer.prototype.param = function (param, fn) { | ||
const stack = this.stack; | ||
const { stack } = this; | ||
const params = this.paramNames; | ||
@@ -193,2 +195,3 @@ const middleware = function (ctx, next) { | ||
}; | ||
middleware.param = param; | ||
@@ -227,3 +230,6 @@ | ||
if (this.path) { | ||
this.path = (this.path !== '/' || this.opts.strict === true) ? `${prefix}${this.path}` : prefix | ||
this.path = | ||
this.path !== '/' || this.opts.strict === true | ||
? `${prefix}${this.path}` | ||
: prefix; | ||
this.paramNames = []; | ||
@@ -248,5 +254,5 @@ this.regexp = pathToRegexp(this.path, this.paramNames, this.opts); | ||
return decodeURIComponent(text); | ||
} catch (e) { | ||
} catch { | ||
return text; | ||
} | ||
} |
@@ -8,9 +8,12 @@ /** | ||
const debug = require('debug')('koa-router'); | ||
const { debuglog } = require('util'); | ||
const compose = require('koa-compose'); | ||
const HttpError = require('http-errors'); | ||
const methods = require('methods'); | ||
const { pathToRegexp } = require('path-to-regexp'); | ||
const Layer = require('./layer'); | ||
const { pathToRegexp } = require('path-to-regexp'); | ||
const debug = debuglog('koa-router'); | ||
/** | ||
@@ -47,2 +50,3 @@ * @module koa-router | ||
* @param {Object=} opts | ||
* @param {Boolean=false} opts.exclusive only run last matched route's controller when there are multiple matches | ||
* @param {String=} opts.prefix prefix router paths | ||
@@ -52,6 +56,6 @@ * @constructor | ||
function Router(opts) { | ||
function Router(opts = {}) { | ||
if (!(this instanceof Router)) return new Router(opts); | ||
this.opts = opts || {}; | ||
this.opts = opts; | ||
this.methods = this.opts.methods || [ | ||
@@ -66,6 +70,7 @@ 'HEAD', | ||
]; | ||
this.exclusive = Boolean(this.opts.exclusive); | ||
this.params = {}; | ||
this.stack = []; | ||
}; | ||
} | ||
@@ -79,3 +84,3 @@ /** | ||
* | ||
* Additionaly, `router.all()` can be used to match against all methods. | ||
* Additionally, `router.all()` can be used to match against all methods. | ||
* | ||
@@ -194,6 +199,6 @@ * ```javascript | ||
for (let i = 0; i < methods.length; i++) { | ||
for (const method_ of methods) { | ||
function setMethodVerb(method) { | ||
Router.prototype[method] = function(name, path, middleware) { | ||
if (typeof path === "string" || path instanceof RegExp) { | ||
Router.prototype[method] = function (name, path, middleware) { | ||
if (typeof path === 'string' || path instanceof RegExp) { | ||
middleware = Array.prototype.slice.call(arguments, 2); | ||
@@ -206,13 +211,23 @@ } else { | ||
this.register(path, [method], middleware, { | ||
name: name | ||
}); | ||
// Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array) | ||
if ( | ||
typeof path !== 'string' && | ||
!(path instanceof RegExp) && | ||
(!Array.isArray(path) || path.length === 0) | ||
) | ||
throw new Error( | ||
`You have to provide a path when adding a ${method} handler` | ||
); | ||
this.register(path, [method], middleware, { name }); | ||
return this; | ||
}; | ||
} | ||
setMethodVerb(methods[i]); | ||
setMethodVerb(method_); | ||
} | ||
// Alias for `router.delete()` because delete is a reserved word | ||
// eslint-disable-next-line dot-notation | ||
Router.prototype.del = Router.prototype['delete']; | ||
@@ -257,5 +272,4 @@ | ||
if (Array.isArray(middleware[0]) && typeof middleware[0][0] === 'string') { | ||
let arrPaths = middleware[0]; | ||
for (let i = 0; i < arrPaths.length; i++) { | ||
const p = arrPaths[i]; | ||
const arrPaths = middleware[0]; | ||
for (const p of arrPaths) { | ||
router.use.apply(router, [p].concat(middleware.slice(1))); | ||
@@ -270,8 +284,11 @@ } | ||
for (let i = 0; i < middleware.length; i++) { | ||
const m = middleware[i]; | ||
for (const m of middleware) { | ||
if (m.router) { | ||
const cloneRouter = Object.assign(Object.create(Router.prototype), m.router, { | ||
stack: m.router.stack.slice(0) | ||
}); | ||
const cloneRouter = Object.assign( | ||
Object.create(Router.prototype), | ||
m.router, | ||
{ | ||
stack: [...m.router.stack] | ||
} | ||
); | ||
@@ -294,7 +311,7 @@ for (let j = 0; j < cloneRouter.stack.length; j++) { | ||
const routerParams = paramArr; | ||
for (let j = 0; j < routerParams.length; j++) { | ||
const key = routerParams[j]; | ||
for (const key of routerParams) { | ||
cloneRouter.param(key, router.params[key]); | ||
} | ||
} | ||
setRouterParams(Object.keys(router.params)); | ||
@@ -306,3 +323,6 @@ } | ||
const routerPrefixHasParam = router.opts.prefix && keys.length; | ||
router.register(path || '([^\/]*)', [], m, { end: false, ignoreCaptures: !hasPath && !routerPrefixHasParam }); | ||
router.register(path || '([^/]*)', [], m, { | ||
end: false, | ||
ignoreCaptures: !hasPath && !routerPrefixHasParam | ||
}); | ||
} | ||
@@ -349,3 +369,3 @@ } | ||
let dispatch = function dispatch(ctx, next) { | ||
const dispatch = function dispatch(ctx, next) { | ||
debug('%s %s', ctx.method, ctx.path); | ||
@@ -367,4 +387,4 @@ | ||
const matchedLayers = matched.pathAndMethod | ||
const mostSpecificLayer = matchedLayers[matchedLayers.length - 1] | ||
const matchedLayers = matched.pathAndMethod; | ||
const mostSpecificLayer = matchedLayers[matchedLayers.length - 1]; | ||
ctx._matchedRoute = mostSpecificLayer.path; | ||
@@ -375,6 +395,12 @@ if (mostSpecificLayer.name) { | ||
layerChain = matchedLayers.reduce(function(memo, layer) { | ||
memo.push(function(ctx, next) { | ||
layerChain = ( | ||
router.exclusive ? [mostSpecificLayer] : matchedLayers | ||
).reduce(function (memo, layer) { | ||
memo.push(function (ctx, next) { | ||
ctx.captures = layer.captures(path, ctx.captures); | ||
ctx.params = ctx.request.params = layer.params(path, ctx.captures, ctx.params); | ||
ctx.params = ctx.request.params = layer.params( | ||
path, | ||
ctx.captures, | ||
ctx.params | ||
); | ||
ctx.routerPath = layer.path; | ||
@@ -386,2 +412,3 @@ ctx.routerName = layer.name; | ||
} | ||
return next(); | ||
@@ -443,8 +470,7 @@ }); | ||
Router.prototype.allowedMethods = function (options) { | ||
options = options || {}; | ||
Router.prototype.allowedMethods = function (options = {}) { | ||
const implemented = this.methods; | ||
return function allowedMethods(ctx, next) { | ||
return next().then(function() { | ||
return next().then(function () { | ||
const allowed = {}; | ||
@@ -465,5 +491,6 @@ | ||
if (options.throw) { | ||
let notImplementedThrowable = (typeof options.notImplemented === 'function') | ||
? options.notImplemented() // set whatever the user returns from their function | ||
: new HttpError.NotImplemented(); | ||
const notImplementedThrowable = | ||
typeof options.notImplemented === 'function' | ||
? options.notImplemented() // set whatever the user returns from their function | ||
: new HttpError.NotImplemented(); | ||
@@ -475,3 +502,3 @@ throw notImplementedThrowable; | ||
} | ||
} else if (allowedArr.length) { | ||
} else if (allowedArr.length > 0) { | ||
if (ctx.method === 'OPTIONS') { | ||
@@ -483,5 +510,6 @@ ctx.status = 200; | ||
if (options.throw) { | ||
let notAllowedThrowable = (typeof options.methodNotAllowed === 'function') | ||
? options.methodNotAllowed() // set whatever the user returns from their function | ||
: new HttpError.MethodNotAllowed(); | ||
const notAllowedThrowable = | ||
typeof options.methodNotAllowed === 'function' | ||
? options.methodNotAllowed() // set whatever the user returns from their function | ||
: new HttpError.MethodNotAllowed(); | ||
@@ -508,3 +536,2 @@ throw notAllowedThrowable; | ||
* @returns {Router} | ||
* @private | ||
*/ | ||
@@ -521,2 +548,10 @@ | ||
// Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array) | ||
if ( | ||
typeof path !== 'string' && | ||
!(path instanceof RegExp) && | ||
(!Array.isArray(path) || path.length === 0) | ||
) | ||
throw new Error('You have to provide a path when adding an all handler'); | ||
this.register(path, methods, middleware, { name }); | ||
@@ -553,8 +588,17 @@ | ||
// lookup source route by name | ||
if (source[0] !== '/') source = this.url(source); | ||
if (typeof source === 'symbol' || source[0] !== '/') { | ||
source = this.url(source); | ||
if (source instanceof Error) throw source; | ||
} | ||
// lookup destination route by name | ||
if (destination[0] !== '/' && !destination.includes('://')) destination = this.url(destination); | ||
if ( | ||
typeof destination === 'symbol' || | ||
(destination[0] !== '/' && !destination.includes('://')) | ||
) { | ||
destination = this.url(destination); | ||
if (destination instanceof Error) throw destination; | ||
} | ||
return this.all(source, ctx => { | ||
return this.all(source, (ctx) => { | ||
ctx.redirect(destination); | ||
@@ -575,12 +619,9 @@ ctx.status = code || 301; | ||
Router.prototype.register = function (path, methods, middleware, opts) { | ||
opts = opts || {}; | ||
Router.prototype.register = function (path, methods, middleware, opts = {}) { | ||
const router = this; | ||
const stack = this.stack; | ||
const { stack } = this; | ||
// support array of paths | ||
if (Array.isArray(path)) { | ||
for (let i = 0; i < path.length; i++) { | ||
const curPath = path[i]; | ||
for (const curPath of path) { | ||
router.register.call(router, curPath, methods, middleware, opts); | ||
@@ -598,3 +639,3 @@ } | ||
strict: opts.strict || this.opts.strict || false, | ||
prefix: opts.prefix || this.opts.prefix || "", | ||
prefix: opts.prefix || this.opts.prefix || '', | ||
ignoreCaptures: opts.ignoreCaptures | ||
@@ -630,3 +671,3 @@ }); | ||
for (let len = routes.length, i=0; i<len; i++) { | ||
for (let len = routes.length, i = 0; i < len; i++) { | ||
if (routes[i].name && routes[i].name === name) return routes[i]; | ||
@@ -681,3 +722,3 @@ } | ||
return new Error(`No route found for name: ${name}`); | ||
return new Error(`No route found for name: ${String(name)}`); | ||
}; | ||
@@ -709,2 +750,3 @@ | ||
// eslint-disable-next-line unicorn/prefer-regexp-test | ||
if (layer.match(path)) { | ||
@@ -715,3 +757,3 @@ matched.path.push(layer); | ||
matched.pathAndMethod.push(layer); | ||
if (layer.methods.length) matched.route = true; | ||
if (layer.methods.length > 0) matched.route = true; | ||
} | ||
@@ -754,3 +796,3 @@ } | ||
Router.prototype.param = function(param, middleware) { | ||
Router.prototype.param = function (param, middleware) { | ||
this.params[param] = middleware; | ||
@@ -757,0 +799,0 @@ for (let i = 0; i < this.stack.length; i++) { |
{ | ||
"name": "koa-router", | ||
"description": "Router middleware for koa. Provides RESTful resource routing.", | ||
"version": "10.1.1", | ||
"description": "Router middleware for koa. Maintained by Forward Email and Lad.", | ||
"version": "11.0.0", | ||
"author": "Alex Mingoia <talk@alexmingoia.com>", | ||
@@ -10,22 +10,37 @@ "bugs": { | ||
}, | ||
"contributors": [ | ||
"Alex Mingoia <talk@alexmingoia.com>", | ||
"@koajs" | ||
], | ||
"dependencies": { | ||
"debug": "^4.1.1", | ||
"http-errors": "^1.7.3", | ||
"http-errors": "^2.0.0", | ||
"koa-compose": "^4.1.0", | ||
"methods": "^1.1.2", | ||
"path-to-regexp": "^6.1.0" | ||
"path-to-regexp": "^6.2.1" | ||
}, | ||
"devDependencies": { | ||
"@ladjs/env": "^1.0.0", | ||
"@commitlint/cli": "^17.0.3", | ||
"@commitlint/config-conventional": "^17.0.3", | ||
"@ladjs/env": "^3.0.0", | ||
"ava": "^4.3.0", | ||
"cross-env": "^7.0.3", | ||
"eslint": "^8.19.0", | ||
"eslint-config-xo-lass": "^2.0.1", | ||
"expect.js": "^0.3.1", | ||
"jsdoc-to-markdown": "^5.0.3", | ||
"koa": "^2.11.0", | ||
"mocha": "^7.0.1", | ||
"nyc": "^15.0.0", | ||
"fixpack": "^4.0.0", | ||
"husky": "^8.0.1", | ||
"jsdoc-to-markdown": "^7.1.1", | ||
"koa": "^2.13.4", | ||
"lint-staged": "^13.0.3", | ||
"mocha": "^10.0.0", | ||
"nyc": "^15.1.0", | ||
"remark-cli": "^11.0.0", | ||
"remark-preset-github": "^4.0.4", | ||
"should": "^13.2.3", | ||
"supertest": "^4.0.2", | ||
"wrk": "^1.2.0" | ||
"supertest": "^6.2.4", | ||
"wrk": "^1.2.1", | ||
"xo": "^0.50.0" | ||
}, | ||
"engines": { | ||
"node": ">= 8.0.0" | ||
"node": ">= 12" | ||
}, | ||
@@ -49,7 +64,11 @@ "files": [ | ||
"scripts": { | ||
"bench": "make -C bench", | ||
"coverage": "nyc npm run test", | ||
"docs": "NODE_ENV=test jsdoc2md -t ./lib/API_tpl.hbs --src ./lib/*.js >| API.md", | ||
"lint": "xo --fix && remark . -qfo && fixpack", | ||
"prepare": "husky install", | ||
"pretest": "npm run lint", | ||
"test": "mocha test/**/*.js", | ||
"coverage": "nyc npm run test", | ||
"bench": "make -C bench" | ||
"test:watch": "mocha test/**/*.js --watch" | ||
} | ||
} |
# [@koa/router](https://github.com/koajs/router) | ||
> Router middleware for [Koa](https://github.com/koajs/koa). | ||
> Router middleware for [Koa](https://github.com/koajs/koa). Maintained by [Forward Email][forward-email] and [Lad][]. | ||
[![NPM version](https://img.shields.io/npm/v/@koa/router.svg?style=flat)](https://npmjs.org/package/@koa/router) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/@koa/router.svg?style=flat)](https://npmjs.org/package/@koa/router) | ||
[![Node.js Version](https://img.shields.io/node/v/@koa/router.svg?style=flat)](http://nodejs.org/download/) | ||
[![Build Status](https://img.shields.io/travis/koajs/router.svg?style=flat)](http://travis-ci.org/koajs/router) | ||
[![gitter](https://img.shields.io/gitter/room/koajs/koa.svg?style=flat)](https://gitter.im/koajs/koa) | ||
[![build status](https://github.com/koajs/router/actions/workflows/ci.yml/badge.svg)](https://github.com/koajs/router/actions/workflows/ci.yml) | ||
[![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) | ||
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) | ||
[![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org) | ||
[![license](https://img.shields.io/github/license/koajs/router.svg)](LICENSE) | ||
## Table of Contents | ||
* [Features](#features) | ||
* [Migrating to 7 / Koa 2](#migrating-to-7--koa-2) | ||
* [Install](#install) | ||
* [Typescript Support](#typescript-support) | ||
* [API Reference](#api-reference) | ||
* [Contributors](#contributors) | ||
* [License](#license) | ||
## Features | ||
* Express-style routing (`app.get`, `app.put`, `app.post`, etc.) | ||
@@ -20,37 +34,52 @@ * Named URL parameters | ||
## Migrating to 7 / Koa 2 | ||
- The API has changed to match the new promise-based middleware | ||
* 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()`, | ||
* Middleware is now always run in the order declared by `.use()` (or `.get()`, | ||
etc.), which matches Express 4 API. | ||
## Installation | ||
```bash | ||
# npm .. | ||
npm i @koa/router | ||
# yarn .. | ||
yarn add @koa/router | ||
## Install | ||
[npm][]: | ||
```sh | ||
npm install @koa/router | ||
``` | ||
## [API Reference](./API.md) | ||
## Contributing | ||
## Typescript Support | ||
Please submit all issues and pull requests to the [koajs/router](http://github.com/koajs/router) repository! | ||
```sh | ||
npm install @types/koa__router | ||
``` | ||
## Support | ||
If you have any problem or suggestion please open an issue [here](https://github.com/koajs/router/issues). | ||
## API Reference | ||
## Call for Maintainers | ||
See [API Reference](./API.md) for more documentation. | ||
This module is forked from the original [koa-router](https://github.com/ZijianHe/koa-router) due to its lack of activity. `koa-router` is the most widely used router module in the Koa community and we need maintainers. If you're interested in fixing bugs or implementing new features feel free to open a pull request. We'll be adding active contributors as collaborators. | ||
Thanks to the original authors @alexmingoia and the original team for their great work. | ||
## Contributors | ||
### License | ||
| Name | | ||
| ---------------- | | ||
| **Alex Mingoia** | | ||
| **@koajs** | | ||
[MIT](LICENSE) | ||
## License | ||
[MIT](LICENSE) © Alex Mingoia | ||
## | ||
[forward-email]: https://forwardemail.net | ||
[lad]: https://lad.js.org | ||
[npm]: https//www.npmjs.com |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4
914
85
31128
21
6
2
+ Addeddepd@2.0.0(transitive)
+ Addedhttp-errors@2.0.0(transitive)
+ Addedstatuses@2.0.1(transitive)
- Removeddebug@^4.1.1
- Removeddebug@4.3.7(transitive)
- Removeddepd@1.1.2(transitive)
- Removedhttp-errors@1.8.1(transitive)
- Removedms@2.1.3(transitive)
- Removedstatuses@1.5.0(transitive)
Updatedhttp-errors@^2.0.0
Updatedpath-to-regexp@^6.2.1