From 111f515ab03868f5677773d1420c40e22e5d68fb Mon Sep 17 00:00:00 2001 From: harryby1149 Date: Thu, 12 Sep 2024 12:07:20 -0400 Subject: [PATCH 1/5] chore: upgrade path-to-regex to v8.1.0 --- lib/layer.js | 66 +++++++++++++++++++++++++++++++++++++++++----- lib/router.js | 39 +++++++++++++++++---------- package.json | 2 +- test/lib/router.js | 58 ++++++++++++++++++++++++---------------- 4 files changed, 121 insertions(+), 44 deletions(-) diff --git a/lib/layer.js b/lib/layer.js index 06f2ed6..336273d 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -1,6 +1,6 @@ const { parse: parseUrl, format: formatUrl } = require('node:url'); -const { pathToRegexp, compile, parse } = require('path-to-regexp'); +const { pathToRegexp, compile, parse, stringify } = require('path-to-regexp'); module.exports = class Layer { /** @@ -41,7 +41,14 @@ module.exports = class Layer { } this.path = path; - this.regexp = pathToRegexp(path, this.paramNames, this.opts); + + if (this.opts.pathIsRegexp === true) { + this.regexp = new RegExp(path); + } else if (this.path) { + const { regexp: regex, keys } = pathToRegexp(this.path, this.opts); + this.regexp = regex; + this.paramNames = keys; + } } /** @@ -116,13 +123,14 @@ module.exports = class Layer { const toPath = compile(url, { encode: encodeURIComponent, ...options }); let replaced; - - const tokens = parse(url); + const { tokens } = parse(url); let replace = {}; if (Array.isArray(args)) { for (let len = tokens.length, i = 0, j = 0; i < len; i++) { - if (tokens[i].name) replace[tokens[i].name] = args[j++]; + if (tokens[i].name) { + replace[tokens[i].name] = args[j++]; + } } } else if (tokens.some((token) => token.name)) { replace = params; @@ -130,6 +138,10 @@ module.exports = class Layer { options = params; } + for (const [key, value] of Object.entries(replace)) { + replace[key] = value.toString(); + } + replaced = toPath(replace); if (options && options.query) { @@ -212,8 +224,13 @@ module.exports = class Layer { this.path !== '/' || this.opts.strict === true ? `${prefix}${this.path}` : prefix; - this.paramNames = []; - this.regexp = pathToRegexp(this.path, this.paramNames, this.opts); + if (this.opts.pathIsRegexp === true || prefix instanceof RegExp) { + this.regexp = new RegExp(this.path); + } else if (this.path) { + const { regexp: regex, keys } = pathToRegexp(this.path, this.opts); + this.regexp = regex; + this.paramNames = keys; + } } return this; @@ -237,3 +254,38 @@ function safeDecodeURIComponent(text) { return text; } } + +/** + * Lifted from Path to Regexp v6.2.1 + * functionality no longer supported in v8 but required here + * @param {*} path + * @param {*} keys + * @returns + */ +// function regexpToRegexp(path) { +// const groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g; + +// let index = 0; + +// let execResult = groupsRegex.exec(path.source); + +// while (execResult) { +// keys.push({ +// // Use parenthesized substring match if available, index otherwise + +// name: execResult[1] || index++, + +// prefix: '', + +// suffix: '', + +// modifier: '', + +// pattern: '' +// }); + +// execResult = groupsRegex.exec(path.source); +// } + +// return { path, keys }; +// } diff --git a/lib/router.js b/lib/router.js index f269482..ad9817c 100644 --- a/lib/router.js +++ b/lib/router.js @@ -165,14 +165,14 @@ class Router { } } } else { - const keys = []; - pathToRegexp(router.opts.prefix || '', keys); + const { keys } = pathToRegexp(router.opts.prefix || '', router.opts); const routerPrefixHasParam = Boolean( router.opts.prefix && keys.length > 0 ); router.register(path || '([^/]*)', [], m, { end: false, - ignoreCaptures: !hasPath && !routerPrefixHasParam + ignoreCaptures: !hasPath && !routerPrefixHasParam, + pathIsRegexp: true }); } } @@ -380,7 +380,7 @@ class Router { * @returns {Router} */ all(name, path, middleware) { - if (typeof path === 'string') { + if (typeof path === 'string' || path instanceof RegExp) { middleware = Array.prototype.slice.call(arguments, 2); } else { middleware = Array.prototype.slice.call(arguments, 1); @@ -396,7 +396,12 @@ class Router { ) throw new Error('You have to provide a path when adding an all handler'); - this.register(path, methods, middleware, { name }); + const opts = { + name, + pathIsRegexp: path instanceof RegExp + }; + + this.register(path, methods, middleware, { ...this.opts, ...opts }); return this; } @@ -455,10 +460,10 @@ class Router { * @returns {Layer} * @private */ - register(path, methods, middleware, opts = {}) { + register(path, methods, middleware, newOpts = {}) { const router = this; const { stack } = this; - + const opts = { ...this.opts, ...newOpts }; // support array of paths if (Array.isArray(path)) { for (const curPath of path) { @@ -472,12 +477,15 @@ class Router { const route = new Layer(path, methods, middleware, { end: opts.end === false ? opts.end : true, name: opts.name, - sensitive: opts.sensitive || this.opts.sensitive || false, - strict: opts.strict || this.opts.strict || false, - prefix: opts.prefix || this.opts.prefix || '', - ignoreCaptures: opts.ignoreCaptures + sensitive: opts.sensitive || false, + strict: opts.strict || false, + prefix: opts.prefix || '', + ignoreCaptures: opts.ignoreCaptures, + pathIsRegexp: opts.pathIsRegexp, + trailing: opts.trailing }); + // if parent prefix exists, add prefix to new route if (this.opts.prefix) { route.setPrefix(this.opts.prefix); } @@ -489,7 +497,6 @@ class Router { } stack.push(route); - debug('defined route %s %s', route.methods, route.path); return route; @@ -572,7 +579,6 @@ class Router { for (let len = layers.length, i = 0; i < len; i++) { layer = layers[i]; - debug('test %s %s', layer.path, layer.regexp); // eslint-disable-next-line unicorn/prefer-regexp-test @@ -812,8 +818,13 @@ for (const method of methods) { `You have to provide a path when adding a ${method} handler` ); - this.register(path, [method], middleware, { name }); + const opts = { + name, + pathIsRegexp: path instanceof RegExp + }; + // pass opts to register call on verb methods + this.register(path, [method], middleware, { ...this.opts, ...opts }); return this; }; } diff --git a/package.json b/package.json index f2255c5..9a31c26 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "dependencies": { "http-errors": "^2.0.0", "koa-compose": "^4.1.0", - "path-to-regexp": "^6.3.0" + "path-to-regexp": "^8.1.0" }, "devDependencies": { "@commitlint/cli": "^17.7.2", diff --git a/test/lib/router.js b/test/lib/router.js index bf2846f..d06cc37 100644 --- a/test/lib/router.js +++ b/test/lib/router.js @@ -220,15 +220,27 @@ describe('Router', () => { const router = new Router(); router - .get('user_page', '/user/(.*).jsx', (ctx) => { - ctx.body = { order: 1 }; - }) - .all('app', '/app/(.*).jsx', (ctx) => { - ctx.body = { order: 2 }; - }) - .all('view', '(.*).jsx', (ctx) => { - ctx.body = { order: 3 }; - }); + .get( + 'user_page', + new RegExp('/user/(.*).jsx'), // eslint-disable-line prefer-regex-literals + (ctx) => { + ctx.body = { order: 1 }; + } + ) + .all( + 'app', + new RegExp('/app/(.*).jsx'), // eslint-disable-line prefer-regex-literals + (ctx) => { + ctx.body = { order: 2 }; + } + ) + .all( + 'view', + new RegExp('(.*).jsx'), // eslint-disable-line prefer-regex-literals + (ctx) => { + ctx.body = { order: 3 }; + } + ); const res = await request( http.createServer(app.use(router.routes()).callback()) @@ -244,7 +256,7 @@ describe('Router', () => { const router = new Router(); router - .get('users_single', '/users/:id(.*)', (ctx, next) => { + .get('users_single', '/users/:id{/*path}', (ctx, next) => { ctx.body = { single: true }; next(); }) @@ -258,7 +270,6 @@ describe('Router', () => { ) .get('/users/all') .expect(200); - assert.strictEqual('single' in res.body, true); assert.strictEqual('all' in res.body, true); }); @@ -268,10 +279,14 @@ describe('Router', () => { const router = new Router({ exclusive: true }); router - .get('users_single', '/users/:id(.*)', (ctx, next) => { - ctx.body = { single: true }; - next(); - }) + .get( + 'users_single', + new RegExp('/users/:id(.*)'), // eslint-disable-line prefer-regex-literals + (ctx, next) => { + ctx.body = { single: true }; + next(); + } + ) .get('users_all', '/users/all', (ctx, next) => { ctx.body = { ...ctx.body, all: true }; next(); @@ -293,7 +308,7 @@ describe('Router', () => { router.get( 'user_page', - '/user/(.*).jsx', + new RegExp('/user/(.*).jsx'), // eslint-disable-line prefer-regex-literals () => { // no next() }, @@ -413,7 +428,6 @@ it('runs parent middleware for subrouter routes', async () => { ) .get('/parent/sub') .expect(200); - assert.strictEqual(res.body.msg, 'router'); }); @@ -458,7 +472,7 @@ it('matches corresponding requests with optional route parameter', async () => { }); const id = '10'; const ext = '.json'; - router.get('/resources/:id{.:ext}?', (ctx) => { + router.get('/resources/:id{.:ext}', (ctx) => { assert.strictEqual('params' in ctx, true); assert.strictEqual(ctx.params.id, id); if (ctx.params.ext) assert.strictEqual(ctx.params.ext, ext.slice(1)); @@ -1653,7 +1667,7 @@ describe('Router#opts', () => { it('responds with 200', async () => { const app = new Koa(); const router = new Router({ - strict: true + trailing: false }); router.get('/info', (ctx) => { ctx.body = 'hello'; @@ -1685,7 +1699,7 @@ describe('Router#opts', () => { it('responds with 404 when has a trailing slash', async () => { const app = new Koa(); const router = new Router({ - strict: true + trailing: false }); router.get('/info', (ctx) => { ctx.body = 'hello'; @@ -1700,7 +1714,7 @@ describe('use middleware with opts', () => { it('responds with 200', async () => { const app = new Koa(); const router = new Router({ - strict: true + trailing: false }); router.get('/info', (ctx) => { ctx.body = 'hello'; @@ -1716,7 +1730,7 @@ describe('use middleware with opts', () => { it('responds with 404 when has a trailing slash', async () => { const app = new Koa(); const router = new Router({ - strict: true + trailing: false }); router.get('/info', (ctx) => { ctx.body = 'hello'; From 2f2e33b296847cc5aac02c96a63145a8f3ea05d3 Mon Sep 17 00:00:00 2001 From: harryby1149 Date: Thu, 12 Sep 2024 12:13:22 -0400 Subject: [PATCH 2/5] chore: upgrade path-to-regex to v8.1.0 --- lib/layer.js | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/lib/layer.js b/lib/layer.js index 336273d..82755be 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -254,38 +254,3 @@ function safeDecodeURIComponent(text) { return text; } } - -/** - * Lifted from Path to Regexp v6.2.1 - * functionality no longer supported in v8 but required here - * @param {*} path - * @param {*} keys - * @returns - */ -// function regexpToRegexp(path) { -// const groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g; - -// let index = 0; - -// let execResult = groupsRegex.exec(path.source); - -// while (execResult) { -// keys.push({ -// // Use parenthesized substring match if available, index otherwise - -// name: execResult[1] || index++, - -// prefix: '', - -// suffix: '', - -// modifier: '', - -// pattern: '' -// }); - -// execResult = groupsRegex.exec(path.source); -// } - -// return { path, keys }; -// } From 82e555d34a9efec0397a81ca41e0875a8d92dfa0 Mon Sep 17 00:00:00 2001 From: harryby1149 Date: Thu, 12 Sep 2024 12:22:00 -0400 Subject: [PATCH 3/5] chore: clean up test syntax --- test/lib/router.js | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/test/lib/router.js b/test/lib/router.js index d06cc37..14c82b9 100644 --- a/test/lib/router.js +++ b/test/lib/router.js @@ -220,27 +220,15 @@ describe('Router', () => { const router = new Router(); router - .get( - 'user_page', - new RegExp('/user/(.*).jsx'), // eslint-disable-line prefer-regex-literals - (ctx) => { - ctx.body = { order: 1 }; - } - ) - .all( - 'app', - new RegExp('/app/(.*).jsx'), // eslint-disable-line prefer-regex-literals - (ctx) => { - ctx.body = { order: 2 }; - } - ) - .all( - 'view', - new RegExp('(.*).jsx'), // eslint-disable-line prefer-regex-literals - (ctx) => { - ctx.body = { order: 3 }; - } - ); + .get('user_page', '/user/{*any}.jsx', (ctx) => { + ctx.body = { order: 1 }; + }) + .all('app', '/app/{*any}.jsx', (ctx) => { + ctx.body = { order: 2 }; + }) + .all('view', '{*any}.jsx', (ctx) => { + ctx.body = { order: 3 }; + }); const res = await request( http.createServer(app.use(router.routes()).callback()) @@ -308,7 +296,7 @@ describe('Router', () => { router.get( 'user_page', - new RegExp('/user/(.*).jsx'), // eslint-disable-line prefer-regex-literals + '/user/{*any}.jsx', () => { // no next() }, From 3e4432d98599795f264988804fc8bc6391dda5e0 Mon Sep 17 00:00:00 2001 From: harryby1149 Date: Thu, 12 Sep 2024 12:27:22 -0400 Subject: [PATCH 4/5] chore: clean up whitespace changes --- lib/router.js | 2 ++ test/lib/router.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/router.js b/lib/router.js index ad9817c..9c5fadc 100644 --- a/lib/router.js +++ b/lib/router.js @@ -497,6 +497,7 @@ class Router { } stack.push(route); + debug('defined route %s %s', route.methods, route.path); return route; @@ -579,6 +580,7 @@ class Router { for (let len = layers.length, i = 0; i < len; i++) { layer = layers[i]; + debug('test %s %s', layer.path, layer.regexp); // eslint-disable-next-line unicorn/prefer-regexp-test diff --git a/test/lib/router.js b/test/lib/router.js index 14c82b9..affbb14 100644 --- a/test/lib/router.js +++ b/test/lib/router.js @@ -258,6 +258,7 @@ describe('Router', () => { ) .get('/users/all') .expect(200); + assert.strictEqual('single' in res.body, true); assert.strictEqual('all' in res.body, true); }); @@ -416,6 +417,7 @@ it('runs parent middleware for subrouter routes', async () => { ) .get('/parent/sub') .expect(200); + assert.strictEqual(res.body.msg, 'router'); }); From 1a2ff6c176986c175614f38ec6e3b8a83dc6b84a Mon Sep 17 00:00:00 2001 From: harryby1149 Date: Fri, 13 Sep 2024 08:33:15 -0400 Subject: [PATCH 5/5] chore: use String constructor instead of .toString for type conversion --- lib/layer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/layer.js b/lib/layer.js index 82755be..c7932d8 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -139,7 +139,7 @@ module.exports = class Layer { } for (const [key, value] of Object.entries(replace)) { - replace[key] = value.toString(); + replace[key] = String(value); } replaced = toPath(replace);