@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.
Files changed (206) hide show
  1. package/README.md +14 -1
  2. package/cjs/{App-vZrUfVgQ.js → App-Dr56ZsQj.js} +476 -99
  3. package/cjs/App-Dr56ZsQj.js.map +1 -0
  4. package/cjs/{ChangePassword.container-ECjEXixF.js → ChangePassword.container-C4Du3Wb1.js} +57 -50
  5. package/cjs/ChangePassword.container-C4Du3Wb1.js.map +1 -0
  6. package/cjs/{SSRContext-DVj_QAC1.js → ContensisDeliveryApi-gN3_MHEl.js} +32 -74
  7. package/cjs/ContensisDeliveryApi-gN3_MHEl.js.map +1 -0
  8. package/cjs/CookieConstants-DfPiWCRZ.js +12 -0
  9. package/cjs/CookieConstants-DfPiWCRZ.js.map +1 -0
  10. package/{esm/CookieHelper.class-FTURFpz3.js → cjs/CookieHelper.class-Det3qfdU.js} +4 -6
  11. package/cjs/CookieHelper.class-Det3qfdU.js.map +1 -0
  12. package/cjs/{RouteLoader-D5Yg7EB5.js → RouteLoader-Bbt-nG3v.js} +13 -8
  13. package/cjs/RouteLoader-Bbt-nG3v.js.map +1 -0
  14. package/cjs/SSRContext-DotLlTQc.js +116 -0
  15. package/cjs/SSRContext-DotLlTQc.js.map +1 -0
  16. package/cjs/ToJs-BsWqWjdm.js +23 -0
  17. package/cjs/ToJs-BsWqWjdm.js.map +1 -0
  18. package/cjs/{VersionInfo-B_dKCubg.js → VersionInfo-zFPsvS8q.js} +3 -25
  19. package/cjs/VersionInfo-zFPsvS8q.js.map +1 -0
  20. package/cjs/client.js +62 -64
  21. package/cjs/client.js.map +1 -1
  22. package/cjs/contensis-react-base.js +235 -127
  23. package/cjs/contensis-react-base.js.map +1 -1
  24. package/cjs/i18n.js +75 -0
  25. package/cjs/i18n.js.map +1 -0
  26. package/cjs/{ToJs-C9jwV7YB.js → matchGroups-dqONU-vY.js} +2 -22
  27. package/cjs/matchGroups-dqONU-vY.js.map +1 -0
  28. package/cjs/redux.js +8 -6
  29. package/cjs/redux.js.map +1 -1
  30. package/cjs/routing.js +15 -7
  31. package/cjs/routing.js.map +1 -1
  32. package/cjs/{sagas-CbZhaRNd.js → sagas-OfBUtx74.js} +523 -370
  33. package/cjs/sagas-OfBUtx74.js.map +1 -0
  34. package/cjs/search.js +54 -29
  35. package/cjs/search.js.map +1 -1
  36. package/cjs/{selectors-wCs5fHD4.js → selectors-BrxJ8-F8.js} +27 -6
  37. package/cjs/selectors-BrxJ8-F8.js.map +1 -0
  38. package/cjs/selectors-DAQR0uZa.js +18 -0
  39. package/cjs/selectors-DAQR0uZa.js.map +1 -0
  40. package/cjs/slice-5xJMH24n.js +69 -0
  41. package/cjs/slice-5xJMH24n.js.map +1 -0
  42. package/cjs/{store-D07FOXvM.js → store-Dn7vP6G0.js} +52 -4
  43. package/cjs/store-Dn7vP6G0.js.map +1 -0
  44. package/cjs/urls-DVIwGZmd.js +25 -0
  45. package/cjs/urls-DVIwGZmd.js.map +1 -0
  46. package/cjs/user.js +20 -17
  47. package/cjs/user.js.map +1 -1
  48. package/cjs/util-wQwG9vit.js +148 -0
  49. package/cjs/util-wQwG9vit.js.map +1 -0
  50. package/cjs/util.js +80 -22
  51. package/cjs/util.js.map +1 -1
  52. package/cjs/{version-B7XFkBhY.js → version-2FamXHhj.js} +15 -16
  53. package/cjs/version-2FamXHhj.js.map +1 -0
  54. package/cjs/{version-CM-bJ62L.js → version-rFG9Y6_B.js} +2 -2
  55. package/cjs/{version-CM-bJ62L.js.map → version-rFG9Y6_B.js.map} +1 -1
  56. package/esm/{App-DLZweVSp.js → App-CrCf7gso.js} +436 -60
  57. package/esm/App-CrCf7gso.js.map +1 -0
  58. package/esm/{ChangePassword.container-BgzIy8dA.js → ChangePassword.container-CUBtn82K.js} +19 -13
  59. package/esm/ChangePassword.container-CUBtn82K.js.map +1 -0
  60. package/esm/{SSRContext-BE8ElZ3X.js → ContensisDeliveryApi-CvEoOLCl.js} +30 -67
  61. package/esm/ContensisDeliveryApi-CvEoOLCl.js.map +1 -0
  62. package/esm/CookieConstants-DEmbwzYr.js +7 -0
  63. package/esm/CookieConstants-DEmbwzYr.js.map +1 -0
  64. package/{cjs/CookieHelper.class-C3Eqoze9.js → esm/CookieHelper.class-C6rTRl_1.js} +2 -14
  65. package/esm/CookieHelper.class-C6rTRl_1.js.map +1 -0
  66. package/esm/{RouteLoader-xeQBXywk.js → RouteLoader-BpHhiAlL.js} +10 -5
  67. package/esm/RouteLoader-BpHhiAlL.js.map +1 -0
  68. package/esm/SSRContext-CYxBWky3.js +106 -0
  69. package/esm/SSRContext-CYxBWky3.js.map +1 -0
  70. package/esm/ToJs-BnRRHk6f.js +17 -0
  71. package/esm/ToJs-BnRRHk6f.js.map +1 -0
  72. package/esm/{VersionInfo-Cno7K0OA.js → VersionInfo-By2ZCZOh.js} +4 -24
  73. package/esm/VersionInfo-By2ZCZOh.js.map +1 -0
  74. package/esm/client.js +62 -63
  75. package/esm/client.js.map +1 -1
  76. package/esm/contensis-react-base.js +227 -121
  77. package/esm/contensis-react-base.js.map +1 -1
  78. package/esm/i18n.js +64 -0
  79. package/esm/i18n.js.map +1 -0
  80. package/esm/{ToJs-CNzfvyxJ.js → matchGroups-_w8BwzCC.js} +3 -18
  81. package/esm/matchGroups-_w8BwzCC.js.map +1 -0
  82. package/esm/redux.js +11 -8
  83. package/esm/redux.js.map +1 -1
  84. package/esm/routing.js +14 -7
  85. package/esm/routing.js.map +1 -1
  86. package/esm/{sagas-xJU-zOpn.js → sagas-BZWjx5by.js} +511 -357
  87. package/esm/sagas-BZWjx5by.js.map +1 -0
  88. package/esm/search.js +73 -47
  89. package/esm/search.js.map +1 -1
  90. package/esm/{selectors-DO2ocdOp.js → selectors-8ROQrTd7.js} +25 -7
  91. package/esm/selectors-8ROQrTd7.js.map +1 -0
  92. package/esm/selectors-DcmvOeX2.js +10 -0
  93. package/esm/selectors-DcmvOeX2.js.map +1 -0
  94. package/esm/slice-C6JLQik8.js +63 -0
  95. package/esm/slice-C6JLQik8.js.map +1 -0
  96. package/esm/{store-3u0RzHZ0.js → store-DSjRYsM2.js} +52 -5
  97. package/esm/store-DSjRYsM2.js.map +1 -0
  98. package/esm/urls-DfCisos-.js +22 -0
  99. package/esm/urls-DfCisos-.js.map +1 -0
  100. package/esm/user.js +9 -6
  101. package/esm/user.js.map +1 -1
  102. package/esm/util-BafFLYzn.js +136 -0
  103. package/esm/util-BafFLYzn.js.map +1 -0
  104. package/esm/util.js +58 -14
  105. package/esm/util.js.map +1 -1
  106. package/esm/{version-BlsI7hX2.js → version-B75wA6Te.js} +16 -16
  107. package/esm/version-B75wA6Te.js.map +1 -0
  108. package/esm/{version-wnf-TITV.js → version-BQAL8sQO.js} +2 -2
  109. package/esm/{version-wnf-TITV.js.map → version-BQAL8sQO.js.map} +1 -1
  110. package/i18n/package.json +5 -0
  111. package/models/app/pages/VersionInfo/components/VersionInfo.d.ts +1 -1
  112. package/models/app/pages/VersionInfo/components/VersionInfo.styled.d.ts +0 -1
  113. package/models/i18n/index.d.ts +5 -0
  114. package/models/i18n/redux/sagas.d.ts +19 -0
  115. package/models/i18n/redux/selectors.d.ts +11 -0
  116. package/models/i18n/redux/slice.d.ts +198 -0
  117. package/models/i18n/routes.d.ts +8 -0
  118. package/models/i18n/useI18n.hook.d.ts +20 -0
  119. package/models/index.d.ts +1 -0
  120. package/models/models/AppState.d.ts +2 -0
  121. package/models/models/ContentTypeMapping.d.ts +5 -0
  122. package/models/models/Locales.d.ts +11 -0
  123. package/models/models/MatchedRoute.d.ts +5 -1
  124. package/models/models/RouteComponent.d.ts +0 -1
  125. package/models/models/SSRContext.d.ts +4 -4
  126. package/models/models/StaticRoute.d.ts +11 -0
  127. package/models/models/WithEvents.d.ts +8 -0
  128. package/models/models/config/AppConfig.d.ts +2 -0
  129. package/models/models/config/I18n.d.ts +38 -0
  130. package/models/models/config/ServerConfig.d.ts +3 -0
  131. package/models/redux/index.d.ts +2 -1
  132. package/models/redux/sagas/index.d.ts +3 -1
  133. package/models/redux/sagas/injector.d.ts +13 -0
  134. package/models/redux/store/injectors/index.d.ts +26 -0
  135. package/models/redux/store/injectors/inject.d.ts +24 -0
  136. package/models/redux/store/injectors/util.d.ts +2 -0
  137. package/models/redux/store/store.d.ts +13 -4
  138. package/models/redux/util.d.ts +1 -1
  139. package/models/routing/components/RouteLoader.d.ts +3 -3
  140. package/models/routing/httpContext.d.ts +0 -1
  141. package/models/routing/index.d.ts +1 -0
  142. package/models/routing/redux/actions.d.ts +1 -1
  143. package/models/routing/redux/invokeSearch.d.ts +22 -0
  144. package/models/routing/redux/selectors.d.ts +47 -4
  145. package/models/routing/util/expressions.d.ts +1 -1
  146. package/models/routing/util/find-contenttype-mapping.d.ts +3 -1
  147. package/models/search/containers/withListing.d.ts +1 -1
  148. package/models/search/containers/withSearch.d.ts +1 -1
  149. package/models/search/models/Queries.d.ts +3 -5
  150. package/models/search/models/Search.d.ts +43 -13
  151. package/models/search/models/SearchActions.d.ts +61 -18
  152. package/models/search/models/SearchProps.d.ts +11 -10
  153. package/models/search/models/SearchState.d.ts +23 -2
  154. package/models/search/models/SearchUtil.d.ts +3 -3
  155. package/models/search/redux/getIn.d.ts +2 -2
  156. package/models/search/redux/reducers.d.ts +3 -4
  157. package/models/search/redux/sagas.d.ts +13 -14
  158. package/models/search/redux/schema.d.ts +3 -3
  159. package/models/search/redux/selectors.d.ts +64 -42
  160. package/models/search/redux/util.d.ts +10 -1
  161. package/models/search/search/ContensisDeliveryApi.d.ts +6 -26
  162. package/models/search/search/expressions.d.ts +6 -4
  163. package/models/search/search/util.d.ts +9 -7
  164. package/models/search/transformations/state-to-queryparams.mapper.d.ts +1 -1
  165. package/models/server/features/linkdepth-api/search.d.ts +1 -1
  166. package/models/server/features/response-handler/render-stream.d.ts +2 -4
  167. package/models/server/features/static-assets/index.d.ts +4 -3
  168. package/models/server/internalServer.d.ts +1 -2
  169. package/models/server/middleware/subsiteDebug.d.ts +11 -0
  170. package/models/server/root.d.ts +3 -0
  171. package/models/server/util/bundles.d.ts +9 -9
  172. package/models/server/util/jsx.d.ts +2 -14
  173. package/models/user/hocs/withRegistration.d.ts +1 -1
  174. package/models/util/CachedDeliveryApi.d.ts +8 -2
  175. package/models/util/ContensisDeliveryApi.d.ts +2 -4
  176. package/models/util/NoSSR.d.ts +6 -0
  177. package/models/util/SSRContext.d.ts +3 -19
  178. package/models/util/donotuse_useHistory.d.ts +6 -0
  179. package/models/util/index.d.ts +6 -1
  180. package/models/util/subsite.d.ts +12 -0
  181. package/models/util/useIsClient.d.ts +6 -0
  182. package/package.json +39 -38
  183. package/cjs/App-vZrUfVgQ.js.map +0 -1
  184. package/cjs/ChangePassword.container-ECjEXixF.js.map +0 -1
  185. package/cjs/CookieHelper.class-C3Eqoze9.js.map +0 -1
  186. package/cjs/RouteLoader-D5Yg7EB5.js.map +0 -1
  187. package/cjs/SSRContext-DVj_QAC1.js.map +0 -1
  188. package/cjs/ToJs-C9jwV7YB.js.map +0 -1
  189. package/cjs/VersionInfo-B_dKCubg.js.map +0 -1
  190. package/cjs/sagas-CbZhaRNd.js.map +0 -1
  191. package/cjs/selectors-wCs5fHD4.js.map +0 -1
  192. package/cjs/store-D07FOXvM.js.map +0 -1
  193. package/cjs/version-B7XFkBhY.js.map +0 -1
  194. package/esm/App-DLZweVSp.js.map +0 -1
  195. package/esm/ChangePassword.container-BgzIy8dA.js.map +0 -1
  196. package/esm/CookieHelper.class-FTURFpz3.js.map +0 -1
  197. package/esm/RouteLoader-xeQBXywk.js.map +0 -1
  198. package/esm/SSRContext-BE8ElZ3X.js.map +0 -1
  199. package/esm/ToJs-CNzfvyxJ.js.map +0 -1
  200. package/esm/VersionInfo-Cno7K0OA.js.map +0 -1
  201. package/esm/sagas-xJU-zOpn.js.map +0 -1
  202. package/esm/selectors-DO2ocdOp.js.map +0 -1
  203. package/esm/store-3u0RzHZ0.js.map +0 -1
  204. package/esm/version-BlsI7hX2.js.map +0 -1
  205. package/models/redux/store/injectors.d.ts +0 -31
  206. package/models/search/search/ToJs.d.ts +0 -4
@@ -1,23 +1,24 @@
1
- import { c as cachedSearch, S as SSRContextProvider, d as deliveryApi } from './SSRContext-BE8ElZ3X.js';
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 { a8 as defaultExpressions, a9 as termExpressions, aa as contentTypeIdExpression, ab as filterExpressions, ac as orderByExpression, ad as customWhereExpressions, ae as cloneDeep } from './sagas-xJU-zOpn.js';
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 './VersionInfo-Cno7K0OA.js';
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 { path as path$1 } from 'app-root-path';
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-FTURFpz3.js';
29
+ import { a as Cookies } from './CookieHelper.class-C6rTRl_1.js';
29
30
  import cookiesMiddleware from 'universal-cookie-express';
30
- import { c as createStore } from './store-3u0RzHZ0.js';
31
- import { h as history, p as pickProject, r as rootSaga } from './App-DLZweVSp.js';
32
- export { A as ReactApp } from './App-DLZweVSp.js';
33
- import { s as setVersionStatus, d as setVersion } from './version-BlsI7hX2.js';
34
- import { a3 as selectSurrogateKeys, a4 as selectSsrApiCalls, h as selectRouteEntry, n as selectCurrentProject, g as getImmutableOrJS, d as setCurrentProject, K as selectCurrentSearch } from './selectors-DO2ocdOp.js';
35
- import { H as HttpContext, m as mergeStaticRoutes } from './RouteLoader-xeQBXywk.js';
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 './version-wnf-TITV.js';
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, (req, res) => {
622
- const target = req.hostname.indexOf('preview-') || req.hostname.indexOf('preview.') || req.hostname === 'localhost' ? servers$1.previewIis || servers$1.iis : servers$1.iis;
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
- assetProxy.on('error', e => {
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/*', '/api/forms/*', '/api/image/*', '/authenticate/*'], (req, res) => {
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
- apiProxy.on('error', e => {
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
- next();
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 = path$1,
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
- let header = '';
807
- let footer = '';
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
- abort();
871
+ abortCleanup();
817
872
  } else {
818
- [header, footer] = html.split('{{APP}}');
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 if enough time passes.
896
+ // Abandon and switch to client rendering after 30s.
839
897
  // Try lowering this to see the client recover.
840
- setTimeout(() => abort(), 30 * 1000);
841
- stream === null || stream === void 0 || stream.pipe(response);
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
- console.info('@loadable/server legacy ChunkExtractor not available');
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}}', '').replace('{{LOADABLE_CHUNKS}}', bundleTags).replace('{{REDUX_DATA}}', state);
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).replace('{{REDUX_DATA}}', state);
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).replace('{{REDUX_DATA}}', state);
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
- return html ? responseHTML.replace('{{APP}}', html) : responseHTML;
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(ChunkExtractor, {
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
- ssrAssets: ssrAssets
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 = 'immutable',
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('/*', cookiesMiddleware(), async (request, response) => {
1211
- const url = encodeURI(request.url);
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.versionStatus = "${versionStatus}"; window.isDynamic = true;</script>`;
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
- // window.versionStatus is not strictly required here and is added to support cases
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 = renderedJsx => {
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: renderedJsx,
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
- const html = renderToString(styledJsx);
1473
- styleTags = sheet.getStyleTags();
1474
- const responseHTML = getContextHtml(html);
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