-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathindex.js
104 lines (92 loc) · 2.85 KB
/
index.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/** @format */
/**
* External dependencies
*/
import { isEmpty } from 'lodash';
import { stringify } from 'qs';
/**
* Internal dependencies
*/
import { serverRender } from 'render';
import { setSection as setSectionMiddlewareFactory } from '../../client/controller';
import { setRoute as setRouteAction } from 'state/ui/actions';
import { setShouldServerSideRender } from '../render';
export function serverRouter( expressApp, setUpRoute, section ) {
return function( route, ...middlewares ) {
if ( middlewares.length === 0 && typeof route === 'function' && route.length === 3 ) {
// No route def -- the route arg is really an error-handling middleware
// `page( someMw )` would be a shorthand for `page( '*', someMw )`, even tho the same isn't true
// for Express, we want things to be isomorphic, so that should be handled by the `else` branch.
// OTOH, if `someMw` takes 3 args `( err, context, next )` instead of the usual 2 `( context, next )`,
// it's an error-handling middleware and needs to be handled by this branch.
expressApp.use(
( err, req, res, next ) => {
route( err, req.context, next );
},
( err, req, res, next ) => {
req.error = err;
res.status( err.status || 404 );
serverRender( req, res, next );
}
);
} else {
expressApp.get(
route,
setUpRoute,
combineMiddlewares(
setSectionMiddlewareFactory( section ),
setRouteMiddleware,
setShouldServerSideRender,
...middlewares
),
serverRender
);
}
};
}
function setRouteMiddleware( context, next ) {
context.store.dispatch( setRouteAction( context.pathname, context.query ) );
next();
}
function combineMiddlewares( ...middlewares ) {
return function( req, res, next ) {
req.context = getEnhancedContext( req, res );
applyMiddlewares( req.context, next, ...middlewares, () => {
next();
} );
};
}
// TODO: Maybe merge into getDefaultContext().
function getEnhancedContext( req, res ) {
return Object.assign( {}, req.context, {
isServerSide: true,
originalUrl: req.originalUrl,
path: req.url,
pathname: req.path,
params: req.params,
query: req.query,
redirect: res.redirect.bind( res ),
res,
} );
}
function applyMiddlewares( context, expressNext, ...middlewares ) {
const liftedMiddlewares = middlewares.map( middleware => next =>
middleware( context, err => {
if ( err ) {
expressNext( err ); // Call express' next( err ) for error handling (and bail early from this route)
} else {
next();
}
} )
); // prettier-ignore
compose( ...liftedMiddlewares )();
}
function compose( ...functions ) {
return functions.reduceRight( ( composed, f ) => () => f( composed ), () => {} );
}
export function getNormalizedPath( pathname, query ) {
if ( isEmpty( query ) ) {
return pathname;
}
return pathname + '?' + stringify( query, { sort: ( a, b ) => a.localeCompare( b ) } );
}