forked from kriasoft/react-firebase-starter
-
Notifications
You must be signed in to change notification settings - Fork 35
/
router.js
87 lines (72 loc) · 2.6 KB
/
router.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* React Static Boilerplate
* https://github.com/kriasoft/react-static-boilerplate
*
* Copyright © 2015-present Kriasoft, LLC. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE.txt file in the root directory of this source tree.
*/
import React from 'react';
function decodeParam(val) {
if (!(typeof val === 'string' || val.length === 0)) {
return val;
}
try {
return decodeURIComponent(val);
} catch (err) {
if (err instanceof URIError) {
err.message = `Failed to decode param '${val}'`;
err.status = 400;
}
throw err;
}
}
// Match the provided URL path pattern to an actual URI string. For example:
// matchURI({ path: '/posts/:id' }, '/dummy') => null
// matchURI({ path: '/posts/:id' }, '/posts/123') => { id: 123 }
function matchURI(route, path) {
const match = route.pattern.exec(path);
if (!match) {
return null;
}
const params = Object.create(null);
for (let i = 1; i < match.length; i++) {
params[route.keys[i - 1].name] = match[i] !== undefined ? decodeParam(match[i]) : undefined;
}
return params;
}
// Find the route matching the specified location (context), fetch the required data,
// instantiate and return a React component
function resolve(routes, context) {
for (const route of routes) {
const params = matchURI(route, context.error ? '/error' : context.pathname);
if (!params) {
continue;
}
// Check if the route has any data requirements, for example:
// { path: '/tasks/:id', data: { task: 'GET /api/tasks/$id' }, page: './pages/task' }
if (route.data) {
// Load page component and all required data in parallel
const keys = Object.keys(route.data);
return Promise.all([
route.load(),
...keys.map(key => {
const query = route.data[key];
const method = query.substring(0, query.indexOf(' ')); // GET
const url = query.substr(query.indexOf(' ') + 1); // /api/tasks/$id
// TODO: Replace query parameters with actual values coming from `params`
return fetch(url, { method }).then(resp => resp.json());
}),
]).then(([Page, ...data]) => {
const props = keys.reduce((result, key, i) => ({ ...result, [key]: data[i] }), {});
return <Page route={{ ...route, params }} error={context.error} {...props} />;
});
}
return route.load().then(Page => <Page route={{ ...route, params }} error={context.error} />);
}
const error = new Error('Page not found');
error.status = 404;
return Promise.reject(error);
}
export default { resolve };