diff --git a/lib/layer.js b/lib/layer.js index 56fc8ee..d83b866 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -1,5 +1,5 @@ var debug = require('debug')('koa-router'); -var pathToRegExp = require('path-to-regexp'); +var { pathToRegexp, compile, parse } = require('path-to-regexp'); var uri = require('urijs'); module.exports = Layer; @@ -45,7 +45,7 @@ function Layer(path, methods, middleware, opts) { } this.path = path; - this.regexp = pathToRegExp(path, this.paramNames, this.opts); + this.regexp = pathToRegexp(path, this.paramNames, this.opts); debug('defined route %s %s', this.methods, this.opts.prefix + this.path); }; @@ -117,8 +117,6 @@ Layer.prototype.captures = function (path) { Layer.prototype.url = function (params, options) { var args = params; var url = this.path.replace(/\(\.\*\)/g, ''); - var toPath = pathToRegExp.compile(url); - var replaced; if (typeof params != 'object') { args = Array.prototype.slice.call(arguments); @@ -128,7 +126,10 @@ Layer.prototype.url = function (params, options) { } } - var tokens = pathToRegExp.parse(url); + var toPath = compile(url, options); + var replaced; + + var tokens = parse(url); var replace = {}; if (args instanceof Array) { @@ -214,9 +215,13 @@ Layer.prototype.param = function (param, fn) { Layer.prototype.setPrefix = function (prefix) { if (this.path) { - this.path = prefix + this.path; + if (this.path !== '/' || this.opts.strict === true) { + this.path = prefix + this.path; + } else { + this.path = prefix; + } this.paramNames = []; - this.regexp = pathToRegExp(this.path, this.paramNames, this.opts); + this.regexp = pathToRegexp(this.path, this.paramNames, this.opts); } return this; diff --git a/package.json b/package.json index 26ba147..97d9073 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "http-errors": "^1.7.3", "koa-compose": "^4.1.0", "methods": "^1.1.2", - "path-to-regexp": "1.x", + "path-to-regexp": "^6.1.0", "urijs": "^1.19.2" }, "devDependencies": { diff --git a/test/lib/layer.js b/test/lib/layer.js index 49ec31f..3508e44 100644 --- a/test/lib/layer.js +++ b/test/lib/layer.js @@ -218,7 +218,7 @@ describe('Layer', function() { }); it('param with paramNames positive check', function () { - var route = new Layer('/:category/:title', ['get'], [function () {}], 'books'); + var route = new Layer('/:category/:title', ['get'], [function () {}], {name: 'books'}); route.paramNames = [{ name: 'category', }] @@ -230,7 +230,7 @@ describe('Layer', function() { describe('Layer#url()', function() { it('generates route URL', function() { - var route = new Layer('/:category/:title', ['get'], [function () {}], 'books'); + var route = new Layer('/:category/:title', ['get'], [function () {}], {name: 'books'}); var url = route.url({ category: 'programming', title: 'how-to-node' }); url.should.equal('/programming/how-to-node'); url = route.url('programming', 'how-to-node'); @@ -238,12 +238,15 @@ describe('Layer', function() { }); it('escapes using encodeURIComponent()', function() { - var route = new Layer('/:category/:title', ['get'], [function () {}], 'books'); - var url = route.url({ category: 'programming', title: 'how to node' }); + var route = new Layer('/:category/:title', ['get'], [function () {}], {name: 'books'}); + var url = route.url( + { category: 'programming', title: 'how to node' }, + { encode: encodeURIComponent } + ); url.should.equal('/programming/how%20to%20node'); }); it('setPrefix method checks Layer for path', function () { - const route = new Layer('/category', ['get'], [function () {}], 'books'); + const route = new Layer('/category', ['get'], [function () {}], {name: 'books'}); route.path = '/hunter2' const prefix = route.setPrefix('TEST') prefix.path.should.equal('TEST/hunter2') diff --git a/test/lib/router.js b/test/lib/router.js index 397335b..41e7a15 100644 --- a/test/lib/router.js +++ b/test/lib/router.js @@ -1107,7 +1107,7 @@ describe('Router', function () { done(); }, error => done(error)); }); - + it('uses a same router middleware at given paths continuously - ZijianHe/koa-router#gh-244 gh-18', function (done) { const app = new Koa(); const base = new Router({ prefix: '/api' }); @@ -1223,12 +1223,18 @@ describe('Router', function () { router.get('books', '/:category/:title', function (ctx) { ctx.status = 204; }); - var url = router.url('books', { category: 'programming', title: 'how to node' }); + var url = router.url( + 'books', + { category: 'programming', title: 'how to node' }, + { encode: encodeURIComponent } + ); url.should.equal('/programming/how%20to%20node'); - url = router.url('books', 'programming', 'how to node'); + url = router.url('books', 'programming', 'how to node', { + encode: encodeURIComponent, + }); url.should.equal('/programming/how%20to%20node'); done(); - + }); it('generates URL for given route name within embedded routers', function (done) { @@ -1245,9 +1251,15 @@ describe('Router', function () { }); router.use(embeddedRouter.routes()); app.use(router.routes()); - var url = router.url('chapters', { chapterName: 'Learning ECMA6', pageNumber: 123 }); + var url = router.url( + 'chapters', + { chapterName: 'Learning ECMA6', pageNumber: 123 }, + { encode: encodeURIComponent } + ); url.should.equal('/books/chapters/Learning%20ECMA6/123'); - url = router.url('chapters', 'Learning ECMA6', 123); + url = router.url('chapters', 'Learning ECMA6', 123, { + encode: encodeURIComponent, + }); url.should.equal('/books/chapters/Learning%20ECMA6/123'); done(); }); @@ -1269,7 +1281,11 @@ describe('Router', function () { embeddedRouter.use(embeddedRouter2.routes()); router.use(embeddedRouter.routes()); app.use(router.routes()); - var url = router.url('chapters', { chapterName: 'Learning ECMA6', pageNumber: 123 }); + var url = router.url( + 'chapters', + { chapterName: 'Learning ECMA6', pageNumber: 123 }, + { encode: encodeURIComponent } + ); url.should.equal('/books/chapters/Learning%20ECMA6/pages/123'); done(); }); @@ -1836,6 +1852,61 @@ describe('Router', function () { }); } } + + it(`prefix and '/' route behavior`, function(done) { + var app = new Koa(); + var router = new Router({ + strict: false, + prefix: '/foo' + }); + + var strictRouter = new Router({ + strict: true, + prefix: '/bar' + }) + + router.get('/', function(ctx) { + ctx.body = ''; + }); + + strictRouter.get('/', function(ctx) { + ctx.body = ''; + }); + + app.use(router.routes()); + app.use(strictRouter.routes()); + + var server = http.createServer(app.callback()); + + request(server) + .get('/foo') + .expect(200) + .end(function (err) { + if (err) return done(err); + + request(server) + .get('/foo/') + .expect(200) + .end(function (err) { + if (err) return done(err); + + request(server) + .get('/bar') + .expect(404) + .end(function (err) { + if (err) return done(err); + + request(server) + .get('/bar/') + .expect(200) + .end(function (err) { + if (err) return done(err); + done(); + }); + }); + }); + }); + }) }); describe('Static Router#url()', function () { @@ -1845,7 +1916,11 @@ describe('Router', function () { }); it('escapes using encodeURIComponent()', function () { - var url = Router.url('/:category/:title', { category: 'programming', title: 'how to node' }); + var url = Router.url( + '/:category/:title', + { category: 'programming', title: 'how to node' }, + { encode: encodeURIComponent } + ); url.should.equal('/programming/how%20to%20node'); });