@koa/router 10.1.0 → 11.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,7 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Alexander C. Mingoia
4
- Copyright (c) 2019-present Nick Baugh and Koajs contributors
3
+ Copyright (c) 2015 Alexander C. Mingoia and @koajs contributors
5
4
 
6
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
6
  of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- THE SOFTWARE.
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,55 +1,85 @@
1
1
  # [@koa/router](https://github.com/koajs/router)
2
2
 
3
- > Router middleware for [Koa](https://github.com/koajs/koa).
3
+ > Router middleware for [Koa](https://github.com/koajs/koa). Maintained by [Forward Email][forward-email] and [Lad][].
4
4
 
5
- [![NPM version](https://img.shields.io/npm/v/@koa/router.svg?style=flat)](https://npmjs.org/package/@koa/router)
6
- [![NPM Downloads](https://img.shields.io/npm/dm/@koa/router.svg?style=flat)](https://npmjs.org/package/@koa/router)
7
- [![Node.js Version](https://img.shields.io/node/v/@koa/router.svg?style=flat)](http://nodejs.org/download/)
8
- [![Build Status](https://img.shields.io/travis/koajs/router.svg?style=flat)](http://travis-ci.org/koajs/router)
9
- [![gitter](https://img.shields.io/gitter/room/koajs/koa.svg?style=flat)](https://gitter.im/koajs/koa)
5
+ [![build status](https://github.com/koajs/router/actions/workflows/ci.yml/badge.svg)](https://github.com/koajs/router/actions/workflows/ci.yml)
6
+ [![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
7
+ [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
8
+ [![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)
9
+ [![license](https://img.shields.io/github/license/koajs/router.svg)](LICENSE)
10
+
11
+
12
+ ## Table of Contents
13
+
14
+ * [Features](#features)
15
+ * [Migrating to 7 / Koa 2](#migrating-to-7--koa-2)
16
+ * [Install](#install)
17
+ * [Typescript Support](#typescript-support)
18
+ * [API Reference](#api-reference)
19
+ * [Contributors](#contributors)
20
+ * [License](#license)
21
+
22
+
23
+ ## Features
10
24
 
11
25
  * Express-style routing (`app.get`, `app.put`, `app.post`, etc.)
12
26
  * Named URL parameters
13
27
  * Named routes with URL generation
28
+ * Match routes with specific host
14
29
  * Responds to `OPTIONS` requests with allowed methods
15
30
  * Support for `405 Method Not Allowed` and `501 Not Implemented`
16
31
  * Multiple route middleware
17
32
  * Multiple and nestable routers
18
33
  * `async/await` support
19
34
 
35
+
20
36
  ## Migrating to 7 / Koa 2
21
37
 
22
- - The API has changed to match the new promise-based middleware
38
+ * The API has changed to match the new promise-based middleware
23
39
  signature of koa 2. See the [koa 2.x readme](https://github.com/koajs/koa/tree/2.0.0-alpha.3) for more
24
40
  information.
25
- - Middleware is now always run in the order declared by `.use()` (or `.get()`,
41
+ * Middleware is now always run in the order declared by `.use()` (or `.get()`,
26
42
  etc.), which matches Express 4 API.
27
43
 
28
- ## Installation
29
44
 
30
- ```bash
31
- # npm ..
32
- npm i @koa/router
33
- # yarn ..
34
- yarn add @koa/router
45
+ ## Install
46
+
47
+ [npm][]:
48
+
49
+ ```sh
50
+ npm install @koa/router
35
51
  ```
36
52
 
37
- ## [API Reference](./API.md)
38
53
 
39
- ## Contributing
54
+ ## Typescript Support
55
+
56
+ ```sh
57
+ npm install @types/koa__router
58
+ ```
59
+
60
+
61
+ ## API Reference
62
+
63
+ See [API Reference](./API.md) for more documentation.
64
+
65
+
66
+ ## Contributors
67
+
68
+ | Name |
69
+ | ---------------- |
70
+ | **Alex Mingoia** |
71
+ | **@koajs** |
40
72
 
41
- Please submit all issues and pull requests to the [koajs/router](http://github.com/koajs/router) repository!
42
73
 
43
- ## Support
74
+ ## License
44
75
 
45
- If you have any problem or suggestion please open an issue [here](https://github.com/koajs/router/issues).
76
+ [MIT](LICENSE) © Alex Mingoia
46
77
 
47
- ## Call for Maintainers
48
78
 
49
- 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.
79
+ ##
50
80
 
51
- Thanks to the original authors @alexmingoia and the original team for their great work.
81
+ [forward-email]: https://forwardemail.net
52
82
 
53
- ### License
83
+ [lad]: https://lad.js.org
54
84
 
55
- [MIT](LICENSE)
85
+ [npm]: https//www.npmjs.com
package/lib/layer.js CHANGED
@@ -1,5 +1,5 @@
1
- const { pathToRegexp, compile, parse } = require('path-to-regexp');
2
1
  const { parse: parseUrl, format: formatUrl } = require('url');
2
+ const { pathToRegexp, compile, parse } = require('path-to-regexp');
3
3
 
4
4
  module.exports = Layer;
5
5
 
@@ -17,31 +17,33 @@ module.exports = Layer;
17
17
  * @private
18
18
  */
19
19
 
20
- function Layer(path, methods, middleware, opts) {
21
- this.opts = opts || {};
20
+ function Layer(path, methods, middleware, opts = {}) {
21
+ this.opts = opts;
22
22
  this.name = this.opts.name || null;
23
23
  this.methods = [];
24
24
  this.paramNames = [];
25
25
  this.stack = Array.isArray(middleware) ? middleware : [middleware];
26
26
 
27
- for (let i = 0; i < methods.length; i++) {
28
- const l = this.methods.push(methods[i].toUpperCase());
27
+ for (const method of methods) {
28
+ const l = this.methods.push(method.toUpperCase());
29
29
  if (this.methods[l - 1] === 'GET') this.methods.unshift('HEAD');
30
30
  }
31
31
 
32
32
  // ensure middleware is a function
33
33
  for (let i = 0; i < this.stack.length; i++) {
34
34
  const fn = this.stack[i];
35
- const type = (typeof fn);
35
+ const type = typeof fn;
36
36
  if (type !== 'function')
37
37
  throw new Error(
38
- `${methods.toString()} \`${this.opts.name || path}\`: \`middleware\` must be a function, not \`${type}\``
38
+ `${methods.toString()} \`${
39
+ this.opts.name || path
40
+ }\`: \`middleware\` must be a function, not \`${type}\``
39
41
  );
40
42
  }
41
43
 
42
44
  this.path = path;
43
45
  this.regexp = pathToRegexp(path, this.paramNames, this.opts);
44
- };
46
+ }
45
47
 
46
48
  /**
47
49
  * Returns whether request `path` matches route.
@@ -60,18 +62,17 @@ Layer.prototype.match = function (path) {
60
62
  *
61
63
  * @param {String} path
62
64
  * @param {Array.<String>} captures
63
- * @param {Object=} existingParams
65
+ * @param {Object=} params
64
66
  * @returns {Object}
65
67
  * @private
66
68
  */
67
69
 
68
- Layer.prototype.params = function (path, captures, existingParams) {
69
- const params = existingParams || {};
70
-
70
+ Layer.prototype.params = function (path, captures, params = {}) {
71
71
  for (let len = captures.length, i = 0; i < len; i++) {
72
72
  if (this.paramNames[i]) {
73
73
  const c = captures[i];
74
- if (c && c.length !== 0) params[this.paramNames[i].name] = c ? safeDecodeURIComponent(c) : c;
74
+ if (c && c.length > 0)
75
+ params[this.paramNames[i].name] = c ? safeDecodeURIComponent(c) : c;
75
76
  }
76
77
  }
77
78
 
@@ -110,11 +111,11 @@ Layer.prototype.url = function (params, options) {
110
111
  let args = params;
111
112
  const url = this.path.replace(/\(\.\*\)/g, '');
112
113
 
113
- if (typeof params != 'object') {
114
+ if (typeof params !== 'object') {
114
115
  args = Array.prototype.slice.call(arguments);
115
- if (typeof args[args.length - 1] == 'object') {
116
+ if (typeof args[args.length - 1] === 'object') {
116
117
  options = args[args.length - 1];
117
- args = args.slice(0, args.length - 1);
118
+ args = args.slice(0, -1);
118
119
  }
119
120
  }
120
121
 
@@ -124,11 +125,11 @@ Layer.prototype.url = function (params, options) {
124
125
  const tokens = parse(url);
125
126
  let replace = {};
126
127
 
127
- if (args instanceof Array) {
128
+ if (Array.isArray(args)) {
128
129
  for (let len = tokens.length, i = 0, j = 0; i < len; i++) {
129
130
  if (tokens[i].name) replace[tokens[i].name] = args[j++];
130
131
  }
131
- } else if (tokens.some(token => token.name)) {
132
+ } else if (tokens.some((token) => token.name)) {
132
133
  replace = params;
133
134
  } else if (!options) {
134
135
  options = params;
@@ -144,6 +145,7 @@ Layer.prototype.url = function (params, options) {
144
145
  replaced.search = undefined;
145
146
  replaced.query = options.query;
146
147
  }
148
+
147
149
  return formatUrl(replaced);
148
150
  }
149
151
 
@@ -159,7 +161,7 @@ Layer.prototype.url = function (params, options) {
159
161
  * router
160
162
  * .param('user', function (id, ctx, next) {
161
163
  * ctx.user = users[id];
162
- * if (!user) return ctx.status = 404;
164
+ * if (!ctx.user) return ctx.status = 404;
163
165
  * next();
164
166
  * })
165
167
  * .get('/users/:user', function (ctx, next) {
@@ -174,11 +176,12 @@ Layer.prototype.url = function (params, options) {
174
176
  */
175
177
 
176
178
  Layer.prototype.param = function (param, fn) {
177
- const stack = this.stack;
179
+ const { stack } = this;
178
180
  const params = this.paramNames;
179
181
  const middleware = function (ctx, next) {
180
182
  return fn.call(this, ctx.params[param], ctx, next);
181
183
  };
184
+
182
185
  middleware.param = param;
183
186
 
184
187
  const names = params.map(function (p) {
@@ -212,7 +215,10 @@ Layer.prototype.param = function (param, fn) {
212
215
 
213
216
  Layer.prototype.setPrefix = function (prefix) {
214
217
  if (this.path) {
215
- this.path = (this.path !== '/' || this.opts.strict === true) ? `${prefix}${this.path}` : prefix
218
+ this.path =
219
+ this.path !== '/' || this.opts.strict === true
220
+ ? `${prefix}${this.path}`
221
+ : prefix;
216
222
  this.paramNames = [];
217
223
  this.regexp = pathToRegexp(this.path, this.paramNames, this.opts);
218
224
  }
@@ -232,7 +238,7 @@ Layer.prototype.setPrefix = function (prefix) {
232
238
  function safeDecodeURIComponent(text) {
233
239
  try {
234
240
  return decodeURIComponent(text);
235
- } catch (e) {
241
+ } catch {
236
242
  return text;
237
243
  }
238
244
  }
package/lib/router.js CHANGED
@@ -5,12 +5,15 @@
5
5
  * @link https://github.com/alexmingoia/koa-router
6
6
  */
7
7
 
8
- const debug = require('debug')('koa-router');
8
+ const { debuglog } = require('util');
9
+
9
10
  const compose = require('koa-compose');
10
11
  const HttpError = require('http-errors');
11
12
  const methods = require('methods');
12
- const Layer = require('./layer');
13
13
  const { pathToRegexp } = require('path-to-regexp');
14
+ const Layer = require('./layer');
15
+
16
+ const debug = debuglog('koa-router');
14
17
 
15
18
  /**
16
19
  * @module koa-router
@@ -43,14 +46,16 @@ module.exports = Router;
43
46
  *
44
47
  * @alias module:koa-router
45
48
  * @param {Object=} opts
49
+ * @param {Boolean=false} opts.exclusive only run last matched route's controller when there are multiple matches
46
50
  * @param {String=} opts.prefix prefix router paths
51
+ * @param {String|RegExp=} opts.host host for router match
47
52
  * @constructor
48
53
  */
49
54
 
50
- function Router(opts) {
55
+ function Router(opts = {}) {
51
56
  if (!(this instanceof Router)) return new Router(opts);
52
57
 
53
- this.opts = opts || {};
58
+ this.opts = opts;
54
59
  this.methods = this.opts.methods || [
55
60
  'HEAD',
56
61
  'OPTIONS',
@@ -60,10 +65,12 @@ function Router(opts) {
60
65
  'POST',
61
66
  'DELETE'
62
67
  ];
68
+ this.exclusive = Boolean(this.opts.exclusive);
63
69
 
64
70
  this.params = {};
65
71
  this.stack = [];
66
- };
72
+ this.host = this.opts.host;
73
+ }
67
74
 
68
75
  /**
69
76
  * Create `router.verb()` methods, where *verb* is one of the HTTP verbs such
@@ -72,7 +79,7 @@ function Router(opts) {
72
79
  * Match URL patterns to callback functions or controller actions using `router.verb()`,
73
80
  * where **verb** is one of the HTTP verbs such as `router.get()` or `router.post()`.
74
81
  *
75
- * Additionaly, `router.all()` can be used to match against all methods.
82
+ * Additionally, `router.all()` can be used to match against all methods.
76
83
  *
77
84
  * ```javascript
78
85
  * router
@@ -178,6 +185,24 @@ function Router(opts) {
178
185
  * The [path-to-regexp](https://github.com/pillarjs/path-to-regexp) module is
179
186
  * used to convert paths to regular expressions.
180
187
  *
188
+ *
189
+ * ### Match host for each router instance
190
+ *
191
+ * ```javascript
192
+ * const router = new Router({
193
+ * host: 'example.domain' // only match if request host exactly equal `example.domain`
194
+ * });
195
+ *
196
+ * ```
197
+ *
198
+ * OR host cloud be a regexp
199
+ *
200
+ * ```javascript
201
+ * const router = new Router({
202
+ * host: /.*\.?example\.domain$/ // all host end with .example.domain would be matched
203
+ * });
204
+ * ```
205
+ *
181
206
  * @name get|put|post|patch|delete|del
182
207
  * @memberof module:koa-router.prototype
183
208
  * @param {String} path
@@ -186,10 +211,10 @@ function Router(opts) {
186
211
  * @returns {Router}
187
212
  */
188
213
 
189
- for (let i = 0; i < methods.length; i++) {
214
+ for (const method_ of methods) {
190
215
  function setMethodVerb(method) {
191
- Router.prototype[method] = function(name, path, middleware) {
192
- if (typeof path === "string" || path instanceof RegExp) {
216
+ Router.prototype[method] = function (name, path, middleware) {
217
+ if (typeof path === 'string' || path instanceof RegExp) {
193
218
  middleware = Array.prototype.slice.call(arguments, 2);
194
219
  } else {
195
220
  middleware = Array.prototype.slice.call(arguments, 1);
@@ -197,17 +222,27 @@ for (let i = 0; i < methods.length; i++) {
197
222
  name = null;
198
223
  }
199
224
 
200
- this.register(path, [method], middleware, {
201
- name: name
202
- });
225
+ // Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
226
+ if (
227
+ typeof path !== 'string' &&
228
+ !(path instanceof RegExp) &&
229
+ (!Array.isArray(path) || path.length === 0)
230
+ )
231
+ throw new Error(
232
+ `You have to provide a path when adding a ${method} handler`
233
+ );
234
+
235
+ this.register(path, [method], middleware, { name });
203
236
 
204
237
  return this;
205
238
  };
206
239
  }
207
- setMethodVerb(methods[i]);
240
+
241
+ setMethodVerb(method_);
208
242
  }
209
243
 
210
244
  // Alias for `router.delete()` because delete is a reserved word
245
+ // eslint-disable-next-line dot-notation
211
246
  Router.prototype.del = Router.prototype['delete'];
212
247
 
213
248
  /**
@@ -247,9 +282,8 @@ Router.prototype.use = function () {
247
282
 
248
283
  // support array of paths
249
284
  if (Array.isArray(middleware[0]) && typeof middleware[0][0] === 'string') {
250
- let arrPaths = middleware[0];
251
- for (let i = 0; i < arrPaths.length; i++) {
252
- const p = arrPaths[i];
285
+ const arrPaths = middleware[0];
286
+ for (const p of arrPaths) {
253
287
  router.use.apply(router, [p].concat(middleware.slice(1)));
254
288
  }
255
289
 
@@ -259,12 +293,15 @@ Router.prototype.use = function () {
259
293
  const hasPath = typeof middleware[0] === 'string';
260
294
  if (hasPath) path = middleware.shift();
261
295
 
262
- for (let i = 0; i < middleware.length; i++) {
263
- const m = middleware[i];
296
+ for (const m of middleware) {
264
297
  if (m.router) {
265
- const cloneRouter = Object.assign(Object.create(Router.prototype), m.router, {
266
- stack: m.router.stack.slice(0)
267
- });
298
+ const cloneRouter = Object.assign(
299
+ Object.create(Router.prototype),
300
+ m.router,
301
+ {
302
+ stack: [...m.router.stack]
303
+ }
304
+ );
268
305
 
269
306
  for (let j = 0; j < cloneRouter.stack.length; j++) {
270
307
  const nestedLayer = cloneRouter.stack[j];
@@ -282,18 +319,21 @@ Router.prototype.use = function () {
282
319
  if (router.params) {
283
320
  function setRouterParams(paramArr) {
284
321
  const routerParams = paramArr;
285
- for (let j = 0; j < routerParams.length; j++) {
286
- const key = routerParams[j];
322
+ for (const key of routerParams) {
287
323
  cloneRouter.param(key, router.params[key]);
288
324
  }
289
325
  }
326
+
290
327
  setRouterParams(Object.keys(router.params));
291
328
  }
292
329
  } else {
293
330
  const keys = [];
294
331
  pathToRegexp(router.opts.prefix || '', keys);
295
332
  const routerPrefixHasParam = router.opts.prefix && keys.length;
296
- router.register(path || '([^\/]*)', [], m, { end: false, ignoreCaptures: !hasPath && !routerPrefixHasParam });
333
+ router.register(path || '([^/]*)', [], m, {
334
+ end: false,
335
+ ignoreCaptures: !hasPath && !routerPrefixHasParam
336
+ });
297
337
  }
298
338
  }
299
339
 
@@ -335,9 +375,15 @@ Router.prototype.prefix = function (prefix) {
335
375
  Router.prototype.routes = Router.prototype.middleware = function () {
336
376
  const router = this;
337
377
 
338
- let dispatch = function dispatch(ctx, next) {
378
+ const dispatch = function dispatch(ctx, next) {
339
379
  debug('%s %s', ctx.method, ctx.path);
340
380
 
381
+ const hostMatched = router.matchHost(ctx.host);
382
+
383
+ if (!hostMatched) {
384
+ return next();
385
+ }
386
+
341
387
  const path = router.opts.routerPath || ctx.routerPath || ctx.path;
342
388
  const matched = router.match(path, ctx.method);
343
389
  let layerChain;
@@ -352,23 +398,30 @@ Router.prototype.routes = Router.prototype.middleware = function () {
352
398
 
353
399
  if (!matched.route) return next();
354
400
 
355
- const matchedLayers = matched.pathAndMethod
356
- const mostSpecificLayer = matchedLayers[matchedLayers.length - 1]
401
+ const matchedLayers = matched.pathAndMethod;
402
+ const mostSpecificLayer = matchedLayers[matchedLayers.length - 1];
357
403
  ctx._matchedRoute = mostSpecificLayer.path;
358
404
  if (mostSpecificLayer.name) {
359
405
  ctx._matchedRouteName = mostSpecificLayer.name;
360
406
  }
361
407
 
362
- layerChain = matchedLayers.reduce(function(memo, layer) {
363
- memo.push(function(ctx, next) {
408
+ layerChain = (
409
+ router.exclusive ? [mostSpecificLayer] : matchedLayers
410
+ ).reduce(function (memo, layer) {
411
+ memo.push(function (ctx, next) {
364
412
  ctx.captures = layer.captures(path, ctx.captures);
365
- ctx.params = ctx.request.params = layer.params(path, ctx.captures, ctx.params);
413
+ ctx.params = ctx.request.params = layer.params(
414
+ path,
415
+ ctx.captures,
416
+ ctx.params
417
+ );
366
418
  ctx.routerPath = layer.path;
367
419
  ctx.routerName = layer.name;
368
420
  ctx._matchedRoute = layer.path;
369
421
  if (layer.name) {
370
422
  ctx._matchedRouteName = layer.name;
371
423
  }
424
+
372
425
  return next();
373
426
  });
374
427
  return memo.concat(layer.stack);
@@ -425,12 +478,11 @@ Router.prototype.routes = Router.prototype.middleware = function () {
425
478
  * @returns {Function}
426
479
  */
427
480
 
428
- Router.prototype.allowedMethods = function (options) {
429
- options = options || {};
481
+ Router.prototype.allowedMethods = function (options = {}) {
430
482
  const implemented = this.methods;
431
483
 
432
484
  return function allowedMethods(ctx, next) {
433
- return next().then(function() {
485
+ return next().then(function () {
434
486
  const allowed = {};
435
487
 
436
488
  if (!ctx.status || ctx.status === 404) {
@@ -446,25 +498,27 @@ Router.prototype.allowedMethods = function (options) {
446
498
 
447
499
  if (!~implemented.indexOf(ctx.method)) {
448
500
  if (options.throw) {
449
- let notImplementedThrowable = (typeof options.notImplemented === 'function')
450
- ? options.notImplemented() // set whatever the user returns from their function
451
- : new HttpError.NotImplemented();
501
+ const notImplementedThrowable =
502
+ typeof options.notImplemented === 'function'
503
+ ? options.notImplemented() // set whatever the user returns from their function
504
+ : new HttpError.NotImplemented();
452
505
 
453
506
  throw notImplementedThrowable;
454
507
  } else {
455
508
  ctx.status = 501;
456
509
  ctx.set('Allow', allowedArr.join(', '));
457
510
  }
458
- } else if (allowedArr.length) {
511
+ } else if (allowedArr.length > 0) {
459
512
  if (ctx.method === 'OPTIONS') {
460
513
  ctx.status = 200;
461
514
  ctx.body = '';
462
515
  ctx.set('Allow', allowedArr.join(', '));
463
516
  } else if (!allowed[ctx.method]) {
464
517
  if (options.throw) {
465
- let notAllowedThrowable = (typeof options.methodNotAllowed === 'function')
466
- ? options.methodNotAllowed() // set whatever the user returns from their function
467
- : new HttpError.MethodNotAllowed();
518
+ const notAllowedThrowable =
519
+ typeof options.methodNotAllowed === 'function'
520
+ ? options.methodNotAllowed() // set whatever the user returns from their function
521
+ : new HttpError.MethodNotAllowed();
468
522
 
469
523
  throw notAllowedThrowable;
470
524
  } else {
@@ -486,7 +540,6 @@ Router.prototype.allowedMethods = function (options) {
486
540
  * @param {Function=} middleware You may also pass multiple middleware.
487
541
  * @param {Function} callback
488
542
  * @returns {Router}
489
- * @private
490
543
  */
491
544
 
492
545
  Router.prototype.all = function (name, path, middleware) {
@@ -498,6 +551,14 @@ Router.prototype.all = function (name, path, middleware) {
498
551
  name = null;
499
552
  }
500
553
 
554
+ // Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
555
+ if (
556
+ typeof path !== 'string' &&
557
+ !(path instanceof RegExp) &&
558
+ (!Array.isArray(path) || path.length === 0)
559
+ )
560
+ throw new Error('You have to provide a path when adding an all handler');
561
+
501
562
  this.register(path, methods, middleware, { name });
502
563
 
503
564
  return this;
@@ -529,12 +590,21 @@ Router.prototype.all = function (name, path, middleware) {
529
590
 
530
591
  Router.prototype.redirect = function (source, destination, code) {
531
592
  // lookup source route by name
532
- if (source[0] !== '/') source = this.url(source);
593
+ if (typeof source === 'symbol' || source[0] !== '/') {
594
+ source = this.url(source);
595
+ if (source instanceof Error) throw source;
596
+ }
533
597
 
534
598
  // lookup destination route by name
535
- if (destination[0] !== '/' && !destination.includes('://')) destination = this.url(destination);
599
+ if (
600
+ typeof destination === 'symbol' ||
601
+ (destination[0] !== '/' && !destination.includes('://'))
602
+ ) {
603
+ destination = this.url(destination);
604
+ if (destination instanceof Error) throw destination;
605
+ }
536
606
 
537
- return this.all(source, ctx => {
607
+ return this.all(source, (ctx) => {
538
608
  ctx.redirect(destination);
539
609
  ctx.status = code || 301;
540
610
  });
@@ -550,16 +620,13 @@ Router.prototype.redirect = function (source, destination, code) {
550
620
  * @private
551
621
  */
552
622
 
553
- Router.prototype.register = function (path, methods, middleware, opts) {
554
- opts = opts || {};
555
-
623
+ Router.prototype.register = function (path, methods, middleware, opts = {}) {
556
624
  const router = this;
557
- const stack = this.stack;
625
+ const { stack } = this;
558
626
 
559
627
  // support array of paths
560
628
  if (Array.isArray(path)) {
561
- for (let i = 0; i < path.length; i++) {
562
- const curPath = path[i];
629
+ for (const curPath of path) {
563
630
  router.register.call(router, curPath, methods, middleware, opts);
564
631
  }
565
632
 
@@ -572,7 +639,7 @@ Router.prototype.register = function (path, methods, middleware, opts) {
572
639
  name: opts.name,
573
640
  sensitive: opts.sensitive || this.opts.sensitive || false,
574
641
  strict: opts.strict || this.opts.strict || false,
575
- prefix: opts.prefix || this.opts.prefix || "",
642
+ prefix: opts.prefix || this.opts.prefix || '',
576
643
  ignoreCaptures: opts.ignoreCaptures
577
644
  });
578
645
 
@@ -603,7 +670,7 @@ Router.prototype.register = function (path, methods, middleware, opts) {
603
670
  Router.prototype.route = function (name) {
604
671
  const routes = this.stack;
605
672
 
606
- for (let len = routes.length, i=0; i<len; i++) {
673
+ for (let len = routes.length, i = 0; i < len; i++) {
607
674
  if (routes[i].name && routes[i].name === name) return routes[i];
608
675
  }
609
676
 
@@ -653,7 +720,7 @@ Router.prototype.url = function (name, params) {
653
720
  return route.url.apply(route, args);
654
721
  }
655
722
 
656
- return new Error(`No route found for name: ${name}`);
723
+ return new Error(`No route found for name: ${String(name)}`);
657
724
  };
658
725
 
659
726
  /**
@@ -680,12 +747,13 @@ Router.prototype.match = function (path, method) {
680
747
 
681
748
  debug('test %s %s', layer.path, layer.regexp);
682
749
 
750
+ // eslint-disable-next-line unicorn/prefer-regexp-test
683
751
  if (layer.match(path)) {
684
752
  matched.path.push(layer);
685
753
 
686
754
  if (layer.methods.length === 0 || ~layer.methods.indexOf(method)) {
687
755
  matched.pathAndMethod.push(layer);
688
- if (layer.methods.length) matched.route = true;
756
+ if (layer.methods.length > 0) matched.route = true;
689
757
  }
690
758
  }
691
759
  }
@@ -693,6 +761,32 @@ Router.prototype.match = function (path, method) {
693
761
  return matched;
694
762
  };
695
763
 
764
+ /**
765
+ * Match given `input` to allowed host
766
+ * @param {String} input
767
+ * @returns {boolean}
768
+ */
769
+
770
+ Router.prototype.matchHost = function (input) {
771
+ const { host } = this;
772
+
773
+ if (!host) {
774
+ return true;
775
+ }
776
+
777
+ if (!input) {
778
+ return false;
779
+ }
780
+
781
+ if (typeof host === 'string') {
782
+ return input === host;
783
+ }
784
+
785
+ if (typeof host === 'object' && host instanceof RegExp) {
786
+ return host.test(input);
787
+ }
788
+ };
789
+
696
790
  /**
697
791
  * Run middleware for named route parameters. Useful for auto-loading or
698
792
  * validation.
@@ -723,7 +817,7 @@ Router.prototype.match = function (path, method) {
723
817
  * @returns {Router}
724
818
  */
725
819
 
726
- Router.prototype.param = function(param, middleware) {
820
+ Router.prototype.param = function (param, middleware) {
727
821
  this.params[param] = middleware;
728
822
  for (let i = 0; i < this.stack.length; i++) {
729
823
  const route = this.stack[i];
package/package.json CHANGED
@@ -1,32 +1,47 @@
1
1
  {
2
2
  "name": "@koa/router",
3
- "description": "Router middleware for koa. Provides RESTful resource routing.",
4
- "version": "10.1.0",
3
+ "description": "Router middleware for koa. Maintained by Forward Email and Lad.",
4
+ "version": "11.0.1",
5
5
  "author": "Alex Mingoia <talk@alexmingoia.com>",
6
6
  "bugs": {
7
7
  "url": "https://github.com/koajs/router/issues",
8
8
  "email": "niftylettuce@gmail.com"
9
9
  },
10
+ "contributors": [
11
+ "Alex Mingoia <talk@alexmingoia.com>",
12
+ "@koajs"
13
+ ],
10
14
  "dependencies": {
11
- "debug": "^4.1.1",
12
- "http-errors": "^1.7.3",
15
+ "http-errors": "^2.0.0",
13
16
  "koa-compose": "^4.1.0",
14
17
  "methods": "^1.1.2",
15
- "path-to-regexp": "^6.1.0"
18
+ "path-to-regexp": "^6.2.1"
16
19
  },
17
20
  "devDependencies": {
18
- "@ladjs/env": "^1.0.0",
21
+ "@commitlint/cli": "^17.0.3",
22
+ "@commitlint/config-conventional": "^17.0.3",
23
+ "@ladjs/env": "^3.0.0",
24
+ "ava": "^4.3.0",
25
+ "cross-env": "^7.0.3",
26
+ "eslint": "^8.19.0",
27
+ "eslint-config-xo-lass": "^2.0.1",
19
28
  "expect.js": "^0.3.1",
20
- "jsdoc-to-markdown": "^5.0.3",
21
- "koa": "^2.11.0",
22
- "mocha": "^7.0.1",
23
- "nyc": "^15.0.0",
29
+ "fixpack": "^4.0.0",
30
+ "husky": "^8.0.1",
31
+ "jsdoc-to-markdown": "^7.1.1",
32
+ "koa": "^2.13.4",
33
+ "lint-staged": "^13.0.3",
34
+ "mocha": "^10.0.0",
35
+ "nyc": "^15.1.0",
36
+ "remark-cli": "^11.0.0",
37
+ "remark-preset-github": "^4.0.4",
24
38
  "should": "^13.2.3",
25
- "supertest": "^4.0.2",
26
- "wrk": "^1.2.0"
39
+ "supertest": "^6.2.4",
40
+ "wrk": "^1.2.1",
41
+ "xo": "^0.50.0"
27
42
  },
28
43
  "engines": {
29
- "node": ">= 8.0.0"
44
+ "node": ">= 12"
30
45
  },
31
46
  "files": [
32
47
  "lib"
@@ -45,9 +60,13 @@
45
60
  "url": "https://github.com/koajs/router.git"
46
61
  },
47
62
  "scripts": {
63
+ "bench": "make -C bench",
64
+ "coverage": "nyc npm run test",
48
65
  "docs": "NODE_ENV=test jsdoc2md -t ./lib/API_tpl.hbs --src ./lib/*.js >| API.md",
66
+ "lint": "xo --fix && remark . -qfo && fixpack",
67
+ "prepare": "husky install",
68
+ "pretest": "npm run lint",
49
69
  "test": "mocha test/**/*.js",
50
- "coverage": "nyc npm run test",
51
- "bench": "make -C bench"
70
+ "test:watch": "mocha test/**/*.js --watch"
52
71
  }
53
72
  }
package/history.md DELETED
@@ -1,157 +0,0 @@
1
- 9.0.0 / 2020-04-09
2
- ==================
3
-
4
- - Update `path-to-regexp`. Migration path: change usage of `'*'` in routes to `(.*)` or `:splat*`.
5
- - Example: `router.get('*', ....)` becomes `router.get('(.*)') ....)`
6
-
7
-
8
- 8.0.0 / 2019-06-16
9
- ==================
10
-
11
- **others**
12
- * [[`b5dd5e8`](http://github.com/koajs/koa-router/commit/b5dd5e8f00e841b7061a62ab6228cbe96a999470)] - chore: rename to @koa/router (dead-horse <<dead_horse@qq.com>>)
13
-
14
- -------------
15
-
16
- # Changelogs inherit from koa-router.
17
-
18
- ## 7.4.0
19
-
20
- - Fix router.url() for multiple nested routers [#407](https://github.com/alexmingoia/koa-router/pull/407)
21
- - `layer.name` added to `ctx` at `ctx.routerName` during routing [#412](https://github.com/alexmingoia/koa-router/pull/412)
22
- - Router.use() was erroneously settings `(.*)` as a prefix to all routers nested with .use that did not pass an explicit prefix string as the first argument. This resulted in routes being matched that should not have been, included the running of multiple route handlers in error. [#369](https://github.com/alexmingoia/koa-router/issues/369) and [#410](https://github.com/alexmingoia/koa-router/issues/410) include information on this issue.
23
-
24
- ## 7.3.0
25
-
26
- - Router#url() now accepts query parameters to add to generated urls [#396](https://github.com/alexmingoia/koa-router/pull/396)
27
-
28
- ## 7.2.1
29
-
30
- - Respond to CORS preflights with 200, 0 length body [#359](https://github.com/alexmingoia/koa-router/issues/359)
31
-
32
- ## 7.2.0
33
-
34
- - Fix a bug in Router#url and append Router object to ctx. [#350](https://github.com/alexmingoia/koa-router/pull/350)
35
- - Adds `_matchedRouteName` to context [#337](https://github.com/alexmingoia/koa-router/pull/337)
36
- - Respond to CORS preflights with 200, 0 length body [#359](https://github.com/alexmingoia/koa-router/issues/359)
37
-
38
- ## 7.1.1
39
-
40
- - Fix bug where param handlers were run out of order [#282](https://github.com/alexmingoia/koa-router/pull/282)
41
-
42
- ## 7.1.0
43
-
44
- - Backports: merge 5.4 work into the 7.x upstream. See 5.4.0 updates for more details.
45
-
46
- ## 7.0.1
47
-
48
- - Fix: allowedMethods should be ctx.method not this.method [#215](https://github.com/alexmingoia/koa-router/pull/215)
49
-
50
- ## 7.0.0
51
-
52
- - The API has changed to match the new promise-based middleware
53
- signature of koa 2. See the
54
- [koa 2.x readme](https://github.com/koajs/koa/tree/2.0.0-alpha.3) for more
55
- information.
56
- - Middleware is now always run in the order declared by `.use()` (or `.get()`,
57
- etc.), which matches Express 4 API.
58
-
59
- ## 5.4.0
60
-
61
- - Expose matched route at `ctx._matchedRoute`.
62
-
63
- ## 5.3.0
64
-
65
- - Register multiple routes with array of paths [#203](https://github.com/alexmingoia/koa-router/issue/143).
66
- - Improved router.url() [#143](https://github.com/alexmingoia/koa-router/pull/143)
67
- - Adds support for named routes and regular expressions
68
- [#152](https://github.com/alexmingoia/koa-router/pull/152)
69
- - Add support for custom throw functions for 405 and 501 responses [#206](https://github.com/alexmingoia/koa-router/pull/206)
70
-
71
- ## 5.2.3
72
-
73
- - Fix for middleware running twice when nesting routes [#184](https://github.com/alexmingoia/koa-router/issues/184)
74
-
75
- ## 5.2.2
76
-
77
- - Register routes without params before those with params [#183](https://github.com/alexmingoia/koa-router/pull/183)
78
- - Fix for allowed methods [#182](https://github.com/alexmingoia/koa-router/issues/182)
79
-
80
- ## 5.2.0
81
-
82
- - Add support for async/await. Resolves [#130](https://github.com/alexmingoia/koa-router/issues/130).
83
- - Add support for array of paths by Router#use(). Resolves [#175](https://github.com/alexmingoia/koa-router/issues/175).
84
- - Inherit param middleware when nesting routers. Fixes [#170](https://github.com/alexmingoia/koa-router/issues/170).
85
- - Default router middleware without path to root. Fixes [#161](https://github.com/alexmingoia/koa-router/issues/161), [#155](https://github.com/alexmingoia/koa-router/issues/155), [#156](https://github.com/alexmingoia/koa-router/issues/156).
86
- - Run nested router middleware after parent's. Fixes [#156](https://github.com/alexmingoia/koa-router/issues/156).
87
- - Remove dependency on koa-compose.
88
-
89
- ## 5.1.1
90
-
91
- - Match routes in order they were defined. Fixes #131.
92
-
93
- ## 5.1.0
94
-
95
- - Support mounting router middleware at a given path.
96
-
97
- ## 5.0.1
98
-
99
- - Fix bug with missing parameters when nesting routers.
100
-
101
- ## 5.0.0
102
-
103
- - Remove confusing API for extending koa app with router methods. Router#use()
104
- does not have the same behavior as app#use().
105
- - Add support for nesting routes.
106
- - Remove support for regular expression routes to achieve nestable routers and
107
- enable future trie-based routing optimizations.
108
-
109
- ## 4.3.2
110
-
111
- - Do not send 405 if route matched but status is 404. Fixes #112, closes #114.
112
-
113
- ## 4.3.1
114
-
115
- - Do not run middleware if not yielded to by previous middleware. Fixes #115.
116
-
117
- ## 4.3.0
118
-
119
- - Add support for router prefixes.
120
- - Add MIT license.
121
-
122
- ## 4.2.0
123
-
124
- - Fixed issue with router middleware being applied even if no route was
125
- matched.
126
- - Router.url - new static method to generate url from url pattern and data
127
-
128
- ## 4.1.0
129
-
130
- Private API changed to separate context parameter decoration from route
131
- matching. `Router#match` and `Route#match` are now pure functions that return
132
- an array of routes that match the URL path.
133
-
134
- For modules using this private API that need to determine if a method and path
135
- match a route, `route.methods` must be checked against the routes returned from
136
- `router.match()`:
137
-
138
- ```javascript
139
- var matchedRoute = router.match(path).filter(function (route) {
140
- return ~route.methods.indexOf(method);
141
- }).shift();
142
- ```
143
-
144
- ## 4.0.0
145
-
146
- 405, 501, and OPTIONS response handling was moved into separate middleware
147
- `router.allowedMethods()`. This resolves incorrect 501 or 405 responses when
148
- using multiple routers.
149
-
150
- ### Breaking changes
151
-
152
- 4.x is mostly backwards compatible with 3.x, except for the following:
153
-
154
- - Instantiating a router with `new` and `app` returns the router instance,
155
- whereas 3.x returns the router middleware. When creating a router in 4.x, the
156
- only time router middleware is returned is when creating using the
157
- `Router(app)` signature (with `app` and without `new`).