react-ark-tools
Version:
Boilerplate and tooling for JavaScript application development with React
91 lines (76 loc) • 2.73 kB
JavaScript
/**
* React App SDK (https://github.com/kriasoft/react-app)
*
* 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) {
// Check if the route has any data requirements, for example:
//
// {
// path: '/tasks/:id',
// data: { task: 'GET /api/tasks/$id' },
// component: './routes/TaskDetails'
// }
//
if (route.data) {
// Load route 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(([Component, ...data]) => {
const props = keys.reduce((result, key, i) => ({ ...result, [key]: data[i] }), {});
return <Component route={route} error={context.error} {...props} />;
});
}
return route.load().then(Component => <Component route={route} error={context.error} />);
}
}
const error = new Error('Page not found');
error.status = 404;
return Promise.reject(error);
}
export default { resolve };