1 |
|
2 |
|
3 | import pathToRegexp from 'path-to-regexp';
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | var cache = new Map();
|
15 |
|
16 | function decodeParam(val) {
|
17 | try {
|
18 | return decodeURIComponent(val);
|
19 | } catch (err) {
|
20 | return val;
|
21 | }
|
22 | }
|
23 |
|
24 | function matchPath(routePath, urlPath, end, parentParams) {
|
25 | var key = routePath + '|' + end;
|
26 | var regexp = cache.get(key);
|
27 |
|
28 | if (!regexp) {
|
29 | var keys = [];
|
30 | regexp = { pattern: pathToRegexp(routePath, keys, { end: end }), keys: keys };
|
31 | cache.set(key, regexp);
|
32 | }
|
33 |
|
34 | var m = regexp.pattern.exec(urlPath);
|
35 | if (!m) {
|
36 | return null;
|
37 | }
|
38 |
|
39 | var path = m[0];
|
40 | var params = Object.create(null);
|
41 |
|
42 | if (parentParams) {
|
43 | Object.assign(params, parentParams);
|
44 | }
|
45 |
|
46 | for (var i = 1; i < m.length; i += 1) {
|
47 | params[regexp.keys[i - 1].name] = m[i] && decodeParam(m[i]);
|
48 | }
|
49 |
|
50 | return { path: path === '' ? '/' : path, keys: regexp.keys.slice(), params: params };
|
51 | }
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | function matchRoute(route, baseUrl, path, parentParams) {
|
63 | var match = void 0;
|
64 | var childMatches = void 0;
|
65 | var childIndex = 0;
|
66 |
|
67 | return {
|
68 | next: function next() {
|
69 | if (!match) {
|
70 | match = matchPath(route.path, path, !route.children, parentParams);
|
71 |
|
72 | if (match) {
|
73 | return {
|
74 | done: false,
|
75 | value: {
|
76 | route: route,
|
77 | baseUrl: baseUrl,
|
78 | path: match.path,
|
79 | keys: match.keys,
|
80 | params: match.params
|
81 | }
|
82 | };
|
83 | }
|
84 | }
|
85 |
|
86 | if (match && route.children) {
|
87 | while (childIndex < route.children.length) {
|
88 | if (!childMatches) {
|
89 | var newPath = path.substr(match.path.length);
|
90 | var childRoute = route.children[childIndex];
|
91 | childRoute.parent = route;
|
92 |
|
93 | childMatches = matchRoute(childRoute, baseUrl + (match.path === '/' ? '' : match.path), newPath.charAt(0) === '/' ? newPath : '/' + newPath, match.params);
|
94 | }
|
95 |
|
96 | var childMatch = childMatches.next();
|
97 | if (!childMatch.done) {
|
98 | return {
|
99 | done: false,
|
100 | value: childMatch.value
|
101 | };
|
102 | }
|
103 |
|
104 | childMatches = null;
|
105 | childIndex += 1;
|
106 | }
|
107 | }
|
108 |
|
109 | return { done: true };
|
110 | }
|
111 | };
|
112 | }
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | function resolveRoute(context, params) {
|
124 | if (typeof context.route.action === 'function') {
|
125 | return context.route.action(context, params);
|
126 | }
|
127 |
|
128 | return null;
|
129 | }
|
130 |
|
131 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
132 |
|
133 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | function isChildRoute(parentRoute, childRoute) {
|
145 | var route = childRoute;
|
146 | while (route) {
|
147 | route = route.parent;
|
148 | if (route === parentRoute) {
|
149 | return true;
|
150 | }
|
151 | }
|
152 | return false;
|
153 | }
|
154 |
|
155 | var Router = function () {
|
156 | function Router(routes) {
|
157 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
158 |
|
159 | _classCallCheck(this, Router);
|
160 |
|
161 | if (Object(routes) !== routes) {
|
162 | throw new TypeError('Invalid routes');
|
163 | }
|
164 |
|
165 | this.baseUrl = options.baseUrl || '';
|
166 | this.resolveRoute = options.resolveRoute || resolveRoute;
|
167 | this.context = Object.assign({ router: this }, options.context);
|
168 | this.root = Array.isArray(routes) ? { path: '/', children: routes, parent: null } : routes;
|
169 | this.root.parent = null;
|
170 | }
|
171 |
|
172 | _createClass(Router, [{
|
173 | key: 'resolve',
|
174 | value: function resolve(pathOrContext) {
|
175 | var context = Object.assign({}, this.context, typeof pathOrContext === 'string' ? { path: pathOrContext } : pathOrContext);
|
176 | var match = matchRoute(this.root, this.baseUrl, context.path.substr(this.baseUrl.length));
|
177 | var resolve = this.resolveRoute;
|
178 | var matches = null;
|
179 | var nextMatches = null;
|
180 |
|
181 | function next(resume) {
|
182 | var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : matches.value.route;
|
183 |
|
184 | matches = nextMatches || match.next();
|
185 | nextMatches = null;
|
186 |
|
187 | if (!resume) {
|
188 | if (matches.done || !isChildRoute(parent, matches.value.route)) {
|
189 | nextMatches = matches;
|
190 | return Promise.resolve(null);
|
191 | }
|
192 | }
|
193 |
|
194 | if (matches.done) {
|
195 | return Promise.reject(Object.assign(new Error('Page not found'), { context: context, status: 404, statusCode: 404 }));
|
196 | }
|
197 |
|
198 | return Promise.resolve(resolve(Object.assign({}, context, matches.value), matches.value.params)).then(function (result) {
|
199 | if (result !== null && result !== undefined) {
|
200 | return result;
|
201 | }
|
202 |
|
203 | return next(resume, parent);
|
204 | });
|
205 | }
|
206 |
|
207 | context.url = context.path;
|
208 | context.next = next;
|
209 |
|
210 | return next(true, this.root);
|
211 | }
|
212 | }]);
|
213 |
|
214 | return Router;
|
215 | }();
|
216 |
|
217 | Router.pathToRegexp = pathToRegexp;
|
218 | Router.matchPath = matchPath;
|
219 | Router.matchRoute = matchRoute;
|
220 | Router.resolveRoute = resolveRoute;
|
221 |
|
222 | export default Router;
|
223 |
|