@salesforce/pwa-kit-react-sdk 3.8.0-preview.0-basepath → 3.8.0-preview.0
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/CHANGELOG.md +3 -7
- package/package.json +6 -5
- package/ssr/browser/main.js +130 -0
- package/ssr/browser/main.test.js +54 -0
- package/ssr/server/react-rendering.js +434 -0
- package/ssr/server/react-rendering.test.js +745 -0
- package/ssr/universal/compatibility.js +31 -0
- package/ssr/universal/components/_app/index.js +35 -0
- package/ssr/universal/components/_app/index.test.js +20 -0
- package/ssr/universal/components/_app-config/index.js +87 -0
- package/ssr/universal/components/_app-config/index.test.js +21 -0
- package/ssr/universal/components/_document/index.js +92 -0
- package/ssr/universal/components/_document/index.test.js +58 -0
- package/ssr/universal/components/_error/index.js +55 -0
- package/ssr/universal/components/_error/index.test.js +28 -0
- package/ssr/universal/components/app-error-boundary/index.js +113 -0
- package/ssr/universal/components/app-error-boundary/index.test.js +109 -0
- package/ssr/universal/components/fetch-strategy/index.js +42 -0
- package/ssr/universal/components/refresh/index.js +123 -0
- package/ssr/universal/components/refresh/index.test.js +78 -0
- package/ssr/universal/components/route-component/index.js +415 -0
- package/ssr/universal/components/route-component/index.test.js +378 -0
- package/ssr/universal/components/switch/index.js +62 -0
- package/ssr/universal/components/throw-404/index.js +36 -0
- package/ssr/universal/components/throw-404/index.test.js +26 -0
- package/ssr/universal/components/with-correlation-id/index.js +36 -0
- package/ssr/universal/components/with-legacy-get-props/index.js +100 -0
- package/ssr/universal/components/with-legacy-get-props/index.test.js +35 -0
- package/ssr/universal/components/with-react-query/index.js +130 -0
- package/ssr/universal/components/with-react-query/index.test.js +101 -0
- package/ssr/universal/contexts/index.js +72 -0
- package/ssr/universal/contexts/index.test.js +101 -0
- package/ssr/universal/errors.js +34 -0
- package/ssr/universal/errors.test.js +20 -0
- package/ssr/universal/events.js +38 -0
- package/ssr/universal/events.test.js +39 -0
- package/ssr/universal/hooks/index.js +84 -0
- package/ssr/universal/routes.js +15 -0
- package/ssr/universal/utils.client.test.js +46 -0
- package/ssr/universal/utils.js +61 -0
- package/ssr/universal/utils.server.test.js +24 -0
- package/utils/assets.js +120 -0
- package/utils/assets.test.js +106 -0
- package/utils/logger-instance.js +19 -0
- package/utils/performance.js +126 -0
- package/utils/performance.test.js +50 -0
- package/utils/url.js +41 -0
- package/utils/url.test.js +47 -0
- package/utils/uuidv4.client.js +21 -0
- package/utils/uuidv4.client.test.js +27 -0
- package/utils/warnings.js +81 -0
- package/utils/warnings.test.js +48 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
## v3.8.0-preview.0
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
## v3.8.0-dev (Aug 08, 2024)
|
|
5
|
-
|
|
6
|
-
- Add support for environment level base paths, a new configuration option `envBasePath` [#1970](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/1970)
|
|
7
|
-
- Add support for React Router level base paths, a new `getBasePath` method to the `AppConfig` component [#1988](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/1988)
|
|
1
|
+
## v3.8.0-preview.0 (Oct 23, 2024)
|
|
2
|
+
- [Server Affinity] - Attach dwsid to SCAPI request headers [#2090](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2090)
|
|
3
|
+
- Create useOrigin hook to return an app origin that takes x-forwarded-host header into consideration. [#2050](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2050)
|
|
8
4
|
|
|
9
5
|
## v3.7.0 (Aug 07, 2024)
|
|
10
6
|
- Add `beforeHydrate` option to withReactQuery component [#1912](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/1912)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/pwa-kit-react-sdk",
|
|
3
|
-
"version": "3.8.0-preview.0
|
|
3
|
+
"version": "3.8.0-preview.0",
|
|
4
4
|
"description": "A library that supports the isomorphic React rendering pipeline for Commerce Cloud Managed Runtime apps",
|
|
5
5
|
"homepage": "https://github.com/SalesforceCommerceCloud/pwa-kit/tree/develop/packages/pwa-kit-react-sdk#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@loadable/babel-plugin": "^5.15.3",
|
|
38
38
|
"@loadable/server": "^5.15.3",
|
|
39
39
|
"@loadable/webpack-plugin": "^5.15.2",
|
|
40
|
-
"@salesforce/pwa-kit-runtime": "3.8.0-preview.0
|
|
40
|
+
"@salesforce/pwa-kit-runtime": "3.8.0-preview.0",
|
|
41
41
|
"@tanstack/react-query": "^4.28.0",
|
|
42
42
|
"cross-env": "^5.2.1",
|
|
43
43
|
"event-emitter": "^0.3.5",
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@loadable/component": "^5.15.3",
|
|
53
|
-
"@salesforce/pwa-kit-dev": "3.8.0-preview.0
|
|
53
|
+
"@salesforce/pwa-kit-dev": "3.8.0-preview.0",
|
|
54
54
|
"@testing-library/jest-dom": "^5.16.5",
|
|
55
55
|
"@testing-library/react": "^14.0.0",
|
|
56
56
|
"@testing-library/user-event": "^14.4.3",
|
|
57
|
-
"internal-lib-build": "3.8.0-preview.0
|
|
57
|
+
"internal-lib-build": "3.8.0-preview.0",
|
|
58
58
|
"node-html-parser": "^3.3.6",
|
|
59
59
|
"nodemon": "^2.0.22",
|
|
60
60
|
"react": "^18.2.0",
|
|
@@ -78,5 +78,6 @@
|
|
|
78
78
|
},
|
|
79
79
|
"publishConfig": {
|
|
80
80
|
"directory": "dist"
|
|
81
|
-
}
|
|
81
|
+
},
|
|
82
|
+
"gitHead": "6f0e672fef7517c7d8a5b80242a9cc0e929bc5ac"
|
|
82
83
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.start = exports.registerServiceWorker = exports.OuterApp = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _client = require("react-dom/client");
|
|
9
|
+
var _reactRouterDom = require("react-router-dom");
|
|
10
|
+
var _contexts = require("../universal/contexts");
|
|
11
|
+
var _app = _interopRequireDefault(require("../universal/components/_app"));
|
|
12
|
+
var _compatibility = require("../universal/compatibility");
|
|
13
|
+
var _switch = _interopRequireDefault(require("../universal/components/switch"));
|
|
14
|
+
var _routeComponent = require("../universal/components/route-component");
|
|
15
|
+
var _component = require("@loadable/component");
|
|
16
|
+
var _uuidv = require("../../utils/uuidv4.client");
|
|
17
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
18
|
+
var _loggerInstance = _interopRequireDefault(require("../../utils/logger-instance"));
|
|
19
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
21
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
22
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /*
|
|
23
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
24
|
+
* All rights reserved.
|
|
25
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
26
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
27
|
+
*/ /* global __webpack_require__ */
|
|
28
|
+
/* istanbul ignore next */
|
|
29
|
+
const registerServiceWorker = url => {
|
|
30
|
+
return Promise.resolve().then(() => {
|
|
31
|
+
if ('serviceWorker' in navigator) {
|
|
32
|
+
return Promise.resolve().then(() => new Promise(resolve => window.addEventListener('load', resolve))).then(() => navigator.serviceWorker.register(url)).then(registration => _loggerInstance.default.info(`ServiceWorker registration successful with scope: ${registration.scope}`, {
|
|
33
|
+
namespace: 'registerServiceWorker'
|
|
34
|
+
})).catch(err => _loggerInstance.default.error('ServiceWorker registration failed', {
|
|
35
|
+
namespace: 'registerServiceWorker',
|
|
36
|
+
additionalProperties: {
|
|
37
|
+
error: err
|
|
38
|
+
}
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
exports.registerServiceWorker = registerServiceWorker;
|
|
44
|
+
const OuterApp = ({
|
|
45
|
+
routes,
|
|
46
|
+
error,
|
|
47
|
+
WrappedApp,
|
|
48
|
+
locals,
|
|
49
|
+
onHydrate
|
|
50
|
+
}) => {
|
|
51
|
+
const AppConfig = (0, _compatibility.getAppConfig)();
|
|
52
|
+
const isInitialPageRef = (0, _react.useRef)(true);
|
|
53
|
+
return /*#__PURE__*/_react.default.createElement(_contexts.ServerContext.Provider, {
|
|
54
|
+
value: {}
|
|
55
|
+
}, /*#__PURE__*/_react.default.createElement(_reactRouterDom.BrowserRouter, {
|
|
56
|
+
ref: onHydrate
|
|
57
|
+
}, /*#__PURE__*/_react.default.createElement(_contexts.CorrelationIdProvider, {
|
|
58
|
+
correlationId: () => {
|
|
59
|
+
// If we are hydrating an error page use the server correlation id.
|
|
60
|
+
if (isInitialPageRef.current && window.__ERROR__) {
|
|
61
|
+
isInitialPageRef.current = false;
|
|
62
|
+
return window.__INITIAL_CORRELATION_ID__;
|
|
63
|
+
}
|
|
64
|
+
return (0, _uuidv.uuidv4)();
|
|
65
|
+
}
|
|
66
|
+
}, /*#__PURE__*/_react.default.createElement(AppConfig, {
|
|
67
|
+
locals: locals
|
|
68
|
+
}, /*#__PURE__*/_react.default.createElement(_switch.default, {
|
|
69
|
+
error: error,
|
|
70
|
+
appState: window.__PRELOADED_STATE__,
|
|
71
|
+
routes: routes,
|
|
72
|
+
App: WrappedApp
|
|
73
|
+
})))));
|
|
74
|
+
};
|
|
75
|
+
exports.OuterApp = OuterApp;
|
|
76
|
+
OuterApp.propTypes = {
|
|
77
|
+
routes: _propTypes.default.array.isRequired,
|
|
78
|
+
error: _propTypes.default.object,
|
|
79
|
+
WrappedApp: _propTypes.default.func.isRequired,
|
|
80
|
+
locals: _propTypes.default.object,
|
|
81
|
+
onHydrate: _propTypes.default.func
|
|
82
|
+
};
|
|
83
|
+
/* istanbul ignore next */
|
|
84
|
+
const start = () => {
|
|
85
|
+
const AppConfig = (0, _compatibility.getAppConfig)();
|
|
86
|
+
const rootEl = document.getElementsByClassName('react-target')[0];
|
|
87
|
+
const data = JSON.parse(document.getElementById('mobify-data').innerHTML);
|
|
88
|
+
|
|
89
|
+
// Set all globals sent from the server on the window object.
|
|
90
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
91
|
+
window[key] = value;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Tell webpack how to find javascript files
|
|
95
|
+
Object.defineProperty(__webpack_require__, 'p', {
|
|
96
|
+
get: () => window.Progressive.buildOrigin
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// On the browser we don't have request.locals, so we just provide an empty
|
|
100
|
+
// object that exists for the lifetime of the app. AppConfig components can use
|
|
101
|
+
// this to set up, eg. Redux stores.
|
|
102
|
+
const locals = {};
|
|
103
|
+
|
|
104
|
+
// AppConfig.restore *must* come before getRoutes()
|
|
105
|
+
AppConfig.restore(locals, window.__PRELOADED_STATE__.__STATE_MANAGEMENT_LIBRARY);
|
|
106
|
+
|
|
107
|
+
// We need to tell the routeComponent HOC when the app is hydrating in order to
|
|
108
|
+
// prevent pages from re-fetching data on the first client-side render. The
|
|
109
|
+
// reason we do this is that we expect a render to have taken place
|
|
110
|
+
// on the server already. That server-side render already called getProps()
|
|
111
|
+
// and froze the application state as a JSON blob on the page.
|
|
112
|
+
//
|
|
113
|
+
// This is VERY fiddly – don't go crazy with window.__HYDRATING__. You have
|
|
114
|
+
// been warned.
|
|
115
|
+
window.__HYDRATING__ = true;
|
|
116
|
+
const props = {
|
|
117
|
+
error: window.__ERROR__,
|
|
118
|
+
locals: locals,
|
|
119
|
+
routes: (0, _routeComponent.getRoutes)(locals),
|
|
120
|
+
WrappedApp: (0, _routeComponent.routeComponent)(_app.default, false, locals)
|
|
121
|
+
};
|
|
122
|
+
return Promise.resolve().then(() => new Promise(resolve => (0, _component.loadableReady)(resolve))).then(() => {
|
|
123
|
+
(0, _client.hydrateRoot)(rootEl, /*#__PURE__*/_react.default.createElement(OuterApp, _extends({}, props, {
|
|
124
|
+
onHydrate: () => {
|
|
125
|
+
window.__HYDRATING__ = false;
|
|
126
|
+
}
|
|
127
|
+
})));
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
exports.start = start;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _react = _interopRequireDefault(require("react"));
|
|
4
|
+
var _main = require("./main");
|
|
5
|
+
var _react2 = require("@testing-library/react");
|
|
6
|
+
var _routeComponent = require("../universal/components/route-component");
|
|
7
|
+
var errors = _interopRequireWildcard(require("../universal/errors"));
|
|
8
|
+
var _uuidv = require("../../utils/uuidv4.client");
|
|
9
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
10
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
/*
|
|
13
|
+
* Copyright (c) 2022, salesforce.com, inc.
|
|
14
|
+
* All rights reserved.
|
|
15
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
16
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
jest.mock('../../utils/uuidv4.client');
|
|
20
|
+
describe('main', function () {
|
|
21
|
+
test('OuterApp renders without error', () => {
|
|
22
|
+
_uuidv.uuidv4.mockReturnValueOnce('7f21aea5-6962-4162-8204-9da85c802022');
|
|
23
|
+
const oldPreloadedState = window.__PRELOADED_STATE__;
|
|
24
|
+
window.__PRELOADED_STATE__ = {
|
|
25
|
+
appProps: {}
|
|
26
|
+
};
|
|
27
|
+
const App = () => /*#__PURE__*/_react.default.createElement("div", null, "App");
|
|
28
|
+
const locals = {};
|
|
29
|
+
const props = {
|
|
30
|
+
error: undefined,
|
|
31
|
+
locals,
|
|
32
|
+
routes: (0, _routeComponent.getRoutes)(locals),
|
|
33
|
+
WrappedApp: (0, _routeComponent.routeComponent)(App, false, locals)
|
|
34
|
+
};
|
|
35
|
+
(0, _react2.render)( /*#__PURE__*/_react.default.createElement(_main.OuterApp, props));
|
|
36
|
+
expect(_react2.screen.getByText('App')).toBeInTheDocument();
|
|
37
|
+
window.__PRELOADED_STATE__ = oldPreloadedState;
|
|
38
|
+
});
|
|
39
|
+
test('OuterApp triggers the error page when there is an error', () => {
|
|
40
|
+
const oldWindowError = window.__ERROR__;
|
|
41
|
+
window.__ERROR__ = new errors.HTTPNotFound('Not found');
|
|
42
|
+
const App = () => /*#__PURE__*/_react.default.createElement("div", null, "App");
|
|
43
|
+
const locals = {};
|
|
44
|
+
const props = {
|
|
45
|
+
error: window.__ERROR__,
|
|
46
|
+
locals,
|
|
47
|
+
routes: (0, _routeComponent.getRoutes)(locals),
|
|
48
|
+
WrappedApp: (0, _routeComponent.routeComponent)(App, false, locals)
|
|
49
|
+
};
|
|
50
|
+
(0, _react2.render)( /*#__PURE__*/_react.default.createElement(_main.OuterApp, props));
|
|
51
|
+
expect(_react2.screen.getByText('Error Status: 404')).toBeInTheDocument();
|
|
52
|
+
window.__ERROR__ = oldWindowError;
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.render = exports.getLocationSearch = exports.default = exports.ALLOWLISTED_INLINE_SCRIPTS = void 0;
|
|
7
|
+
var _path = _interopRequireDefault(require("path"));
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
var _server = _interopRequireDefault(require("react-dom/server"));
|
|
10
|
+
var _reactHelmet = require("react-helmet");
|
|
11
|
+
var _server2 = require("@loadable/server");
|
|
12
|
+
var _reactRouterDom = require("react-router-dom");
|
|
13
|
+
var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
|
|
14
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
15
|
+
var _sprite = _interopRequireDefault(require("svg-sprite-loader/runtime/sprite.build"));
|
|
16
|
+
var _ssrServer = require("@salesforce/pwa-kit-runtime/utils/ssr-server");
|
|
17
|
+
var _ssrShared = require("@salesforce/pwa-kit-runtime/utils/ssr-shared");
|
|
18
|
+
var _ssrConfig = require("@salesforce/pwa-kit-runtime/utils/ssr-config");
|
|
19
|
+
var _utils = require("../universal/utils");
|
|
20
|
+
var _contexts = require("../universal/contexts");
|
|
21
|
+
var _document = _interopRequireDefault(require("../universal/components/_document"));
|
|
22
|
+
var _app = _interopRequireDefault(require("../universal/components/_app"));
|
|
23
|
+
var _throw2 = _interopRequireDefault(require("../universal/components/throw-404"));
|
|
24
|
+
var _compatibility = require("../universal/compatibility");
|
|
25
|
+
var _switch = _interopRequireDefault(require("../universal/components/switch"));
|
|
26
|
+
var _routeComponent = require("../universal/components/route-component");
|
|
27
|
+
var errors = _interopRequireWildcard(require("../universal/errors"));
|
|
28
|
+
var _loggerInstance = _interopRequireDefault(require("../../utils/logger-instance"));
|
|
29
|
+
var _performance = _interopRequireWildcard(require("../../utils/performance"));
|
|
30
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
31
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
32
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
33
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
34
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
35
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
36
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
37
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
38
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
39
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } /*
|
|
40
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
41
|
+
* All rights reserved.
|
|
42
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
43
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
44
|
+
*/ /**
|
|
45
|
+
* @module progressive-web-sdk/ssr/server/react-rendering
|
|
46
|
+
*/
|
|
47
|
+
const CWD = process.cwd();
|
|
48
|
+
const BUNDLES_PATH = _path.default.resolve(CWD, 'build/loadable-stats.json');
|
|
49
|
+
const VALID_TAG_NAMES = ['base', 'body', 'head', 'html', 'link', 'meta', 'noscript', 'script', 'style', 'title'];
|
|
50
|
+
const ALLOWLISTED_INLINE_SCRIPTS = exports.ALLOWLISTED_INLINE_SCRIPTS = [];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Convert from thrown Error or String to {message, status} that we need for
|
|
54
|
+
* rendering.
|
|
55
|
+
* @private
|
|
56
|
+
* @param err - Error to be converted
|
|
57
|
+
* @function
|
|
58
|
+
* @return {Object}
|
|
59
|
+
*/
|
|
60
|
+
const logAndFormatError = err => {
|
|
61
|
+
if (err instanceof errors.HTTPError) {
|
|
62
|
+
// These are safe to display – we expect end-users to throw them
|
|
63
|
+
return {
|
|
64
|
+
message: err.message,
|
|
65
|
+
status: err.status,
|
|
66
|
+
stack: err.stack
|
|
67
|
+
};
|
|
68
|
+
} else {
|
|
69
|
+
const cause = err.stack || err.toString();
|
|
70
|
+
_loggerInstance.default.error(cause, {
|
|
71
|
+
namespace: 'react-rendering.render'
|
|
72
|
+
});
|
|
73
|
+
const safeMessage = 'Internal Server Error';
|
|
74
|
+
return {
|
|
75
|
+
message: safeMessage,
|
|
76
|
+
status: 500,
|
|
77
|
+
stack: err.stack
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Because multi-value params are not supported in `aws-serverless-express` create a proper
|
|
83
|
+
// search string using the `query` property. We pay special attention to the order the params
|
|
84
|
+
// as best as we can.
|
|
85
|
+
const getLocationSearch = (req, opts = {}) => {
|
|
86
|
+
const {
|
|
87
|
+
interpretPlusSignAsSpace = false
|
|
88
|
+
} = opts;
|
|
89
|
+
const [_, search] = req.originalUrl.split('?');
|
|
90
|
+
const params = new URLSearchParams(search);
|
|
91
|
+
const newParams = new URLSearchParams();
|
|
92
|
+
const orderedKeys = [...new Set(params.keys())];
|
|
93
|
+
|
|
94
|
+
// Maintain the original order of the parameters by iterating the
|
|
95
|
+
// ordered list of keys, and using the `req.query` object as the source of values.
|
|
96
|
+
orderedKeys.forEach(key => {
|
|
97
|
+
const value = req.query[key];
|
|
98
|
+
const values = Array.isArray(value) ? value : [value];
|
|
99
|
+
values.forEach(v => {
|
|
100
|
+
// To have feature parity to SFRA, the + sign can be treated as space
|
|
101
|
+
// However, this could potential a breaking change since not all users want to treat it as such
|
|
102
|
+
// Therefore, we create a flag for it via the app configuration
|
|
103
|
+
newParams.append(key, interpretPlusSignAsSpace ? decodeURIComponent(v).replace(/\+/, ' ') : v);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
const searchString = newParams.toString();
|
|
107
|
+
// Update the location objects reference.
|
|
108
|
+
return searchString ? `?${searchString}` : '';
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* This is the main react-rendering function for SSR. It is an Express handler.
|
|
113
|
+
*
|
|
114
|
+
* @param req - Request
|
|
115
|
+
* @param res - Response
|
|
116
|
+
*
|
|
117
|
+
* @function
|
|
118
|
+
*
|
|
119
|
+
* @return {Promise}
|
|
120
|
+
*/
|
|
121
|
+
exports.getLocationSearch = getLocationSearch;
|
|
122
|
+
const render = exports.render = /*#__PURE__*/function () {
|
|
123
|
+
var _ref = _asyncToGenerator(function* (req, res, next) {
|
|
124
|
+
var _config$app, _config$app$url;
|
|
125
|
+
const includeServerTimingHeader = ('__server_timing' in req.query);
|
|
126
|
+
const shouldTrackPerformance = includeServerTimingHeader || process.env.SERVER_TIMING;
|
|
127
|
+
res.__performanceTimer = new _performance.default({
|
|
128
|
+
enabled: shouldTrackPerformance
|
|
129
|
+
});
|
|
130
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.total, 'start');
|
|
131
|
+
const AppConfig = (0, _compatibility.getAppConfig)();
|
|
132
|
+
// Get the application config which should have been stored at this point.
|
|
133
|
+
const config = (0, _ssrConfig.getConfig)();
|
|
134
|
+
AppConfig.restore(res.locals);
|
|
135
|
+
const routes = (0, _routeComponent.getRoutes)(res.locals);
|
|
136
|
+
const WrappedApp = (0, _routeComponent.routeComponent)(_app.default, false, res.locals);
|
|
137
|
+
const [pathname] = req.originalUrl.split('?');
|
|
138
|
+
const location = {
|
|
139
|
+
pathname,
|
|
140
|
+
search: getLocationSearch(req, {
|
|
141
|
+
interpretPlusSignAsSpace: config === null || config === void 0 ? void 0 : (_config$app = config.app) === null || _config$app === void 0 ? void 0 : (_config$app$url = _config$app.url) === null || _config$app$url === void 0 ? void 0 : _config$app$url.interpretPlusSignAsSpace
|
|
142
|
+
})
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Step 1 - Find the match.
|
|
146
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.routeMatching, 'start');
|
|
147
|
+
let route;
|
|
148
|
+
let match;
|
|
149
|
+
routes.some(_route => {
|
|
150
|
+
const _match = (0, _reactRouterDom.matchPath)(req.path, _route);
|
|
151
|
+
if (_match) {
|
|
152
|
+
match = _match;
|
|
153
|
+
route = _route;
|
|
154
|
+
}
|
|
155
|
+
return !!match;
|
|
156
|
+
});
|
|
157
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.routeMatching, 'end');
|
|
158
|
+
|
|
159
|
+
// Step 2 - Get the component
|
|
160
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.loadComponent, 'start');
|
|
161
|
+
const component = yield route.component.getComponent();
|
|
162
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.loadComponent, 'end');
|
|
163
|
+
|
|
164
|
+
// Step 3 - Init the app state
|
|
165
|
+
const props = {
|
|
166
|
+
error: null,
|
|
167
|
+
appState: {},
|
|
168
|
+
routerContext: {},
|
|
169
|
+
req,
|
|
170
|
+
res,
|
|
171
|
+
App: WrappedApp,
|
|
172
|
+
routes,
|
|
173
|
+
location
|
|
174
|
+
};
|
|
175
|
+
let appJSX = /*#__PURE__*/_react.default.createElement(OuterApp, props);
|
|
176
|
+
let appState, appStateError;
|
|
177
|
+
if (component === _throw2.default) {
|
|
178
|
+
appState = {};
|
|
179
|
+
appStateError = new errors.HTTPNotFound('Not found');
|
|
180
|
+
} else {
|
|
181
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.fetchStrategies, 'start');
|
|
182
|
+
const ret = yield AppConfig.initAppState({
|
|
183
|
+
App: WrappedApp,
|
|
184
|
+
component,
|
|
185
|
+
match,
|
|
186
|
+
route,
|
|
187
|
+
req,
|
|
188
|
+
res,
|
|
189
|
+
location,
|
|
190
|
+
appJSX
|
|
191
|
+
});
|
|
192
|
+
appState = _objectSpread(_objectSpread({}, ret.appState), {}, {
|
|
193
|
+
__STATE_MANAGEMENT_LIBRARY: AppConfig.freeze(res.locals)
|
|
194
|
+
});
|
|
195
|
+
appStateError = ret.error;
|
|
196
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.fetchStrategies, 'end');
|
|
197
|
+
}
|
|
198
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.renderToString, 'start');
|
|
199
|
+
appJSX = /*#__PURE__*/_react.default.cloneElement(appJSX, {
|
|
200
|
+
error: appStateError,
|
|
201
|
+
appState
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Step 4 - Render the App
|
|
205
|
+
let renderResult;
|
|
206
|
+
try {
|
|
207
|
+
renderResult = renderApp({
|
|
208
|
+
App: WrappedApp,
|
|
209
|
+
appState,
|
|
210
|
+
appStateError: appStateError && logAndFormatError(appStateError),
|
|
211
|
+
routes,
|
|
212
|
+
req,
|
|
213
|
+
res,
|
|
214
|
+
location,
|
|
215
|
+
config,
|
|
216
|
+
appJSX
|
|
217
|
+
});
|
|
218
|
+
} catch (e) {
|
|
219
|
+
// This is an unrecoverable error.
|
|
220
|
+
// (errors handled by the AppErrorBoundary are considered recoverable)
|
|
221
|
+
// Here, we use Express's convention to invoke error middleware.
|
|
222
|
+
// Note, we don't have an error handling middleware yet! This is calling the
|
|
223
|
+
// default error handling middleware provided by Express
|
|
224
|
+
return next(e);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Step 5 - Determine what is going to happen, redirect, or send html with
|
|
228
|
+
// the correct status code.
|
|
229
|
+
const {
|
|
230
|
+
html,
|
|
231
|
+
routerContext,
|
|
232
|
+
error
|
|
233
|
+
} = renderResult;
|
|
234
|
+
const redirectUrl = routerContext.url;
|
|
235
|
+
const status = error && error.status || res.statusCode;
|
|
236
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.renderToString, 'end');
|
|
237
|
+
res.__performanceTimer.mark(_performance.PERFORMANCE_MARKS.total, 'end');
|
|
238
|
+
res.__performanceTimer.log();
|
|
239
|
+
if (includeServerTimingHeader) {
|
|
240
|
+
res.setHeader('Server-Timing', res.__performanceTimer.buildServerTimingHeader());
|
|
241
|
+
}
|
|
242
|
+
if (redirectUrl) {
|
|
243
|
+
res.redirect(302, redirectUrl);
|
|
244
|
+
} else {
|
|
245
|
+
res.status(status).send(html);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
return function render(_x, _x2, _x3) {
|
|
249
|
+
return _ref.apply(this, arguments);
|
|
250
|
+
};
|
|
251
|
+
}();
|
|
252
|
+
const OuterApp = ({
|
|
253
|
+
req,
|
|
254
|
+
res,
|
|
255
|
+
error,
|
|
256
|
+
App,
|
|
257
|
+
appState,
|
|
258
|
+
routes,
|
|
259
|
+
routerContext,
|
|
260
|
+
location
|
|
261
|
+
}) => {
|
|
262
|
+
const AppConfig = (0, _compatibility.getAppConfig)();
|
|
263
|
+
return /*#__PURE__*/_react.default.createElement(_contexts.ServerContext.Provider, {
|
|
264
|
+
value: {
|
|
265
|
+
req,
|
|
266
|
+
res
|
|
267
|
+
}
|
|
268
|
+
}, /*#__PURE__*/_react.default.createElement(_reactRouterDom.StaticRouter, {
|
|
269
|
+
location: location,
|
|
270
|
+
context: routerContext
|
|
271
|
+
}, /*#__PURE__*/_react.default.createElement(_contexts.CorrelationIdProvider, {
|
|
272
|
+
correlationId: res.locals.requestId,
|
|
273
|
+
resetOnPageChange: false
|
|
274
|
+
}, /*#__PURE__*/_react.default.createElement(AppConfig, {
|
|
275
|
+
locals: res.locals
|
|
276
|
+
}, /*#__PURE__*/_react.default.createElement(_switch.default, {
|
|
277
|
+
error: error,
|
|
278
|
+
appState: appState,
|
|
279
|
+
routes: routes,
|
|
280
|
+
App: App
|
|
281
|
+
})))));
|
|
282
|
+
};
|
|
283
|
+
OuterApp.propTypes = {
|
|
284
|
+
req: _propTypes.default.object,
|
|
285
|
+
res: _propTypes.default.object,
|
|
286
|
+
error: _propTypes.default.object,
|
|
287
|
+
App: _propTypes.default.elementType,
|
|
288
|
+
appState: _propTypes.default.object,
|
|
289
|
+
routes: _propTypes.default.array,
|
|
290
|
+
routerContext: _propTypes.default.object,
|
|
291
|
+
location: _propTypes.default.object
|
|
292
|
+
};
|
|
293
|
+
const renderToString = (jsx, extractor) => _server.default.renderToString(extractor.collectChunks(jsx));
|
|
294
|
+
const renderApp = args => {
|
|
295
|
+
const {
|
|
296
|
+
req,
|
|
297
|
+
res,
|
|
298
|
+
appStateError,
|
|
299
|
+
appJSX,
|
|
300
|
+
appState,
|
|
301
|
+
config
|
|
302
|
+
} = args;
|
|
303
|
+
const extractor = new _server2.ChunkExtractor({
|
|
304
|
+
statsFile: BUNDLES_PATH,
|
|
305
|
+
publicPath: (0, _utils.getAssetUrl)()
|
|
306
|
+
});
|
|
307
|
+
const ssrOnly = 'mobify_server_only' in req.query || '__server_only' in req.query;
|
|
308
|
+
const prettyPrint = 'mobify_pretty' in req.query || '__pretty_print' in req.query;
|
|
309
|
+
const indent = prettyPrint ? 8 : 0;
|
|
310
|
+
let routerContext;
|
|
311
|
+
let appHtml;
|
|
312
|
+
let renderError;
|
|
313
|
+
// It's important that we render the App before extracting the script elements,
|
|
314
|
+
// otherwise it won't return the correct chunks.
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
routerContext = {};
|
|
318
|
+
appHtml = renderToString( /*#__PURE__*/_react.default.cloneElement(appJSX, {
|
|
319
|
+
routerContext
|
|
320
|
+
}), extractor);
|
|
321
|
+
} catch (e) {
|
|
322
|
+
// This will catch errors thrown from the app and pass the error
|
|
323
|
+
// to the AppErrorBoundary component, and renders the error page.
|
|
324
|
+
routerContext = {};
|
|
325
|
+
renderError = logAndFormatError(e);
|
|
326
|
+
appHtml = renderToString( /*#__PURE__*/_react.default.cloneElement(appJSX, {
|
|
327
|
+
routerContext,
|
|
328
|
+
error: renderError
|
|
329
|
+
}), extractor);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Setting type: 'application/json' stops the browser from executing the code.
|
|
333
|
+
const scriptProps = ssrOnly ? {
|
|
334
|
+
type: 'application/json'
|
|
335
|
+
} : {};
|
|
336
|
+
let bundles = [];
|
|
337
|
+
/* istanbul ignore next */
|
|
338
|
+
if (extractor) {
|
|
339
|
+
bundles = extractor.getScriptElements().map(el => /*#__PURE__*/_react.default.cloneElement(el, _objectSpread(_objectSpread({}, el.props), scriptProps)));
|
|
340
|
+
}
|
|
341
|
+
const helmet = _reactHelmet.Helmet.renderStatic();
|
|
342
|
+
|
|
343
|
+
// Return the first error encountered during the rendering pipeline.
|
|
344
|
+
const error = appStateError || renderError;
|
|
345
|
+
// Remove the stacktrace when executing remotely as to not leak any important
|
|
346
|
+
// information to users about our system.
|
|
347
|
+
if (error && (0, _ssrServer.isRemote)()) {
|
|
348
|
+
delete error.stack;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Do not include *dynamic*, executable inline scripts – these cause issues with
|
|
352
|
+
// strict CSP headers that customers often want to use. Avoid inline scripts,
|
|
353
|
+
// full-stop, whenever possible.
|
|
354
|
+
|
|
355
|
+
// Each key in `windowGlobals` is expected to be set on the window
|
|
356
|
+
// object, client-side, by code in ssr/browser/main.jsx.
|
|
357
|
+
//
|
|
358
|
+
// Do *not* add to these without a very good reason - globals are a liability.
|
|
359
|
+
const windowGlobals = {
|
|
360
|
+
__INITIAL_CORRELATION_ID__: res.locals.requestId,
|
|
361
|
+
__CONFIG__: config,
|
|
362
|
+
__PRELOADED_STATE__: appState,
|
|
363
|
+
__ERROR__: error,
|
|
364
|
+
// `window.Progressive` has a long history at Mobify and some
|
|
365
|
+
// client-side code depends on it. Maintain its name out of tradition.
|
|
366
|
+
Progressive: getWindowProgressive(req, res)
|
|
367
|
+
};
|
|
368
|
+
const scripts = [/*#__PURE__*/_react.default.createElement("script", {
|
|
369
|
+
id: "mobify-data",
|
|
370
|
+
key: "mobify-data",
|
|
371
|
+
type: "application/json" // Not executable
|
|
372
|
+
,
|
|
373
|
+
dangerouslySetInnerHTML: {
|
|
374
|
+
__html: (0, _serializeJavascript.default)(windowGlobals, {
|
|
375
|
+
isJSON: true,
|
|
376
|
+
space: indent
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
}), ...bundles];
|
|
380
|
+
const svgs = [/*#__PURE__*/_react.default.createElement("div", {
|
|
381
|
+
key: "svg_sprite",
|
|
382
|
+
dangerouslySetInnerHTML: {
|
|
383
|
+
__html: _sprite.default.stringify()
|
|
384
|
+
}
|
|
385
|
+
})];
|
|
386
|
+
const helmetHeadTags = VALID_TAG_NAMES.map(tag => helmet[tag] && helmet[tag].toComponent()).filter(tag => tag);
|
|
387
|
+
const html = _server.default.renderToString( /*#__PURE__*/_react.default.createElement(_document.default, {
|
|
388
|
+
head: [...helmetHeadTags],
|
|
389
|
+
html: appHtml,
|
|
390
|
+
afterBodyStart: svgs,
|
|
391
|
+
beforeBodyEnd: scripts,
|
|
392
|
+
htmlAttributes: helmet.htmlAttributes.toComponent(),
|
|
393
|
+
bodyAttributes: helmet.bodyAttributes.toComponent()
|
|
394
|
+
}));
|
|
395
|
+
return {
|
|
396
|
+
error,
|
|
397
|
+
html: ['<!doctype html>', html].join(''),
|
|
398
|
+
routerContext
|
|
399
|
+
};
|
|
400
|
+
};
|
|
401
|
+
const getWindowProgressive = (req, res) => {
|
|
402
|
+
const options = req.app.options || {};
|
|
403
|
+
return {
|
|
404
|
+
buildOrigin: (0, _utils.getAssetUrl)(''),
|
|
405
|
+
cacheManifest: options.cacheHashManifest || {},
|
|
406
|
+
ssrOptions: {
|
|
407
|
+
// The hostname and origin under which this page is served
|
|
408
|
+
appHostname: options.appHostname,
|
|
409
|
+
appOrigin: options.appOrigin,
|
|
410
|
+
// The id of the bundle being served, as a string,
|
|
411
|
+
// defaulting to 'development' for the local dev server
|
|
412
|
+
bundleId: process.env.BUNDLE_ID || 'development',
|
|
413
|
+
// The id of the deploy as a string, defaulting to '0'
|
|
414
|
+
// for the local dev server
|
|
415
|
+
deployId: process.env.DEPLOY_ID || '0',
|
|
416
|
+
// On a local dev server, the DEPLOY_TARGET environment variable
|
|
417
|
+
// isn't defined by default. Developers may define it if it's
|
|
418
|
+
// used by the UPWA to modify behaviour.
|
|
419
|
+
deployTarget: process.env.DEPLOY_TARGET || 'local',
|
|
420
|
+
proxyConfigs: _ssrShared.proxyConfigs,
|
|
421
|
+
// The request class (undefined by default)
|
|
422
|
+
requestClass: res.locals.requestClass
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
const serverRenderer =
|
|
427
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
428
|
+
({
|
|
429
|
+
clientStats,
|
|
430
|
+
serverStats
|
|
431
|
+
}) => {
|
|
432
|
+
return (req, res, next) => render(req, res, next);
|
|
433
|
+
};
|
|
434
|
+
var _default = exports.default = serverRenderer;
|