UNPKG

3.25 kBJavaScriptView Raw
1import { Thunker, PromiseWell } from '@r/middleware';
2import Koa from 'koa';
3import KoaRouter from 'koa-router';
4import KoaBodyParser from 'koa-bodyparser';
5import { combineReducers, createStore, applyMiddleware } from 'redux';
6import { values } from 'lodash/object';
7import { isEqual, isEmpty } from 'lodash/lang';
8
9import navigationMiddleware from './navigationMiddleware';
10import platform from './reducer';
11import actions from './actions';
12import { createQuery } from './pageUtils';
13import { METHODS } from './router';
14
15export default config => {
16 const {
17 port=8888,
18 preRouteServerMiddleware=[],
19 reduxMiddleware=[],
20 reducers={},
21 routes=[],
22 getServerRouter=() => {},
23 template=() => {},
24 dispatchBeforeNavigation=async () => {},
25 onHandlerComplete=() => {},
26 } = config;
27
28 const server = new Koa();
29 const bodyparser = KoaBodyParser();
30 const router = new KoaRouter();
31
32 const handleRoute = async (ctx, next) => {
33 const nav = navigationMiddleware.create(routes, true, onHandlerComplete);
34 const well = PromiseWell.create();
35 const thunk = Thunker.create();
36
37 const r = combineReducers({ ...reducers, platform });
38
39 const store = createStore(r, {}, applyMiddleware(
40 ...reduxMiddleware,
41 nav,
42 thunk,
43 well.middleware,
44 ));
45
46 await store.dispatch(async (dispatch, getState, utils) => {
47 await dispatchBeforeNavigation(ctx, dispatch, getState, utils);
48 });
49
50 store.dispatch(actions.navigateToUrl(
51 ctx.request.method.toLowerCase(),
52 ctx.path,
53 {
54 queryParams: ctx.request.query,
55 bodyParams: ctx.request.body,
56 referrer: ctx.headers.referer,
57 }
58 ));
59
60 await well.onComplete();
61 const state = store.getState();
62
63 // check for redirects
64 const currentUrl = state.platform.currentPage.url;
65 const currentQuery = state.platform.currentPage.queryParams;
66
67 if (!isEqual(currentUrl, ctx.path) || !isEqual(currentQuery, ctx.request.query)) {
68 if (currentUrl) {
69 let newUrl = currentUrl;
70 if (!isEmpty(currentQuery)) { newUrl += createQuery(currentQuery); }
71 ctx.redirect(newUrl);
72 } else {
73 ctx.redirect('/');
74 }
75 } else {
76 // HEAD request must not have a response body but otherwise
77 // are equivalent to GETs.
78 // HEAD requests are converted into GETS by navigation middleware
79 // if there isn't an explicit HEAD method on our handler.
80 if (ctx.request.method.toLowerCase() !== METHODS.HEAD) {
81 ctx.body = template(state, store);
82 }
83
84 ctx.status = state.platform.currentPage.status;
85 }
86 };
87
88 // set up server routes before setting up shared routes
89 getServerRouter(router);
90
91 for (let route of routes) {
92 let [path, handler] = route;
93 for (let method of values(METHODS)) {
94 if (handler.prototype[method]) {
95 router[method](path, handleRoute);
96 }
97 }
98 }
99
100 // hook up all the middleware to the koa instance
101 preRouteServerMiddleware.forEach(m => server.use(m));
102 server.use(bodyparser);
103 server.use(router.routes());
104 server.use(router.allowedMethods());
105
106 return () => {
107 server.listen(port, () => {
108 console.log(`App launching on port ${port}`);
109 });
110 };
111};