Skip to content

Commit b65fb06

Browse files
committed
feat: add full path matching against tree
1 parent 8034208 commit b65fb06

3 files changed

Lines changed: 124 additions & 30 deletions

File tree

index.js

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ var RouteNode = (function () {
2424

2525
this.name = name;
2626
this.path = path;
27-
if (path) {
28-
this.parser = new _pathParser2['default'](path);
29-
}
27+
this.parser = path ? new _pathParser2['default'](path) : {};
3028
this.children = [];
3129

3230
this.add(childRoutes);
@@ -69,11 +67,8 @@ var RouteNode = (function () {
6967
this.children.push(route);
7068
}
7169
}, {
72-
key: 'findRouteByPath',
73-
value: function findRouteByPath(path) {}
74-
}, {
75-
key: 'findRouteByName',
76-
value: function findRouteByName(routeName) {
70+
key: '_findRouteByName',
71+
value: function _findRouteByName(routeName) {
7772
var findSegmentByName = function findSegmentByName(name, routes) {
7873
var filteredRoutes = routes.filter(function (r) {
7974
return r.name === name;
@@ -96,10 +91,57 @@ var RouteNode = (function () {
9691

9792
return matched ? segments : [];
9893
}
94+
}, {
95+
key: 'matchRoute',
96+
value: function matchRoute(path) {
97+
var segments = [];
98+
99+
var matchChildren = function matchChildren(node, pathSegment, matched) {
100+
var _loop = function (i) {
101+
var child = node.children[i];
102+
var match = child.parser.partialMatch(pathSegment);
103+
if (match) {
104+
// Append name and extend params
105+
matched.name += (matched.name ? '.' : '') + child.name;
106+
Object.keys(match).forEach(function (p) {
107+
return matched.params[p] = match[p];
108+
});
109+
// Remove consumed segment from path
110+
var remainingPath = pathSegment.replace(child.parser.build(match), '');
111+
// If fully matched
112+
if (!remainingPath.length && !child.children.length) {
113+
return {
114+
v: matched
115+
};
116+
}
117+
// If no children to match against but segment left
118+
if (!child.children.length) {
119+
return {
120+
v: false
121+
};
122+
}
123+
// Else: remaining path and children
124+
return {
125+
v: matchChildren(child, remainingPath, matched)
126+
};
127+
}
128+
};
129+
130+
// for (child of node.children) {
131+
for (var i in node.children) {
132+
var _ret = _loop(i);
133+
134+
if (typeof _ret === 'object') return _ret.v;
135+
}
136+
return false;
137+
};
138+
139+
return matchChildren(this, path, { name: '', params: {} });
140+
}
99141
}, {
100142
key: 'getPath',
101143
value: function getPath(routeName) {
102-
var segments = this.findRouteByName(routeName);
144+
var segments = this._findRouteByName(routeName);
103145

104146
return segments.map(function (segment) {
105147
return segment.path;
@@ -110,7 +152,7 @@ var RouteNode = (function () {
110152
value: function buildPath(routeName) {
111153
var params = arguments[1] === undefined ? {} : arguments[1];
112154

113-
var segments = this.findRouteByName(routeName);
155+
var segments = this._findRouteByName(routeName);
114156

115157
return segments.map(function (segment) {
116158
return segment.parser.build(params);

lib/RouteNode.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ export default class RouteNode {
44
constructor(name = '', path = '', childRoutes = []) {
55
this.name = name
66
this.path = path
7-
if (path) {
8-
this.parser = new Path(path)
9-
}
7+
this.parser = path ? new Path(path) : {}
108
this.children = []
119

1210
this.add(childRoutes)
@@ -39,11 +37,7 @@ export default class RouteNode {
3937
this.children.push(route)
4038
}
4139

42-
findRouteByPath(path) {
43-
44-
}
45-
46-
findRouteByName(routeName) {
40+
_findRouteByName(routeName) {
4741
let findSegmentByName = (name, routes) => {
4842
let filteredRoutes = routes.filter(r => r.name === name)
4943
return filteredRoutes.length ? filteredRoutes[0] : undefined
@@ -65,14 +59,47 @@ export default class RouteNode {
6559
return matched ? segments : []
6660
}
6761

62+
matchRoute(path) {
63+
let segments = []
64+
65+
let matchChildren = (node, pathSegment, matched) => {
66+
// for (child of node.children) {
67+
for (let i in node.children) {
68+
let child = node.children[i]
69+
// Partially match path
70+
let match = child.parser.partialMatch(pathSegment)
71+
if (match) {
72+
// Append name and extend params
73+
matched.name += (matched.name ? '.' : '') + child.name
74+
Object.keys(match).forEach(p => matched.params[p] = match[p])
75+
// Remove consumed segment from path
76+
let remainingPath = pathSegment.replace(child.parser.build(match), '')
77+
// If fully matched
78+
if (!remainingPath.length && !child.children.length) {
79+
return matched
80+
}
81+
// If no children to match against but unmatched path left
82+
if (!child.children.length) {
83+
return false
84+
}
85+
// Else: remaining path and children
86+
return matchChildren(child, remainingPath, matched);
87+
}
88+
}
89+
return false;
90+
}
91+
92+
return matchChildren(this, path, {name: '', params: {}})
93+
}
94+
6895
getPath(routeName) {
69-
let segments = this.findRouteByName(routeName)
96+
let segments = this._findRouteByName(routeName)
7097

7198
return segments.map(segment => segment.path).join('')
7299
}
73100

74101
buildPath(routeName, params = {}) {
75-
let segments = this.findRouteByName(routeName)
102+
let segments = this._findRouteByName(routeName)
76103

77104
return segments.map(segment => segment.parser.build(params)).join('')
78105
}

test/main.js

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,46 @@ describe('RouteNode', function () {
5858
node.children.length.should.equal(2);
5959
});
6060

61-
it('should find route path by name', function () {
62-
var usersNode = new RouteNode('users', '/users', [
63-
new RouteNode('list', '/list'),
64-
new RouteNode('view', '/view/:id')
65-
])
66-
67-
var node = new RouteNode('', '', [
68-
new RouteNode('home', '/home'),
69-
usersNode
70-
]);
61+
it('should find a nested route by name', function () {
62+
var node = getRoutes();
7163

7264
node.getPath('home').should.equal('/home');
7365
node.getPath('users').should.equal('/users');
7466
node.getPath('users.list').should.equal('/users/list');
7567
node.getPath('users.view').should.equal('/users/view/:id');
68+
});
69+
70+
it('should build the path of a nested route', function () {
71+
var node = getRoutes();
72+
// Building paths
73+
node.buildPath('home').should.equal('/home');
74+
node.buildPath('users').should.equal('/users');
75+
node.buildPath('users.list').should.equal('/users/list');
7676
node.buildPath('users.view', {id: 1}).should.equal('/users/view/1');
77+
// Missing parameters
78+
(function () {
79+
node.buildPath('users.view');
80+
}).should.throw();
81+
});
82+
83+
it('should find a nested route by path', function () {
84+
var node = getRoutes();
85+
// Building paths
86+
node.matchRoute('/users/view/1').should.eql({name: 'users.view', params: {id: '1'}});
87+
node.matchRoute('/users/profile/1').should.be.false;
88+
node.matchRoute('/users/view/profile/1').should.be.false;
7789
});
7890
});
91+
92+
93+
function getRoutes() {
94+
var usersNode = new RouteNode('users', '/users', [
95+
new RouteNode('list', '/list'),
96+
new RouteNode('view', '/view/:id')
97+
])
98+
99+
return new RouteNode('', '', [
100+
new RouteNode('home', '/home'),
101+
usersNode
102+
]);
103+
}

0 commit comments

Comments
 (0)