@squide/firefly 3.0.3 → 4.0.1
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 +97 -0
- package/dist/AppRouter.d.ts +13 -6
- package/dist/AppRouter.js +1 -1
- package/dist/chunk-72TYI5MX.js +156 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +6 -6
- package/dist/chunk-MKFAFYJH.js +0 -136
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,102 @@
|
|
|
1
1
|
# @squide/firefly
|
|
2
2
|
|
|
3
|
+
## 4.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#133](https://github.com/gsoft-inc/wl-squide/pull/133) [`1cda1be`](https://github.com/gsoft-inc/wl-squide/commit/1cda1be30779d1a1d5d2e21eac043baff20c0f7e) Thanks [@patricklafrance](https://github.com/patricklafrance)! - - To prevent the consumer from always handling the AbortSignal error, a default catch has been added to the `AppRouter` component. The consumer can still handler the AbortSignal error if he needs to.
|
|
8
|
+
|
|
9
|
+
- When a user makes a direct hit to a deferred route that depends on protected data, the protected data was undefined. The reason was that by default, an unregistered route was considered as a public route. The code has been updated to consider an uregistered route as a protected route. The upside is that deferred routes can now depends on protected data. The downside is that a public deferred route will trigger the loading of the protected data. As we don't expect to have public deferred route at the moment it doesn't seems like an issue.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`1cda1be`](https://github.com/gsoft-inc/wl-squide/commit/1cda1be30779d1a1d5d2e21eac043baff20c0f7e), [`1cda1be`](https://github.com/gsoft-inc/wl-squide/commit/1cda1be30779d1a1d5d2e21eac043baff20c0f7e), [`1cda1be`](https://github.com/gsoft-inc/wl-squide/commit/1cda1be30779d1a1d5d2e21eac043baff20c0f7e)]:
|
|
12
|
+
- @squide/webpack-module-federation@3.0.4
|
|
13
|
+
- @squide/react-router@4.0.1
|
|
14
|
+
- @squide/core@3.2.1
|
|
15
|
+
- @squide/webpack-configs@1.1.3
|
|
16
|
+
- @squide/msw@2.0.9
|
|
17
|
+
|
|
18
|
+
## 4.0.0
|
|
19
|
+
|
|
20
|
+
### Major Changes
|
|
21
|
+
|
|
22
|
+
- [#131](https://github.com/gsoft-inc/wl-squide/pull/131) [`7caa44b`](https://github.com/gsoft-inc/wl-squide/commit/7caa44ba81a97d0705caf2f56e6536ae285c920d) Thanks [@patricklafrance](https://github.com/patricklafrance)! - - The `AppRouter` component now requires to define a `RouterProvider` as a child. This change has been made to provide more flexibility on the consumer side about the definition of the React Router router.
|
|
23
|
+
|
|
24
|
+
Before:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
<AppRouter
|
|
28
|
+
fallbackElement={...}
|
|
29
|
+
errorElement={...}
|
|
30
|
+
waitForMsw={...}
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Now:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<AppRouter
|
|
38
|
+
fallbackElement={...}
|
|
39
|
+
errorElement={...}
|
|
40
|
+
waitForMsw={...}
|
|
41
|
+
>
|
|
42
|
+
{(routes, providerProps) => (
|
|
43
|
+
<RouterProvider router={createBrowserRouter(routes)} {...providerProps} />
|
|
44
|
+
)}
|
|
45
|
+
</AppRouter>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- When in development and using React strict mode, the public and protected handler can be called twice. This issue highlighted that the `AppRouter` component doesn't equipe correctly the handlers to dispose of previous HTTP requests if they are called multiple times because of re-renders. Therefore, the handlers now receives an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that should be forwared to the HTTP client initiating the fetch request.
|
|
49
|
+
- The fix also requires the consumer to provide new properties (`isPublicDataLoaded` and `isProtectedDataLoaded`) indicating whether or not the public and/or protected data has been loaded.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
async function fetchPublicData(setFeatureFlags: (featureFlags: FeatureFlags) => void, signal: AbortSignal) {
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch("/api/feature-flags", {
|
|
55
|
+
signal
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (response.ok) {
|
|
59
|
+
const data = await response.json();
|
|
60
|
+
|
|
61
|
+
setFeatureFlags(data);
|
|
62
|
+
}
|
|
63
|
+
} catch (error: unknown) {
|
|
64
|
+
if (!signal.aborted) {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const [featureFlags, setFeatureFlags] = useState<FeatureFlags>();
|
|
71
|
+
|
|
72
|
+
const handleLoadPublicData = useCallback((signal: AbortSignal) => {
|
|
73
|
+
return fetchPublicData(setFeatureFlags, signal);
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
<AppRouter
|
|
77
|
+
onLoadPublicData={handleLoadPublicData}
|
|
78
|
+
isPublicDataLoaded={!!featureFlags}
|
|
79
|
+
fallbackElement={...}
|
|
80
|
+
errorElement={...}
|
|
81
|
+
waitForMsw={...}
|
|
82
|
+
>
|
|
83
|
+
{(routes, providerProps) => (
|
|
84
|
+
<RouterProvider router={createBrowserRouter(routes)} {...providerProps} />
|
|
85
|
+
)}
|
|
86
|
+
</AppRouter>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- Fixed an issue where the deferred registrations could be completed before the protected data has been loaded.
|
|
90
|
+
|
|
91
|
+
### Patch Changes
|
|
92
|
+
|
|
93
|
+
- Updated dependencies [[`7caa44b`](https://github.com/gsoft-inc/wl-squide/commit/7caa44ba81a97d0705caf2f56e6536ae285c920d), [`7caa44b`](https://github.com/gsoft-inc/wl-squide/commit/7caa44ba81a97d0705caf2f56e6536ae285c920d)]:
|
|
94
|
+
- @squide/core@3.2.0
|
|
95
|
+
- @squide/react-router@4.0.0
|
|
96
|
+
- @squide/msw@2.0.8
|
|
97
|
+
- @squide/webpack-module-federation@3.0.3
|
|
98
|
+
- @squide/webpack-configs@1.1.2
|
|
99
|
+
|
|
3
100
|
## 3.0.3
|
|
4
101
|
|
|
5
102
|
### Patch Changes
|
package/dist/AppRouter.d.ts
CHANGED
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { ReactElement } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
import { Route } from '@squide/react-router';
|
|
3
5
|
import { RouterProviderProps } from 'react-router-dom';
|
|
4
6
|
|
|
5
|
-
type OnLoadPublicDataFunction = () => Promise<unknown>;
|
|
6
|
-
type OnLoadProtectedDataFunction = () => Promise<unknown>;
|
|
7
|
+
type OnLoadPublicDataFunction = (signal: AbortSignal) => Promise<unknown>;
|
|
8
|
+
type OnLoadProtectedDataFunction = (signal: AbortSignal) => Promise<unknown>;
|
|
7
9
|
type OnCompleteRegistrationsFunction = () => Promise<unknown>;
|
|
8
10
|
interface BootstrappingRouteProps {
|
|
9
11
|
fallbackElement: ReactElement;
|
|
10
12
|
onLoadPublicData?: OnLoadPublicDataFunction;
|
|
11
13
|
onLoadProtectedData?: OnLoadProtectedDataFunction;
|
|
14
|
+
isPublicDataLoaded: boolean;
|
|
15
|
+
isProtectedDataLoaded: boolean;
|
|
12
16
|
onCompleteRegistrations?: OnCompleteRegistrationsFunction;
|
|
13
17
|
waitForMsw: boolean;
|
|
14
18
|
areModulesRegistered: boolean;
|
|
15
19
|
areModulesReady: boolean;
|
|
16
20
|
}
|
|
17
21
|
declare function BootstrappingRoute(props: BootstrappingRouteProps): react_jsx_runtime.JSX.Element;
|
|
22
|
+
type RenderRouterProviderFunction = (routes: Route[], providerProps: Omit<RouterProviderProps, "router">) => ReactElement;
|
|
18
23
|
interface AppRouterProps {
|
|
19
24
|
fallbackElement: ReactElement;
|
|
20
25
|
errorElement: ReactElement;
|
|
21
26
|
onLoadPublicData?: OnLoadPublicDataFunction;
|
|
22
27
|
onLoadProtectedData?: OnLoadProtectedDataFunction;
|
|
28
|
+
isPublicDataLoaded?: boolean;
|
|
29
|
+
isProtectedDataLoaded?: boolean;
|
|
23
30
|
onCompleteRegistrations?: OnCompleteRegistrationsFunction;
|
|
24
31
|
waitForMsw: boolean;
|
|
25
|
-
|
|
32
|
+
children: RenderRouterProviderFunction;
|
|
26
33
|
}
|
|
27
|
-
declare function AppRouter(props: AppRouterProps):
|
|
34
|
+
declare function AppRouter(props: AppRouterProps): ReactElement<any, string | react.JSXElementConstructor<any>>;
|
|
28
35
|
|
|
29
|
-
export { AppRouter, type AppRouterProps, BootstrappingRoute, type OnCompleteRegistrationsFunction, type OnLoadProtectedDataFunction, type OnLoadPublicDataFunction };
|
|
36
|
+
export { AppRouter, type AppRouterProps, BootstrappingRoute, type OnCompleteRegistrationsFunction, type OnLoadProtectedDataFunction, type OnLoadPublicDataFunction, type RenderRouterProviderFunction };
|
package/dist/AppRouter.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { AppRouter, BootstrappingRoute } from './chunk-
|
|
1
|
+
export { AppRouter, BootstrappingRoute } from './chunk-72TYI5MX.js';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { useLogOnceLogger, isNil } from '@squide/core';
|
|
2
|
+
import { useIsMswStarted } from '@squide/msw';
|
|
3
|
+
import { useIsRouteMatchProtected, useRoutes } from '@squide/react-router';
|
|
4
|
+
import { useAreModulesRegistered, useAreModulesReady } from '@squide/webpack-module-federation';
|
|
5
|
+
import { useEffect, useCallback, cloneElement, useMemo } from 'react';
|
|
6
|
+
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';
|
|
7
|
+
import { useLocation, Outlet } from 'react-router-dom';
|
|
8
|
+
import { jsx } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
// src/AppRouter.tsx
|
|
11
|
+
function useLoadPublicData(areModulesRegistered, areModulesReady, isMswStarted, isLoaded, onLoadData) {
|
|
12
|
+
const logger = useLogOnceLogger();
|
|
13
|
+
const { showBoundary } = useErrorBoundary();
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (onLoadData && !isLoaded) {
|
|
16
|
+
if ((areModulesRegistered || areModulesReady) && isMswStarted) {
|
|
17
|
+
logger.debugOnce("loading-public-data", "[shell] Loading public data.");
|
|
18
|
+
const abortController = new AbortController();
|
|
19
|
+
const signal = abortController.signal;
|
|
20
|
+
const result = onLoadData(signal);
|
|
21
|
+
if (!isPromise(result)) {
|
|
22
|
+
throw Error("[squide] An AppRouter onLoadPublicData handler must return a promise object.");
|
|
23
|
+
}
|
|
24
|
+
result.then(() => {
|
|
25
|
+
logger.debugOnce("public-data-loaded", "[shell] Public data has been loaded.");
|
|
26
|
+
}).catch((error) => {
|
|
27
|
+
if (!signal.aborted) {
|
|
28
|
+
showBoundary(error);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return () => {
|
|
32
|
+
abortController.abort();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}, [areModulesRegistered, areModulesReady, isMswStarted, isLoaded, showBoundary, onLoadData, logger]);
|
|
37
|
+
}
|
|
38
|
+
function useLoadProtectedData(areModulesRegistered, areModulesReady, isMswStarted, isActiveRouteProtected, isLoaded, onLoadData) {
|
|
39
|
+
const logger = useLogOnceLogger();
|
|
40
|
+
const { showBoundary } = useErrorBoundary();
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (onLoadData && !isLoaded) {
|
|
43
|
+
if ((areModulesRegistered || areModulesReady) && isMswStarted) {
|
|
44
|
+
if (isActiveRouteProtected) {
|
|
45
|
+
logger.debugOnce("loading-protected-data", `[shell] Loading protected data as "${location.pathname}" is a protected route.`);
|
|
46
|
+
const abortController = new AbortController();
|
|
47
|
+
const signal = abortController.signal;
|
|
48
|
+
const result = onLoadData(signal);
|
|
49
|
+
if (!isPromise(result)) {
|
|
50
|
+
throw Error("[squide] An AppRouter onLoadProtectedData handler must return a promise object.");
|
|
51
|
+
}
|
|
52
|
+
result.then(() => {
|
|
53
|
+
logger.debugOnce("protected-data-loaded", "[shell] Protected data has been loaded.");
|
|
54
|
+
}).catch((error) => {
|
|
55
|
+
if (!signal.aborted) {
|
|
56
|
+
showBoundary(error);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return () => {
|
|
60
|
+
abortController.abort();
|
|
61
|
+
};
|
|
62
|
+
} else {
|
|
63
|
+
logger.debugOnce("is-a-public-route", `[shell] Not loading protected data as "${location.pathname}" is a public route.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}, [areModulesRegistered, areModulesReady, isMswStarted, isActiveRouteProtected, isLoaded, showBoundary, onLoadData, logger]);
|
|
68
|
+
}
|
|
69
|
+
function isPromise(value) {
|
|
70
|
+
return !isNil(value) && !isNil(value.then) && !isNil(value.catch);
|
|
71
|
+
}
|
|
72
|
+
function BootstrappingRoute(props) {
|
|
73
|
+
const {
|
|
74
|
+
fallbackElement,
|
|
75
|
+
onLoadPublicData,
|
|
76
|
+
onLoadProtectedData,
|
|
77
|
+
isPublicDataLoaded,
|
|
78
|
+
isProtectedDataLoaded,
|
|
79
|
+
onCompleteRegistrations,
|
|
80
|
+
waitForMsw,
|
|
81
|
+
areModulesRegistered,
|
|
82
|
+
areModulesReady
|
|
83
|
+
} = props;
|
|
84
|
+
const logger = useLogOnceLogger();
|
|
85
|
+
const location2 = useLocation();
|
|
86
|
+
const isMswStarted = useIsMswStarted(waitForMsw);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (waitForMsw) {
|
|
89
|
+
if ((areModulesRegistered || areModulesReady) && !isMswStarted) {
|
|
90
|
+
logger.debugOnce("waiting-for-msw", `[shell] Modules are ${areModulesReady ? "ready" : "registered"}, waiting for MSW to start...`);
|
|
91
|
+
} else if (!areModulesRegistered && !areModulesReady && isMswStarted) {
|
|
92
|
+
logger.debugOnce("waiting-for-modules", "[shell] MSW is started, waiting for the modules...");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}, [logger, areModulesRegistered, areModulesReady, isMswStarted, waitForMsw]);
|
|
96
|
+
useLoadPublicData(areModulesRegistered, areModulesReady, isMswStarted, isPublicDataLoaded, onLoadPublicData);
|
|
97
|
+
const isActiveRouteProtected = useIsRouteMatchProtected(location2, { throwWhenThereIsNoMatch: areModulesReady });
|
|
98
|
+
useLoadProtectedData(areModulesRegistered, areModulesReady, isMswStarted, isActiveRouteProtected, isProtectedDataLoaded, onLoadProtectedData);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (onCompleteRegistrations) {
|
|
101
|
+
if (areModulesRegistered && isMswStarted && isPublicDataLoaded && (!isActiveRouteProtected || isProtectedDataLoaded)) {
|
|
102
|
+
if (!areModulesReady) {
|
|
103
|
+
onCompleteRegistrations();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, [areModulesRegistered, areModulesReady, isMswStarted, isPublicDataLoaded, isProtectedDataLoaded, isActiveRouteProtected, onCompleteRegistrations]);
|
|
108
|
+
if (!areModulesReady || !isMswStarted || !isPublicDataLoaded || isActiveRouteProtected && !isProtectedDataLoaded) {
|
|
109
|
+
return fallbackElement;
|
|
110
|
+
}
|
|
111
|
+
return /* @__PURE__ */ jsx(Outlet, {});
|
|
112
|
+
}
|
|
113
|
+
function AppRouter(props) {
|
|
114
|
+
const {
|
|
115
|
+
fallbackElement,
|
|
116
|
+
errorElement,
|
|
117
|
+
onLoadPublicData,
|
|
118
|
+
onLoadProtectedData,
|
|
119
|
+
isPublicDataLoaded = true,
|
|
120
|
+
isProtectedDataLoaded = true,
|
|
121
|
+
onCompleteRegistrations,
|
|
122
|
+
waitForMsw,
|
|
123
|
+
children: renderRouterProvider
|
|
124
|
+
} = props;
|
|
125
|
+
const areModulesRegistered = useAreModulesRegistered();
|
|
126
|
+
const areModulesReady = useAreModulesReady();
|
|
127
|
+
const routes = useRoutes();
|
|
128
|
+
const errorRenderer = useCallback(({ error }) => {
|
|
129
|
+
return cloneElement(errorElement, {
|
|
130
|
+
error
|
|
131
|
+
});
|
|
132
|
+
}, [errorElement]);
|
|
133
|
+
return useMemo(() => {
|
|
134
|
+
return renderRouterProvider([
|
|
135
|
+
{
|
|
136
|
+
element: /* @__PURE__ */ jsx(ErrorBoundary, { fallbackRender: errorRenderer, children: /* @__PURE__ */ jsx(
|
|
137
|
+
BootstrappingRoute,
|
|
138
|
+
{
|
|
139
|
+
fallbackElement,
|
|
140
|
+
onLoadPublicData,
|
|
141
|
+
onLoadProtectedData,
|
|
142
|
+
isPublicDataLoaded,
|
|
143
|
+
isProtectedDataLoaded,
|
|
144
|
+
onCompleteRegistrations,
|
|
145
|
+
waitForMsw,
|
|
146
|
+
areModulesRegistered,
|
|
147
|
+
areModulesReady
|
|
148
|
+
}
|
|
149
|
+
) }),
|
|
150
|
+
children: routes
|
|
151
|
+
}
|
|
152
|
+
], {});
|
|
153
|
+
}, [areModulesRegistered, areModulesReady, routes, onLoadPublicData, onLoadProtectedData, isPublicDataLoaded, isProtectedDataLoaded, onCompleteRegistrations, waitForMsw, errorRenderer, fallbackElement, renderRouterProvider]);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { AppRouter, BootstrappingRoute };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ export * from '@squide/core';
|
|
|
2
2
|
export * from '@squide/msw';
|
|
3
3
|
export * from '@squide/react-router';
|
|
4
4
|
export * from '@squide/webpack-module-federation';
|
|
5
|
-
export { AppRouter, AppRouterProps, BootstrappingRoute, OnCompleteRegistrationsFunction, OnLoadProtectedDataFunction, OnLoadPublicDataFunction } from './AppRouter.js';
|
|
5
|
+
export { AppRouter, AppRouterProps, BootstrappingRoute, OnCompleteRegistrationsFunction, OnLoadProtectedDataFunction, OnLoadPublicDataFunction, RenderRouterProviderFunction } from './AppRouter.js';
|
|
6
6
|
export { FireflyRuntime } from './fireflyRuntime.js';
|
|
7
|
-
import 'react/jsx-runtime';
|
|
8
7
|
import 'react';
|
|
8
|
+
import 'react/jsx-runtime';
|
|
9
9
|
import 'react-router-dom';
|
|
10
10
|
import 'msw';
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squide/firefly",
|
|
3
3
|
"author": "Workleap",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.1",
|
|
5
5
|
"description": "Helpers to facilitate the creation of a shell package with Squide firefly technology stack.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -88,13 +88,13 @@
|
|
|
88
88
|
"tsup": "8.0.1",
|
|
89
89
|
"typescript": "5.2.2",
|
|
90
90
|
"webpack": "5.89.0",
|
|
91
|
-
"@squide/webpack-configs": "1.1.
|
|
91
|
+
"@squide/webpack-configs": "1.1.3"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"@squide/core": "3.
|
|
95
|
-
"@squide/msw": "2.0.
|
|
96
|
-
"@squide/react-router": "
|
|
97
|
-
"@squide/webpack-module-federation": "3.0.
|
|
94
|
+
"@squide/core": "3.2.1",
|
|
95
|
+
"@squide/msw": "2.0.9",
|
|
96
|
+
"@squide/react-router": "4.0.1",
|
|
97
|
+
"@squide/webpack-module-federation": "3.0.4"
|
|
98
98
|
},
|
|
99
99
|
"sideEffects": false,
|
|
100
100
|
"engines": {
|
package/dist/chunk-MKFAFYJH.js
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { useLogger, isNil } from '@squide/core';
|
|
2
|
-
import { useIsMswStarted } from '@squide/msw';
|
|
3
|
-
import { useIsRouteMatchProtected, useRoutes } from '@squide/react-router';
|
|
4
|
-
import { useAreModulesRegistered, useAreModulesReady } from '@squide/webpack-module-federation';
|
|
5
|
-
import { useState, useEffect, useCallback, cloneElement, useMemo } from 'react';
|
|
6
|
-
import { useErrorBoundary, ErrorBoundary } from 'react-error-boundary';
|
|
7
|
-
import { useLocation, Outlet, createBrowserRouter, RouterProvider } from 'react-router-dom';
|
|
8
|
-
import { jsx } from 'react/jsx-runtime';
|
|
9
|
-
|
|
10
|
-
// src/AppRouter.tsx
|
|
11
|
-
function isPromise(value) {
|
|
12
|
-
return !isNil(value) && !isNil(value.then) && !isNil(value.catch);
|
|
13
|
-
}
|
|
14
|
-
function BootstrappingRoute(props) {
|
|
15
|
-
const {
|
|
16
|
-
fallbackElement,
|
|
17
|
-
onLoadPublicData,
|
|
18
|
-
onLoadProtectedData,
|
|
19
|
-
onCompleteRegistrations,
|
|
20
|
-
waitForMsw,
|
|
21
|
-
areModulesRegistered,
|
|
22
|
-
areModulesReady
|
|
23
|
-
} = props;
|
|
24
|
-
const [isPublicDataLoaded, setIsPublicDataLoaded] = useState(!onLoadPublicData);
|
|
25
|
-
const [isProtectedDataLoaded, setIsProtectedDataLoaded] = useState(!onLoadProtectedData);
|
|
26
|
-
const { showBoundary } = useErrorBoundary();
|
|
27
|
-
const logger = useLogger();
|
|
28
|
-
const location = useLocation();
|
|
29
|
-
const isMswStarted = useIsMswStarted(waitForMsw);
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (waitForMsw) {
|
|
32
|
-
if ((areModulesRegistered || areModulesReady) && !isMswStarted) {
|
|
33
|
-
logger.debug(`[shell] Modules are ${areModulesReady ? "ready" : "registered"}, waiting for MSW to start...`);
|
|
34
|
-
} else if (!areModulesRegistered && !areModulesReady && isMswStarted) {
|
|
35
|
-
logger.debug("[shell] MSW is started, waiting for the modules...");
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}, [logger, areModulesRegistered, areModulesReady, isMswStarted, waitForMsw]);
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (onLoadPublicData) {
|
|
41
|
-
if ((areModulesRegistered || areModulesReady) && isMswStarted) {
|
|
42
|
-
if (!isPublicDataLoaded) {
|
|
43
|
-
logger.debug("[shell] Loading public data.");
|
|
44
|
-
const result = onLoadPublicData();
|
|
45
|
-
if (!isPromise(result)) {
|
|
46
|
-
throw Error("[squide] An AppRouter onLoadPublicData handler must return a promise object.");
|
|
47
|
-
}
|
|
48
|
-
result.then(() => {
|
|
49
|
-
setIsPublicDataLoaded(true);
|
|
50
|
-
logger.debug("[shell] Public data has been loaded.");
|
|
51
|
-
}).catch((error) => {
|
|
52
|
-
showBoundary(error);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}, [logger, areModulesRegistered, areModulesReady, isMswStarted, isPublicDataLoaded, showBoundary, onLoadPublicData]);
|
|
58
|
-
const isActiveRouteProtected = useIsRouteMatchProtected(location, { throwWhenThereIsNoMatch: areModulesReady });
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
if (onLoadProtectedData) {
|
|
61
|
-
if ((areModulesRegistered || areModulesReady) && isMswStarted) {
|
|
62
|
-
if (isActiveRouteProtected) {
|
|
63
|
-
if (!isProtectedDataLoaded) {
|
|
64
|
-
logger.debug(`[shell] Loading protected data as "${location.pathname}" is a protected route.`);
|
|
65
|
-
const result = onLoadProtectedData();
|
|
66
|
-
if (!isPromise(result)) {
|
|
67
|
-
throw Error("[squide] An AppRouter onLoadProtectedData handler must return a promise object.");
|
|
68
|
-
}
|
|
69
|
-
result.then(() => {
|
|
70
|
-
setIsProtectedDataLoaded(true);
|
|
71
|
-
logger.debug("[shell] Protected data has been loaded.");
|
|
72
|
-
}).catch((error) => {
|
|
73
|
-
showBoundary(error);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
logger.debug(`[shell] Not loading protected data as "${location.pathname}" is a public route.`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}, [logger, location, areModulesRegistered, areModulesReady, isMswStarted, isActiveRouteProtected, isProtectedDataLoaded, showBoundary, onLoadProtectedData]);
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
if (onCompleteRegistrations) {
|
|
84
|
-
if (areModulesRegistered && isMswStarted && isPublicDataLoaded) {
|
|
85
|
-
if (!areModulesReady) {
|
|
86
|
-
onCompleteRegistrations();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}, [areModulesRegistered, areModulesReady, isMswStarted, isPublicDataLoaded, onCompleteRegistrations]);
|
|
91
|
-
if (!areModulesReady || !isMswStarted || !isPublicDataLoaded || isActiveRouteProtected && !isProtectedDataLoaded) {
|
|
92
|
-
return fallbackElement;
|
|
93
|
-
}
|
|
94
|
-
return /* @__PURE__ */ jsx(Outlet, {});
|
|
95
|
-
}
|
|
96
|
-
function AppRouter(props) {
|
|
97
|
-
const {
|
|
98
|
-
fallbackElement,
|
|
99
|
-
errorElement,
|
|
100
|
-
onLoadPublicData,
|
|
101
|
-
onLoadProtectedData,
|
|
102
|
-
onCompleteRegistrations,
|
|
103
|
-
waitForMsw,
|
|
104
|
-
routerProvidersProps = {}
|
|
105
|
-
} = props;
|
|
106
|
-
const areModulesRegistered = useAreModulesRegistered();
|
|
107
|
-
const areModulesReady = useAreModulesReady();
|
|
108
|
-
const routes = useRoutes();
|
|
109
|
-
const errorRenderer = useCallback(({ error }) => {
|
|
110
|
-
return cloneElement(errorElement, {
|
|
111
|
-
error
|
|
112
|
-
});
|
|
113
|
-
}, [errorElement]);
|
|
114
|
-
const router = useMemo(() => {
|
|
115
|
-
return createBrowserRouter([
|
|
116
|
-
{
|
|
117
|
-
element: /* @__PURE__ */ jsx(ErrorBoundary, { fallbackRender: errorRenderer, children: /* @__PURE__ */ jsx(
|
|
118
|
-
BootstrappingRoute,
|
|
119
|
-
{
|
|
120
|
-
fallbackElement,
|
|
121
|
-
onLoadPublicData,
|
|
122
|
-
onLoadProtectedData,
|
|
123
|
-
onCompleteRegistrations,
|
|
124
|
-
waitForMsw,
|
|
125
|
-
areModulesRegistered,
|
|
126
|
-
areModulesReady
|
|
127
|
-
}
|
|
128
|
-
) }),
|
|
129
|
-
children: routes
|
|
130
|
-
}
|
|
131
|
-
]);
|
|
132
|
-
}, [areModulesRegistered, areModulesReady, routes, onLoadPublicData, onLoadProtectedData, onCompleteRegistrations, waitForMsw, errorRenderer, fallbackElement]);
|
|
133
|
-
return /* @__PURE__ */ jsx(RouterProvider, { ...routerProvidersProps, router });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export { AppRouter, BootstrappingRoute };
|