@eggjs/router 3.0.5 → 4.0.0-beta.16

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.
@@ -1,7 +0,0 @@
1
- import { Router } from './Router.js';
2
- export type * from './types.js';
3
- export * from './Layer.js';
4
- export * from './Router.js';
5
- export * from './EggRouter.js';
6
- export declare const KoaRouter: typeof Router;
7
- export default Router;
package/dist/esm/index.js DELETED
@@ -1,7 +0,0 @@
1
- import { Router } from './Router.js';
2
- export * from './Layer.js';
3
- export * from './Router.js';
4
- export * from './EggRouter.js';
5
- export const KoaRouter = Router;
6
- export default Router;
7
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUdyQyxjQUFjLFlBQVksQ0FBQztBQUMzQixjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLGdCQUFnQixDQUFDO0FBRS9CLE1BQU0sQ0FBQyxNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUM7QUFDaEMsZUFBZSxNQUFNLENBQUMifQ==
@@ -1,3 +0,0 @@
1
- {
2
- "type": "module"
3
- }
@@ -1,18 +0,0 @@
1
- export type Next = () => Promise<void>;
2
- export type MiddlewareFunc = (ctx: any, next: Next) => Promise<void> | void;
3
- export type MiddlewareFuncWithParamProperty = MiddlewareFunc & {
4
- param?: string;
5
- };
6
- export type ParamMiddlewareFunc = (param: string, ctx: any, next: Next) => Promise<void> | void;
7
- export type MiddlewareFuncWithRouter<T> = MiddlewareFunc & {
8
- router: T;
9
- };
10
- export interface ResourcesController {
11
- index?: MiddlewareFunc;
12
- new?: MiddlewareFunc;
13
- create?: MiddlewareFunc;
14
- show?: MiddlewareFunc;
15
- edit?: MiddlewareFunc;
16
- update?: MiddlewareFunc;
17
- destroy?: MiddlewareFunc;
18
- }
package/dist/esm/types.js DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
package/src/EggRouter.ts DELETED
@@ -1,349 +0,0 @@
1
- import assert from 'node:assert';
2
- import { encodeURIComponent as safeEncodeURIComponent } from 'utility';
3
- import inflection from 'inflection';
4
- import methods from 'methods';
5
- import { isGeneratorFunction } from 'is-type-of';
6
- import { RegisterOptions, Router, RouterMethod, RouterOptions } from './Router.js';
7
- import { MiddlewareFunc, Next, ResourcesController } from './types.js';
8
-
9
- interface RestfulOptions {
10
- suffix?: string;
11
- namePrefix?: string;
12
- method: string | string[];
13
- member?: true;
14
- }
15
-
16
- const REST_MAP: Record<string, RestfulOptions> = {
17
- index: {
18
- suffix: '',
19
- method: 'GET',
20
- },
21
- new: {
22
- namePrefix: 'new_',
23
- member: true,
24
- suffix: 'new',
25
- method: 'GET',
26
- },
27
- create: {
28
- suffix: '',
29
- method: 'POST',
30
- },
31
- show: {
32
- member: true,
33
- suffix: ':id',
34
- method: 'GET',
35
- },
36
- edit: {
37
- member: true,
38
- namePrefix: 'edit_',
39
- suffix: ':id/edit',
40
- method: 'GET',
41
- },
42
- update: {
43
- member: true,
44
- namePrefix: '',
45
- suffix: ':id',
46
- method: [ 'PATCH', 'PUT' ],
47
- },
48
- destroy: {
49
- member: true,
50
- namePrefix: 'destroy_',
51
- suffix: ':id',
52
- method: 'DELETE',
53
- },
54
- };
55
-
56
- interface Application {
57
- controller: Record<string, any>;
58
- }
59
-
60
- /**
61
- * FIXME: move these patch into @eggjs/router
62
- */
63
- export class EggRouter extends Router {
64
- readonly app: Application;
65
-
66
- /**
67
- * @class
68
- * @param {Object} opts - Router options.
69
- * @param {Application} app - Application object.
70
- */
71
- constructor(opts: RouterOptions, app: Application) {
72
- super(opts);
73
- this.app = app;
74
- }
75
-
76
- verb(method: RouterMethod | RouterMethod[],
77
- nameOrPath: string | RegExp | (string | RegExp)[],
78
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
79
- ...middleware: (MiddlewareFunc | string)[]) {
80
- const { path, middlewares, options } = this._formatRouteParams(nameOrPath, pathOrMiddleware, middleware);
81
- if (typeof method === 'string') {
82
- method = [ method ];
83
- }
84
- this.register(path, method, middlewares, options);
85
- return this;
86
- }
87
-
88
- // const METHODS = [ 'head', 'options', 'get', 'put', 'patch', 'post', 'delete', 'all' ];
89
- head(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
90
- head(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
91
- head(nameOrPath: string | RegExp | (string | RegExp)[],
92
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
93
- ...middlewares: (MiddlewareFunc | string)[]): Router {
94
- return this.verb('head', nameOrPath, pathOrMiddleware, ...middlewares);
95
- }
96
- options(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
97
- options(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
98
- options(nameOrPath: string | RegExp | (string | RegExp)[],
99
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
100
- ...middlewares: (MiddlewareFunc | string)[]): Router {
101
- return this.verb('options', nameOrPath, pathOrMiddleware, ...middlewares);
102
- }
103
- get(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
104
- get(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
105
- get(nameOrPath: string | RegExp | (string | RegExp)[],
106
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
107
- ...middlewares: (MiddlewareFunc | string)[]): Router {
108
- return this.verb('get', nameOrPath, pathOrMiddleware, ...middlewares);
109
- }
110
- put(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
111
- put(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
112
- put(nameOrPath: string | RegExp | (string | RegExp)[],
113
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
114
- ...middlewares: (MiddlewareFunc | string)[]): Router {
115
- return this.verb('put', nameOrPath, pathOrMiddleware, ...middlewares);
116
- }
117
- patch(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
118
- patch(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
119
- patch(nameOrPath: string | RegExp | (string | RegExp)[],
120
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
121
- ...middlewares: (MiddlewareFunc | string)[]): Router {
122
- return this.verb('patch', nameOrPath, pathOrMiddleware, ...middlewares);
123
- }
124
- post(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
125
- post(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
126
- post(nameOrPath: string | RegExp | (string | RegExp)[],
127
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
128
- ...middlewares: (MiddlewareFunc | string)[]): Router {
129
- return this.verb('post', nameOrPath, pathOrMiddleware, ...middlewares);
130
- }
131
- delete(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
132
- delete(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
133
- delete(nameOrPath: string | RegExp | (string | RegExp)[],
134
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
135
- ...middlewares: (MiddlewareFunc | string)[]): Router {
136
- return this.verb('delete', nameOrPath, pathOrMiddleware, ...middlewares);
137
- }
138
- all(path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
139
- all(name: string, path: string | RegExp | (string | RegExp)[], ...middlewares: (MiddlewareFunc | string)[]): Router;
140
- all(nameOrPath: string | RegExp | (string | RegExp)[],
141
- pathOrMiddleware: string | RegExp | (string | RegExp)[] | MiddlewareFunc,
142
- ...middlewares: (MiddlewareFunc | string)[]): Router {
143
- return this.verb(methods, nameOrPath, pathOrMiddleware, ...middlewares);
144
- }
145
-
146
- register(path: string | RegExp | (string | RegExp)[],
147
- methods: string[],
148
- middleware: MiddlewareFunc | string | (MiddlewareFunc | string | ResourcesController)[],
149
- opts?: RegisterOptions) {
150
- // patch register to support bind ctx function middleware and string controller
151
- middleware = Array.isArray(middleware) ? middleware : [ middleware ];
152
- for (const mw of middleware) {
153
- if (isGeneratorFunction(mw)) {
154
- throw new TypeError(
155
- methods.toString() + ' `' + path + '`: Please use async function instead of generator function',
156
- );
157
- }
158
- }
159
- const middlewares = convertMiddlewares(middleware, this.app);
160
- return super.register(path, methods, middlewares, opts);
161
- }
162
-
163
- /**
164
- * restful router api
165
- * @param {String} name - Router name
166
- * @param {String} prefix - url prefix
167
- * @param {Function} middleware - middleware or controller
168
- * @example
169
- * ```js
170
- * app.resources('/posts', 'posts')
171
- * app.resources('posts', '/posts', 'posts')
172
- * app.resources('posts', '/posts', app.role.can('user'), app.controller.posts)
173
- * ```
174
- *
175
- * Examples:
176
- *
177
- * ```js
178
- * app.resources('/posts', 'posts')
179
- * ```
180
- *
181
- * yield router mapping
182
- *
183
- * Method | Path | Route Name | Controller.Action
184
- * -------|-----------------|----------------|-----------------------------
185
- * GET | /posts | posts | app.controller.posts.index
186
- * GET | /posts/new | new_post | app.controller.posts.new
187
- * GET | /posts/:id | post | app.controller.posts.show
188
- * GET | /posts/:id/edit | edit_post | app.controller.posts.edit
189
- * POST | /posts | posts | app.controller.posts.create
190
- * PATCH | /posts/:id | post | app.controller.posts.update
191
- * DELETE | /posts/:id | post | app.controller.posts.destroy
192
- *
193
- * app.router.url can generate url based on arguments
194
- * ```js
195
- * app.router.url('posts')
196
- * => /posts
197
- * app.router.url('post', { id: 1 })
198
- * => /posts/1
199
- * app.router.url('new_post')
200
- * => /posts/new
201
- * app.router.url('edit_post', { id: 1 })
202
- * => /posts/1/edit
203
- * ```
204
- * @return {Router} return route object.
205
- * @since 1.0.0
206
- */
207
- resources(prefix: string, controller: string | ResourcesController): Router;
208
- resources(prefix: string, middleware: MiddlewareFunc, controller: string | ResourcesController): Router;
209
- resources(name: string, prefix: string, controller: string | ResourcesController): Router;
210
- resources(name: string, prefix: string, middleware: MiddlewareFunc, controller: string | ResourcesController): Router;
211
- resources(nameOrPath: string | RegExp, pathOrMiddleware: string | RegExp | MiddlewareFunc | ResourcesController,
212
- ...middleware: (MiddlewareFunc | string | ResourcesController)[]): Router {
213
- const { path, middlewares, options } = this._formatRouteParams(nameOrPath, pathOrMiddleware, middleware);
214
- // last argument is Controller object
215
- const controller = resolveController(middlewares.pop()!, this.app);
216
- for (const key in REST_MAP) {
217
- const action = controller[key] as MiddlewareFunc;
218
- if (!action) continue;
219
-
220
- const opts = REST_MAP[key];
221
- let routeName;
222
- if (opts.member) {
223
- routeName = inflection.singularize(options.name ?? '');
224
- } else {
225
- routeName = inflection.pluralize(options.name ?? '');
226
- }
227
- if (opts.namePrefix) {
228
- routeName = opts.namePrefix + routeName;
229
- }
230
- const prefix = (path as string).replace(/\/$/, '');
231
- const urlPath = opts.suffix ? `${prefix}/${opts.suffix}` : prefix;
232
- const method = Array.isArray(opts.method) ? opts.method : [ opts.method ];
233
- this.register(urlPath, method, middlewares.concat(action), { name: routeName });
234
- }
235
- return this;
236
- }
237
-
238
- /**
239
- * @param {String} name - Router name
240
- * @param {Object} params - more parameters
241
- * @example
242
- * ```js
243
- * router.url('edit_post', { id: 1, name: 'foo', page: 2 })
244
- * => /posts/1/edit?name=foo&page=2
245
- * router.url('posts', { name: 'foo&1', page: 2 })
246
- * => /posts?name=foo%261&page=2
247
- * ```
248
- * @return {String} url by path name and query params.
249
- * @since 1.0.0
250
- */
251
- url(name: string, params?: Record<string, string | number | (string | number)[]>): string {
252
- const route = this.route(name);
253
- if (!route) return '';
254
-
255
- const args = params;
256
- let url = route.path;
257
-
258
- assert(!(url instanceof RegExp), `Can't get the url for regExp ${url} for by name '${name}'`);
259
-
260
- const queries = [];
261
- if (typeof args === 'object' && args !== null) {
262
- const replacedParams: string[] = [];
263
- url = url.replace(/:([a-zA-Z_]\w*)/g, ($0, key) => {
264
- if (key in args) {
265
- const values = args[key];
266
- replacedParams.push(key);
267
- return safeEncodeURIComponent(Array.isArray(values) ? String(values[0]) : String(values));
268
- }
269
- return $0;
270
- });
271
-
272
- for (const key in args) {
273
- if (replacedParams.includes(key)) {
274
- continue;
275
- }
276
- const values = args[key];
277
- const encodedKey = safeEncodeURIComponent(key);
278
- if (Array.isArray(values)) {
279
- for (const val of values) {
280
- queries.push(`${encodedKey}=${safeEncodeURIComponent(String(val))}`);
281
- }
282
- } else {
283
- queries.push(`${encodedKey}=${safeEncodeURIComponent(String(values))}`);
284
- }
285
- }
286
- }
287
-
288
- if (queries.length > 0) {
289
- const queryStr = queries.join('&');
290
- if (!url.includes('?')) {
291
- url = `${url}?${queryStr}`;
292
- } else {
293
- url = `${url}&${queryStr}`;
294
- }
295
- }
296
-
297
- return url;
298
- }
299
-
300
- /**
301
- * @alias to url()
302
- */
303
- pathFor(name: string, params?: Record<string, string | number | (string | number)[]>) {
304
- return this.url(name, params);
305
- }
306
- }
307
-
308
- /**
309
- * resolve controller from string to function
310
- * @param {String|Function} controller input controller
311
- * @param {Application} app egg application instance
312
- */
313
- function resolveController(controller: string | MiddlewareFunc | ResourcesController, app: Application) {
314
- if (typeof controller === 'string') {
315
- // resolveController('foo.bar.Home', app)
316
- const actions = controller.split('.');
317
- let obj = app.controller;
318
- actions.forEach(key => {
319
- obj = obj[key];
320
- if (!obj) throw new Error(`app.controller.${controller} not exists`);
321
- });
322
- controller = obj as any;
323
- }
324
- // ensure controller is exists
325
- if (!controller) throw new Error('controller not exists');
326
- return controller as any;
327
- }
328
-
329
- /**
330
- * 1. ensure controller(last argument) support string
331
- * - [url, controller]: app.get('/home', 'home');
332
- * - [name, url, controller(string)]: app.get('posts', '/posts', 'posts.list');
333
- * - [name, url, controller]: app.get('posts', '/posts', app.controller.posts.list);
334
- * - [name, url(regexp), controller]: app.get('regRouter', /\/home\/index/, 'home.index');
335
- * - [name, url, middleware, [...], controller]: `app.get(/user/:id', hasLogin, canGetUser, 'user.show');`
336
- *
337
- * 2. bind ctx to controller `this`
338
- *
339
- * @param {Array} middlewares middlewares and controller(last middleware)
340
- * @param {Application} app egg application instance
341
- */
342
- function convertMiddlewares(middlewares: (MiddlewareFunc | string | ResourcesController)[], app: Application) {
343
- // ensure controller is resolved
344
- const controller = resolveController(middlewares.pop()!, app);
345
- function wrappedController(ctx: any, next: Next) {
346
- return controller.apply(ctx, [ ctx, next ]);
347
- }
348
- return [ ...middlewares as MiddlewareFunc[], wrappedController ];
349
- }
package/src/Layer.ts DELETED
@@ -1,274 +0,0 @@
1
- import { debuglog } from 'node:util';
2
- import pathToRegExp, { type Key } from 'path-to-regexp';
3
- import URI from 'urijs';
4
- import { decodeURIComponent as safeDecodeURIComponent } from 'utility';
5
- import { isGeneratorFunction } from 'is-type-of';
6
- import type {
7
- MiddlewareFunc,
8
- MiddlewareFuncWithParamProperty,
9
- ParamMiddlewareFunc,
10
- } from './types.js';
11
-
12
- const debug = debuglog('@eggjs/router:Layer');
13
-
14
- export interface LayerOptions {
15
- prefix?: string;
16
- /** route name */
17
- name?: string;
18
- /** case sensitive (default: false) */
19
- sensitive?: boolean;
20
- /** require the trailing slash (default: false) */
21
- strict?: boolean;
22
- ignoreCaptures?: boolean;
23
- end?: boolean;
24
- }
25
-
26
- export interface LayerURLOptions {
27
- query?: string | object;
28
- }
29
-
30
- export class Layer {
31
- readonly opts: LayerOptions;
32
- readonly name?: string;
33
- readonly methods: string[] = [];
34
- readonly stack: MiddlewareFuncWithParamProperty[];
35
- path: string | RegExp;
36
- regexp: RegExp;
37
- paramNames: Key[] = [];
38
-
39
- /**
40
- * Initialize a new routing Layer with given `method`, `path`, and `middleware`.
41
- *
42
- * @param {String|RegExp} path Path string or regular expression.
43
- * @param {Array} methods Array of HTTP verbs.
44
- * @param {Array|Function} middlewares Layer callback/middleware or series of.
45
- * @param {Object=} opts optional params
46
- * @param {String=} opts.name route name
47
- * @param {String=} opts.sensitive case sensitive (default: false)
48
- * @param {String=} opts.strict require the trailing slash (default: false)
49
- * @private
50
- */
51
- constructor(path: string | RegExp, methods: string[], middlewares: MiddlewareFunc | MiddlewareFunc[],
52
- opts?: LayerOptions | string) {
53
- if (typeof opts === 'string') {
54
- // new Layer(path, methods, middlewares, name);
55
- opts = { name: opts };
56
- }
57
- this.opts = opts ?? {};
58
- this.opts.prefix = this.opts.prefix ?? '';
59
- this.name = this.opts.name;
60
- this.stack = Array.isArray(middlewares) ? middlewares : [ middlewares ];
61
-
62
- for (const method of methods) {
63
- const l = this.methods.push(method.toUpperCase());
64
- if (this.methods[l - 1] === 'GET') {
65
- this.methods.unshift('HEAD');
66
- }
67
- }
68
-
69
- // ensure middleware is a function
70
- this.stack.forEach(fn => {
71
- const type = typeof fn;
72
- if (type !== 'function') {
73
- throw new TypeError(
74
- methods.toString() + ' `' + (this.opts.name || path) + '`: `middleware` '
75
- + 'must be a function, not `' + type + '`',
76
- );
77
- }
78
- if (isGeneratorFunction(fn)) {
79
- throw new TypeError(
80
- methods.toString() + ' `' + (this.opts.name || path) + '`: Please use async function instead of generator function',
81
- );
82
- }
83
- });
84
-
85
- this.path = path;
86
- this.regexp = pathToRegExp(path, this.paramNames, this.opts);
87
-
88
- debug('defined route %s %s', this.methods, this.opts.prefix + this.path);
89
- }
90
-
91
- /**
92
- * Returns whether request `path` matches route.
93
- *
94
- * @param {String} path path string
95
- * @return {Boolean} matched or not
96
- * @private
97
- */
98
- match(path: string): boolean {
99
- return this.regexp.test(path);
100
- }
101
-
102
- /**
103
- * Returns map of URL parameters for given `path` and `paramNames`.
104
- *
105
- * @param {String} _path path string
106
- * @param {Array.<String>} captures captures strings
107
- * @param {Object=} [existingParams] existing params
108
- * @return {Object} params object
109
- * @private
110
- */
111
- params(_path: string, captures: Array<string>, existingParams?: Record<string, string>): Record<string, string> {
112
- const params = existingParams ?? {};
113
-
114
- for (let len = captures.length, i = 0; i < len; i++) {
115
- const paramName = this.paramNames[i];
116
- if (paramName) {
117
- const c = captures[i];
118
- params[paramName.name] = c ? safeDecodeURIComponent(c) : c;
119
- }
120
- }
121
- return params;
122
- }
123
-
124
- /**
125
- * Returns array of regexp url path captures.
126
- *
127
- * @param {String} path path string
128
- * @return {Array.<String>} captures strings
129
- * @private
130
- */
131
- captures(path: string): Array<string> {
132
- if (this.opts.ignoreCaptures) return [];
133
- const m = path.match(this.regexp);
134
- return m ? m.slice(1) : [];
135
- }
136
-
137
- /**
138
- * Generate URL for route using given `params`.
139
- *
140
- * @example
141
- *
142
- * ```javascript
143
- * var route = new Layer(['GET'], '/users/:id', fn);
144
- *
145
- * route.url(123); // => "/users/123"
146
- * route.url('123'); // => "/users/123"
147
- * route.url({ id: 123 }); // => "/users/123"
148
- * ```
149
- *
150
- * @param {Object} params url parameters
151
- * @param {Object} paramsOrOptions optional parameters
152
- * @return {String} url string
153
- * @private
154
- */
155
- url(params?: string | number | object, ...paramsOrOptions: (string | number | object | LayerURLOptions)[]): string {
156
- let args: Array<string | number | object> | object = params as object;
157
- const url = (this.path as string).replace(/\(\.\*\)/g, '');
158
- const toPath = pathToRegExp.compile(url);
159
- let options: LayerURLOptions | undefined;
160
-
161
- if (params !== undefined && typeof params !== 'object') {
162
- args = [ params, ...paramsOrOptions ];
163
- // route.url(stringOrNumber, params1, ..., options);
164
- if (Array.isArray(args)) {
165
- const lastIndex = args.length - 1;
166
- if (typeof args[lastIndex] === 'object') {
167
- options = args[lastIndex];
168
- args = args.slice(0, lastIndex);
169
- }
170
- }
171
- } else if (typeof params === 'object') {
172
- if (typeof paramsOrOptions[0] === 'object' && 'query' in paramsOrOptions[0]) {
173
- // route.url(param, options);
174
- options = paramsOrOptions[0];
175
- }
176
- }
177
-
178
- const tokens = pathToRegExp.parse(url);
179
- let replace: Record<string, any> = {};
180
-
181
- if (Array.isArray(args)) {
182
- for (let len = tokens.length, i = 0, j = 0; i < len; i++) {
183
- const token = tokens[i];
184
- if (typeof token === 'object' && token.name) {
185
- replace[token.name] = args[j++];
186
- }
187
- }
188
- } else if (tokens.some(token => typeof token === 'object' && token.name)) {
189
- // route.url(params);
190
- replace = params as object;
191
- } else {
192
- // route.url(options);
193
- options = params as LayerURLOptions;
194
- }
195
-
196
- const replaced = toPath(replace);
197
-
198
- if (options?.query) {
199
- const urlObject = new URI(replaced);
200
- urlObject.search(options.query);
201
- return urlObject.toString();
202
- }
203
-
204
- return replaced;
205
- }
206
-
207
- /**
208
- * Run validations on route named parameters.
209
- *
210
- * @example
211
- *
212
- * ```javascript
213
- * router
214
- * .param('user', function (id, ctx, next) {
215
- * ctx.user = users[id];
216
- * if (!user) return ctx.status = 404;
217
- * next();
218
- * })
219
- * .get('/users/:user', function (ctx, next) {
220
- * ctx.body = ctx.user;
221
- * });
222
- * ```
223
- *
224
- * @param {String} param param string
225
- * @param {Function} fn middleware function
226
- * @return {Layer} layer instance
227
- * @private
228
- */
229
- param(param: string, fn: ParamMiddlewareFunc): Layer {
230
- const stack = this.stack;
231
- const params = this.paramNames;
232
- const middleware: MiddlewareFuncWithParamProperty = function(this: any, ctx, next) {
233
- return fn.call(this, ctx.params[param], ctx, next);
234
- };
235
- middleware.param = param;
236
-
237
- const names = params.map(p => {
238
- return p.name;
239
- });
240
-
241
- const x = names.indexOf(param);
242
- if (x > -1) {
243
- // iterate through the stack, to figure out where to place the handler fn
244
- stack.some(function(fn, i) {
245
- // param handlers are always first, so when we find an fn w/o a param property, stop here
246
- // if the param handler at this part of the stack comes after the one we are adding, stop here
247
- if (!fn.param || names.indexOf(fn.param) > x) {
248
- // inject this param handler right before the current item
249
- stack.splice(i, 0, middleware);
250
- return true; // then break the loop
251
- }
252
- return false;
253
- });
254
- }
255
-
256
- return this;
257
- }
258
-
259
- /**
260
- * Prefix route path.
261
- *
262
- * @param {String} prefix prefix string
263
- * @return {Layer} layer instance
264
- * @private
265
- */
266
- setPrefix(prefix: string): Layer {
267
- if (this.path) {
268
- this.path = prefix + this.path;
269
- this.paramNames = [];
270
- this.regexp = pathToRegExp(this.path, this.paramNames, this.opts);
271
- }
272
- return this;
273
- }
274
- }