UNPKG

5.69 kBJavaScriptView Raw
1/*! Universal Router | MIT License | https://www.kriasoft.com/universal-router/ */
2
3'use strict';
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7var pathToRegexp = _interopDefault(require('path-to-regexp'));
8
9/**
10 * Universal Router (https://www.kriasoft.com/universal-router/)
11 *
12 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
13 *
14 * This source code is licensed under the Apache 2.0 license found in the
15 * LICENSE.txt file in the root directory of this source tree.
16 */
17
18const cache = new Map();
19
20function decodeParam(val) {
21 try {
22 return decodeURIComponent(val);
23 } catch (err) {
24 return val;
25 }
26}
27
28function matchPath(routePath, urlPath, end, parentParams) {
29 const key = `${routePath}|${end}`;
30 let regexp = cache.get(key);
31
32 if (!regexp) {
33 const keys = [];
34 regexp = { pattern: pathToRegexp(routePath, keys, { end }), keys };
35 cache.set(key, regexp);
36 }
37
38 const m = regexp.pattern.exec(urlPath);
39 if (!m) {
40 return null;
41 }
42
43 const path = m[0];
44 const params = Object.create(null);
45
46 if (parentParams) {
47 Object.assign(params, parentParams);
48 }
49
50 for (let i = 1; i < m.length; i += 1) {
51 params[regexp.keys[i - 1].name] = m[i] && decodeParam(m[i]);
52 }
53
54 return { path: path === '' ? '/' : path, keys: regexp.keys.slice(), params };
55}
56
57/**
58 * Universal Router (https://www.kriasoft.com/universal-router/)
59 *
60 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
61 *
62 * This source code is licensed under the Apache 2.0 license found in the
63 * LICENSE.txt file in the root directory of this source tree.
64 */
65
66function matchRoute(route, baseUrl, path, parentParams) {
67 let match;
68 let childMatches;
69 let childIndex = 0;
70
71 return {
72 next() {
73 if (!match) {
74 match = matchPath(route.path, path, !route.children, parentParams);
75
76 if (match) {
77 return {
78 done: false,
79 value: {
80 route,
81 baseUrl,
82 path: match.path,
83 keys: match.keys,
84 params: match.params
85 }
86 };
87 }
88 }
89
90 if (match && route.children) {
91 while (childIndex < route.children.length) {
92 if (!childMatches) {
93 const newPath = path.substr(match.path.length);
94 const childRoute = route.children[childIndex];
95 childRoute.parent = route;
96
97 childMatches = matchRoute(childRoute, baseUrl + (match.path === '/' ? '' : match.path), newPath.charAt(0) === '/' ? newPath : `/${newPath}`, match.params);
98 }
99
100 const childMatch = childMatches.next();
101 if (!childMatch.done) {
102 return {
103 done: false,
104 value: childMatch.value
105 };
106 }
107
108 childMatches = null;
109 childIndex += 1;
110 }
111 }
112
113 return { done: true };
114 }
115 };
116}
117
118/**
119 * Universal Router (https://www.kriasoft.com/universal-router/)
120 *
121 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
122 *
123 * This source code is licensed under the Apache 2.0 license found in the
124 * LICENSE.txt file in the root directory of this source tree.
125 */
126
127function resolveRoute(context, params) {
128 if (typeof context.route.action === 'function') {
129 return context.route.action(context, params);
130 }
131
132 return null;
133}
134
135/**
136 * Universal Router (https://www.kriasoft.com/universal-router/)
137 *
138 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
139 *
140 * This source code is licensed under the Apache 2.0 license found in the
141 * LICENSE.txt file in the root directory of this source tree.
142 */
143
144function isChildRoute(parentRoute, childRoute) {
145 let route = childRoute;
146 while (route) {
147 route = route.parent;
148 if (route === parentRoute) {
149 return true;
150 }
151 }
152 return false;
153}
154
155class Router {
156 constructor(routes, options = {}) {
157 if (Object(routes) !== routes) {
158 throw new TypeError('Invalid routes');
159 }
160
161 this.baseUrl = options.baseUrl || '';
162 this.resolveRoute = options.resolveRoute || resolveRoute;
163 this.context = Object.assign({ router: this }, options.context);
164 this.root = Array.isArray(routes) ? { path: '/', children: routes, parent: null } : routes;
165 this.root.parent = null;
166 }
167
168 resolve(pathOrContext) {
169 const context = Object.assign({}, this.context, typeof pathOrContext === 'string' ? { path: pathOrContext } : pathOrContext);
170 const match = matchRoute(this.root, this.baseUrl, context.path.substr(this.baseUrl.length));
171 const resolve = this.resolveRoute;
172 let matches = null;
173 let nextMatches = null;
174
175 function next(resume, parent = matches.value.route) {
176 matches = nextMatches || match.next();
177 nextMatches = null;
178
179 if (!resume) {
180 if (matches.done || !isChildRoute(parent, matches.value.route)) {
181 nextMatches = matches;
182 return Promise.resolve(null);
183 }
184 }
185
186 if (matches.done) {
187 return Promise.reject(Object.assign(new Error('Page not found'), { context, status: 404, statusCode: 404 }));
188 }
189
190 return Promise.resolve(resolve(Object.assign({}, context, matches.value), matches.value.params)).then(result => {
191 if (result !== null && result !== undefined) {
192 return result;
193 }
194
195 return next(resume, parent);
196 });
197 }
198
199 context.url = context.path;
200 context.next = next;
201
202 return next(true, this.root);
203 }
204}
205
206Router.pathToRegexp = pathToRegexp;
207Router.matchPath = matchPath;
208Router.matchRoute = matchRoute;
209Router.resolveRoute = resolveRoute;
210
211module.exports = Router;
212//# sourceMappingURL=main.js.map