UNPKG

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
'use strict'; 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); } } }; }