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`.

637 lines (510 loc) 20.3 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.NOOP = exports.DESTROY_SESSION = exports.UPDATE_SESSION = exports.PENDING_FORM = exports.PENDING_PAGE = exports.SUBMITTED_FORM = exports.SUBMIT_FORM = exports.SUBMIT_REQUEST = exports.GOT_PAGE_STATES = exports.GET_PAGE_STATES = exports.SYNC_WITH_ROUTER = exports.SET_REQUEST_ERROR = exports.SET_JS_FILES = exports.SET_CSS_FILES = exports.SET_ICON_FILE = exports.SET_META_ROBOTS = exports.SET_META_DESCRIPTION = exports.SET_DOCUMENT_TITLE = exports.SET_STATUS_CODE = exports.SET_HEADERS = exports.defaultRenderDocumentToString = exports.createMiddleware = exports.Form = exports.Link = undefined; 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 _reduxThunk = require('redux-thunk'); var _reduxThunk2 = _interopRequireDefault(_reduxThunk); var _exenv = require('exenv'); var _Link2 = require('./components/Link'); var _Link3 = _interopRequireDefault(_Link2); var _Form2 = require('./components/Form'); var _Form3 = _interopRequireDefault(_Form2); var _createMiddleware2 = require('./createMiddleware'); var _createMiddleware3 = _interopRequireDefault(_createMiddleware2); var _defaultRenderDocumentToString2 = require('./defaultRenderDocumentToString'); var _defaultRenderDocumentToString3 = _interopRequireDefault(_defaultRenderDocumentToString2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.Link = _Link3.default; exports.Form = _Form3.default; exports.createMiddleware = _createMiddleware3.default; exports.defaultRenderDocumentToString = _defaultRenderDocumentToString3.default; var SET_HEADERS = exports.SET_HEADERS = 'SET_HEADERS'; var SET_STATUS_CODE = exports.SET_STATUS_CODE = 'SET_STATUS_CODE'; var SET_DOCUMENT_TITLE = exports.SET_DOCUMENT_TITLE = 'SET_DOCUMENT_TITLE'; var SET_META_DESCRIPTION = exports.SET_META_DESCRIPTION = 'SET_META_DESCRIPTION'; var SET_META_ROBOTS = exports.SET_META_ROBOTS = 'SET_META_ROBOTS'; var SET_ICON_FILE = exports.SET_ICON_FILE = 'SET_ICON_FILE'; var SET_CSS_FILES = exports.SET_CSS_FILES = 'SET_CSS_FILES'; var SET_JS_FILES = exports.SET_JS_FILES = 'SET_JS_FILES'; var SET_REQUEST_ERROR = exports.SET_REQUEST_ERROR = 'SET_REQUEST_ERROR'; var SYNC_WITH_ROUTER = exports.SYNC_WITH_ROUTER = 'SYNC_WITH_ROUTER'; var GET_PAGE_STATES = exports.GET_PAGE_STATES = 'GET_PAGE_STATES'; var GOT_PAGE_STATES = exports.GOT_PAGE_STATES = 'GOT_PAGE_STATES'; var SUBMIT_REQUEST = exports.SUBMIT_REQUEST = 'SUBMIT_REQUEST'; var SUBMIT_FORM = exports.SUBMIT_FORM = 'SUBMIT_FORM'; var SUBMITTED_FORM = exports.SUBMITTED_FORM = 'SUBMITTED_FORM'; var PENDING_PAGE = exports.PENDING_PAGE = 'PENDING_PAGE'; var PENDING_FORM = exports.PENDING_FORM = 'PENDING_FORM'; var UPDATE_SESSION = exports.UPDATE_SESSION = 'UPDATE_SESSION'; var DESTROY_SESSION = exports.DESTROY_SESSION = 'DESTROY_SESSION'; var NOOP = exports.NOOP = 'NOOP'; function getUrl(location) { return location ? location.pathname + location.search : null; } function clearPending(dispatch, getState) { var state = getState(); if (state.pendingForms.length) { var _state$pendingForms$s = state.pendingForms.shift(), formData = _state$pendingForms$s.formData, onSubmit = _state$pendingForms$s.onSubmit; dispatch(actions.submitForm(formData, onSubmit)); } else if (state.pendingPage) { dispatch(actions.getPageStates(typeof window === 'undefined' ? state.routerLocation : window.location)); } } var _noEffect = true; var batchAction = function batchAction(action, immediate) { if (immediate) { return action; } return function (dispatch, getState) { var type = action.type; var _getState = getState(), pageBatchedActions = _getState.pageBatchedActions; var timeout = setTimeout(function () { while (pageBatchedActions[type].count) { pageBatchedActions[type].count--; dispatch({ type: NOOP, _noEffect: _noEffect }); } delete pageBatchedActions[type]; dispatch(action); }); if (pageBatchedActions[type]) { clearTimeout(pageBatchedActions[type].timeout); pageBatchedActions[type].timeout = timeout; pageBatchedActions[type].count++; } else { pageBatchedActions[type] = { timeout: timeout, count: 0 }; } }; }; var actions = { setHeaders: function setHeaders(headers, immediate) { return batchAction({ type: SET_HEADERS, headers: headers, _noEffect: _noEffect }, immediate); }, setStatusCode: function setStatusCode(statusCode, immediate) { return batchAction({ type: SET_STATUS_CODE, statusCode: statusCode, _noEffect: _noEffect }, immediate); }, setDocumentTitle: function setDocumentTitle() { var documentTitle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var immediate = arguments[1]; return batchAction({ type: SET_DOCUMENT_TITLE, documentTitle: documentTitle, _noEffect: _noEffect }, immediate); }, setMetaDescription: function setMetaDescription() { var metaDescription = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var immediate = arguments[1]; return batchAction({ type: SET_META_DESCRIPTION, metaDescription: metaDescription, _noEffect: _noEffect }, immediate); }, setMetaRobots: function setMetaRobots() { var metaRobots = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var immediate = arguments[1]; return batchAction({ type: SET_META_ROBOTS, metaRobots: metaRobots, _noEffect: _noEffect }, immediate); }, setIconFile: function setIconFile() { var iconFile = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var immediate = arguments[1]; return batchAction({ type: SET_ICON_FILE, iconFile: iconFile, _noEffect: _noEffect }, immediate); }, setCssFiles: function setCssFiles() { var cssFiles = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var immediate = arguments[1]; return batchAction({ type: SET_CSS_FILES, cssFiles: cssFiles, _noEffect: _noEffect }, immediate); }, setJsFiles: function setJsFiles() { var jsFiles = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var immediate = arguments[1]; return batchAction({ type: SET_JS_FILES, jsFiles: jsFiles, _noEffect: _noEffect }, immediate); }, setRequestError: function setRequestError(requestError, immediate) { return batchAction({ type: SET_REQUEST_ERROR, requestError: requestError }, immediate); }, syncWithRouter: function syncWithRouter(routerHistory, routerLocation) { return function (dispatch, getState) { dispatch({ type: SYNC_WITH_ROUTER, routerHistory: routerHistory, routerLocation: routerLocation, _noEffect: _noEffect }); routerHistory.listen(function (nextRouterLocation) { dispatch(actions.getPageStates(nextRouterLocation)); }); }; }, getPageStates: function getPageStates(routerLocation) { return function (dispatch, getState, _ref) { var setStates = _ref.setStates; var _getState2 = getState(), ssrDisabled = _getState2.ssrDisabled, waitingForResponse = _getState2.waitingForResponse, routerHistory = _getState2.routerHistory; if (ssrDisabled === true || typeof XMLHttpRequest === 'undefined') { dispatch({ type: NOOP, _noEffect: _noEffect }); return; } if (waitingForResponse) { dispatch({ type: PENDING_PAGE, routerLocation: routerLocation, _noEffect: _noEffect }); return; } var xhr = new XMLHttpRequest(); var headers = { 'content-type': 'application/json;charset=UTF-8', 'accept': 'application/json' }; dispatch({ type: GET_PAGE_STATES, routerLocation: routerLocation, _noEffect: _noEffect }); xhr.open('GET', getUrl(routerLocation), true); for (var header in headers) { xhr.setRequestHeader(header, headers[header]); } xhr.onload = function () { var states = JSON.parse(xhr.response); setStates(states); dispatch({ type: GOT_PAGE_STATES, states: states }); clearPending(dispatch, getState); }; xhr.send(); }; }, submitRequest: function submitRequest(_ref2) { var _ref2$requestMethod = _ref2.requestMethod, requestMethod = _ref2$requestMethod === undefined ? 'POST' : _ref2$requestMethod, _ref2$requestBody = _ref2.requestBody, requestBody = _ref2$requestBody === undefined ? {} : _ref2$requestBody, _ref2$requestHeaders = _ref2.requestHeaders, requestHeaders = _ref2$requestHeaders === undefined ? {} : _ref2$requestHeaders, requestSession = _ref2.requestSession, acceptJson = _ref2.acceptJson; return { type: SUBMIT_REQUEST, requestMethod: requestMethod, requestBody: requestBody, requestHeaders: requestHeaders, requestSession: requestSession, acceptJson: acceptJson }; }, submitForm: function submitForm(formData, onSubmit) { return function (dispatch, getState, _ref3) { var setStates = _ref3.setStates; var _getState3 = getState(), ssrDisabled = _getState3.ssrDisabled, waitingForResponse = _getState3.waitingForResponse, routerLocation = _getState3.routerLocation; if (ssrDisabled === true || typeof XMLHttpRequest === 'undefined') { formData._formHandled = true; if (onSubmit) { onSubmit(null, formData); } dispatch({ type: NOOP, _noEffect: _noEffect }); return; } if (waitingForResponse) { dispatch({ type: PENDING_FORM, formData: formData, onSubmit: onSubmit, _noEffect: _noEffect }); return; } var xhr = new XMLHttpRequest(); var headers = { 'content-type': 'application/json;charset=UTF-8', 'accept': 'application/json' }; if (onSubmit) { headers['x-server-side'] = true; } dispatch({ type: SUBMIT_FORM, formData: formData, onSubmit: onSubmit }); xhr.open('POST', getUrl(routerLocation), true); for (var header in headers) { xhr.setRequestHeader(header, headers[header]); } xhr.onload = function () { var states = JSON.parse(xhr.response); formData._formHandled = true; setStates(states); if (onSubmit) { onSubmit(null, formData); } dispatch({ type: SUBMITTED_FORM, formData: formData, onSubmit: onSubmit, states: states }); clearPending(dispatch, getState); }; xhr.send(JSON.stringify(formData)); }; }, updateSession: function updateSession(updates, immediate) { return batchAction({ type: UPDATE_SESSION, updates: updates }, immediate); }, destroySession: function destroySession(immediate) { return batchAction({ type: DESTROY_SESSION }, immediate); } }; var reducers = { headers: function headers() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var action = arguments[1]; switch (action.type) { case SET_HEADERS: return action.headers; default: return state; } }, statusCode: function statusCode() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var action = arguments[1]; switch (action.type) { case SET_STATUS_CODE: return action.statusCode; default: return state; } }, documentTitle: function documentTitle() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _exenv.canUseDOM && document.title; var action = arguments[1]; switch (action.type) { case SET_DOCUMENT_TITLE: if (_exenv.canUseDOM) { document.title = action.documentTitle; } return action.documentTitle; default: return state; } }, metaDescription: function metaDescription() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Built with provide-page.'; var action = arguments[1]; switch (action.type) { case SET_META_DESCRIPTION: return action.metaDescription; default: return state; } }, metaRobots: function metaRobots() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'index,follow'; var action = arguments[1]; switch (action.type) { case SET_META_ROBOTS: return action.metaRobots; default: return state; } }, iconFile: function iconFile() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '/static/favicon.ico'; var action = arguments[1]; switch (action.type) { case SET_ICON_FILE: return action.iconFile; default: return state; } }, cssFiles: function cssFiles() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var action = arguments[1]; switch (action.type) { case SET_CSS_FILES: return action.cssFiles; default: return state; } }, jsFiles: function jsFiles() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var action = arguments[1]; switch (action.type) { case SET_JS_FILES: return action.jsFiles; default: return state; } }, requestError: function requestError() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var action = arguments[1]; switch (action.type) { case SET_REQUEST_ERROR: return action.requestError; default: return state; } }, requestSession: function requestSession() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var action = arguments[1]; if (!state.__actualSession && typeof state.destroy !== 'undefined') { state = _extends({}, state, { __actualSession: state }); } switch (action.type) { case SUBMIT_REQUEST: var requestSession = action.requestSession; return requestSession ? _extends({}, requestSession, { __actualSession: requestSession }) : state; case UPDATE_SESSION: var nextState = _extends({}, state); var _action$updates = action.updates, updates = _action$updates === undefined ? {} : _action$updates; delete updates.__actualSession; // just in case if (!nextState.__actualSession) { nextState.__actualSession = {}; } for (var key in updates) { nextState[key] = updates[key]; nextState.__actualSession[key] = updates[key]; } if (nextState.__actualSession.save) { nextState.__actualSession.save(); } return nextState; case DESTROY_SESSION: if (state.__actualSession && state.__actualSession.destroy) { state.__actualSession.destroy(); return { __actualSession: state.__actualSession }; } return { __actualSession: {} }; default: return state; } }, requestMethod: function requestMethod() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var action = arguments[1]; switch (action.type) { case SUBMIT_REQUEST: return action.requestMethod; default: return state; } }, requestBody: function requestBody() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var action = arguments[1]; switch (action.type) { case SUBMIT_FORM: return action.formData; case SUBMIT_REQUEST: return action.requestBody; default: return state; } }, requestHeaders: function requestHeaders() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var action = arguments[1]; switch (action.type) { case SUBMIT_FORM: case SUBMIT_REQUEST: return action.requestHeaders || {}; default: return state; } }, acceptJson: function acceptJson() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var action = arguments[1]; switch (action.type) { case SUBMIT_REQUEST: return typeof action.acceptJson === 'undefined' ? state : action.acceptJson; default: return state; } }, routerHistory: function routerHistory() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var action = arguments[1]; switch (action.type) { case SYNC_WITH_ROUTER: return action.routerHistory; default: return state; } }, routerLocation: function routerLocation() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var action = arguments[1]; switch (action.type) { case SYNC_WITH_ROUTER: case GET_PAGE_STATES: return action.routerLocation; default: return state; } }, waitingForResponse: function waitingForResponse() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var action = arguments[1]; switch (action.type) { case GET_PAGE_STATES: case SUBMIT_FORM: return true; case GOT_PAGE_STATES: case SUBMITTED_FORM: return false; default: return state; } }, pendingPage: function pendingPage() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var action = arguments[1]; switch (action.type) { case PENDING_PAGE: return true; case GET_PAGE_STATES: case SUBMIT_FORM: return false; default: return state; } }, pendingForms: function pendingForms() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var _ref4 = arguments[1]; var type = _ref4.type, formData = _ref4.formData, onSubmit = _ref4.onSubmit; if (type === PENDING_FORM) { state.push({ formData: formData, onSubmit: onSubmit }); } return state; }, pageBatchedActions: function pageBatchedActions() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return state; }, ssrDisabled: function ssrDisabled() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; return state; } }; var merge = { formData: { keys: ['requestBody'], get: function get(_ref5, _ref6) { var requestBody = _ref5.requestBody; var formId = _ref6.formId; return requestBody && requestBody._formId === formId ? requestBody : null; } } }; var middleware = _reduxThunk2.default; var clientStateKeys = ['requestSession', 'requestError']; var subscribeTo = { router: function router(_ref7, _ref8) { var routerStore = _ref7.store; var pageStore = _ref8.store; // there's probably a better way to do this but whatever var _pageStore$getState = pageStore.getState(), routerHistory = _pageStore$getState.routerHistory; if (typeof window !== 'undefined' && !routerHistory) { var _routerStore$getState = routerStore.getState(), history = _routerStore$getState.history, routing = _routerStore$getState.routing; var location = routing && routing.locationBeforeTransitions || routing && routing.location || routerStore.getState().location || history.location; if (history && location) { pageStore.dispatch(actions.syncWithRouter(history, location)); } } } }; exports.default = { actions: actions, reducers: reducers, merge: merge, middleware: middleware, clientStateKeys: clientStateKeys, subscribeTo: subscribeTo };