-
Notifications
You must be signed in to change notification settings - Fork 82
Expand file tree
/
Copy pathconnect-middleware.js
More file actions
86 lines (78 loc) · 2.59 KB
/
connect-middleware.js
File metadata and controls
86 lines (78 loc) · 2.59 KB
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
"use strict";
var parseUrl = require('url').parse;
var React = require('react');
var ReactAsync = require('react-async');
var merge = require('react/lib/merge');
var invariant = require('react/lib/invariant');
var getContentMeta = require('../getContentMeta');
/**
* A connect middleware for server-side rendering of React Router components.
* Typically, these are instances of Router, but the only requirements are that
* the component accepts "path" on "onDispatch" props.
*/
function reactRouter(Router, options) {
invariant(
React.isValidClass(Router),
'Argument must be a React component class but got: %s', Router
);
var meta = getContentMeta(options && options.doctype,
options && options.contentType);
var props = options && options.props || {};
return function(req, res, next) {
var pathname = parseUrl(req.url).pathname;
if (pathname == null) {
next(new Error('Invalid URL: ' + req.url));
return;
}
// This callback is meant to be called multiple times with whatever
// information is available. It will aggregate the information and send a
// response or emit an error as soon as is possible.
var memo = {};
var callback = function(err, statusCode, markup) {
if (memo.complete) {
return; // Don't take action more than once.
}
if (err != null) {
// If we errored, we're done.
memo.complete = true;
next(err);
return;
}
// Remember the status code and markup (since we may not get them at the
// same time).
if (statusCode != null) {
memo.statusCode = statusCode;
}
if (markup != null) {
memo.markup = markup;
}
// Wait for the render to complete and onDispatch to be invoked.
if (memo.markup == null || memo.statusCode == null) {
return;
}
memo.complete = true;
res.statusCode = memo.statusCode;
res.setHeader('Content-Type', meta.contentType);
res.end('' + meta.doctype + memo.markup);
};
try {
var app = Router(merge(props, {
path: pathname,
onDispatch: function(path, navigation) {
var statusCode = (navigation && navigation.match &&
navigation.match.isNotFound ? 404 : 200);
callback(null, statusCode);
}
}));
ReactAsync.renderComponentToStringWithAsyncState(
app,
function(err, markup) {
callback(err, null, markup);
}
);
} catch (err) {
next(err);
}
};
}
module.exports = reactRouter;