provide-page
Version:
Provides automatic server-side rendering and actions (regardless of whether or not client has JavaScript enabled) to React components. Use in conjunction with `provide-router`.
302 lines (231 loc) • 9.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
exports.default = createMiddleware;
var _reactReduxProvide = require('react-redux-provide');
var _defaultRenderDocumentToString = require('./defaultRenderDocumentToString');
var _defaultRenderDocumentToString2 = _interopRequireDefault(_defaultRenderDocumentToString);
var _extractStates = require('./extractStates');
var _getProviders = require('./getProviders');
var _getProviders2 = _interopRequireDefault(_getProviders);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var inDevelopment = process.env.NODE_ENV === 'development';
function createMiddleware(_ref) {
var defaultProps = _ref.defaultProps,
renderToString = _ref.renderToString,
_ref$renderDocumentTo = _ref.renderDocumentToString,
renderDocumentToString = _ref$renderDocumentTo === undefined ? _defaultRenderDocumentToString2.default : _ref$renderDocumentTo,
getStates = _ref.getStates,
initStates = _ref.initStates,
_ref$maxRenders = _ref.maxRenders,
maxRenders = _ref$maxRenders === undefined ? 20 : _ref$maxRenders,
_ref$maxResponseTime = _ref.maxResponseTime,
maxResponseTime = _ref$maxResponseTime === undefined ? 2000 : _ref$maxResponseTime;
return function (request, response, next) {
var bodyType = _typeof(request.body);
if (bodyType === 'undefined') {
console.warn('Server needs to use `body-parser` or something like it!');
}
try {
var renderState = function renderState() {
if (responded) {
return;
}
wait();
if (inDevelopment) {
console.log('--- rendering (' + renderCount + ') ' + request.url);
}
html = renderToString(_extends({}, defaultProps, {
providers: providers,
providerInstances: providerInstances,
activeQueries: activeQueries,
queryResults: queryResults,
partialStates: partialStates
}));
clear(false);
};
var wait = function wait() {
waitCount++;
};
var clear = function clear(doRerender) {
if (doRerender) {
rerender = true;
}
if (--waitCount === 0) {
respondOrRerender();
}
};
var respondOrRerender = function respondOrRerender() {
if (!rerender && !handledRequest) {
handledRequest = true;
handleRequest();
}
renderCount++;
if (rerender && renderCount < maxRenders) {
rerender = false;
renderState();
} else if (waitCount === 0) {
respond();
}
};
var handleRequest = function handleRequest() {
var page = providerInstances.page;
var requestState = {
requestMethod: request.method,
requestBody: request.body,
requestHeaders: request.headers
};
if (serverSide && shouldSubmitRequest) {
preActionStates = (0, _extractStates.extractServerStates)(providerInstances, getStates);
}
if (!page) {
providers.page.state = _extends({}, providers.page.state, requestState);
} else if (shouldSubmitRequest) {
page.actionCreators.submitRequest(requestState);
}
};
var respond = function respond() {
if (responseTimeout) {
clearTimeout(responseTimeout);
responseTimeout = null;
}
if (responded) {
return;
}
var states = (0, _extractStates.extractServerStates)(providerInstances, getStates, preActionStates);
var clientStates = (0, _extractStates.extractClientStates)(providerInstances, _extends({}, partialStates, states));
var _ref3 = states.page || {},
headers = _ref3.headers,
statusCode = _ref3.statusCode;
var documentString = null;
if (headers && !response.headersSent) {
response.set(headers);
}
if (acceptJson) {
if (statusCode && !response.headersSent) {
response.status(statusCode).send(clientStates);
} else {
response.send(clientStates);
}
} else if (!redirect() && html) {
documentString = renderDocumentToString(html, states, clientStates);
if (statusCode && !response.headersSent) {
response.status(statusCode).send(documentString);
} else {
response.send(documentString);
}
} else if (statusCode && !response.headersSent) {
response.sendStatus(statusCode);
}
responded = true;
if (inDevelopment) {
console.log('--- sent response for ' + request.url);
}
};
var redirect = function redirect() {
var router = providerInstances.router;
var _router$store$getStat = router.store.getState(),
history = _router$store$getStat.history,
routing = _router$store$getStat.routing;
var location = routing && routing.locationBeforeTransitions || routing && routing.location || router.store.getState().location || history.location;
var url = location.pathname + location.search;
if (url !== request.originalUrl) {
if (!response.headersSent) {
response.redirect(303, location.pathname);
}
return true;
}
return false;
};
var send408Status = function send408Status() {
responseTimeout = null;
if (!response.headersSent) {
response.sendStatus(408);
}
responded = true;
};
var providers = (0, _getProviders2.default)(defaultProps.providers, request);
var providerInstances = _extends({}, defaultProps.providerInstances);
var activeQueries = _extends({}, defaultProps.activeQueries);
var queryResults = _extends({}, defaultProps.queryResults);
var partialStates = _extends({}, defaultProps.partialStates);
providers.page.state = _extends({}, providers.page.state, {
requestMethod: request.method,
requestHeaders: request.headers
});
if (initStates) {
initStates(providers, providerInstances);
}
var html = null;
var waitCount = 0;
var renderCount = 0;
var rerender = false;
var handledRequest = false;
var shouldSubmitRequest = false;
var preActionStates = void 0;
if (Array.isArray(request.body)) {
shouldSubmitRequest = true;
} else if (bodyType === 'object') {
shouldSubmitRequest = Object.keys(request.body).length > 0;
} else if (bodyType === 'string') {
shouldSubmitRequest = request.body.length > 0;
} else {
shouldSubmitRequest = Boolean(request.body);
}
var acceptJson = providers.page.state.acceptJson;
var serverSide = acceptJson && request.headers['x-server-side'];
var actions = null;
if (serverSide && shouldSubmitRequest) {
Object.keys(providers).forEach(function (key) {
(0, _reactReduxProvide.pushReplication)(_defineProperty({}, key, providers[key]), {
replicator: {
postReduction: function postReduction(_ref2) {
var store = _ref2.store,
state = _ref2.state,
nextState = _ref2.nextState,
action = _ref2.action;
var providerKey = store.key;
if (actions) {
if (preActionStates && !preActionStates[providerKey]) {
preActionStates[providerKey] = state;
}
if (!action._noEffect) {
actions.push({ providerKey: providerKey, action: action });
}
} else if (handledRequest) {
actions = [];
}
}
}
});
});
}
var responded = false;
var responseTimeout = maxResponseTime ? setTimeout(send408Status, maxResponseTime) : null;
(0, _reactReduxProvide.pushWait)(providers, wait);
(0, _reactReduxProvide.pushClear)(providers, clear);
(0, _reactReduxProvide.unshiftMiddleware)(providers, function (_ref4) {
var dispatch = _ref4.dispatch,
getState = _ref4.getState;
return function (next) {
return function (action) {
if (typeof action !== 'function' && !action._noEffect) {
rerender = true;
}
return next(action);
};
};
});
renderState();
} catch (error) {
console.error(error.stack);
if (!response.headersSent) {
response.sendStatus(500);
}
}
};
}