@zengenti/contensis-react-base 4.0.0-beta.6 → 4.0.0-beta.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/cjs/{App-vZrUfVgQ.js → App-Dr56ZsQj.js} +476 -99
- package/cjs/App-Dr56ZsQj.js.map +1 -0
- package/cjs/{ChangePassword.container-ECjEXixF.js → ChangePassword.container-C4Du3Wb1.js} +57 -50
- package/cjs/ChangePassword.container-C4Du3Wb1.js.map +1 -0
- package/cjs/{SSRContext-DVj_QAC1.js → ContensisDeliveryApi-gN3_MHEl.js} +32 -74
- package/cjs/ContensisDeliveryApi-gN3_MHEl.js.map +1 -0
- package/cjs/CookieConstants-DfPiWCRZ.js +12 -0
- package/cjs/CookieConstants-DfPiWCRZ.js.map +1 -0
- package/{esm/CookieHelper.class-FTURFpz3.js → cjs/CookieHelper.class-Det3qfdU.js} +4 -6
- package/cjs/CookieHelper.class-Det3qfdU.js.map +1 -0
- package/cjs/{RouteLoader-D5Yg7EB5.js → RouteLoader-Bbt-nG3v.js} +13 -8
- package/cjs/RouteLoader-Bbt-nG3v.js.map +1 -0
- package/cjs/SSRContext-DotLlTQc.js +116 -0
- package/cjs/SSRContext-DotLlTQc.js.map +1 -0
- package/cjs/ToJs-BsWqWjdm.js +23 -0
- package/cjs/ToJs-BsWqWjdm.js.map +1 -0
- package/cjs/{VersionInfo-B_dKCubg.js → VersionInfo-zFPsvS8q.js} +3 -25
- package/cjs/VersionInfo-zFPsvS8q.js.map +1 -0
- package/cjs/client.js +62 -64
- package/cjs/client.js.map +1 -1
- package/cjs/contensis-react-base.js +235 -127
- package/cjs/contensis-react-base.js.map +1 -1
- package/cjs/i18n.js +75 -0
- package/cjs/i18n.js.map +1 -0
- package/cjs/{ToJs-C9jwV7YB.js → matchGroups-dqONU-vY.js} +2 -22
- package/cjs/matchGroups-dqONU-vY.js.map +1 -0
- package/cjs/redux.js +8 -6
- package/cjs/redux.js.map +1 -1
- package/cjs/routing.js +15 -7
- package/cjs/routing.js.map +1 -1
- package/cjs/{sagas-CbZhaRNd.js → sagas-OfBUtx74.js} +523 -370
- package/cjs/sagas-OfBUtx74.js.map +1 -0
- package/cjs/search.js +54 -29
- package/cjs/search.js.map +1 -1
- package/cjs/{selectors-wCs5fHD4.js → selectors-BrxJ8-F8.js} +27 -6
- package/cjs/selectors-BrxJ8-F8.js.map +1 -0
- package/cjs/selectors-DAQR0uZa.js +18 -0
- package/cjs/selectors-DAQR0uZa.js.map +1 -0
- package/cjs/slice-5xJMH24n.js +69 -0
- package/cjs/slice-5xJMH24n.js.map +1 -0
- package/cjs/{store-D07FOXvM.js → store-Dn7vP6G0.js} +52 -4
- package/cjs/store-Dn7vP6G0.js.map +1 -0
- package/cjs/urls-DVIwGZmd.js +25 -0
- package/cjs/urls-DVIwGZmd.js.map +1 -0
- package/cjs/user.js +20 -17
- package/cjs/user.js.map +1 -1
- package/cjs/util-wQwG9vit.js +148 -0
- package/cjs/util-wQwG9vit.js.map +1 -0
- package/cjs/util.js +80 -22
- package/cjs/util.js.map +1 -1
- package/cjs/{version-B7XFkBhY.js → version-2FamXHhj.js} +15 -16
- package/cjs/version-2FamXHhj.js.map +1 -0
- package/cjs/{version-CM-bJ62L.js → version-rFG9Y6_B.js} +2 -2
- package/cjs/{version-CM-bJ62L.js.map → version-rFG9Y6_B.js.map} +1 -1
- package/esm/{App-DLZweVSp.js → App-CrCf7gso.js} +436 -60
- package/esm/App-CrCf7gso.js.map +1 -0
- package/esm/{ChangePassword.container-BgzIy8dA.js → ChangePassword.container-CUBtn82K.js} +19 -13
- package/esm/ChangePassword.container-CUBtn82K.js.map +1 -0
- package/esm/{SSRContext-BE8ElZ3X.js → ContensisDeliveryApi-CvEoOLCl.js} +30 -67
- package/esm/ContensisDeliveryApi-CvEoOLCl.js.map +1 -0
- package/esm/CookieConstants-DEmbwzYr.js +7 -0
- package/esm/CookieConstants-DEmbwzYr.js.map +1 -0
- package/{cjs/CookieHelper.class-C3Eqoze9.js → esm/CookieHelper.class-C6rTRl_1.js} +2 -14
- package/esm/CookieHelper.class-C6rTRl_1.js.map +1 -0
- package/esm/{RouteLoader-xeQBXywk.js → RouteLoader-BpHhiAlL.js} +10 -5
- package/esm/RouteLoader-BpHhiAlL.js.map +1 -0
- package/esm/SSRContext-CYxBWky3.js +106 -0
- package/esm/SSRContext-CYxBWky3.js.map +1 -0
- package/esm/ToJs-BnRRHk6f.js +17 -0
- package/esm/ToJs-BnRRHk6f.js.map +1 -0
- package/esm/{VersionInfo-Cno7K0OA.js → VersionInfo-By2ZCZOh.js} +4 -24
- package/esm/VersionInfo-By2ZCZOh.js.map +1 -0
- package/esm/client.js +62 -63
- package/esm/client.js.map +1 -1
- package/esm/contensis-react-base.js +227 -121
- package/esm/contensis-react-base.js.map +1 -1
- package/esm/i18n.js +64 -0
- package/esm/i18n.js.map +1 -0
- package/esm/{ToJs-CNzfvyxJ.js → matchGroups-_w8BwzCC.js} +3 -18
- package/esm/matchGroups-_w8BwzCC.js.map +1 -0
- package/esm/redux.js +11 -8
- package/esm/redux.js.map +1 -1
- package/esm/routing.js +14 -7
- package/esm/routing.js.map +1 -1
- package/esm/{sagas-xJU-zOpn.js → sagas-BZWjx5by.js} +511 -357
- package/esm/sagas-BZWjx5by.js.map +1 -0
- package/esm/search.js +73 -47
- package/esm/search.js.map +1 -1
- package/esm/{selectors-DO2ocdOp.js → selectors-8ROQrTd7.js} +25 -7
- package/esm/selectors-8ROQrTd7.js.map +1 -0
- package/esm/selectors-DcmvOeX2.js +10 -0
- package/esm/selectors-DcmvOeX2.js.map +1 -0
- package/esm/slice-C6JLQik8.js +63 -0
- package/esm/slice-C6JLQik8.js.map +1 -0
- package/esm/{store-3u0RzHZ0.js → store-DSjRYsM2.js} +52 -5
- package/esm/store-DSjRYsM2.js.map +1 -0
- package/esm/urls-DfCisos-.js +22 -0
- package/esm/urls-DfCisos-.js.map +1 -0
- package/esm/user.js +9 -6
- package/esm/user.js.map +1 -1
- package/esm/util-BafFLYzn.js +136 -0
- package/esm/util-BafFLYzn.js.map +1 -0
- package/esm/util.js +58 -14
- package/esm/util.js.map +1 -1
- package/esm/{version-BlsI7hX2.js → version-B75wA6Te.js} +16 -16
- package/esm/version-B75wA6Te.js.map +1 -0
- package/esm/{version-wnf-TITV.js → version-BQAL8sQO.js} +2 -2
- package/esm/{version-wnf-TITV.js.map → version-BQAL8sQO.js.map} +1 -1
- package/i18n/package.json +5 -0
- package/models/app/pages/VersionInfo/components/VersionInfo.d.ts +1 -1
- package/models/app/pages/VersionInfo/components/VersionInfo.styled.d.ts +0 -1
- package/models/i18n/index.d.ts +5 -0
- package/models/i18n/redux/sagas.d.ts +19 -0
- package/models/i18n/redux/selectors.d.ts +11 -0
- package/models/i18n/redux/slice.d.ts +198 -0
- package/models/i18n/routes.d.ts +8 -0
- package/models/i18n/useI18n.hook.d.ts +20 -0
- package/models/index.d.ts +1 -0
- package/models/models/AppState.d.ts +2 -0
- package/models/models/ContentTypeMapping.d.ts +5 -0
- package/models/models/Locales.d.ts +11 -0
- package/models/models/MatchedRoute.d.ts +5 -1
- package/models/models/RouteComponent.d.ts +0 -1
- package/models/models/SSRContext.d.ts +4 -4
- package/models/models/StaticRoute.d.ts +11 -0
- package/models/models/WithEvents.d.ts +8 -0
- package/models/models/config/AppConfig.d.ts +2 -0
- package/models/models/config/I18n.d.ts +38 -0
- package/models/models/config/ServerConfig.d.ts +3 -0
- package/models/redux/index.d.ts +2 -1
- package/models/redux/sagas/index.d.ts +3 -1
- package/models/redux/sagas/injector.d.ts +13 -0
- package/models/redux/store/injectors/index.d.ts +26 -0
- package/models/redux/store/injectors/inject.d.ts +24 -0
- package/models/redux/store/injectors/util.d.ts +2 -0
- package/models/redux/store/store.d.ts +13 -4
- package/models/redux/util.d.ts +1 -1
- package/models/routing/components/RouteLoader.d.ts +3 -3
- package/models/routing/httpContext.d.ts +0 -1
- package/models/routing/index.d.ts +1 -0
- package/models/routing/redux/actions.d.ts +1 -1
- package/models/routing/redux/invokeSearch.d.ts +22 -0
- package/models/routing/redux/selectors.d.ts +47 -4
- package/models/routing/util/expressions.d.ts +1 -1
- package/models/routing/util/find-contenttype-mapping.d.ts +3 -1
- package/models/search/containers/withListing.d.ts +1 -1
- package/models/search/containers/withSearch.d.ts +1 -1
- package/models/search/models/Queries.d.ts +3 -5
- package/models/search/models/Search.d.ts +43 -13
- package/models/search/models/SearchActions.d.ts +61 -18
- package/models/search/models/SearchProps.d.ts +11 -10
- package/models/search/models/SearchState.d.ts +23 -2
- package/models/search/models/SearchUtil.d.ts +3 -3
- package/models/search/redux/getIn.d.ts +2 -2
- package/models/search/redux/reducers.d.ts +3 -4
- package/models/search/redux/sagas.d.ts +13 -14
- package/models/search/redux/schema.d.ts +3 -3
- package/models/search/redux/selectors.d.ts +64 -42
- package/models/search/redux/util.d.ts +10 -1
- package/models/search/search/ContensisDeliveryApi.d.ts +6 -26
- package/models/search/search/expressions.d.ts +6 -4
- package/models/search/search/util.d.ts +9 -7
- package/models/search/transformations/state-to-queryparams.mapper.d.ts +1 -1
- package/models/server/features/linkdepth-api/search.d.ts +1 -1
- package/models/server/features/response-handler/render-stream.d.ts +2 -4
- package/models/server/features/static-assets/index.d.ts +4 -3
- package/models/server/internalServer.d.ts +1 -2
- package/models/server/middleware/subsiteDebug.d.ts +11 -0
- package/models/server/root.d.ts +3 -0
- package/models/server/util/bundles.d.ts +9 -9
- package/models/server/util/jsx.d.ts +2 -14
- package/models/user/hocs/withRegistration.d.ts +1 -1
- package/models/util/CachedDeliveryApi.d.ts +8 -2
- package/models/util/ContensisDeliveryApi.d.ts +2 -4
- package/models/util/NoSSR.d.ts +6 -0
- package/models/util/SSRContext.d.ts +3 -19
- package/models/util/donotuse_useHistory.d.ts +6 -0
- package/models/util/index.d.ts +6 -1
- package/models/util/subsite.d.ts +12 -0
- package/models/util/useIsClient.d.ts +6 -0
- package/package.json +39 -38
- package/cjs/App-vZrUfVgQ.js.map +0 -1
- package/cjs/ChangePassword.container-ECjEXixF.js.map +0 -1
- package/cjs/CookieHelper.class-C3Eqoze9.js.map +0 -1
- package/cjs/RouteLoader-D5Yg7EB5.js.map +0 -1
- package/cjs/SSRContext-DVj_QAC1.js.map +0 -1
- package/cjs/ToJs-C9jwV7YB.js.map +0 -1
- package/cjs/VersionInfo-B_dKCubg.js.map +0 -1
- package/cjs/sagas-CbZhaRNd.js.map +0 -1
- package/cjs/selectors-wCs5fHD4.js.map +0 -1
- package/cjs/store-D07FOXvM.js.map +0 -1
- package/cjs/version-B7XFkBhY.js.map +0 -1
- package/esm/App-DLZweVSp.js.map +0 -1
- package/esm/ChangePassword.container-BgzIy8dA.js.map +0 -1
- package/esm/CookieHelper.class-FTURFpz3.js.map +0 -1
- package/esm/RouteLoader-xeQBXywk.js.map +0 -1
- package/esm/SSRContext-BE8ElZ3X.js.map +0 -1
- package/esm/ToJs-CNzfvyxJ.js.map +0 -1
- package/esm/VersionInfo-Cno7K0OA.js.map +0 -1
- package/esm/sagas-xJU-zOpn.js.map +0 -1
- package/esm/selectors-DO2ocdOp.js.map +0 -1
- package/esm/store-3u0RzHZ0.js.map +0 -1
- package/esm/version-BlsI7hX2.js.map +0 -1
- package/models/redux/store/injectors.d.ts +0 -31
- package/models/search/search/ToJs.d.ts +0 -4
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import { c as cachedSearch,
|
|
1
|
+
import { c as cachedSearch, d as deliveryApi } from './ContensisDeliveryApi-CvEoOLCl.js';
|
|
2
2
|
import { Query as Query$1 } from 'contensis-delivery-api';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Provider } from 'react-redux';
|
|
5
|
+
import { a as actions } from './slice-C6JLQik8.js';
|
|
5
6
|
import mapJson from 'jsonpath-mapper';
|
|
6
|
-
import {
|
|
7
|
+
import { a7 as defaultExpressions, a8 as termExpressions, a9 as contentTypeIdExpression, aa as filterExpressions, ab as orderByExpression, ac as customWhereExpressions, ad as cloneDeep } from './sagas-BZWjx5by.js';
|
|
7
8
|
import 'reselect';
|
|
8
9
|
import 'immer';
|
|
9
10
|
import 'deep-equal';
|
|
10
11
|
import 'deepmerge';
|
|
11
12
|
import 'query-string';
|
|
12
13
|
import { Op, Query } from 'contensis-core-api';
|
|
13
|
-
import { s as setCachingHeaders, u as url } from './
|
|
14
|
+
import { s as setCachingHeaders, u as url } from './urls-DfCisos-.js';
|
|
14
15
|
import 'isomorphic-fetch';
|
|
15
16
|
import express from 'express';
|
|
16
17
|
import http from 'http';
|
|
17
18
|
import httpProxy from 'http-proxy';
|
|
18
19
|
import fs from 'fs';
|
|
19
20
|
import path from 'path';
|
|
20
|
-
import
|
|
21
|
+
import appRootPath from 'app-root-path';
|
|
21
22
|
import { renderToPipeableStream, renderToString } from 'react-dom/server';
|
|
22
23
|
import { matchRoutes } from 'react-router-dom';
|
|
23
24
|
import { Helmet } from 'react-helmet';
|
|
@@ -25,32 +26,40 @@ import { ServerStyleSheet } from 'styled-components';
|
|
|
25
26
|
import serialize from 'serialize-javascript';
|
|
26
27
|
import { noop, identity } from 'lodash';
|
|
27
28
|
import { buildCleaner } from 'lodash-clean';
|
|
28
|
-
import { a as Cookies } from './CookieHelper.class-
|
|
29
|
+
import { a as Cookies } from './CookieHelper.class-C6rTRl_1.js';
|
|
29
30
|
import cookiesMiddleware from 'universal-cookie-express';
|
|
30
|
-
import { c as
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
import { s as setVersionStatus,
|
|
34
|
-
import {
|
|
35
|
-
import { H as HttpContext, m as mergeStaticRoutes } from './RouteLoader-
|
|
31
|
+
import { c as createLocaleRoutes, h as history, p as pickProject, r as rootSaga } from './App-CrCf7gso.js';
|
|
32
|
+
export { A as ReactApp } from './App-CrCf7gso.js';
|
|
33
|
+
import { c as createStore } from './store-DSjRYsM2.js';
|
|
34
|
+
import { s as setVersionStatus, c as setVersion } from './version-B75wA6Te.js';
|
|
35
|
+
import { a6 as selectSurrogateKeys, a7 as selectSsrApiCalls, j as selectRouteEntry, f as selectCurrentProject, g as getImmutableOrJS, s as setCurrentProject, F as selectCurrentSearch } from './selectors-8ROQrTd7.js';
|
|
36
|
+
import { H as HttpContext, m as mergeStaticRoutes } from './RouteLoader-BpHhiAlL.js';
|
|
36
37
|
import { Transform } from 'stream';
|
|
37
38
|
import { ChunkExtractor, ChunkExtractorManager } from '@loadable/server';
|
|
38
39
|
import chalk from 'chalk';
|
|
39
40
|
import minifyCssString from 'minify-css-string';
|
|
40
41
|
import { CookiesProvider } from 'react-cookie';
|
|
42
|
+
import { HelmetProvider } from 'react-helmet-async';
|
|
41
43
|
import { StaticRouter } from 'react-router-dom/server';
|
|
44
|
+
import { S as SSRContextProvider, g as getSubsitePath } from './SSRContext-CYxBWky3.js';
|
|
45
|
+
import './VersionInfo-By2ZCZOh.js';
|
|
46
|
+
import './CookieConstants-DEmbwzYr.js';
|
|
47
|
+
import '@reduxjs/toolkit';
|
|
42
48
|
import 'loglevel';
|
|
43
49
|
import '@redux-saga/core/effects';
|
|
50
|
+
import './version-BQAL8sQO.js';
|
|
51
|
+
import './util-BafFLYzn.js';
|
|
52
|
+
import './selectors-DcmvOeX2.js';
|
|
44
53
|
import './_commonjsHelpers-BFTU3MAI.js';
|
|
45
|
-
import '
|
|
54
|
+
import 'history';
|
|
55
|
+
import 'await-to-js';
|
|
56
|
+
import './ChangePassword.container-CUBtn82K.js';
|
|
57
|
+
import './matchGroups-_w8BwzCC.js';
|
|
58
|
+
import './ToJs-BnRRHk6f.js';
|
|
46
59
|
import 'redux';
|
|
47
60
|
import 'redux-thunk';
|
|
48
61
|
import 'redux-saga';
|
|
49
62
|
import 'redux-injectors-19';
|
|
50
|
-
import 'history';
|
|
51
|
-
import 'await-to-js';
|
|
52
|
-
import './ChangePassword.container-BgzIy8dA.js';
|
|
53
|
-
import './ToJs-CNzfvyxJ.js';
|
|
54
63
|
|
|
55
64
|
/**
|
|
56
65
|
* Util class holds our search results helper boilerplate methods
|
|
@@ -610,42 +619,66 @@ const makeLinkDepthMiddleware = ({
|
|
|
610
619
|
}
|
|
611
620
|
};
|
|
612
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Development proxy for Subsite PoC
|
|
624
|
+
* Catch all routes before they hit CRB handlers
|
|
625
|
+
* and rewrite them to include the subsite base path,
|
|
626
|
+
* this allows us to run the subsite in a subfolder in development
|
|
627
|
+
* In production we will handle this with a path rewrite in the Cloud Dashboard site configuration,
|
|
628
|
+
* @param subsitePath the content base path we will rewrite to
|
|
629
|
+
* @param exceptions an array of path prefixes to ignore when rewriting, useful for ignoring assets that do not live in the subsite base path
|
|
630
|
+
*/
|
|
631
|
+
const subsiteDebugMiddleware = (subsitePath, exceptions = []) => (req, res, next) => {
|
|
632
|
+
if (!subsitePath || req.hostname !== 'localhost' || req.path.startsWith('/api/') || exceptions.some(exception => req.path.startsWith(exception))) return next();
|
|
633
|
+
if (!req.path.startsWith(`${subsitePath}/`)) {
|
|
634
|
+
console.warn(`[subsite-debug-middleware] Rewriting (${subsitePath})${req.url}`);
|
|
635
|
+
if (req.path === '/' || req.path === subsitePath) req.url = subsitePath;else req.url = `${subsitePath}${req.url}`;
|
|
636
|
+
res.setHeader('x-crb-subsite-content-path', req.url);
|
|
637
|
+
|
|
638
|
+
// Important to set the subsite_path header as this drives the subsite-scoped routing logic
|
|
639
|
+
req.headers['subsite_path'] = subsitePath;
|
|
640
|
+
}
|
|
641
|
+
next();
|
|
642
|
+
};
|
|
643
|
+
|
|
613
644
|
const servers$1 = SERVERS; /* global SERVERS */
|
|
614
645
|
const project = PROJECT; /* global PROJECT */
|
|
615
646
|
const alias$1 = ALIAS; /* global ALIAS */
|
|
616
647
|
const deliveryApiHostname = url(alias$1, project).api;
|
|
648
|
+
const proxyTimeoutMs = 45_000;
|
|
617
649
|
const assetProxy = httpProxy.createProxyServer();
|
|
618
650
|
const deliveryProxy = httpProxy.createProxyServer();
|
|
619
651
|
const reverseProxies = (app, reverseProxyPaths = []) => {
|
|
620
652
|
deliveryApiProxy(deliveryProxy, app);
|
|
621
|
-
app.all(reverseProxyPaths
|
|
622
|
-
|
|
653
|
+
app.all(reverseProxyPaths.map(proxyPath =>
|
|
654
|
+
// Patch to update paths for express v5
|
|
655
|
+
proxyPath.endsWith('/*') ? `${proxyPath.slice(0, -2)}/{*splat}` : proxyPath.endsWith('/**') ? `${proxyPath.slice(0, -3)}/{*splat}` : proxyPath), (req, res) => {
|
|
656
|
+
const target = req.hostname.includes('preview-') || req.hostname.includes('preview.') || req.hostname === 'localhost' ? servers$1.previewIis || servers$1.iis : servers$1.iis;
|
|
623
657
|
assetProxy.web(req, res, {
|
|
624
658
|
target,
|
|
625
|
-
changeOrigin: true
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
/* eslint-disable no-console */
|
|
629
|
-
console.log(`Proxy Request for ${req.path} HostName:${req.hostname} failed with ${e}`);
|
|
630
|
-
/* eslint-enable no-console */
|
|
659
|
+
changeOrigin: true,
|
|
660
|
+
proxyTimeout: proxyTimeoutMs,
|
|
661
|
+
timeout: proxyTimeoutMs
|
|
631
662
|
});
|
|
632
663
|
});
|
|
664
|
+
assetProxy.on('error', (e, req) => {
|
|
665
|
+
console.log(`Proxy request for ${req.url} HostName:${req.headers.host} failed with ${e}`);
|
|
666
|
+
});
|
|
633
667
|
};
|
|
634
668
|
const deliveryApiProxy = (apiProxy, app) => {
|
|
635
669
|
// This is just here to stop cors requests on localhost. In Production this is mapped using varnish.
|
|
636
|
-
app.all(['/api/delivery
|
|
637
|
-
/* eslint-disable no-console */
|
|
670
|
+
app.all(['/api/delivery/{*splat}', '/api/forms/{*splat}', '/api/image/{*splat}', '/authenticate/{*splat}'], (req, res) => {
|
|
638
671
|
console.log(`Proxying api request to ${servers$1.alias}`);
|
|
639
672
|
apiProxy.web(req, res, {
|
|
640
673
|
target: deliveryApiHostname,
|
|
641
|
-
changeOrigin: true
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
/* eslint-disable no-console */
|
|
645
|
-
console.log(`Proxy request for ${req.path} HostName:${req.hostname} failed with ${e}`);
|
|
646
|
-
/* eslint-enable no-console */
|
|
674
|
+
changeOrigin: true,
|
|
675
|
+
proxyTimeout: proxyTimeoutMs,
|
|
676
|
+
timeout: proxyTimeoutMs
|
|
647
677
|
});
|
|
648
678
|
});
|
|
679
|
+
apiProxy.on('error', (e, req) => {
|
|
680
|
+
console.log(`Proxy request for ${req.url} HostName:${req.headers.host} failed with ${e}`);
|
|
681
|
+
});
|
|
649
682
|
};
|
|
650
683
|
|
|
651
684
|
const CacheDuration = {
|
|
@@ -720,9 +753,8 @@ const resolveStartupMiddleware = ({
|
|
|
720
753
|
if (maxage) res.set('Cache-Control', `public, max-age=${maxage}`);
|
|
721
754
|
res.sendFile(startupFileLocation);
|
|
722
755
|
} catch (sendFileError) {
|
|
723
|
-
// eslint-disable-next-line no-console
|
|
724
756
|
console.log(`Unable to send file startup.js at '${startupFileLocation}'`, sendFileError);
|
|
725
|
-
|
|
757
|
+
res.status(404).send();
|
|
726
758
|
}
|
|
727
759
|
} else {
|
|
728
760
|
next();
|
|
@@ -730,8 +762,11 @@ const resolveStartupMiddleware = ({
|
|
|
730
762
|
};
|
|
731
763
|
|
|
732
764
|
// Serving static assets
|
|
765
|
+
const {
|
|
766
|
+
path: appPath
|
|
767
|
+
} = appRootPath;
|
|
733
768
|
const staticAssets = (app, {
|
|
734
|
-
appRootPath =
|
|
769
|
+
appRootPath = appPath,
|
|
735
770
|
scripts = {},
|
|
736
771
|
startupScriptFilename = 'startup.js',
|
|
737
772
|
staticFolderPath = 'static',
|
|
@@ -750,9 +785,7 @@ const staticAssets = (app, {
|
|
|
750
785
|
maxage: CacheDuration.static,
|
|
751
786
|
startupScriptFilename: scripts.startup || startupScriptFilename,
|
|
752
787
|
staticFolderPath
|
|
753
|
-
}),
|
|
754
|
-
// eslint-disable-next-line import/no-named-as-default-member
|
|
755
|
-
express.static(`dist/${staticFolderPath}`, {
|
|
788
|
+
}), express.static(`dist/${staticFolderPath}`, {
|
|
756
789
|
// these maxage values are different in config but the same in runtime,
|
|
757
790
|
// this one is somehow converted and should end up being the same as CacheDuration.static
|
|
758
791
|
maxAge: CacheDuration.expressStatic
|
|
@@ -802,29 +835,54 @@ const handleResponse = (request, response, content, send = 'send') => {
|
|
|
802
835
|
* @param response the express Response object
|
|
803
836
|
* @param stream all chunks are piped to this stream to add additional style elements to each streamed chunk
|
|
804
837
|
*/
|
|
805
|
-
const renderStream = (getContextHtml, jsx, response, stream) => {
|
|
806
|
-
|
|
807
|
-
let
|
|
838
|
+
const renderStream = (getContextHtml, jsx, request, response, stream) => {
|
|
839
|
+
// Store timeout reference for cleanup on normal or abnormal termination
|
|
840
|
+
let timeoutId = null;
|
|
841
|
+
const disposeTimeout = () => {
|
|
842
|
+
if (timeoutId) {
|
|
843
|
+
clearTimeout(timeoutId);
|
|
844
|
+
timeoutId = null;
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// Only used for abnormal termination
|
|
849
|
+
const abortCleanup = err => {
|
|
850
|
+
disposeTimeout();
|
|
851
|
+
stream.destroy(err instanceof Error ? err : undefined);
|
|
852
|
+
abort();
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
// Guard against client disconnect
|
|
856
|
+
request.on('close', () => abortCleanup());
|
|
857
|
+
|
|
858
|
+
// Guard against transform errors
|
|
859
|
+
stream.on('error', err => {
|
|
860
|
+
abortCleanup(err);
|
|
861
|
+
if (!response.headersSent) response.destroy(err);
|
|
862
|
+
});
|
|
808
863
|
const {
|
|
809
864
|
abort,
|
|
810
865
|
pipe
|
|
811
866
|
} = renderToPipeableStream(jsx, {
|
|
812
867
|
onShellReady() {
|
|
813
|
-
const html = getContextHtml();
|
|
868
|
+
const html = getContextHtml(false);
|
|
814
869
|
if (!html) {
|
|
815
870
|
// this means we have finished with the response already
|
|
816
|
-
|
|
871
|
+
abortCleanup();
|
|
817
872
|
} else {
|
|
818
|
-
|
|
873
|
+
const header = html.split('{{APP}}')[0];
|
|
819
874
|
response.setHeader('content-type', 'text/html; charset=utf-8');
|
|
820
875
|
stream.write(header);
|
|
821
876
|
pipe(stream);
|
|
822
877
|
}
|
|
823
878
|
},
|
|
824
879
|
onAllReady() {
|
|
880
|
+
const footer = getContextHtml(true).split('{{APP}}')[1];
|
|
825
881
|
stream.write(footer);
|
|
882
|
+
disposeTimeout(); // Clear the timeout, let stream end naturally
|
|
826
883
|
},
|
|
827
884
|
onShellError(error) {
|
|
885
|
+
abortCleanup(error); // Abnormal - destroy everything
|
|
828
886
|
response.statusCode = 500;
|
|
829
887
|
response.setHeader('content-type', 'text/html; charset=utf-8');
|
|
830
888
|
response.send('<h1>Something went wrong</h1>');
|
|
@@ -835,10 +893,13 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
|
|
|
835
893
|
}
|
|
836
894
|
});
|
|
837
895
|
|
|
838
|
-
// Abandon and switch to client rendering
|
|
896
|
+
// Abandon and switch to client rendering after 30s.
|
|
839
897
|
// Try lowering this to see the client recover.
|
|
840
|
-
setTimeout(() =>
|
|
841
|
-
|
|
898
|
+
timeoutId = setTimeout(() => {
|
|
899
|
+
timeoutId = null;
|
|
900
|
+
abortCleanup();
|
|
901
|
+
}, 30_000);
|
|
902
|
+
stream.pipe(response);
|
|
842
903
|
};
|
|
843
904
|
|
|
844
905
|
/**
|
|
@@ -878,6 +939,23 @@ const styledComponentsStream = sheet => {
|
|
|
878
939
|
this.push(styledCSS + renderedHtml);
|
|
879
940
|
}
|
|
880
941
|
callback();
|
|
942
|
+
},
|
|
943
|
+
destroy(err, callback) {
|
|
944
|
+
// Called on both stream.destroy() and natural end
|
|
945
|
+
// Stops the sheet intercepting styles & releases its references
|
|
946
|
+
|
|
947
|
+
// try/catch is required if sheet.seal() throws for any reason,
|
|
948
|
+
// callback(err) must still be called, as Node.js stream internals depend
|
|
949
|
+
// on it to complete teardown. Omitting it causes the stream to hang.
|
|
950
|
+
try {
|
|
951
|
+
sheet.seal();
|
|
952
|
+
} catch (sealErr) {
|
|
953
|
+
// Catch any errors from sealing the sheet, we MUST always call the
|
|
954
|
+
// callback to prevent hanging the stream
|
|
955
|
+
|
|
956
|
+
console.error('[styledComponentsStream] sheet.seal() failed - styles may leak:', sealErr);
|
|
957
|
+
}
|
|
958
|
+
callback(err);
|
|
881
959
|
}
|
|
882
960
|
});
|
|
883
961
|
return readerWriter;
|
|
@@ -930,7 +1008,8 @@ const loadableChunkExtractors = () => {
|
|
|
930
1008
|
statsFile: path.resolve('dist/legacy/loadable-stats.json')
|
|
931
1009
|
});
|
|
932
1010
|
} catch (e) {
|
|
933
|
-
|
|
1011
|
+
// legacy bundling deprecated in v4
|
|
1012
|
+
// console.info('@loadable/server legacy ChunkExtractor not available');
|
|
934
1013
|
}
|
|
935
1014
|
commonLoadableExtractor.addChunk = chunk => {
|
|
936
1015
|
var _modern, _legacy, _legacy2;
|
|
@@ -1071,7 +1150,6 @@ const addVarnishAuthenticationHeaders = (state, response, groups = {}) => {
|
|
|
1071
1150
|
globalGroups,
|
|
1072
1151
|
allowedGroups
|
|
1073
1152
|
} = groups;
|
|
1074
|
-
// console.info(globalGroups, allowedGroups);
|
|
1075
1153
|
let allGroups = Array.from(globalGroups && globalGroups[project] || {});
|
|
1076
1154
|
if (stateEntry && getImmutableOrJS(stateEntry, ['authentication', 'isLoginRequired']) && allowedGroups && allowedGroups[project]) {
|
|
1077
1155
|
allGroups = [...allGroups, ...allowedGroups[project]];
|
|
@@ -1104,14 +1182,17 @@ const replaceHtml = ({
|
|
|
1104
1182
|
let responseHTML = '';
|
|
1105
1183
|
// Serve a blank HTML page with client scripts to load the app in the browser
|
|
1106
1184
|
if (accessMethod.DYNAMIC) {
|
|
1107
|
-
responseHTML = templateHTML.replace('{{TITLE}}', '').replace('{{SEO_CRITICAL_METADATA}}', '').replace('{{CRITICAL_CSS}}', '').replace('{{APP}}', '')
|
|
1185
|
+
responseHTML = templateHTML.replace('{{TITLE}}', '').replace('{{SEO_CRITICAL_METADATA}}', '').replace('{{CRITICAL_CSS}}', '').replace('{{APP}}', '')
|
|
1186
|
+
// .replace('{{LOADABLE_CHUNKS}}', bundleTags)
|
|
1187
|
+
.replace('{{REDUX_DATA}}', state);
|
|
1108
1188
|
}
|
|
1109
1189
|
|
|
1110
1190
|
// Page fragment served with client scripts and redux data that hydrate the app client side
|
|
1111
1191
|
else if (accessMethod.FRAGMENT && !accessMethod.STATIC) {
|
|
1112
1192
|
responseHTML = templateHTMLFragment.replace('{{TITLE}}', title).replace('{{SEO_CRITICAL_METADATA}}', metadata).replace('{{CRITICAL_CSS}}', minifyCssString(styleTags))
|
|
1113
1193
|
//.replace('{{APP}}', html)
|
|
1114
|
-
.replace('{{LOADABLE_CHUNKS}}', bundleTags)
|
|
1194
|
+
// .replace('{{LOADABLE_CHUNKS}}', bundleTags)
|
|
1195
|
+
.replace('{{REDUX_DATA}}', state);
|
|
1115
1196
|
}
|
|
1116
1197
|
|
|
1117
1198
|
// Full HTML page served statically
|
|
@@ -1125,7 +1206,8 @@ const replaceHtml = ({
|
|
|
1125
1206
|
else if (!accessMethod.FRAGMENT && !accessMethod.STATIC) {
|
|
1126
1207
|
responseHTML = templateHTML.replace('{{TITLE}}', title).replace('{{SEO_CRITICAL_METADATA}}', metadata).replace('{{CRITICAL_CSS}}', styleTags)
|
|
1127
1208
|
//.replace('{{APP}}', html)
|
|
1128
|
-
.replace('{{LOADABLE_CHUNKS}}', bundleTags)
|
|
1209
|
+
// .replace('{{LOADABLE_CHUNKS}}', bundleTags)
|
|
1210
|
+
.replace('{{REDUX_DATA}}', state);
|
|
1129
1211
|
}
|
|
1130
1212
|
|
|
1131
1213
|
// If react-helmet htmlAttributes are being used,
|
|
@@ -1134,7 +1216,12 @@ const replaceHtml = ({
|
|
|
1134
1216
|
if (htmlAttributes) {
|
|
1135
1217
|
responseHTML = responseHTML.replace(/<html?.+?>/, `<html ${htmlAttributes}>`);
|
|
1136
1218
|
}
|
|
1137
|
-
|
|
1219
|
+
responseHTML = html ? responseHTML.replace('{{APP}}', html) : responseHTML;
|
|
1220
|
+
|
|
1221
|
+
// Only replace bundle tags at the very end when we have rendered and are
|
|
1222
|
+
// streaming out the HTML "footer"
|
|
1223
|
+
if (bundleTags) responseHTML = responseHTML.replace('{{LOADABLE_CHUNKS}}', bundleTags);
|
|
1224
|
+
return responseHTML;
|
|
1138
1225
|
};
|
|
1139
1226
|
|
|
1140
1227
|
/**
|
|
@@ -1146,13 +1233,15 @@ const replaceHtml = ({
|
|
|
1146
1233
|
*/
|
|
1147
1234
|
const ssrJsxProducer = (ReactApp, {
|
|
1148
1235
|
providers,
|
|
1149
|
-
props
|
|
1150
|
-
ssrAssets
|
|
1236
|
+
props
|
|
1237
|
+
// ssrAssets,
|
|
1151
1238
|
}) => {
|
|
1152
1239
|
var _providers$styledComp;
|
|
1153
1240
|
// Recast ChunkExtractorManager to avoid TS error `Property 'children' does not exist on type...`
|
|
1154
1241
|
const ChunkExtractor = ChunkExtractorManager;
|
|
1155
|
-
const jsx = /*#__PURE__*/React.createElement(
|
|
1242
|
+
const jsx = /*#__PURE__*/React.createElement(HelmetProvider, {
|
|
1243
|
+
context: providers.helmet
|
|
1244
|
+
}, /*#__PURE__*/React.createElement(ChunkExtractor, {
|
|
1156
1245
|
extractor: providers.loadable.extractor
|
|
1157
1246
|
}, /*#__PURE__*/React.createElement(CookiesProvider, {
|
|
1158
1247
|
cookies: providers.cookies
|
|
@@ -1161,16 +1250,20 @@ const ssrJsxProducer = (ReactApp, {
|
|
|
1161
1250
|
}, /*#__PURE__*/React.createElement(HttpContext.Provider, {
|
|
1162
1251
|
value: providers.httpContext
|
|
1163
1252
|
}, /*#__PURE__*/React.createElement(StaticRouter, {
|
|
1164
|
-
location: providers.router.url
|
|
1253
|
+
location: providers.router.url,
|
|
1254
|
+
future: {
|
|
1255
|
+
v7_startTransition: true,
|
|
1256
|
+
v7_relativeSplatPath: true
|
|
1257
|
+
}
|
|
1165
1258
|
}, /*#__PURE__*/React.createElement(SSRContextProvider, {
|
|
1166
1259
|
accessMethod: providers.ssrContext.accessMethod,
|
|
1167
1260
|
request: providers.ssrContext.request,
|
|
1168
|
-
response: providers.ssrContext.response
|
|
1169
|
-
|
|
1261
|
+
response: providers.ssrContext.response
|
|
1262
|
+
// ssrAssets={ssrAssets}
|
|
1170
1263
|
}, /*#__PURE__*/React.createElement(ReactApp, {
|
|
1171
1264
|
routes: props.routes,
|
|
1172
1265
|
withEvents: props.withEvents
|
|
1173
|
-
})))))));
|
|
1266
|
+
}))))))));
|
|
1174
1267
|
|
|
1175
1268
|
// Wrap the JSX in a StyleSheetManager if a ServerStyleSheet is provided
|
|
1176
1269
|
return !((_providers$styledComp = providers.styledComponents) !== null && _providers$styledComp !== void 0 && _providers$styledComp.sheet) ? jsx : providers.styledComponents.sheet.collectStyles(jsx);
|
|
@@ -1178,7 +1271,7 @@ const ssrJsxProducer = (ReactApp, {
|
|
|
1178
1271
|
|
|
1179
1272
|
const webApp = (app, ReactApp, config) => {
|
|
1180
1273
|
const {
|
|
1181
|
-
stateType = '
|
|
1274
|
+
stateType = 'js',
|
|
1182
1275
|
routes,
|
|
1183
1276
|
withReducers,
|
|
1184
1277
|
withSagas,
|
|
@@ -1192,8 +1285,12 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1192
1285
|
disableSsrRedux,
|
|
1193
1286
|
enableSsrCookies,
|
|
1194
1287
|
handleResponses,
|
|
1195
|
-
handleExceptions = true
|
|
1288
|
+
handleExceptions = true,
|
|
1289
|
+
i18n
|
|
1196
1290
|
} = config;
|
|
1291
|
+
|
|
1292
|
+
// process locales in static routes for i18n
|
|
1293
|
+
const localeRoutes = createLocaleRoutes(routes);
|
|
1197
1294
|
const staticRoutePath = config.staticRoutePath || staticFolderPath;
|
|
1198
1295
|
let isRenderingJsxToString = config.renderToString || false;
|
|
1199
1296
|
const bundleData = getBundleData(config, staticRoutePath);
|
|
@@ -1207,8 +1304,16 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1207
1304
|
if (handleExceptions !== false) unhandledExceptionHandler(handleExceptions); // Create `process.on` event handlers for unhandled exceptions (Node v15+)
|
|
1208
1305
|
|
|
1209
1306
|
const versionInfo = getVersionInfo(staticFolderPath);
|
|
1210
|
-
app.get('
|
|
1211
|
-
|
|
1307
|
+
app.get('/{*splat}', cookiesMiddleware(), async (request, response) => {
|
|
1308
|
+
/*
|
|
1309
|
+
* Do not inject url directly into HTML as it can lead to XSS attacks
|
|
1310
|
+
* CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
|
|
1311
|
+
* CWE-96: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)
|
|
1312
|
+
* Removed URL encoding as it causes inconsistencies when routes contain encoded characters in SSR
|
|
1313
|
+
* e.g. /search?category=sport%20and%20wellbeing becomes /search?category=sport%2520and%2520wellbeing
|
|
1314
|
+
* // const url = encodeURI(request.url);
|
|
1315
|
+
*/
|
|
1316
|
+
const url = request.url;
|
|
1212
1317
|
const matchedStaticRoute = matchRoutes(routes.StaticRoutes, request.path);
|
|
1213
1318
|
const isStaticRoute = matchedStaticRoute && matchedStaticRoute.length > 0;
|
|
1214
1319
|
if (isStaticRoute) {
|
|
@@ -1248,25 +1353,22 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1248
1353
|
// In server-side blocks world, the hostname requested by the client resides in the x-orig-host header
|
|
1249
1354
|
// Because of this, we prioritize x-orig-host when setting our hostname
|
|
1250
1355
|
const hostname = request.headers['x-orig-host'] || request.hostname;
|
|
1356
|
+
const subsitePath = getSubsitePath(request);
|
|
1357
|
+
const subsitePathScript = subsitePath ? `window.subsitePath = ${serialize(subsitePath)};` : '';
|
|
1251
1358
|
console.info(`Request for ${request.path} hostname: ${hostname} versionStatus: ${versionStatus}`);
|
|
1252
1359
|
store.dispatch(setVersionStatus(versionStatus));
|
|
1253
1360
|
store.dispatch(setVersion(versionInfo.commitRef, versionInfo.buildNo));
|
|
1254
1361
|
const project = pickProject(hostname, request.query);
|
|
1255
1362
|
const groups = allowedGroups && allowedGroups[project];
|
|
1256
1363
|
store.dispatch(setCurrentProject(project, groups, hostname));
|
|
1364
|
+
if (i18n) {
|
|
1365
|
+
store.dispatch(actions.INIT_LOCALES({
|
|
1366
|
+
locales: {},
|
|
1367
|
+
routes: localeRoutes,
|
|
1368
|
+
...i18n
|
|
1369
|
+
}));
|
|
1370
|
+
}
|
|
1257
1371
|
const loadableExtractor = loadableChunkExtractors();
|
|
1258
|
-
|
|
1259
|
-
// type ChunkExtractorManagerPropsForReact18 = ChunkExtractorManagerProps & {
|
|
1260
|
-
// children?: React.ReactNode;
|
|
1261
|
-
// };
|
|
1262
|
-
|
|
1263
|
-
// // Recast ChunkExtractorManager to avoid TS error `Property 'children' does not exist on type...`
|
|
1264
|
-
// const ChunkExtractor = ChunkExtractorManager as ClassType<
|
|
1265
|
-
// ChunkExtractorManagerPropsForReact18,
|
|
1266
|
-
// Component<ChunkExtractorManagerPropsForReact18>,
|
|
1267
|
-
// ComponentClass<ChunkExtractorManagerPropsForReact18>
|
|
1268
|
-
// >;
|
|
1269
|
-
|
|
1270
1372
|
const ssrCookies = enableSsrCookies ?
|
|
1271
1373
|
// these cookies are managed by the cookiesMiddleware and contain listeners
|
|
1272
1374
|
// when cookies are read or written in ssr can be added to the `set-cookie` response header
|
|
@@ -1281,12 +1383,17 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1281
1383
|
// and read back any context props set by the ReactApp
|
|
1282
1384
|
const context = {};
|
|
1283
1385
|
|
|
1386
|
+
// Per-request helmet context object — populated by HelmetProvider during renderToString
|
|
1387
|
+
// Using a fresh object per request ensures thread safety under concurrent SSR requests
|
|
1388
|
+
const helmetContext = {};
|
|
1389
|
+
|
|
1284
1390
|
// Amalgamate all props for the various Providers we wrap the ReactApp with
|
|
1285
1391
|
const jsxProviderProps = {
|
|
1286
1392
|
loadable: {
|
|
1287
1393
|
extractor: loadableExtractor.commonLoadableExtractor
|
|
1288
1394
|
},
|
|
1289
1395
|
cookies: ssrCookies,
|
|
1396
|
+
helmet: helmetContext,
|
|
1290
1397
|
redux: store,
|
|
1291
1398
|
httpContext: context,
|
|
1292
1399
|
router: {
|
|
@@ -1316,13 +1423,10 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1316
1423
|
// Dynamic doesn't need sagas
|
|
1317
1424
|
// or styles, or any split component bundles
|
|
1318
1425
|
// nor are we streaming responses
|
|
1319
|
-
const isDynamicHints = `<script ${attributes}>window.
|
|
1426
|
+
const isDynamicHints = `<script ${attributes}>window.isDynamic = true; ${subsitePathScript}</script>`;
|
|
1320
1427
|
const jsx = ssrJsxProducer(ReactApp, {
|
|
1321
1428
|
providers: jsxProviderProps,
|
|
1322
|
-
props: jsxReactAppProps
|
|
1323
|
-
ssrAssets: {
|
|
1324
|
-
serializedState: isDynamicHints
|
|
1325
|
-
}
|
|
1429
|
+
props: jsxReactAppProps
|
|
1326
1430
|
});
|
|
1327
1431
|
renderToString(jsx);
|
|
1328
1432
|
|
|
@@ -1381,40 +1485,16 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1381
1485
|
return true;
|
|
1382
1486
|
}
|
|
1383
1487
|
if (!disableSsrRedux) {
|
|
1384
|
-
|
|
1385
|
-
// where a consumer may not be using the contensisVersionStatus in redux and calling
|
|
1386
|
-
// the `getClientSideVersionStatus()` method directly
|
|
1387
|
-
serialisedReduxData = `<script ${attributes}>window.versionStatus = "${versionStatus}"; window.REDUX_DATA = ${serialisedReduxData}</script>`;
|
|
1488
|
+
serialisedReduxData = `<script ${attributes}>${subsitePathScript} window.__USE_HYDRATE__ = true; window.REDUX_DATA = ${serialisedReduxData}</script>`;
|
|
1388
1489
|
}
|
|
1389
1490
|
}
|
|
1390
1491
|
|
|
1391
1492
|
// Responses
|
|
1392
|
-
|
|
1393
|
-
const helmet = Helmet.renderStatic();
|
|
1394
|
-
Helmet.rewind();
|
|
1395
|
-
const htmlAttributes = helmet.htmlAttributes.toString();
|
|
1396
|
-
let title = helmet.title.toString();
|
|
1397
|
-
const metadata = helmet.meta.toString().concat(helmet.base.toString()).concat(helmet.link.toString()).concat(helmet.script.toString()).concat(helmet.noscript.toString());
|
|
1398
1493
|
addStandardHeaders(reduxState, response, packagejson, {
|
|
1399
1494
|
allowedGroups,
|
|
1400
1495
|
globalGroups
|
|
1401
1496
|
});
|
|
1402
|
-
|
|
1403
|
-
// After running rootSaga there should be an additional react-loadable
|
|
1404
|
-
// code-split bundles for any page components as well as core app bundles
|
|
1405
|
-
const bundleTags = getBundleTags(loadableExtractor, scripts, staticRoutePath);
|
|
1406
1497
|
const sheet = new ServerStyleSheet();
|
|
1407
|
-
// Produce the ssr jsx one time so we can get any style tags to pass back in
|
|
1408
|
-
ssrJsxProducer(ReactApp, {
|
|
1409
|
-
providers: {
|
|
1410
|
-
...jsxProviderProps,
|
|
1411
|
-
styledComponents: {
|
|
1412
|
-
sheet
|
|
1413
|
-
}
|
|
1414
|
-
},
|
|
1415
|
-
props: jsxReactAppProps
|
|
1416
|
-
});
|
|
1417
|
-
let styleTags = sheet.getStyleTags();
|
|
1418
1498
|
const styledJsx = ssrJsxProducer(ReactApp, {
|
|
1419
1499
|
providers: {
|
|
1420
1500
|
...jsxProviderProps,
|
|
@@ -1422,14 +1502,31 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1422
1502
|
sheet
|
|
1423
1503
|
}
|
|
1424
1504
|
},
|
|
1425
|
-
props: jsxReactAppProps
|
|
1426
|
-
ssrAssets: {
|
|
1427
|
-
bundleTags,
|
|
1428
|
-
htmlAttributes,
|
|
1429
|
-
metadata,
|
|
1430
|
-
title
|
|
1431
|
-
}
|
|
1505
|
+
props: jsxReactAppProps
|
|
1432
1506
|
});
|
|
1507
|
+
|
|
1508
|
+
// We have to call renderToString() in order for all components to have
|
|
1509
|
+
// had chance to set the helmet metadata
|
|
1510
|
+
const html = renderToString(styledJsx);
|
|
1511
|
+
// Helmet.renderStatic() has to be called synchronously immediately after calling renderToString()
|
|
1512
|
+
// as it is not thread-safe (or specifically scoped to only this request)
|
|
1513
|
+
// TODO: deprecate `react-helmet`
|
|
1514
|
+
const helmet = Helmet.renderStatic();
|
|
1515
|
+
|
|
1516
|
+
// helmetContext is populated synchronously by HelmetProvider during renderToString()
|
|
1517
|
+
// It is scoped per-request via the helmetContext object, making this thread-safe
|
|
1518
|
+
// under concurrent SSR requests (unlike the previous Helmet.renderStatic() global singleton)
|
|
1519
|
+
const {
|
|
1520
|
+
helmet: helmetAsync
|
|
1521
|
+
} = helmetContext;
|
|
1522
|
+
|
|
1523
|
+
// Because we have had to call renderToString() here to reliably gather all helmet metadata
|
|
1524
|
+
// We could potentially call sheet.getStyleTags() here too and avoid piping a react-rendered
|
|
1525
|
+
// stream to a second stream to inject styled-components CSS
|
|
1526
|
+
|
|
1527
|
+
const htmlAttributes = helmetAsync.htmlAttributes.toString() || helmet.htmlAttributes.toString();
|
|
1528
|
+
let title = helmet.title.toString().includes('><') ? helmetAsync.title.toString() : helmet.title.toString();
|
|
1529
|
+
const metadata = helmetAsync.meta.toString().concat(helmetAsync.base.toString()).concat(helmetAsync.priority.toString()).concat(helmetAsync.link.toString()).concat(helmetAsync.script.toString()).concat(helmetAsync.noscript.toString()).concat(helmet.meta.toString()).concat(helmet.base.toString()).concat(helmet.link.toString()).concat(helmet.script.toString()).concat(helmet.noscript.toString());
|
|
1433
1530
|
try {
|
|
1434
1531
|
/**
|
|
1435
1532
|
* Loads all page assets into the provided templateHTML
|
|
@@ -1440,7 +1537,8 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1440
1537
|
* we render the page as STATIC or render nothing
|
|
1441
1538
|
* if the context has requested a redirect
|
|
1442
1539
|
* */
|
|
1443
|
-
const getContextHtml =
|
|
1540
|
+
const getContextHtml = (isFinal = false, styleTags, renderedJsxMarkup) => {
|
|
1541
|
+
var _loadableExtractor$mo;
|
|
1444
1542
|
if (context.url) {
|
|
1445
1543
|
response.redirect(context.statusCode || 302, context.url);
|
|
1446
1544
|
return '';
|
|
@@ -1454,13 +1552,19 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1454
1552
|
|
|
1455
1553
|
// Set response.status from React StaticRouter
|
|
1456
1554
|
if (typeof context.statusCode === 'number') response.status(context.statusCode);
|
|
1555
|
+
const bundleTags = isFinal ? getBundleTags(loadableExtractor, scripts, staticRoutePath) : '';
|
|
1556
|
+
|
|
1557
|
+
// Getting style tags generated by "CSS Modules" because they will be
|
|
1558
|
+
// available to loadable stats if we have built parts of the app with CSS
|
|
1559
|
+
// plugins that are not within styled-components
|
|
1560
|
+
const styles = loadableExtractor === null || loadableExtractor === void 0 || (_loadableExtractor$mo = loadableExtractor.modern) === null || _loadableExtractor$mo === void 0 ? void 0 : _loadableExtractor$mo.getStyleTags();
|
|
1457
1561
|
const html = replaceHtml({
|
|
1458
1562
|
bundleTags,
|
|
1459
|
-
html:
|
|
1563
|
+
html: renderedJsxMarkup,
|
|
1460
1564
|
htmlAttributes,
|
|
1461
1565
|
metadata,
|
|
1462
1566
|
state: serialisedReduxData,
|
|
1463
|
-
styleTags
|
|
1567
|
+
styleTags: `${styleTags || ''}${styles || ''}`,
|
|
1464
1568
|
title,
|
|
1465
1569
|
templateHTML,
|
|
1466
1570
|
templateHTMLFragment,
|
|
@@ -1469,12 +1573,14 @@ const webApp = (app, ReactApp, config) => {
|
|
|
1469
1573
|
return html;
|
|
1470
1574
|
};
|
|
1471
1575
|
if (isRenderingJsxToString) {
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
const
|
|
1576
|
+
// We have already (begrudgingly) rendered the JSX to a string above
|
|
1577
|
+
// so we can get all of the Helmet metadata out from any rendered component
|
|
1578
|
+
// const html = renderToString(styledJsx);
|
|
1579
|
+
const styleTags = sheet.getStyleTags();
|
|
1580
|
+
const responseHTML = getContextHtml(true, styleTags, html);
|
|
1475
1581
|
responseHandler(request, response, responseHTML);
|
|
1476
1582
|
} else {
|
|
1477
|
-
renderStream(getContextHtml, styledJsx, response, styledComponentsStream(sheet));
|
|
1583
|
+
renderStream(getContextHtml, styledJsx, request, response, styledComponentsStream(sheet));
|
|
1478
1584
|
}
|
|
1479
1585
|
} catch (err) {
|
|
1480
1586
|
console.info(err.message);
|
|
@@ -1536,5 +1642,5 @@ var internalServer = {
|
|
|
1536
1642
|
start
|
|
1537
1643
|
};
|
|
1538
1644
|
|
|
1539
|
-
export { internalServer as default, makeLinkDepthApi as linkDepthApi };
|
|
1645
|
+
export { subsiteDebugMiddleware as DO_NOT_COMMIT_subsiteDebugMiddleware, internalServer as default, makeLinkDepthApi as linkDepthApi };
|
|
1540
1646
|
//# sourceMappingURL=contensis-react-base.js.map
|