@koa/router 13.1.0 → 14.0.0

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/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  > Router middleware for [Koa](https://github.com/koajs/koa). Maintained by [Forward Email][forward-email] and [Lad][].
4
4
 
5
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)
6
+
7
+ <!-- [![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) -->
8
+
7
9
  [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
8
10
  [![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)
9
11
  [![license](https://img.shields.io/github/license/koajs/router.svg)](LICENSE)
package/lib/layer.js CHANGED
@@ -13,6 +13,7 @@ module.exports = class Layer {
13
13
  * @param {String=} opts.name route name
14
14
  * @param {String=} opts.sensitive case sensitive (default: false)
15
15
  * @param {String=} opts.strict require the trailing slash (default: false)
16
+ * @param {Boolean=} opts.pathAsRegExp if true, treat `path` as a regular expression
16
17
  * @returns {Layer}
17
18
  * @private
18
19
  */
@@ -20,14 +21,12 @@ module.exports = class Layer {
20
21
  this.opts = opts;
21
22
  this.name = this.opts.name || null;
22
23
  this.methods = [];
23
- this.paramNames = [];
24
- this.stack = Array.isArray(middleware) ? middleware : [middleware];
25
-
26
24
  for (const method of methods) {
27
25
  const l = this.methods.push(method.toUpperCase());
28
26
  if (this.methods[l - 1] === 'GET') this.methods.unshift('HEAD');
29
27
  }
30
28
 
29
+ this.stack = Array.isArray(middleware) ? middleware : [middleware];
31
30
  // ensure middleware is a function
32
31
  for (let i = 0; i < this.stack.length; i++) {
33
32
  const fn = this.stack[i];
@@ -41,7 +40,20 @@ module.exports = class Layer {
41
40
  }
42
41
 
43
42
  this.path = path;
44
- this.regexp = pathToRegexp(path, this.paramNames, this.opts);
43
+ this.paramNames = [];
44
+
45
+ if (this.opts.pathAsRegExp === true) {
46
+ this.regexp = new RegExp(path);
47
+ } else if (this.path) {
48
+ if ('strict' in this.opts) {
49
+ // path-to-regexp renamed strict to trailing in v8.1.0
50
+ this.opts.trailing = this.opts.strict !== true;
51
+ }
52
+
53
+ const { regexp, keys } = pathToRegexp(this.path, this.opts);
54
+ this.regexp = regexp;
55
+ this.paramNames = keys;
56
+ }
45
57
  }
46
58
 
47
59
  /**
@@ -116,13 +128,14 @@ module.exports = class Layer {
116
128
 
117
129
  const toPath = compile(url, { encode: encodeURIComponent, ...options });
118
130
  let replaced;
119
-
120
- const tokens = parse(url);
131
+ const { tokens } = parse(url);
121
132
  let replace = {};
122
133
 
123
134
  if (Array.isArray(args)) {
124
135
  for (let len = tokens.length, i = 0, j = 0; i < len; i++) {
125
- if (tokens[i].name) replace[tokens[i].name] = args[j++];
136
+ if (tokens[i].name) {
137
+ replace[tokens[i].name] = args[j++];
138
+ }
126
139
  }
127
140
  } else if (tokens.some((token) => token.name)) {
128
141
  replace = params;
@@ -130,6 +143,10 @@ module.exports = class Layer {
130
143
  options = params;
131
144
  }
132
145
 
146
+ for (const [key, value] of Object.entries(replace)) {
147
+ replace[key] = String(value);
148
+ }
149
+
133
150
  replaced = toPath(replace);
134
151
 
135
152
  if (options && options.query) {
@@ -212,8 +229,13 @@ module.exports = class Layer {
212
229
  this.path !== '/' || this.opts.strict === true
213
230
  ? `${prefix}${this.path}`
214
231
  : prefix;
215
- this.paramNames = [];
216
- this.regexp = pathToRegexp(this.path, this.paramNames, this.opts);
232
+ if (this.opts.pathAsRegExp === true || prefix instanceof RegExp) {
233
+ this.regexp = new RegExp(this.path);
234
+ } else if (this.path) {
235
+ const { regexp, keys } = pathToRegexp(this.path, this.opts);
236
+ this.regexp = regexp;
237
+ this.paramNames = keys;
238
+ }
217
239
  }
218
240
 
219
241
  return this;
@@ -231,6 +253,7 @@ module.exports = class Layer {
231
253
 
232
254
  function safeDecodeURIComponent(text) {
233
255
  try {
256
+ // TODO: take a look on `safeDecodeURIComponent` if we use it only with route params let's remove the `replace` method otherwise make it flexible.
234
257
  // @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#decoding_query_parameters_from_a_url
235
258
  return decodeURIComponent(text.replace(/\+/g, ' '));
236
259
  } catch {
package/lib/router.js CHANGED
@@ -5,9 +5,8 @@
5
5
  * @link https://github.com/alexmingoia/koa-router
6
6
  */
7
7
  const http = require('node:http');
8
- const util = require('node:util');
9
8
 
10
- const debug = util.debuglog('koa-router');
9
+ const debug = require('debug')('koa-router');
11
10
 
12
11
  const compose = require('koa-compose');
13
12
  const HttpError = require('http-errors');
@@ -52,7 +51,7 @@ class Router {
52
51
  * @constructor
53
52
  */
54
53
  constructor(opts = {}) {
55
- if (!(this instanceof Router)) return new Router(opts); // eslint-disable-line no-constructor-return
54
+ if (!(this instanceof Router)) return new Router(opts);
56
55
 
57
56
  this.opts = opts;
58
57
  this.methods = this.opts.methods || [
@@ -165,14 +164,14 @@ class Router {
165
164
  }
166
165
  }
167
166
  } else {
168
- const keys = [];
169
- pathToRegexp(router.opts.prefix || '', keys);
167
+ const { keys } = pathToRegexp(router.opts.prefix || '', router.opts);
170
168
  const routerPrefixHasParam = Boolean(
171
169
  router.opts.prefix && keys.length > 0
172
170
  );
173
171
  router.register(path || '([^/]*)', [], m, {
174
172
  end: false,
175
- ignoreCaptures: !hasPath && !routerPrefixHasParam
173
+ ignoreCaptures: !hasPath && !routerPrefixHasParam,
174
+ pathAsRegExp: true
176
175
  });
177
176
  }
178
177
  }
@@ -380,7 +379,7 @@ class Router {
380
379
  * @returns {Router}
381
380
  */
382
381
  all(name, path, middleware) {
383
- if (typeof path === 'string') {
382
+ if (typeof path === 'string' || path instanceof RegExp) {
384
383
  middleware = Array.prototype.slice.call(arguments, 2);
385
384
  } else {
386
385
  middleware = Array.prototype.slice.call(arguments, 1);
@@ -396,7 +395,12 @@ class Router {
396
395
  )
397
396
  throw new Error('You have to provide a path when adding an all handler');
398
397
 
399
- this.register(path, methods, middleware, { name });
398
+ const opts = {
399
+ name,
400
+ pathAsRegExp: path instanceof RegExp
401
+ };
402
+
403
+ this.register(path, methods, middleware, { ...this.opts, ...opts });
400
404
 
401
405
  return this;
402
406
  }
@@ -455,10 +459,10 @@ class Router {
455
459
  * @returns {Layer}
456
460
  * @private
457
461
  */
458
- register(path, methods, middleware, opts = {}) {
462
+ register(path, methods, middleware, newOpts = {}) {
459
463
  const router = this;
460
464
  const { stack } = this;
461
-
465
+ const opts = { ...this.opts, ...newOpts };
462
466
  // support array of paths
463
467
  if (Array.isArray(path)) {
464
468
  for (const curPath of path) {
@@ -472,12 +476,14 @@ class Router {
472
476
  const route = new Layer(path, methods, middleware, {
473
477
  end: opts.end === false ? opts.end : true,
474
478
  name: opts.name,
475
- sensitive: opts.sensitive || this.opts.sensitive || false,
476
- strict: opts.strict || this.opts.strict || false,
477
- prefix: opts.prefix || this.opts.prefix || '',
478
- ignoreCaptures: opts.ignoreCaptures
479
+ sensitive: opts.sensitive || false,
480
+ strict: opts.strict || false,
481
+ prefix: opts.prefix || '',
482
+ ignoreCaptures: opts.ignoreCaptures,
483
+ pathAsRegExp: opts.pathAsRegExp
479
484
  });
480
485
 
486
+ // if parent prefix exists, add prefix to new route
481
487
  if (this.opts.prefix) {
482
488
  route.setPrefix(this.opts.prefix);
483
489
  }
@@ -575,7 +581,6 @@ class Router {
575
581
 
576
582
  debug('test %s %s', layer.path, layer.regexp);
577
583
 
578
- // eslint-disable-next-line unicorn/prefer-regexp-test
579
584
  if (layer.match(path)) {
580
585
  matched.path.push(layer);
581
586
 
@@ -812,14 +817,19 @@ for (const method of methods) {
812
817
  `You have to provide a path when adding a ${method} handler`
813
818
  );
814
819
 
815
- this.register(path, [method], middleware, { name });
820
+ const opts = {
821
+ name,
822
+ pathAsRegExp: path instanceof RegExp
823
+ };
816
824
 
825
+ // pass opts to register call on verb methods
826
+ this.register(path, [method], middleware, { ...this.opts, ...opts });
817
827
  return this;
818
828
  };
819
829
  }
820
830
 
821
831
  // Alias for `router.delete()` because delete is a reserved word
822
- // eslint-disable-next-line dot-notation
832
+
823
833
  Router.prototype.del = Router.prototype['delete'];
824
834
 
825
835
  module.exports = Router;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@koa/router",
3
3
  "description": "Router middleware for koa. Maintained by Forward Email and Lad.",
4
- "version": "13.1.0",
4
+ "version": "14.0.0",
5
5
  "author": "Alex Mingoia <talk@alexmingoia.com>",
6
6
  "bugs": {
7
7
  "url": "https://github.com/koajs/router/issues",
@@ -21,30 +21,31 @@
21
21
  }
22
22
  ],
23
23
  "dependencies": {
24
+ "debug": "^4.4.1",
24
25
  "http-errors": "^2.0.0",
25
26
  "koa-compose": "^4.1.0",
26
- "path-to-regexp": "^6.3.0"
27
+ "path-to-regexp": "^8.2.0"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@commitlint/cli": "^17.7.2",
30
31
  "@commitlint/config-conventional": "^17.7.0",
31
32
  "@ladjs/env": "^4.0.0",
32
- "eslint": "^8.39.0",
33
- "eslint-config-xo-lass": "^2.0.1",
33
+ "chalk": "^5.4.1",
34
+ "eslint": "^9.32.0",
35
+ "eslint-plugin-unicorn": "^60.0.0",
34
36
  "fixpack": "^4.0.0",
35
- "husky": "^8.0.3",
37
+ "husky": "^9.1.7",
36
38
  "jsdoc-to-markdown": "^8.0.0",
37
- "koa": "^2.15.3",
39
+ "koa": "^3.0.1",
38
40
  "lint-staged": "^14.0.1",
39
- "mocha": "^10.7.3",
41
+ "mocha": "^11.7.1",
40
42
  "nyc": "^17.0.0",
41
43
  "remark-cli": "11",
42
44
  "remark-preset-github": "^4.0.4",
43
- "supertest": "^7.0.0",
44
- "xo": "0.53.1"
45
+ "supertest": "^7.0.0"
45
46
  },
46
47
  "engines": {
47
- "node": ">= 18"
48
+ "node": ">= 20"
48
49
  },
49
50
  "files": [
50
51
  "lib"
@@ -64,11 +65,12 @@
64
65
  },
65
66
  "scripts": {
66
67
  "bench": "make -C bench",
68
+ "benchmark": "node bench/run.js",
67
69
  "coverage": "nyc npm run test",
68
70
  "docs": "NODE_ENV=test jsdoc2md -t ./lib/API_tpl.hbs --src ./lib/*.js >| API.md",
69
- "lint": "xo --fix && remark . -qfo && fixpack",
71
+ "lint": "eslint . --fix && remark . -qfo && fixpack",
70
72
  "prepare": "husky install",
71
- "prextest": "npm run lint",
73
+ "pretest": "npm run lint",
72
74
  "test": "mocha test/**/*.js",
73
75
  "test:watch": "mocha test/**/*.js --watch"
74
76
  }