UNPKG

5.54 kBJavaScriptView Raw
1/*! Universal Router | MIT License | https://www.kriasoft.com/universal-router/ */
2
3import pathToRegexp from 'path-to-regexp';
4
5/**
6 * Universal Router (https://www.kriasoft.com/universal-router/)
7 *
8 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
9 *
10 * This source code is licensed under the Apache 2.0 license found in the
11 * LICENSE.txt file in the root directory of this source tree.
12 */
13
14const cache = new Map();
15
16function decodeParam(val) {
17 try {
18 return decodeURIComponent(val);
19 } catch (err) {
20 return val;
21 }
22}
23
24function matchPath(routePath, urlPath, end, parentParams) {
25 const key = `${routePath}|${end}`;
26 let regexp = cache.get(key);
27
28 if (!regexp) {
29 const keys = [];
30 regexp = { pattern: pathToRegexp(routePath, keys, { end }), keys };
31 cache.set(key, regexp);
32 }
33
34 const m = regexp.pattern.exec(urlPath);
35 if (!m) {
36 return null;
37 }
38
39 const path = m[0];
40 const params = Object.create(null);
41
42 if (parentParams) {
43 Object.assign(params, parentParams);
44 }
45
46 for (let 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 };
51}
52
53/**
54 * Universal Router (https://www.kriasoft.com/universal-router/)
55 *
56 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
57 *
58 * This source code is licensed under the Apache 2.0 license found in the
59 * LICENSE.txt file in the root directory of this source tree.
60 */
61
62function matchRoute(route, baseUrl, path, parentParams) {
63 let match;
64 let childMatches;
65 let childIndex = 0;
66
67 return {
68 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,
77 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 const newPath = path.substr(match.path.length);
90 const 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 const 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 * Universal Router (https://www.kriasoft.com/universal-router/)
116 *
117 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
118 *
119 * This source code is licensed under the Apache 2.0 license found in the
120 * LICENSE.txt file in the root directory of this source tree.
121 */
122
123function 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/**
132 * Universal Router (https://www.kriasoft.com/universal-router/)
133 *
134 * Copyright © 2015-present Kriasoft, LLC. All rights reserved.
135 *
136 * This source code is licensed under the Apache 2.0 license found in the
137 * LICENSE.txt file in the root directory of this source tree.
138 */
139
140function isChildRoute(parentRoute, childRoute) {
141 let route = childRoute;
142 while (route) {
143 route = route.parent;
144 if (route === parentRoute) {
145 return true;
146 }
147 }
148 return false;
149}
150
151class Router {
152 constructor(routes, options = {}) {
153 if (Object(routes) !== routes) {
154 throw new TypeError('Invalid routes');
155 }
156
157 this.baseUrl = options.baseUrl || '';
158 this.resolveRoute = options.resolveRoute || resolveRoute;
159 this.context = Object.assign({ router: this }, options.context);
160 this.root = Array.isArray(routes) ? { path: '/', children: routes, parent: null } : routes;
161 this.root.parent = null;
162 }
163
164 resolve(pathOrContext) {
165 const context = Object.assign({}, this.context, typeof pathOrContext === 'string' ? { path: pathOrContext } : pathOrContext);
166 const match = matchRoute(this.root, this.baseUrl, context.path.substr(this.baseUrl.length));
167 const resolve = this.resolveRoute;
168 let matches = null;
169 let nextMatches = null;
170
171 function next(resume, parent = matches.value.route) {
172 matches = nextMatches || match.next();
173 nextMatches = null;
174
175 if (!resume) {
176 if (matches.done || !isChildRoute(parent, matches.value.route)) {
177 nextMatches = matches;
178 return Promise.resolve(null);
179 }
180 }
181
182 if (matches.done) {
183 return Promise.reject(Object.assign(new Error('Page not found'), { context, status: 404, statusCode: 404 }));
184 }
185
186 return Promise.resolve(resolve(Object.assign({}, context, matches.value), matches.value.params)).then(result => {
187 if (result !== null && result !== undefined) {
188 return result;
189 }
190
191 return next(resume, parent);
192 });
193 }
194
195 context.url = context.path;
196 context.next = next;
197
198 return next(true, this.root);
199 }
200}
201
202Router.pathToRegexp = pathToRegexp;
203Router.matchPath = matchPath;
204Router.matchRoute = matchRoute;
205Router.resolveRoute = resolveRoute;
206
207export default Router;
208//# sourceMappingURL=main.mjs.map