@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +3 -7
  2. package/package.json +6 -5
  3. package/ssr/browser/main.js +130 -0
  4. package/ssr/browser/main.test.js +54 -0
  5. package/ssr/server/react-rendering.js +434 -0
  6. package/ssr/server/react-rendering.test.js +745 -0
  7. package/ssr/universal/compatibility.js +31 -0
  8. package/ssr/universal/components/_app/index.js +35 -0
  9. package/ssr/universal/components/_app/index.test.js +20 -0
  10. package/ssr/universal/components/_app-config/index.js +87 -0
  11. package/ssr/universal/components/_app-config/index.test.js +21 -0
  12. package/ssr/universal/components/_document/index.js +92 -0
  13. package/ssr/universal/components/_document/index.test.js +58 -0
  14. package/ssr/universal/components/_error/index.js +55 -0
  15. package/ssr/universal/components/_error/index.test.js +28 -0
  16. package/ssr/universal/components/app-error-boundary/index.js +113 -0
  17. package/ssr/universal/components/app-error-boundary/index.test.js +109 -0
  18. package/ssr/universal/components/fetch-strategy/index.js +42 -0
  19. package/ssr/universal/components/refresh/index.js +123 -0
  20. package/ssr/universal/components/refresh/index.test.js +78 -0
  21. package/ssr/universal/components/route-component/index.js +415 -0
  22. package/ssr/universal/components/route-component/index.test.js +378 -0
  23. package/ssr/universal/components/switch/index.js +62 -0
  24. package/ssr/universal/components/throw-404/index.js +36 -0
  25. package/ssr/universal/components/throw-404/index.test.js +26 -0
  26. package/ssr/universal/components/with-correlation-id/index.js +36 -0
  27. package/ssr/universal/components/with-legacy-get-props/index.js +100 -0
  28. package/ssr/universal/components/with-legacy-get-props/index.test.js +35 -0
  29. package/ssr/universal/components/with-react-query/index.js +130 -0
  30. package/ssr/universal/components/with-react-query/index.test.js +101 -0
  31. package/ssr/universal/contexts/index.js +72 -0
  32. package/ssr/universal/contexts/index.test.js +101 -0
  33. package/ssr/universal/errors.js +34 -0
  34. package/ssr/universal/errors.test.js +20 -0
  35. package/ssr/universal/events.js +38 -0
  36. package/ssr/universal/events.test.js +39 -0
  37. package/ssr/universal/hooks/index.js +84 -0
  38. package/ssr/universal/routes.js +15 -0
  39. package/ssr/universal/utils.client.test.js +46 -0
  40. package/ssr/universal/utils.js +61 -0
  41. package/ssr/universal/utils.server.test.js +24 -0
  42. package/utils/assets.js +120 -0
  43. package/utils/assets.test.js +106 -0
  44. package/utils/logger-instance.js +19 -0
  45. package/utils/performance.js +126 -0
  46. package/utils/performance.test.js +50 -0
  47. package/utils/url.js +41 -0
  48. package/utils/url.test.js +47 -0
  49. package/utils/uuidv4.client.js +21 -0
  50. package/utils/uuidv4.client.test.js +27 -0
  51. package/utils/warnings.js +81 -0
  52. package/utils/warnings.test.js +48 -0
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.FetchStrategy = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ 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); }
10
+ 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); }
11
+ 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); }); }; } /*
12
+ * Copyright (c) 2022, Salesforce, Inc.
13
+ * All rights reserved.
14
+ * SPDX-License-Identifier: BSD-3-Clause
15
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
16
+ */
17
+ /**
18
+ * @private
19
+ */
20
+ class FetchStrategy extends _react.default.Component {
21
+ /**
22
+ * @private
23
+ */
24
+ static initAppState(args) {
25
+ var _this = this;
26
+ return _asyncToGenerator(function* () {
27
+ try {
28
+ const promises = _this.getInitializers().map(fn => fn(args));
29
+ return {
30
+ error: undefined,
31
+ appState: _extends({}, ...(yield Promise.all(promises)))
32
+ };
33
+ } catch (error) {
34
+ return {
35
+ error: error || new Error(),
36
+ appState: {}
37
+ };
38
+ }
39
+ })();
40
+ }
41
+ }
42
+ exports.FetchStrategy = FetchStrategy;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactRouterDom = require("react-router-dom");
9
+ var _reactQuery = require("@tanstack/react-query");
10
+ var _loggerInstance = _interopRequireDefault(require("../../../../utils/logger-instance"));
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ 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); }
13
+ 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; }
14
+ 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); }
15
+ 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); }); }; } /*
16
+ * Copyright (c) 2023, Salesforce, Inc.
17
+ * All rights reserved.
18
+ * SPDX-License-Identifier: BSD-3-Clause
19
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
20
+ */
21
+ // For good UX, show loading spinner long enough for users to see
22
+ const LOADING_SPINNER_MIN_DURATION = 500;
23
+
24
+ /**
25
+ * A _private_ component to show loading spinner while refetching data on the client-side.
26
+ * To trigger this refetch, we do soft navigation back to the referrer.
27
+ * So this component is meant to be used as a route with `referrer` search param.
28
+ *
29
+ * @example
30
+ * const navigate = useNavigation()
31
+ * navigate(`/__refetch-data?referrer=${encodeURIComponent(urlOfCurrentPage)}`, 'replace')
32
+ *
33
+ * @private
34
+ */
35
+ const Refresh = () => {
36
+ const history = (0, _reactRouterDom.useHistory)();
37
+ const location = (0, _reactRouterDom.useLocation)();
38
+ let queryClient;
39
+ try {
40
+ // eslint-disable-next-line react-hooks/rules-of-hooks
41
+ queryClient = (0, _reactQuery.useQueryClient)();
42
+ } catch (err) {
43
+ // `useQueryClient` throws an error if the project does not use react-query.
44
+ // So in this case, leave `queryClient` as undefined. Continue to navigate to the referrer.
45
+ }
46
+ (0, _react.useEffect)(() => {
47
+ const refetchData = /*#__PURE__*/function () {
48
+ var _ref = _asyncToGenerator(function* () {
49
+ var _queryClient;
50
+ const showLoadingSpinner = new Promise(resolve => setTimeout(resolve, LOADING_SPINNER_MIN_DURATION));
51
+ const invalidateQueries = (_queryClient = queryClient) === null || _queryClient === void 0 ? void 0 : _queryClient.invalidateQueries();
52
+ yield Promise.all([showLoadingSpinner, invalidateQueries]);
53
+
54
+ // Soft navigate to the referrer
55
+ let referrer = new URLSearchParams(location.search).get('referrer');
56
+ if (!referrer) {
57
+ _loggerInstance.default.warn('Could not find `referrer` search param - redirecting to home page.', {
58
+ namespace: 'Refresh.useEffect'
59
+ });
60
+ referrer = '/';
61
+ }
62
+ history.replace(referrer);
63
+ });
64
+ return function refetchData() {
65
+ return _ref.apply(this, arguments);
66
+ };
67
+ }();
68
+ refetchData();
69
+ }, []);
70
+ return /*#__PURE__*/_react.default.createElement(LoadingSpinner, null);
71
+ };
72
+ Refresh.displayName = 'Refresh';
73
+ const LoadingSpinner = () => {
74
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("style", null, `
75
+ .pwa-kit-loading-spinner-outer {
76
+ z-index: 1300;
77
+ position: absolute;
78
+ top: 0px;
79
+ left: 0px;
80
+ right: 0px;
81
+ bottom: 0px;
82
+ background: rgba(255, 255, 255, 0.80);
83
+ }
84
+ .pwa-kit-loading-spinner-wrapper {
85
+ display: inline-block;
86
+ border-color: currentColor;
87
+ border-style: solid;
88
+ border-radius: 99999px;
89
+ border-width: 4px;
90
+ border-bottom-color: #C9C9C9;
91
+ border-left-color: #C9C9C9;
92
+ -webkit-animation: animation-b7n1on 0.65s linear infinite;
93
+ animation: animation-b7n1on 0.65s linear infinite;
94
+ width: 3rem;
95
+ height: 3rem;
96
+ position: absolute;
97
+ top: 50%;
98
+ left: 50%;
99
+ margin-left: -1.5em;
100
+ margin-top: -1.5em;
101
+ color: #0176D3;
102
+ }
103
+ .pwa-kit-loading-spinner-inner {
104
+ border: 0px;
105
+ clip: rect(0, 0, 0, 0);
106
+ width: 1px;
107
+ height: 1px;
108
+ margin: -1px;
109
+ padding: 0px;
110
+ overflow: hidden;
111
+ white-space: nowrap;
112
+ position: absolute;
113
+ }
114
+ `), /*#__PURE__*/_react.default.createElement("div", {
115
+ className: "pwa-kit-loading-spinner-outer",
116
+ "data-testid": "loading-spinner"
117
+ }, /*#__PURE__*/_react.default.createElement("div", {
118
+ className: "pwa-kit-loading-spinner-wrapper"
119
+ }, /*#__PURE__*/_react.default.createElement("span", {
120
+ className: "pwa-kit-loading-spinner-inner"
121
+ }, "Loading..."))));
122
+ };
123
+ var _default = exports.default = Refresh;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ var _reactQuery = require("@tanstack/react-query");
4
+ var _react = require("@testing-library/react");
5
+ var _react2 = _interopRequireDefault(require("react"));
6
+ var _reactRouterDom = require("react-router-dom");
7
+ var _index = _interopRequireDefault(require("./index"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ 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); }
10
+ 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); }); }; } /*
11
+ * Copyright (c) 2023, Salesforce, Inc.
12
+ * All rights reserved.
13
+ * SPDX-License-Identifier: BSD-3-Clause
14
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
15
+ */
16
+ jest.useFakeTimers();
17
+ const referrerURL = 'some-url';
18
+ jest.mock('react-router-dom', () => {
19
+ const replace = jest.fn();
20
+ return {
21
+ useHistory: jest.fn(() => ({
22
+ replace
23
+ })),
24
+ useLocation: jest.fn(() => ({
25
+ search: `?referrer=${referrerURL}`
26
+ }))
27
+ };
28
+ });
29
+ jest.mock('@tanstack/react-query', () => {
30
+ const invalidateQueries = jest.fn();
31
+ return {
32
+ useQueryClient: jest.fn(() => ({
33
+ invalidateQueries
34
+ }))
35
+ };
36
+ });
37
+ test('renders a loading spinner initially', () => {
38
+ (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_index.default, null));
39
+ expect(_react.screen.getByTestId('loading-spinner')).toBeInTheDocument();
40
+ });
41
+ test('wait for react-query cache to be invalidated', /*#__PURE__*/_asyncToGenerator(function* () {
42
+ (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_index.default, null));
43
+ yield (0, _react.waitFor)(() => {
44
+ expect((0, _reactQuery.useQueryClient)().invalidateQueries).toHaveBeenCalled();
45
+ });
46
+ }));
47
+ test('a project not using react-query', /*#__PURE__*/_asyncToGenerator(function* () {
48
+ // If customer project does not use react-query, calling useQueryClient would throw an error
49
+ _reactQuery.useQueryClient.mockImplementationOnce(() => {
50
+ throw new Error();
51
+ });
52
+ (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_index.default, null));
53
+ jest.runAllTimers();
54
+ yield (0, _react.waitFor)(() => {
55
+ // Expect to still continue despite the project not using react-query,
56
+ // specifically continue to navigate back to the referrer.
57
+ expect((0, _reactRouterDom.useHistory)().replace).toHaveBeenCalledWith(referrerURL);
58
+ });
59
+ }));
60
+ test('wait for soft navigation to the referrer', /*#__PURE__*/_asyncToGenerator(function* () {
61
+ (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_index.default, null));
62
+ jest.runAllTimers();
63
+ yield (0, _react.waitFor)(() => {
64
+ expect((0, _reactRouterDom.useHistory)().replace).toHaveBeenCalledWith(referrerURL);
65
+ });
66
+ }));
67
+ test('navigate to homepage if `referrer` search param cannot be found in the page url', /*#__PURE__*/_asyncToGenerator(function* () {
68
+ jest.spyOn(console, 'warn');
69
+ _reactRouterDom.useLocation.mockImplementationOnce(() => ({
70
+ search: ''
71
+ }));
72
+ (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_index.default, null));
73
+ jest.runAllTimers();
74
+ yield (0, _react.waitFor)(() => {
75
+ expect(console.warn).toHaveBeenCalled();
76
+ expect((0, _reactRouterDom.useHistory)().replace).toHaveBeenCalledWith('/');
77
+ });
78
+ }));
@@ -0,0 +1,415 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.routeComponent = exports.getRoutes = void 0;
7
+ var _propTypes = _interopRequireDefault(require("prop-types"));
8
+ var _react = _interopRequireDefault(require("react"));
9
+ var _reactRouterDom = require("react-router-dom");
10
+ var _hoistNonReactStatics = _interopRequireDefault(require("hoist-non-react-statics"));
11
+ var _appErrorBoundary = require("../../components/app-error-boundary");
12
+ var _throw2 = _interopRequireDefault(require("../../components/throw-404"));
13
+ var _compatibility = require("../../compatibility");
14
+ var _routes2 = _interopRequireDefault(require("../../routes"));
15
+ var _events = require("../../events");
16
+ var _withLegacyGetProps = require("../../components/with-legacy-get-props");
17
+ var _refresh = _interopRequireDefault(require("../refresh"));
18
+ const _excluded = ["component"];
19
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
20
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], t.indexOf(o) >= 0 || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
21
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.indexOf(n) >= 0) continue; t[n] = r[n]; } return t; }
22
+ 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); }
23
+ 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); }); }; }
24
+ 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; }
25
+ 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; }
26
+ 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; }
27
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
28
+ 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); }
29
+ 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); } /*
30
+ * Copyright (c) 2021, salesforce.com, inc.
31
+ * All rights reserved.
32
+ * SPDX-License-Identifier: BSD-3-Clause
33
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
34
+ */
35
+ const noop = () => undefined;
36
+ const isServerSide = typeof window === 'undefined';
37
+ const isHydrating = () => !isServerSide && window.__HYDRATING__;
38
+ const hasPerformanceAPI = !isServerSide && window.performance && window.performance.timing;
39
+
40
+ /* istanbul ignore next */
41
+ const now = () => {
42
+ return hasPerformanceAPI ? window.performance.timing.navigationStart + window.performance.now() : Date.now();
43
+ };
44
+
45
+ /**
46
+ * @private
47
+ */
48
+ const withErrorHandling = Wrapped => {
49
+ /* istanbul ignore next */
50
+ const wrappedComponentName = Wrapped.displayName || Wrapped.name;
51
+ const WithErrorHandling = props => /*#__PURE__*/_react.default.createElement(_appErrorBoundary.AppErrorContext.Consumer, null, ctx => /*#__PURE__*/_react.default.createElement(Wrapped, _extends({}, props, ctx)));
52
+
53
+ // Expose statics from the wrapped component on the HOC
54
+ (0, _hoistNonReactStatics.default)(WithErrorHandling, Wrapped);
55
+ WithErrorHandling.displayName = `WithErrorHandling(${wrappedComponentName})`;
56
+ return WithErrorHandling;
57
+ };
58
+
59
+ /**
60
+ * The `routeComponent` HOC is automatically used on every component in a project's
61
+ * route-config. It provides an interface, via static methods on React components,
62
+ * that can be used to fetch data on the server and on the client, seamlessly.
63
+ */
64
+ const routeComponent = (Wrapped, isPage, locals) => {
65
+ const AppConfig = (0, _compatibility.getAppConfig)();
66
+ const hocs = AppConfig.getHOCsInUse();
67
+ const getPropsEnabled = hocs.indexOf(_withLegacyGetProps.withLegacyGetProps) >= 0;
68
+ const extraArgs = getPropsEnabled ? AppConfig.extraGetPropsArgs(locals) : {};
69
+
70
+ /* istanbul ignore next */
71
+ const wrappedComponentName = Wrapped.displayName || Wrapped.name;
72
+ class RouteComponent extends _react.default.Component {
73
+ constructor(props, context) {
74
+ super(props, context);
75
+ this.state = {
76
+ childProps: _objectSpread(_objectSpread({}, isServerSide || isHydrating() ? this.props.preloadedProps : undefined), {}, {
77
+ isLoading: false
78
+ })
79
+ };
80
+ this._suppressUpdate = false;
81
+ }
82
+
83
+ /**
84
+ * Route-components implement `shouldGetProps()` to control when the
85
+ * component should fetch data from the server by calling `getProps()`.
86
+ * Typically, this is done by looking at the request URL.
87
+ *
88
+ * If not implemented, route-components will call `getProps()` again whenever
89
+ * `location.pathname` changes.
90
+ *
91
+ * The `shouldGetProps` function is called once on the server and every time
92
+ * a component updates on the client.
93
+ *
94
+ * @param {Object} args
95
+ *
96
+ * @param {Location} args.previousLocation - the previous value of
97
+ * window.location, or a server-side equivalent.
98
+ *
99
+ * @param {Location} args.location - the current value of window.location,
100
+ * or a server-side equivalent.
101
+ *
102
+ * @param {Object} args.previousParams - the previous parameters that were
103
+ * parsed from the URL by react-router.
104
+ *
105
+ * @param {Object} args.params - the current parameters that were parsed
106
+ * from the URL by react-router.
107
+ *
108
+ * @return {Promise<Boolean>}
109
+ */
110
+ static shouldGetProps(args) {
111
+ return _asyncToGenerator(function* () {
112
+ if (!getPropsEnabled) {
113
+ return false;
114
+ }
115
+ const defaultImpl = () => {
116
+ const {
117
+ previousLocation,
118
+ location
119
+ } = args;
120
+ return !previousLocation || previousLocation.pathname !== location.pathname;
121
+ };
122
+ const component = yield RouteComponent.getComponent();
123
+ return component.shouldGetProps ? component.shouldGetProps(args) : defaultImpl();
124
+ })();
125
+ }
126
+
127
+ /**
128
+ * Route-components implement `getProps()` to fetch the data they need to
129
+ * display. The `getProps` function must return an Object which is later
130
+ * passed to the component as props for rendering. The returned Object is
131
+ * serialzied and embedded into the rendered HTML as the initial app
132
+ * state when running server-side.
133
+ *
134
+ * Throwing or rejecting inside `getProps` will cause the server to return
135
+ * an Error, with an appropriate status code.
136
+ *
137
+ * Note that `req` and `res` are only defined on the server – the only place
138
+ * the code actually has access to Express requests or responses.
139
+ *
140
+ * If not implemented `getProps()` does nothing and the component will not
141
+ * fetch any data.
142
+ *
143
+ * Before the promise is returned, a reference is stored for later
144
+ * comparision with a call to isLatestPropsPromise. This is used to
145
+ * resolve race conditions when there are multiple getProps calls
146
+ * active.
147
+ *
148
+ * @param {Object} args
149
+ *
150
+ * @param {Request} args.req - an Express HTTP Request object on the server,
151
+ * undefined on the client.
152
+ *
153
+ * @param {Response} args.res - an Express HTTP Response object on the server,
154
+ * undefined on the client.
155
+ *
156
+ * @param {Object} args.params - the parameters that were parsed from the URL
157
+ * by react-router.
158
+ *
159
+ * @param {Location} args.location - the current value of window.location,
160
+ * or a server-side equivalent.
161
+ *
162
+ * @param {Boolean} args.isLoading - the current execution state of `getProps`,
163
+ * `true` while `getProp` is executing, and `false` when it's not.
164
+ *
165
+ * @return {Promise<Object>}
166
+ */
167
+ static getProps(args) {
168
+ if (!getPropsEnabled) {
169
+ return Promise.resolve({});
170
+ }
171
+ RouteComponent._latestPropsPromise = RouteComponent.getComponent().then(component => component.getProps ? component.getProps(_objectSpread(_objectSpread({}, args), extraArgs)) : Promise.resolve());
172
+ return RouteComponent._latestPropsPromise;
173
+ }
174
+
175
+ /**
176
+ * Get the underlying component this HoC wraps. This handles loading of
177
+ * `@loadable/component` components.
178
+ *
179
+ * @return {Promise<React.Component>}
180
+ */
181
+ static getComponent() {
182
+ return _asyncToGenerator(function* () {
183
+ return Wrapped.load ? Wrapped.load().then(module => module.default) : Promise.resolve(Wrapped);
184
+ })();
185
+ }
186
+
187
+ /**
188
+ * Route-components implement `getTemplateName()` to return a readable
189
+ * name for the component that is used internally for analytics-tracking –
190
+ * eg. performance/page-view events.
191
+ *
192
+ * If not implemented defaults to the `displayName` of the React component.
193
+ *
194
+ * @return {Promise<String>}
195
+ */
196
+ static getTemplateName() {
197
+ return _asyncToGenerator(function* () {
198
+ return RouteComponent.getComponent().then(c => c.getTemplateName ? c.getTemplateName() : Promise.resolve(wrappedComponentName));
199
+ })();
200
+ }
201
+
202
+ /**
203
+ * Check if a promise is still the latest call to getProps. This is used
204
+ * to check if the results are outdated before using them.
205
+ *
206
+ * @param {Promise} propsPromise - The promise from the call to getProps to check
207
+ * @returns true or false
208
+ */
209
+ static isLatestPropsPromise(propsPromise) {
210
+ return propsPromise === RouteComponent._latestPropsPromise;
211
+ }
212
+ componentDidMount() {
213
+ this.componentDidUpdate({});
214
+ }
215
+ componentDidUpdate(previousProps) {
216
+ var _this = this;
217
+ return _asyncToGenerator(function* () {
218
+ // Because we are setting the component state from within this function we need a
219
+ // guard prevent various events (update, error, complete, and load) from being
220
+ // called multiple times.
221
+ if (_this._suppressUpdate) {
222
+ _this._suppressUpdate = false;
223
+ return;
224
+ }
225
+ const {
226
+ location: previousLocation,
227
+ match: previousMatch
228
+ } = previousProps;
229
+ const {
230
+ location,
231
+ match,
232
+ onGetPropsComplete,
233
+ onGetPropsError,
234
+ onUpdateComplete
235
+ } = _this.props;
236
+ const {
237
+ params
238
+ } = match || {};
239
+ const {
240
+ params: previousParams
241
+ } = previousMatch || {};
242
+
243
+ // The wasHydratingOnUpdate flag MUST only be used to decide whether
244
+ // or not to call static lifecycle methods. Do not use it in
245
+ // component rendering - you will not be able to trigger updates,
246
+ // because this is intentionally outside of a component's
247
+ // state/props.
248
+ const wasHydratingOnUpdate = isHydrating();
249
+
250
+ /* istanbul ignore next */
251
+ // Don't getProps() when hydrating - the server has already done
252
+ // getProps() frozen the state in the page.
253
+ const shouldGetPropsNow = /*#__PURE__*/function () {
254
+ var _ref = _asyncToGenerator(function* () {
255
+ return !wasHydratingOnUpdate && (yield RouteComponent.shouldGetProps({
256
+ previousLocation,
257
+ location,
258
+ previousParams,
259
+ params
260
+ }));
261
+ });
262
+ return function shouldGetPropsNow() {
263
+ return _ref.apply(this, arguments);
264
+ };
265
+ }();
266
+ const setStateAsync = newState => {
267
+ return new Promise(resolve => {
268
+ _this.setState(newState, resolve);
269
+ });
270
+ };
271
+
272
+ // Note: We've built a reasonable notion of a "page load time" here:
273
+ //
274
+ // 1. For first loads the load time is the time elapsed between the
275
+ // user pressing enter in the URL bar and the first pageLoad event
276
+ // fired by this component.
277
+ //
278
+ // 2. For subsequent loads the load time is the time elapsed while
279
+ // running the getProps() function.
280
+ //
281
+ // Since the time is overwhelmingly spent fetching data on soft-navs,
282
+ // we think this is a good approximation in both cases.
283
+
284
+ const templateName = yield RouteComponent.getTemplateName();
285
+ const start = now();
286
+ const emitPageLoadEvent = (templateName, end) => isPage && _events.pages.pageLoad(templateName, start, end);
287
+ const emitPageErrorEvent = (name, content) => isPage && _events.pages.error(name, content);
288
+
289
+ // If hydrating, we know that the server just fetched and
290
+ // rendered for us, embedding the app-state in the page HTML.
291
+ // For that reason, we don't ever do getProps while Hydrating.
292
+ // However, we still want to report a page load time for this
293
+ // initial render. Rather than fetching again, trigger the event
294
+ // right away and do nothing.
295
+
296
+ if (wasHydratingOnUpdate) {
297
+ emitPageLoadEvent(templateName, now());
298
+ }
299
+ const willGetProps = yield shouldGetPropsNow();
300
+ if (!willGetProps) {
301
+ onUpdateComplete();
302
+ return;
303
+ }
304
+ try {
305
+ _this._suppressUpdate = true;
306
+ yield setStateAsync({
307
+ childProps: _objectSpread(_objectSpread({}, _this.state.childProps), {}, {
308
+ isLoading: true
309
+ })
310
+ });
311
+
312
+ /**
313
+ * When a user triggers two getProps for the same component,
314
+ * we'd like to always use the one for the later user action
315
+ * instead of the one that resolves last. getProps
316
+ * stores a reference to the promise that we check before we use
317
+ * the results from it.
318
+ */
319
+ const req = undefined;
320
+ const res = undefined;
321
+ const propsPromise = RouteComponent.getProps({
322
+ req,
323
+ res,
324
+ params,
325
+ location
326
+ });
327
+ const childProps = yield propsPromise;
328
+ _this._suppressUpdate = false;
329
+ if (RouteComponent.isLatestPropsPromise(propsPromise)) {
330
+ yield setStateAsync({
331
+ childProps: _objectSpread(_objectSpread({}, childProps), {}, {
332
+ isLoading: false
333
+ })
334
+ });
335
+ }
336
+ onGetPropsComplete();
337
+ emitPageLoadEvent(templateName, now());
338
+ } catch (err) {
339
+ onGetPropsError(err);
340
+ emitPageErrorEvent(templateName, err);
341
+ }
342
+ onUpdateComplete();
343
+ })();
344
+ }
345
+
346
+ /**
347
+ * Return the props that are intended for the wrapped component, excluding
348
+ * private or test-only props for this HOC.
349
+ */
350
+ getChildProps() {
351
+ const excludes = ['onGetPropsComplete', 'onGetPropsError', 'onUpdateComplete', 'preloadedProps'];
352
+ return _extends({}, ...Object.entries(this.props).filter(entry => excludes.indexOf(entry[0]) < 0).map(([k, v]) => ({
353
+ [k]: v
354
+ })));
355
+ }
356
+ render() {
357
+ return /*#__PURE__*/_react.default.createElement(Wrapped, _extends({}, this.getChildProps(), this.state.childProps));
358
+ }
359
+ }
360
+ RouteComponent.displayName = `routeComponent(${wrappedComponentName})`;
361
+ RouteComponent.defaultProps = {
362
+ onGetPropsComplete: noop,
363
+ onGetPropsError: noop,
364
+ onUpdateComplete: noop
365
+ };
366
+ RouteComponent.propTypes = {
367
+ location: _propTypes.default.object,
368
+ match: _propTypes.default.object,
369
+ onGetPropsComplete: _propTypes.default.func,
370
+ onGetPropsError: _propTypes.default.func,
371
+ onUpdateComplete: _propTypes.default.func,
372
+ preloadedProps: _propTypes.default.object
373
+ };
374
+ const excludes = {
375
+ shouldGetProps: true,
376
+ getProps: true,
377
+ getTemplateName: true
378
+ };
379
+ (0, _hoistNonReactStatics.default)(RouteComponent, Wrapped, excludes);
380
+ return withErrorHandling((0, _reactRouterDom.withRouter)(RouteComponent));
381
+ };
382
+
383
+ /**
384
+ * Wrap all the components found in the application's route config with the
385
+ * route-component HOC so that they all support `getProps` methods server-side
386
+ * and client-side in the same way.
387
+ *
388
+ * @private
389
+ */
390
+ exports.routeComponent = routeComponent;
391
+ const getRoutes = locals => {
392
+ let _routes = _routes2.default;
393
+ if (typeof _routes2.default === 'function') {
394
+ _routes = (0, _routes2.default)();
395
+ }
396
+ const allRoutes = [
397
+ // NOTE: this route needs to be above _routes, in case _routes has a fallback route of `path: '*'`
398
+ {
399
+ path: '/__pwa-kit/refresh',
400
+ component: _refresh.default
401
+ }, ..._routes, {
402
+ path: '*',
403
+ component: _throw2.default
404
+ }];
405
+ return allRoutes.map(_ref2 => {
406
+ let {
407
+ component
408
+ } = _ref2,
409
+ rest = _objectWithoutProperties(_ref2, _excluded);
410
+ return _objectSpread({
411
+ component: component ? routeComponent(component, true, locals) : component
412
+ }, rest);
413
+ });
414
+ };
415
+ exports.getRoutes = getRoutes;