Skip to content

Commit

Permalink
feat: treat repeating slashes as pathless paths (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgieselaar authored and troch committed Oct 5, 2017
1 parent 99f9e71 commit 4a8a41f
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 32 deletions.
23 changes: 16 additions & 7 deletions dist/amd/route-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ var RouteNode = function () {
strictQueryParams = options.strictQueryParams,
strongMatching = options.strongMatching;

var matchChildren = function matchChildren(nodes, pathSegment, segments) {
var matchChildren = function matchChildren(nodes, pathSegment, segments, consumedBefore) {
var isRoot = nodes.length === 1 && nodes[0].name === '';
// for (child of node.children) {

Expand All @@ -771,13 +771,20 @@ var RouteNode = function () {
// Partially match path
var match = void 0;
var remainingPath = void 0;
var segment = pathSegment;

if (consumedBefore === '/' && child.path === '/') {
// when we encounter repeating slashes we add the slash
// back to the URL to make it de facto pathless
segment = '/' + pathSegment;
}

if (!child.children.length) {
match = child.parser.test(pathSegment, { trailingSlash: trailingSlash });
match = child.parser.test(segment, { trailingSlash: trailingSlash });
}

if (!match) {
match = child.parser.partialTest(pathSegment, { delimiter: strongMatching });
match = child.parser.partialTest(segment, { delimiter: strongMatching });
}

if (match) {
Expand All @@ -787,13 +794,13 @@ var RouteNode = function () {
consumedPath = consumedPath.replace(/\/$/, '');
}

remainingPath = pathSegment.replace(consumedPath, '');
remainingPath = segment.replace(consumedPath, '');

if (trailingSlash && !child.children.length) {
remainingPath = remainingPath.replace(/^\/\?/, '?');
}

var search = omit(getSearch(pathSegment.replace(consumedPath, '')), child.parser.queryParams.concat(child.parser.queryParamsBr));
var search = omit(getSearch(segment.replace(consumedPath, '')), child.parser.queryParams.concat(child.parser.queryParamsBr));
remainingPath = getPath(remainingPath) + (search ? '?' + search : '');
if (trailingSlash && !isRoot && remainingPath === '/' && !/\/$/.test(consumedPath)) {
remainingPath = '';
Expand Down Expand Up @@ -833,7 +840,7 @@ var RouteNode = function () {
}
// Else: remaining path and children
return {
v: matchChildren(children, remainingPath, segments)
v: matchChildren(children, remainingPath, segments, consumedPath)
};
}
};
Expand Down Expand Up @@ -920,7 +927,9 @@ var RouteNode = function () {
var segmentPath = segment.parser.build(params, { ignoreSearch: true });

return segment.absolute ? segmentPath : path + segmentPath;
}, '');
}, '')
// remove repeated slashes
.replace(/\/\/{1,}/g, '/');

var finalPath = path;

Expand Down
23 changes: 16 additions & 7 deletions dist/commonjs/route-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ var RouteNode = function () {
strictQueryParams = options.strictQueryParams,
strongMatching = options.strongMatching;

var matchChildren = function matchChildren(nodes, pathSegment, segments) {
var matchChildren = function matchChildren(nodes, pathSegment, segments, consumedBefore) {
var isRoot = nodes.length === 1 && nodes[0].name === '';
// for (child of node.children) {

Expand All @@ -256,13 +256,20 @@ var RouteNode = function () {
// Partially match path
var match = void 0;
var remainingPath = void 0;
var segment = pathSegment;

if (consumedBefore === '/' && child.path === '/') {
// when we encounter repeating slashes we add the slash
// back to the URL to make it de facto pathless
segment = '/' + pathSegment;
}

if (!child.children.length) {
match = child.parser.test(pathSegment, { trailingSlash: trailingSlash });
match = child.parser.test(segment, { trailingSlash: trailingSlash });
}

if (!match) {
match = child.parser.partialTest(pathSegment, { delimiter: strongMatching });
match = child.parser.partialTest(segment, { delimiter: strongMatching });
}

if (match) {
Expand All @@ -272,13 +279,13 @@ var RouteNode = function () {
consumedPath = consumedPath.replace(/\/$/, '');
}

remainingPath = pathSegment.replace(consumedPath, '');
remainingPath = segment.replace(consumedPath, '');

if (trailingSlash && !child.children.length) {
remainingPath = remainingPath.replace(/^\/\?/, '?');
}

var search = (0, _searchParams.omit)((0, _searchParams.getSearch)(pathSegment.replace(consumedPath, '')), child.parser.queryParams.concat(child.parser.queryParamsBr));
var search = (0, _searchParams.omit)((0, _searchParams.getSearch)(segment.replace(consumedPath, '')), child.parser.queryParams.concat(child.parser.queryParamsBr));
remainingPath = (0, _searchParams.getPath)(remainingPath) + (search ? '?' + search : '');
if (trailingSlash && !isRoot && remainingPath === '/' && !/\/$/.test(consumedPath)) {
remainingPath = '';
Expand Down Expand Up @@ -318,7 +325,7 @@ var RouteNode = function () {
}
// Else: remaining path and children
return {
v: matchChildren(children, remainingPath, segments)
v: matchChildren(children, remainingPath, segments, consumedPath)
};
}
};
Expand Down Expand Up @@ -405,7 +412,9 @@ var RouteNode = function () {
var segmentPath = segment.parser.build(params, { ignoreSearch: true });

return segment.absolute ? segmentPath : path + segmentPath;
}, '');
}, '')
// remove repeated slashes
.replace(/\/\/{1,}/g, '/');

var finalPath = path;

Expand Down
23 changes: 16 additions & 7 deletions dist/umd/route-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ var RouteNode = function () {
strictQueryParams = options.strictQueryParams,
strongMatching = options.strongMatching;

var matchChildren = function matchChildren(nodes, pathSegment, segments) {
var matchChildren = function matchChildren(nodes, pathSegment, segments, consumedBefore) {
var isRoot = nodes.length === 1 && nodes[0].name === '';
// for (child of node.children) {

Expand All @@ -775,13 +775,20 @@ var RouteNode = function () {
// Partially match path
var match = void 0;
var remainingPath = void 0;
var segment = pathSegment;

if (consumedBefore === '/' && child.path === '/') {
// when we encounter repeating slashes we add the slash
// back to the URL to make it de facto pathless
segment = '/' + pathSegment;
}

if (!child.children.length) {
match = child.parser.test(pathSegment, { trailingSlash: trailingSlash });
match = child.parser.test(segment, { trailingSlash: trailingSlash });
}

if (!match) {
match = child.parser.partialTest(pathSegment, { delimiter: strongMatching });
match = child.parser.partialTest(segment, { delimiter: strongMatching });
}

if (match) {
Expand All @@ -791,13 +798,13 @@ var RouteNode = function () {
consumedPath = consumedPath.replace(/\/$/, '');
}

remainingPath = pathSegment.replace(consumedPath, '');
remainingPath = segment.replace(consumedPath, '');

if (trailingSlash && !child.children.length) {
remainingPath = remainingPath.replace(/^\/\?/, '?');
}

var search = omit(getSearch(pathSegment.replace(consumedPath, '')), child.parser.queryParams.concat(child.parser.queryParamsBr));
var search = omit(getSearch(segment.replace(consumedPath, '')), child.parser.queryParams.concat(child.parser.queryParamsBr));
remainingPath = getPath(remainingPath) + (search ? '?' + search : '');
if (trailingSlash && !isRoot && remainingPath === '/' && !/\/$/.test(consumedPath)) {
remainingPath = '';
Expand Down Expand Up @@ -837,7 +844,7 @@ var RouteNode = function () {
}
// Else: remaining path and children
return {
v: matchChildren(children, remainingPath, segments)
v: matchChildren(children, remainingPath, segments, consumedPath)
};
}
};
Expand Down Expand Up @@ -924,7 +931,9 @@ var RouteNode = function () {
var segmentPath = segment.parser.build(params, { ignoreSearch: true });

return segment.absolute ? segmentPath : path + segmentPath;
}, '');
}, '')
// remove repeated slashes
.replace(/\/\/{1,}/g, '/');

var finalPath = path;

Expand Down
28 changes: 19 additions & 9 deletions modules/RouteNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,22 +186,30 @@ export default class RouteNode {

getSegmentsMatchingPath(path, options) {
const { trailingSlash, strictQueryParams, strongMatching } = options;
let matchChildren = (nodes, pathSegment, segments) => {
let matchChildren = (nodes, pathSegment, segments, consumedBefore) => {
const isRoot = nodes.length === 1 && nodes[0].name === '';
// for (child of node.children) {
for (let i = 0; i < nodes.length; i += 1) {
const child = nodes[i];


// Partially match path
let match;
let remainingPath;
let segment = pathSegment;

if (consumedBefore === '/' && child.path === '/') {
// when we encounter repeating slashes we add the slash
// back to the URL to make it de facto pathless
segment = '/' + pathSegment;
}

if (!child.children.length) {
match = child.parser.test(pathSegment, { trailingSlash });
match = child.parser.test(segment, { trailingSlash });
}

if (!match) {
match = child.parser.partialTest(pathSegment, { delimiter: strongMatching });
match = child.parser.partialTest(segment, { delimiter: strongMatching });
}

if (match) {
Expand All @@ -211,14 +219,14 @@ export default class RouteNode {
consumedPath = consumedPath.replace(/\/$/, '');
}

remainingPath = pathSegment.replace(consumedPath, '');

remainingPath = segment.replace(consumedPath, '');
if (trailingSlash && !child.children.length) {
remainingPath = remainingPath.replace(/^\/\?/, '?');
}

const search = omit(
getSearch(pathSegment.replace(consumedPath, '')),
getSearch(segment.replace(consumedPath, '')),
child.parser.queryParams.concat(child.parser.queryParamsBr)
);
remainingPath = getPath(remainingPath) + (search ? `?${search}` : '');
Expand All @@ -245,7 +253,7 @@ export default class RouteNode {
return null;
}
// Else: remaining path and children
return matchChildren(children, remainingPath, segments);
return matchChildren(children, remainingPath, segments, consumedPath);
}
}

Expand Down Expand Up @@ -326,7 +334,9 @@ export default class RouteNode {
const segmentPath = segment.parser.build(params, {ignoreSearch: true});

return segment.absolute ? segmentPath : path + segmentPath;
}, '');
}, '')
// remove repeated slashes
.replace(/\/\/{1,}/g, '/');

let finalPath = path;

Expand Down
32 changes: 30 additions & 2 deletions test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,6 @@ describe('RouteNode', function () {
withoutMeta(rootNode.matchPath('/users/list', { trailingSlash: true })).should.eql({name: 'users.list', params: {}});
withoutMeta(rootNode.matchPath('/users/list')).should.eql({name: 'users.list', params: {}});
withoutMeta(rootNode.matchPath('/users/list/', { trailingSlash: true })).should.eql({name: 'users.list', params: {}});
should.not.exists(rootNode.matchPath('/users/list//', { trailingSlash: true }));

rootNode = getRoutes(true);
should.not.exists(rootNode.matchPath('/users/list'));
Expand All @@ -323,7 +322,6 @@ describe('RouteNode', function () {
withoutMeta(rootNode.matchPath('/')).should.eql({name: 'default', params: {}});
withoutMeta(rootNode.matchPath('', { trailingSlash: true })).should.eql({name: 'default', params: {}});
should.not.exists(rootNode.matchPath('', { trailingSlash: false }));
should.not.exists(rootNode.matchPath('/users/list//', { trailingSlash: true }));
});

it('should match paths with optional trailing slashes and a non-empty root node', function () {
Expand Down Expand Up @@ -527,6 +525,36 @@ describe('RouteNode', function () {
node.buildPath('c', { c: 1 }, { trailingSlash: false }).should.eql('/?c=1');
});

it('should remove repeated slashes when building paths', ( ) => {

const node = new RouteNode('', '', [
new RouteNode('a', '/', [
new RouteNode('b', '/', [
new RouteNode('c', '/')
])
])
]);

node.buildPath('a.b', {}).should.eql('/');
node.buildPath('a.b.c', {}).should.eql('/');

});

it('should match paths with repeating slashes', ( ) => {

const node = new RouteNode('', '', [
new RouteNode('a', '/', [
new RouteNode('b', '/', [
new RouteNode('c', ':bar')
])
])
]);

withoutMeta(node.matchPath('/')).should.eql({ name: 'a.b', params: {}});
withoutMeta(node.matchPath('/foo')).should.eql({ name: 'a.b.c', params: { bar: 'foo' }});

});

});


Expand Down

0 comments on commit 4a8a41f

Please sign in to comment.