@koa/router 14.0.0 → 15.1.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/lib/router.js DELETED
@@ -1,835 +0,0 @@
1
- /**
2
- * RESTful resource routing middleware for koa.
3
- *
4
- * @author Alex Mingoia <talk@alexmingoia.com>
5
- * @link https://github.com/alexmingoia/koa-router
6
- */
7
- const http = require('node:http');
8
-
9
- const debug = require('debug')('koa-router');
10
-
11
- const compose = require('koa-compose');
12
- const HttpError = require('http-errors');
13
- const { pathToRegexp } = require('path-to-regexp');
14
-
15
- const Layer = require('./layer');
16
-
17
- const methods = http.METHODS.map((method) => method.toLowerCase());
18
-
19
- /**
20
- * @module koa-router
21
- */
22
- class Router {
23
- /**
24
- * Create a new router.
25
- *
26
- * @example
27
- *
28
- * Basic usage:
29
- *
30
- * ```javascript
31
- * const Koa = require('koa');
32
- * const Router = require('@koa/router');
33
- *
34
- * const app = new Koa();
35
- * const router = new Router();
36
- *
37
- * router.get('/', (ctx, next) => {
38
- * // ctx.router available
39
- * });
40
- *
41
- * app
42
- * .use(router.routes())
43
- * .use(router.allowedMethods());
44
- * ```
45
- *
46
- * @alias module:koa-router
47
- * @param {Object=} opts
48
- * @param {Boolean=false} opts.exclusive only run last matched route's controller when there are multiple matches
49
- * @param {String=} opts.prefix prefix router paths
50
- * @param {String|RegExp=} opts.host host for router match
51
- * @constructor
52
- */
53
- constructor(opts = {}) {
54
- if (!(this instanceof Router)) return new Router(opts);
55
-
56
- this.opts = opts;
57
- this.methods = this.opts.methods || [
58
- 'HEAD',
59
- 'OPTIONS',
60
- 'GET',
61
- 'PUT',
62
- 'PATCH',
63
- 'POST',
64
- 'DELETE'
65
- ];
66
- this.exclusive = Boolean(this.opts.exclusive);
67
-
68
- this.params = {};
69
- this.stack = [];
70
- this.host = this.opts.host;
71
- }
72
-
73
- /**
74
- * Generate URL from url pattern and given `params`.
75
- *
76
- * @example
77
- *
78
- * ```javascript
79
- * const url = Router.url('/users/:id', {id: 1});
80
- * // => "/users/1"
81
- * ```
82
- *
83
- * @param {String} path url pattern
84
- * @param {Object} params url parameters
85
- * @returns {String}
86
- */
87
- static url(path, ...args) {
88
- return Layer.prototype.url.apply({ path }, args);
89
- }
90
-
91
- /**
92
- * Use given middleware.
93
- *
94
- * Middleware run in the order they are defined by `.use()`. They are invoked
95
- * sequentially, requests start at the first middleware and work their way
96
- * "down" the middleware stack.
97
- *
98
- * @example
99
- *
100
- * ```javascript
101
- * // session middleware will run before authorize
102
- * router
103
- * .use(session())
104
- * .use(authorize());
105
- *
106
- * // use middleware only with given path
107
- * router.use('/users', userAuth());
108
- *
109
- * // or with an array of paths
110
- * router.use(['/users', '/admin'], userAuth());
111
- *
112
- * app.use(router.routes());
113
- * ```
114
- *
115
- * @param {String=} path
116
- * @param {Function} middleware
117
- * @param {Function=} ...
118
- * @returns {Router}
119
- */
120
- use(...middleware) {
121
- const router = this;
122
- let path;
123
-
124
- // support array of paths
125
- if (Array.isArray(middleware[0]) && typeof middleware[0][0] === 'string') {
126
- const arrPaths = middleware[0];
127
- for (const p of arrPaths) {
128
- router.use.apply(router, [p, ...middleware.slice(1)]);
129
- }
130
-
131
- return this;
132
- }
133
-
134
- const hasPath = typeof middleware[0] === 'string';
135
- if (hasPath) path = middleware.shift();
136
-
137
- for (const m of middleware) {
138
- if (m.router) {
139
- const cloneRouter = Object.assign(
140
- Object.create(Router.prototype),
141
- m.router,
142
- {
143
- stack: [...m.router.stack]
144
- }
145
- );
146
-
147
- for (let j = 0; j < cloneRouter.stack.length; j++) {
148
- const nestedLayer = cloneRouter.stack[j];
149
- const cloneLayer = Object.assign(
150
- Object.create(Layer.prototype),
151
- nestedLayer
152
- );
153
-
154
- if (path) cloneLayer.setPrefix(path);
155
- if (router.opts.prefix) cloneLayer.setPrefix(router.opts.prefix);
156
- router.stack.push(cloneLayer);
157
- cloneRouter.stack[j] = cloneLayer;
158
- }
159
-
160
- if (router.params) {
161
- const routerParams = Object.keys(router.params);
162
- for (const key of routerParams) {
163
- cloneRouter.param(key, router.params[key]);
164
- }
165
- }
166
- } else {
167
- const { keys } = pathToRegexp(router.opts.prefix || '', router.opts);
168
- const routerPrefixHasParam = Boolean(
169
- router.opts.prefix && keys.length > 0
170
- );
171
- router.register(path || '([^/]*)', [], m, {
172
- end: false,
173
- ignoreCaptures: !hasPath && !routerPrefixHasParam,
174
- pathAsRegExp: true
175
- });
176
- }
177
- }
178
-
179
- return this;
180
- }
181
-
182
- /**
183
- * Set the path prefix for a Router instance that was already initialized.
184
- *
185
- * @example
186
- *
187
- * ```javascript
188
- * router.prefix('/things/:thing_id')
189
- * ```
190
- *
191
- * @param {String} prefix
192
- * @returns {Router}
193
- */
194
- prefix(prefix) {
195
- prefix = prefix.replace(/\/$/, '');
196
-
197
- this.opts.prefix = prefix;
198
-
199
- for (let i = 0; i < this.stack.length; i++) {
200
- const route = this.stack[i];
201
- route.setPrefix(prefix);
202
- }
203
-
204
- return this;
205
- }
206
-
207
- /**
208
- * Returns router middleware which dispatches a route matching the request.
209
- *
210
- * @returns {Function}
211
- */
212
- middleware() {
213
- const router = this;
214
- const dispatch = (ctx, next) => {
215
- debug('%s %s', ctx.method, ctx.path);
216
-
217
- const hostMatched = router.matchHost(ctx.host);
218
-
219
- if (!hostMatched) {
220
- return next();
221
- }
222
-
223
- const path =
224
- router.opts.routerPath ||
225
- ctx.newRouterPath ||
226
- ctx.path ||
227
- ctx.routerPath;
228
- const matched = router.match(path, ctx.method);
229
- if (ctx.matched) {
230
- ctx.matched.push.apply(ctx.matched, matched.path);
231
- } else {
232
- ctx.matched = matched.path;
233
- }
234
-
235
- ctx.router = router;
236
-
237
- if (!matched.route) return next();
238
-
239
- const matchedLayers = matched.pathAndMethod;
240
- const mostSpecificLayer = matchedLayers[matchedLayers.length - 1];
241
- ctx._matchedRoute = mostSpecificLayer.path;
242
- if (mostSpecificLayer.name) {
243
- ctx._matchedRouteName = mostSpecificLayer.name;
244
- }
245
-
246
- const layerChain = (
247
- router.exclusive ? [mostSpecificLayer] : matchedLayers
248
- ).reduce((memo, layer) => {
249
- memo.push((ctx, next) => {
250
- ctx.captures = layer.captures(path, ctx.captures);
251
- ctx.request.params = layer.params(path, ctx.captures, ctx.params);
252
- ctx.params = ctx.request.params;
253
- ctx.routerPath = layer.path;
254
- ctx.routerName = layer.name;
255
- ctx._matchedRoute = layer.path;
256
- if (layer.name) {
257
- ctx._matchedRouteName = layer.name;
258
- }
259
-
260
- return next();
261
- });
262
- return [...memo, ...layer.stack];
263
- }, []);
264
-
265
- return compose(layerChain)(ctx, next);
266
- };
267
-
268
- dispatch.router = this;
269
-
270
- return dispatch;
271
- }
272
-
273
- routes() {
274
- return this.middleware();
275
- }
276
-
277
- /**
278
- * Returns separate middleware for responding to `OPTIONS` requests with
279
- * an `Allow` header containing the allowed methods, as well as responding
280
- * with `405 Method Not Allowed` and `501 Not Implemented` as appropriate.
281
- *
282
- * @example
283
- *
284
- * ```javascript
285
- * const Koa = require('koa');
286
- * const Router = require('@koa/router');
287
- *
288
- * const app = new Koa();
289
- * const router = new Router();
290
- *
291
- * app.use(router.routes());
292
- * app.use(router.allowedMethods());
293
- * ```
294
- *
295
- * **Example with [Boom](https://github.com/hapijs/boom)**
296
- *
297
- * ```javascript
298
- * const Koa = require('koa');
299
- * const Router = require('@koa/router');
300
- * const Boom = require('boom');
301
- *
302
- * const app = new Koa();
303
- * const router = new Router();
304
- *
305
- * app.use(router.routes());
306
- * app.use(router.allowedMethods({
307
- * throw: true,
308
- * notImplemented: () => new Boom.notImplemented(),
309
- * methodNotAllowed: () => new Boom.methodNotAllowed()
310
- * }));
311
- * ```
312
- *
313
- * @param {Object=} options
314
- * @param {Boolean=} options.throw throw error instead of setting status and header
315
- * @param {Function=} options.notImplemented throw the returned value in place of the default NotImplemented error
316
- * @param {Function=} options.methodNotAllowed throw the returned value in place of the default MethodNotAllowed error
317
- * @returns {Function}
318
- */
319
- allowedMethods(options = {}) {
320
- const implemented = this.methods;
321
-
322
- return (ctx, next) => {
323
- return next().then(() => {
324
- const allowed = {};
325
-
326
- if (ctx.matched && (!ctx.status || ctx.status === 404)) {
327
- for (let i = 0; i < ctx.matched.length; i++) {
328
- const route = ctx.matched[i];
329
- for (let j = 0; j < route.methods.length; j++) {
330
- const method = route.methods[j];
331
- allowed[method] = method;
332
- }
333
- }
334
-
335
- const allowedArr = Object.keys(allowed);
336
- if (!implemented.includes(ctx.method)) {
337
- if (options.throw) {
338
- const notImplementedThrowable =
339
- typeof options.notImplemented === 'function'
340
- ? options.notImplemented() // set whatever the user returns from their function
341
- : new HttpError.NotImplemented();
342
-
343
- throw notImplementedThrowable;
344
- } else {
345
- ctx.status = 501;
346
- ctx.set('Allow', allowedArr.join(', '));
347
- }
348
- } else if (allowedArr.length > 0) {
349
- if (ctx.method === 'OPTIONS') {
350
- ctx.status = 200;
351
- ctx.body = '';
352
- ctx.set('Allow', allowedArr.join(', '));
353
- } else if (!allowed[ctx.method]) {
354
- if (options.throw) {
355
- const notAllowedThrowable =
356
- typeof options.methodNotAllowed === 'function'
357
- ? options.methodNotAllowed() // set whatever the user returns from their function
358
- : new HttpError.MethodNotAllowed();
359
-
360
- throw notAllowedThrowable;
361
- } else {
362
- ctx.status = 405;
363
- ctx.set('Allow', allowedArr.join(', '));
364
- }
365
- }
366
- }
367
- }
368
- });
369
- };
370
- }
371
-
372
- /**
373
- * Register route with all methods.
374
- *
375
- * @param {String} name Optional.
376
- * @param {String} path
377
- * @param {Function=} middleware You may also pass multiple middleware.
378
- * @param {Function} callback
379
- * @returns {Router}
380
- */
381
- all(name, path, middleware) {
382
- if (typeof path === 'string' || path instanceof RegExp) {
383
- middleware = Array.prototype.slice.call(arguments, 2);
384
- } else {
385
- middleware = Array.prototype.slice.call(arguments, 1);
386
- path = name;
387
- name = null;
388
- }
389
-
390
- // Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
391
- if (
392
- typeof path !== 'string' &&
393
- !(path instanceof RegExp) &&
394
- (!Array.isArray(path) || path.length === 0)
395
- )
396
- throw new Error('You have to provide a path when adding an all handler');
397
-
398
- const opts = {
399
- name,
400
- pathAsRegExp: path instanceof RegExp
401
- };
402
-
403
- this.register(path, methods, middleware, { ...this.opts, ...opts });
404
-
405
- return this;
406
- }
407
-
408
- /**
409
- * Redirect `source` to `destination` URL with optional 30x status `code`.
410
- *
411
- * Both `source` and `destination` can be route names.
412
- *
413
- * ```javascript
414
- * router.redirect('/login', 'sign-in');
415
- * ```
416
- *
417
- * This is equivalent to:
418
- *
419
- * ```javascript
420
- * router.all('/login', ctx => {
421
- * ctx.redirect('/sign-in');
422
- * ctx.status = 301;
423
- * });
424
- * ```
425
- *
426
- * @param {String} source URL or route name.
427
- * @param {String} destination URL or route name.
428
- * @param {Number=} code HTTP status code (default: 301).
429
- * @returns {Router}
430
- */
431
- redirect(source, destination, code) {
432
- // lookup source route by name
433
- if (typeof source === 'symbol' || source[0] !== '/') {
434
- source = this.url(source);
435
- if (source instanceof Error) throw source;
436
- }
437
-
438
- // lookup destination route by name
439
- if (
440
- typeof destination === 'symbol' ||
441
- (destination[0] !== '/' && !destination.includes('://'))
442
- ) {
443
- destination = this.url(destination);
444
- if (destination instanceof Error) throw destination;
445
- }
446
-
447
- return this.all(source, (ctx) => {
448
- ctx.redirect(destination);
449
- ctx.status = code || 301;
450
- });
451
- }
452
-
453
- /**
454
- * Create and register a route.
455
- *
456
- * @param {String} path Path string.
457
- * @param {Array.<String>} methods Array of HTTP verbs.
458
- * @param {Function} middleware Multiple middleware also accepted.
459
- * @returns {Layer}
460
- * @private
461
- */
462
- register(path, methods, middleware, newOpts = {}) {
463
- const router = this;
464
- const { stack } = this;
465
- const opts = { ...this.opts, ...newOpts };
466
- // support array of paths
467
- if (Array.isArray(path)) {
468
- for (const curPath of path) {
469
- router.register.call(router, curPath, methods, middleware, opts);
470
- }
471
-
472
- return this;
473
- }
474
-
475
- // create route
476
- const route = new Layer(path, methods, middleware, {
477
- end: opts.end === false ? opts.end : true,
478
- name: opts.name,
479
- sensitive: opts.sensitive || false,
480
- strict: opts.strict || false,
481
- prefix: opts.prefix || '',
482
- ignoreCaptures: opts.ignoreCaptures,
483
- pathAsRegExp: opts.pathAsRegExp
484
- });
485
-
486
- // if parent prefix exists, add prefix to new route
487
- if (this.opts.prefix) {
488
- route.setPrefix(this.opts.prefix);
489
- }
490
-
491
- // add parameter middleware
492
- for (let i = 0; i < Object.keys(this.params).length; i++) {
493
- const param = Object.keys(this.params)[i];
494
- route.param(param, this.params[param]);
495
- }
496
-
497
- stack.push(route);
498
-
499
- debug('defined route %s %s', route.methods, route.path);
500
-
501
- return route;
502
- }
503
-
504
- /**
505
- * Lookup route with given `name`.
506
- *
507
- * @param {String} name
508
- * @returns {Layer|false}
509
- */
510
- route(name) {
511
- const routes = this.stack;
512
-
513
- for (let len = routes.length, i = 0; i < len; i++) {
514
- if (routes[i].name && routes[i].name === name) return routes[i];
515
- }
516
-
517
- return false;
518
- }
519
-
520
- /**
521
- * Generate URL for route. Takes a route name and map of named `params`.
522
- *
523
- * @example
524
- *
525
- * ```javascript
526
- * router.get('user', '/users/:id', (ctx, next) => {
527
- * // ...
528
- * });
529
- *
530
- * router.url('user', 3);
531
- * // => "/users/3"
532
- *
533
- * router.url('user', { id: 3 });
534
- * // => "/users/3"
535
- *
536
- * router.use((ctx, next) => {
537
- * // redirect to named route
538
- * ctx.redirect(ctx.router.url('sign-in'));
539
- * })
540
- *
541
- * router.url('user', { id: 3 }, { query: { limit: 1 } });
542
- * // => "/users/3?limit=1"
543
- *
544
- * router.url('user', { id: 3 }, { query: "limit=1" });
545
- * // => "/users/3?limit=1"
546
- * ```
547
- *
548
- * @param {String} name route name
549
- * @param {Object} params url parameters
550
- * @param {Object} [options] options parameter
551
- * @param {Object|String} [options.query] query options
552
- * @returns {String|Error}
553
- */
554
- url(name, ...args) {
555
- const route = this.route(name);
556
- if (route) return route.url.apply(route, args);
557
-
558
- return new Error(`No route found for name: ${String(name)}`);
559
- }
560
-
561
- /**
562
- * Match given `path` and return corresponding routes.
563
- *
564
- * @param {String} path
565
- * @param {String} method
566
- * @returns {Object.<path, pathAndMethod>} returns layers that matched path and
567
- * path and method.
568
- * @private
569
- */
570
- match(path, method) {
571
- const layers = this.stack;
572
- let layer;
573
- const matched = {
574
- path: [],
575
- pathAndMethod: [],
576
- route: false
577
- };
578
-
579
- for (let len = layers.length, i = 0; i < len; i++) {
580
- layer = layers[i];
581
-
582
- debug('test %s %s', layer.path, layer.regexp);
583
-
584
- if (layer.match(path)) {
585
- matched.path.push(layer);
586
-
587
- if (layer.methods.length === 0 || layer.methods.includes(method)) {
588
- matched.pathAndMethod.push(layer);
589
- if (layer.methods.length > 0) matched.route = true;
590
- }
591
- }
592
- }
593
-
594
- return matched;
595
- }
596
-
597
- /**
598
- * Match given `input` to allowed host
599
- * @param {String} input
600
- * @returns {boolean}
601
- */
602
- matchHost(input) {
603
- const { host } = this;
604
-
605
- if (!host) {
606
- return true;
607
- }
608
-
609
- if (!input) {
610
- return false;
611
- }
612
-
613
- if (typeof host === 'string') {
614
- return input === host;
615
- }
616
-
617
- if (typeof host === 'object' && host instanceof RegExp) {
618
- return host.test(input);
619
- }
620
- }
621
-
622
- /**
623
- * Run middleware for named route parameters. Useful for auto-loading or
624
- * validation.
625
- *
626
- * @example
627
- *
628
- * ```javascript
629
- * router
630
- * .param('user', (id, ctx, next) => {
631
- * ctx.user = users[id];
632
- * if (!ctx.user) return ctx.status = 404;
633
- * return next();
634
- * })
635
- * .get('/users/:user', ctx => {
636
- * ctx.body = ctx.user;
637
- * })
638
- * .get('/users/:user/friends', ctx => {
639
- * return ctx.user.getFriends().then(function(friends) {
640
- * ctx.body = friends;
641
- * });
642
- * })
643
- * // /users/3 => {"id": 3, "name": "Alex"}
644
- * // /users/3/friends => [{"id": 4, "name": "TJ"}]
645
- * ```
646
- *
647
- * @param {String} param
648
- * @param {Function} middleware
649
- * @returns {Router}
650
- */
651
- param(param, middleware) {
652
- this.params[param] = middleware;
653
- for (let i = 0; i < this.stack.length; i++) {
654
- const route = this.stack[i];
655
- route.param(param, middleware);
656
- }
657
-
658
- return this;
659
- }
660
- }
661
-
662
- /**
663
- * Create `router.verb()` methods, where *verb* is one of the HTTP verbs such
664
- * as `router.get()` or `router.post()`.
665
- *
666
- * Match URL patterns to callback functions or controller actions using `router.verb()`,
667
- * where **verb** is one of the HTTP verbs such as `router.get()` or `router.post()`.
668
- *
669
- * Additionally, `router.all()` can be used to match against all methods.
670
- *
671
- * ```javascript
672
- * router
673
- * .get('/', (ctx, next) => {
674
- * ctx.body = 'Hello World!';
675
- * })
676
- * .post('/users', (ctx, next) => {
677
- * // ...
678
- * })
679
- * .put('/users/:id', (ctx, next) => {
680
- * // ...
681
- * })
682
- * .del('/users/:id', (ctx, next) => {
683
- * // ...
684
- * })
685
- * .all('/users/:id', (ctx, next) => {
686
- * // ...
687
- * });
688
- * ```
689
- *
690
- * When a route is matched, its path is available at `ctx._matchedRoute` and if named,
691
- * the name is available at `ctx._matchedRouteName`
692
- *
693
- * Route paths will be translated to regular expressions using
694
- * [path-to-regexp](https://github.com/pillarjs/path-to-regexp).
695
- *
696
- * Query strings will not be considered when matching requests.
697
- *
698
- * #### Named routes
699
- *
700
- * Routes can optionally have names. This allows generation of URLs and easy
701
- * renaming of URLs during development.
702
- *
703
- * ```javascript
704
- * router.get('user', '/users/:id', (ctx, next) => {
705
- * // ...
706
- * });
707
- *
708
- * router.url('user', 3);
709
- * // => "/users/3"
710
- * ```
711
- *
712
- * #### Multiple middleware
713
- *
714
- * Multiple middleware may be given:
715
- *
716
- * ```javascript
717
- * router.get(
718
- * '/users/:id',
719
- * (ctx, next) => {
720
- * return User.findOne(ctx.params.id).then(function(user) {
721
- * ctx.user = user;
722
- * next();
723
- * });
724
- * },
725
- * ctx => {
726
- * console.log(ctx.user);
727
- * // => { id: 17, name: "Alex" }
728
- * }
729
- * );
730
- * ```
731
- *
732
- * ### Nested routers
733
- *
734
- * Nesting routers is supported:
735
- *
736
- * ```javascript
737
- * const forums = new Router();
738
- * const posts = new Router();
739
- *
740
- * posts.get('/', (ctx, next) => {...});
741
- * posts.get('/:pid', (ctx, next) => {...});
742
- * forums.use('/forums/:fid/posts', posts.routes(), posts.allowedMethods());
743
- *
744
- * // responds to "/forums/123/posts" and "/forums/123/posts/123"
745
- * app.use(forums.routes());
746
- * ```
747
- *
748
- * #### Router prefixes
749
- *
750
- * Route paths can be prefixed at the router level:
751
- *
752
- * ```javascript
753
- * const router = new Router({
754
- * prefix: '/users'
755
- * });
756
- *
757
- * router.get('/', ...); // responds to "/users"
758
- * router.get('/:id', ...); // responds to "/users/:id"
759
- * ```
760
- *
761
- * #### URL parameters
762
- *
763
- * Named route parameters are captured and added to `ctx.params`.
764
- *
765
- * ```javascript
766
- * router.get('/:category/:title', (ctx, next) => {
767
- * console.log(ctx.params);
768
- * // => { category: 'programming', title: 'how-to-node' }
769
- * });
770
- * ```
771
- *
772
- * The [path-to-regexp](https://github.com/pillarjs/path-to-regexp) module is
773
- * used to convert paths to regular expressions.
774
- *
775
- *
776
- * ### Match host for each router instance
777
- *
778
- * ```javascript
779
- * const router = new Router({
780
- * host: 'example.domain' // only match if request host exactly equal `example.domain`
781
- * });
782
- *
783
- * ```
784
- *
785
- * OR host cloud be a regexp
786
- *
787
- * ```javascript
788
- * const router = new Router({
789
- * host: /.*\.?example\.domain$/ // all host end with .example.domain would be matched
790
- * });
791
- * ```
792
- *
793
- * @name get|put|post|patch|delete|del
794
- * @memberof module:koa-router.prototype
795
- * @param {String} path
796
- * @param {Function=} middleware route middleware(s)
797
- * @param {Function} callback route callback
798
- * @returns {Router}
799
- */
800
- for (const method of methods) {
801
- Router.prototype[method] = function (name, path, middleware) {
802
- if (typeof path === 'string' || path instanceof RegExp) {
803
- middleware = Array.prototype.slice.call(arguments, 2);
804
- } else {
805
- middleware = Array.prototype.slice.call(arguments, 1);
806
- path = name;
807
- name = null;
808
- }
809
-
810
- // Sanity check to ensure we have a viable path candidate (eg: string|regex|non-empty array)
811
- if (
812
- typeof path !== 'string' &&
813
- !(path instanceof RegExp) &&
814
- (!Array.isArray(path) || path.length === 0)
815
- )
816
- throw new Error(
817
- `You have to provide a path when adding a ${method} handler`
818
- );
819
-
820
- const opts = {
821
- name,
822
- pathAsRegExp: path instanceof RegExp
823
- };
824
-
825
- // pass opts to register call on verb methods
826
- this.register(path, [method], middleware, { ...this.opts, ...opts });
827
- return this;
828
- };
829
- }
830
-
831
- // Alias for `router.delete()` because delete is a reserved word
832
-
833
- Router.prototype.del = Router.prototype['delete'];
834
-
835
- module.exports = Router;