From b28065350e869f2691006ccd13dcdfd27272f1a7 Mon Sep 17 00:00:00 2001 From: Retype GitHub Action Date: Mon, 4 Dec 2023 20:03:06 +0000 Subject: [PATCH] Refreshes Retype-generated documentation. Process triggered by patricklafrance. --- 404.html | 8 +- about/index.html | 8 +- getting-started/create-host/index.html | 13 +-- .../create-local-module/index.html | 10 +-- .../create-remote-module/index.html | 10 +-- getting-started/deploy/index.html | 10 +-- getting-started/index.html | 8 +- getting-started/learn-the-api/index.html | 10 +-- guides/add-a-public-page/index.html | 10 +-- guides/add-a-shared-dependency/index.html | 10 +-- guides/add-authentication/index.html | 10 +-- .../develop-a-module-in-isolation/index.html | 10 +-- guides/federated-tabs/index.html | 10 +-- guides/fetch-initial-data/index.html | 10 +-- guides/fetch-page-data/index.html | 10 +-- guides/implement-a-custom-logger/index.html | 10 +-- guides/index.html | 8 +- guides/isolate-module-failures/index.html | 10 +-- guides/manage-shared-state/index.html | 8 +- guides/migrate-from-a-monolith/index.html | 8 +- guides/override-a-react-context/index.html | 10 +-- guides/override-the-host-layout/index.html | 85 ++---------------- guides/setup-localization/index.html | 10 +-- guides/setup-msw/index.html | 10 +-- guides/use-feature-flags/index.html | 10 +-- index.html | 8 +- .../localstoragesessionmanager/index.html | 10 +-- .../readonlysessionlocalstorage/index.html | 10 +-- reference/i18next/geti18nextplugin/index.html | 10 +-- .../i18nextnavigationitemlabel/index.html | 10 +-- reference/i18next/i18nextplugin/index.html | 10 +-- .../i18next/usechangelanguage/index.html | 10 +-- .../i18next/usecurrentlanguage/index.html | 10 +-- .../i18next/usei18nextinstance/index.html | 10 +-- reference/index.html | 8 +- reference/logging/consolelogger/index.html | 10 +-- reference/logging/logger/index.html | 10 +-- reference/messaging/eventbus/index.html | 10 +-- .../useeventbusdispatcher/index.html | 10 +-- .../messaging/useeventbuslistener/index.html | 10 +-- reference/msw/setmswasstarted/index.html | 10 +-- reference/msw/useismswready/index.html | 10 +-- reference/packages/index.html | 8 +- reference/plugins/plugin/index.html | 10 +-- .../index.html | 10 +-- .../completemoduleregistrations/index.html | 10 +-- .../index.html | 10 +-- .../registerlocalmodules/index.html | 10 +-- .../registerremotemodules/index.html | 10 +-- .../usearemodulesready/index.html | 10 +-- .../usearemodulesregistered/index.html | 10 +-- reference/routing/approuter/index.html | 10 +-- reference/routing/managedroutes/index.html | 10 +-- .../useisroutematchprotected/index.html | 10 +-- .../userenderednavigationitems/index.html | 10 +-- reference/routing/useroutematch/index.html | 10 +-- reference/runtime/runtime-class/index.html | 10 +-- reference/runtime/runtime-context/index.html | 10 +-- reference/runtime/uselogger/index.html | 10 +-- .../runtime/usenavigationitems/index.html | 10 +-- reference/runtime/useroutes/index.html | 10 +-- reference/runtime/useruntime/index.html | 10 +-- reference/runtime/usesession/index.html | 10 +-- .../session/useisauthenticated/index.html | 10 +-- .../webpack/definebuildhostconfig/index.html | 10 +-- .../definebuildremotemoduleconfig/index.html | 10 +-- .../webpack/definedevhostconfig/index.html | 10 +-- .../definedevremotemoduleconfig/index.html | 10 +-- resources/js/config.js | 2 +- resources/js/search.json | 2 +- samples/index.html | 8 +- sitemap.xml.gz | Bin 1086 -> 1090 bytes troubleshooting/index.html | 10 +-- 73 files changed, 348 insertions(+), 414 deletions(-) diff --git a/404.html b/404.html index 8b8b51dfa..8837b44c8 100644 --- a/404.html +++ b/404.html @@ -4,7 +4,7 @@ - + @@ -29,11 +29,11 @@ - + - + - + diff --git a/about/index.html b/about/index.html index d27e68e63..b99e245d5 100644 --- a/about/index.html +++ b/about/index.html @@ -4,7 +4,7 @@ - + @@ -32,11 +32,11 @@ - + - + - + diff --git a/getting-started/create-host/index.html b/getting-started/create-host/index.html index 37afad695..a60a449bd 100644 --- a/getting-started/create-host/index.html +++ b/getting-started/create-host/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + @@ -459,7 +459,7 @@

Navigation items

-

Next, create a layout component to render the navigation items:

+

Next, create a layout component to render the navigation items. In many applications, multiple pages often share a common layout that includes elements such as a navigation bar, a user profile menu, and a main content section. In a React Router application, this shared layout is commonly referred to as a RootLayout:

host/src/RootLayout.tsx
import { Suspense } from "react";
@@ -514,6 +514,7 @@ 

); }

+

In the previous code sample, the RootLayout will serves as the default layout for the homepage as well as for every page (route) registered by a module that are not nested under a parent route with either the parentPath or the parentName option.

# diff --git a/getting-started/create-local-module/index.html b/getting-started/create-local-module/index.html index b703749bc..8b308c02a 100644 --- a/getting-started/create-local-module/index.html +++ b/getting-started/create-local-module/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/getting-started/create-remote-module/index.html b/getting-started/create-remote-module/index.html index aff62a39e..7c7e26692 100644 --- a/getting-started/create-remote-module/index.html +++ b/getting-started/create-remote-module/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/getting-started/deploy/index.html b/getting-started/deploy/index.html index 6c2cdac14..5879fa293 100644 --- a/getting-started/deploy/index.html +++ b/getting-started/deploy/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/getting-started/index.html b/getting-started/index.html index 52daaac2f..862de2c4a 100644 --- a/getting-started/index.html +++ b/getting-started/index.html @@ -4,7 +4,7 @@ - + @@ -32,11 +32,11 @@ - + - + - + diff --git a/getting-started/learn-the-api/index.html b/getting-started/learn-the-api/index.html index 8d48027c9..542554835 100644 --- a/getting-started/learn-the-api/index.html +++ b/getting-started/learn-the-api/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/add-a-public-page/index.html b/guides/add-a-public-page/index.html index 95f607c60..550104560 100644 --- a/guides/add-a-public-page/index.html +++ b/guides/add-a-public-page/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/add-a-shared-dependency/index.html b/guides/add-a-shared-dependency/index.html index babf4d951..a6c013fb4 100644 --- a/guides/add-a-shared-dependency/index.html +++ b/guides/add-a-shared-dependency/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/add-authentication/index.html b/guides/add-authentication/index.html index 7d63259be..97797ea6d 100644 --- a/guides/add-authentication/index.html +++ b/guides/add-authentication/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/develop-a-module-in-isolation/index.html b/guides/develop-a-module-in-isolation/index.html index 6506af85e..f26746ae0 100644 --- a/guides/develop-a-module-in-isolation/index.html +++ b/guides/develop-a-module-in-isolation/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/federated-tabs/index.html b/guides/federated-tabs/index.html index 492b6635c..0950ca5ea 100644 --- a/guides/federated-tabs/index.html +++ b/guides/federated-tabs/index.html @@ -4,7 +4,7 @@ - + @@ -34,12 +34,12 @@ - + - + - - + + diff --git a/guides/fetch-initial-data/index.html b/guides/fetch-initial-data/index.html index 22a8b0055..e3a9def60 100644 --- a/guides/fetch-initial-data/index.html +++ b/guides/fetch-initial-data/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/fetch-page-data/index.html b/guides/fetch-page-data/index.html index a95ad6d3c..57d3b8a58 100644 --- a/guides/fetch-page-data/index.html +++ b/guides/fetch-page-data/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/implement-a-custom-logger/index.html b/guides/implement-a-custom-logger/index.html index 040df5a9e..b34127495 100644 --- a/guides/implement-a-custom-logger/index.html +++ b/guides/implement-a-custom-logger/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/index.html b/guides/index.html index a8f5af75f..879180380 100644 --- a/guides/index.html +++ b/guides/index.html @@ -4,7 +4,7 @@ - + @@ -32,11 +32,11 @@ - + - + - + diff --git a/guides/isolate-module-failures/index.html b/guides/isolate-module-failures/index.html index 2ddf7dfe4..7fef49d6f 100644 --- a/guides/isolate-module-failures/index.html +++ b/guides/isolate-module-failures/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/manage-shared-state/index.html b/guides/manage-shared-state/index.html index 4396536fd..3a071e6ef 100644 --- a/guides/manage-shared-state/index.html +++ b/guides/manage-shared-state/index.html @@ -4,7 +4,7 @@ - + @@ -32,11 +32,11 @@ - + - + - + diff --git a/guides/migrate-from-a-monolith/index.html b/guides/migrate-from-a-monolith/index.html index 47f717dc7..6ee825dea 100644 --- a/guides/migrate-from-a-monolith/index.html +++ b/guides/migrate-from-a-monolith/index.html @@ -4,7 +4,7 @@ - + @@ -32,11 +32,11 @@ - + - + - + diff --git a/guides/override-a-react-context/index.html b/guides/override-a-react-context/index.html index 3f3f8036b..c980d21ef 100644 --- a/guides/override-a-react-context/index.html +++ b/guides/override-a-react-context/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/override-the-host-layout/index.html b/guides/override-the-host-layout/index.html index 39cf5e460..441186af6 100644 --- a/guides/override-the-host-layout/index.html +++ b/guides/override-the-host-layout/index.html @@ -4,7 +4,7 @@ - + @@ -12,7 +12,7 @@ Override the host layout - + @@ -21,23 +21,23 @@ - + - + - + - + - - + + @@ -305,74 +305,7 @@

Override the host layout

- -

- # - Define a root layout -

-
-

In many applications, multiple pages often share a common layout that includes elements such as a navigation bar, a user profile menu, and a main content section. In a React Router application, this shared layout is commonly referred to as a RootLayout:

-
-
host/src/register.tsx
-
import { ManagedRoutes, type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly";
-import { HomePage } from "./HomePage.tsx";
-import { AuthenticationBoundary } from "./AuthenticationBoundary.tsx";
-import { RootLayout } from "./RootLayout.tsx";
-import { RootErrorBoundary } from "./RootErrorBoundary.tsx";
-
-export const registerHost: ModuleRegisterFunction<FireflyRuntime> = runtime => {
-    runtime.registerRoute({
-        element: <AuthenticationBoundary />,
-        children: [
-            element: <RootLayout />,
-            children: [
-                {
-                    errorElement: <RootErrorBoundary />,
-                    children: [
-                        ManagedRoutes
-                    ]
-                }
-            ]
-        ]
-    }, {
-        // We will talk about this in the next section of this guide.
-        hoist: true
-    });
-
-    runtime.registerRoute({
-        index: true,
-        element: <HomePage />
-    });
-};
-
-
-
host/src/RootLayout.tsx
-
import { Suspense } from "react";
-import { Outlet } from "react-router-dom";
-import { useNavigationItems, useRenderedNavigationItems } from "@squide/firefly";
-import { UserMenu } from "./UserMenu.tsx";
-
-export function RootLayout() {
-    const navigationItems = useNavigationItems();
-
-    // To keep things simple, we are omitting the definition of "renderItem" and "renderSection".
-    // For a full example, view: https://gsoft-inc.github.io/wl-squide/reference/routing/userenderednavigationitems/.
-    const navigationElements = useRenderedNavigationItems(navigationItems, renderItem, renderSection);
-
-    return (
-        <>
-            <div>
-                <nav>{navigationElements}</nav>
-                <UserMenu />
-            </div>
-            <Suspense fallback={<div>Loading...</div>}>
-                <Outlet />
-            </Suspense>
-        </>
-    );
-}
-
-

In the previous code sample, the RootLayout serves as the default layout for the homepage as well as for every page (route) registered by a module that are not nested under a parent route with either the parentPath or the parentName option.

+

The RootLayout component that as been defined in the create an host application starting guide serves as the default layout for the homepage as well as for every page (route) registered by a module that are not nested under a parent route with either the parentPath or the parentName option.

For most pages, this is the behavior expected by the author. However, for pages such as a login, the default RootLayout isn't suitable because the page is not bound to a user session (the user is not even authenticated yet).

To accomodate pages that require a different layout, a mechanism is needed to move their route declaration at the root of the React Router router instance, before the RootLayout is declared.

diff --git a/guides/setup-localization/index.html b/guides/setup-localization/index.html index 53b4bde11..5b75278a5 100644 --- a/guides/setup-localization/index.html +++ b/guides/setup-localization/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/setup-msw/index.html b/guides/setup-msw/index.html index 57e0aef3e..4f3624631 100644 --- a/guides/setup-msw/index.html +++ b/guides/setup-msw/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/guides/use-feature-flags/index.html b/guides/use-feature-flags/index.html index 9d58dacc4..a0e7e18ce 100644 --- a/guides/use-feature-flags/index.html +++ b/guides/use-feature-flags/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/index.html b/index.html index 504cce650..b1d8510c8 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - + @@ -30,11 +30,11 @@ - + - + - + diff --git a/reference/fakes/localstoragesessionmanager/index.html b/reference/fakes/localstoragesessionmanager/index.html index d964f9e64..afbdaf883 100644 --- a/reference/fakes/localstoragesessionmanager/index.html +++ b/reference/fakes/localstoragesessionmanager/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/fakes/readonlysessionlocalstorage/index.html b/reference/fakes/readonlysessionlocalstorage/index.html index f119e18b7..c878c72ba 100644 --- a/reference/fakes/readonlysessionlocalstorage/index.html +++ b/reference/fakes/readonlysessionlocalstorage/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/i18next/geti18nextplugin/index.html b/reference/i18next/geti18nextplugin/index.html index 419bbc4b6..a7dff931c 100644 --- a/reference/i18next/geti18nextplugin/index.html +++ b/reference/i18next/geti18nextplugin/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/i18next/i18nextnavigationitemlabel/index.html b/reference/i18next/i18nextnavigationitemlabel/index.html index edaf59960..bb44686f6 100644 --- a/reference/i18next/i18nextnavigationitemlabel/index.html +++ b/reference/i18next/i18nextnavigationitemlabel/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/i18next/i18nextplugin/index.html b/reference/i18next/i18nextplugin/index.html index e8a158e66..a4aaad8fc 100644 --- a/reference/i18next/i18nextplugin/index.html +++ b/reference/i18next/i18nextplugin/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/i18next/usechangelanguage/index.html b/reference/i18next/usechangelanguage/index.html index 929954463..ff73d03e7 100644 --- a/reference/i18next/usechangelanguage/index.html +++ b/reference/i18next/usechangelanguage/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/i18next/usecurrentlanguage/index.html b/reference/i18next/usecurrentlanguage/index.html index 613056af2..797ea8ddd 100644 --- a/reference/i18next/usecurrentlanguage/index.html +++ b/reference/i18next/usecurrentlanguage/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/i18next/usei18nextinstance/index.html b/reference/i18next/usei18nextinstance/index.html index 5600f9268..3d3cd797f 100644 --- a/reference/i18next/usei18nextinstance/index.html +++ b/reference/i18next/usei18nextinstance/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/index.html b/reference/index.html index cd8af5d02..0cf90fc77 100644 --- a/reference/index.html +++ b/reference/index.html @@ -4,7 +4,7 @@ - + @@ -32,11 +32,11 @@ - + - + - + diff --git a/reference/logging/consolelogger/index.html b/reference/logging/consolelogger/index.html index 0a62a6526..ba7b1a0c9 100644 --- a/reference/logging/consolelogger/index.html +++ b/reference/logging/consolelogger/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/logging/logger/index.html b/reference/logging/logger/index.html index 31196d736..2e5c9dd85 100644 --- a/reference/logging/logger/index.html +++ b/reference/logging/logger/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/messaging/eventbus/index.html b/reference/messaging/eventbus/index.html index 9e1130710..a226d766b 100644 --- a/reference/messaging/eventbus/index.html +++ b/reference/messaging/eventbus/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/messaging/useeventbusdispatcher/index.html b/reference/messaging/useeventbusdispatcher/index.html index 09dbcd323..565f47bd0 100644 --- a/reference/messaging/useeventbusdispatcher/index.html +++ b/reference/messaging/useeventbusdispatcher/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/messaging/useeventbuslistener/index.html b/reference/messaging/useeventbuslistener/index.html index 3b89f415f..bb5669f80 100644 --- a/reference/messaging/useeventbuslistener/index.html +++ b/reference/messaging/useeventbuslistener/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/msw/setmswasstarted/index.html b/reference/msw/setmswasstarted/index.html index 503c4397a..c1a2189a7 100644 --- a/reference/msw/setmswasstarted/index.html +++ b/reference/msw/setmswasstarted/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/msw/useismswready/index.html b/reference/msw/useismswready/index.html index 67ab88852..c7d36fa90 100644 --- a/reference/msw/useismswready/index.html +++ b/reference/msw/useismswready/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/packages/index.html b/reference/packages/index.html index cd32cb5f2..1c2341030 100644 --- a/reference/packages/index.html +++ b/reference/packages/index.html @@ -4,7 +4,7 @@ - + @@ -34,11 +34,11 @@ - + - + - + diff --git a/reference/plugins/plugin/index.html b/reference/plugins/plugin/index.html index 6d2572161..85e688489 100644 --- a/reference/plugins/plugin/index.html +++ b/reference/plugins/plugin/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/registration/completelocalmoduleregistrations/index.html b/reference/registration/completelocalmoduleregistrations/index.html index eca923f6d..9db0cd432 100644 --- a/reference/registration/completelocalmoduleregistrations/index.html +++ b/reference/registration/completelocalmoduleregistrations/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/registration/completemoduleregistrations/index.html b/reference/registration/completemoduleregistrations/index.html index b2901e17e..22389d29c 100644 --- a/reference/registration/completemoduleregistrations/index.html +++ b/reference/registration/completemoduleregistrations/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/registration/completeremotemoduleregistrations/index.html b/reference/registration/completeremotemoduleregistrations/index.html index 179870efc..1876ac594 100644 --- a/reference/registration/completeremotemoduleregistrations/index.html +++ b/reference/registration/completeremotemoduleregistrations/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/registration/registerlocalmodules/index.html b/reference/registration/registerlocalmodules/index.html index 0860f7e49..4df63894a 100644 --- a/reference/registration/registerlocalmodules/index.html +++ b/reference/registration/registerlocalmodules/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/registration/registerremotemodules/index.html b/reference/registration/registerremotemodules/index.html index d2df2018b..c5d2f0340 100644 --- a/reference/registration/registerremotemodules/index.html +++ b/reference/registration/registerremotemodules/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/registration/usearemodulesready/index.html b/reference/registration/usearemodulesready/index.html index dadf46c83..b7507a2ee 100644 --- a/reference/registration/usearemodulesready/index.html +++ b/reference/registration/usearemodulesready/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/registration/usearemodulesregistered/index.html b/reference/registration/usearemodulesregistered/index.html index 16ed6a104..1ed0191b3 100644 --- a/reference/registration/usearemodulesregistered/index.html +++ b/reference/registration/usearemodulesregistered/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/routing/approuter/index.html b/reference/routing/approuter/index.html index 9c9e82000..d80c1289f 100644 --- a/reference/routing/approuter/index.html +++ b/reference/routing/approuter/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/routing/managedroutes/index.html b/reference/routing/managedroutes/index.html index c8e76d787..b276a5bb9 100644 --- a/reference/routing/managedroutes/index.html +++ b/reference/routing/managedroutes/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/routing/useisroutematchprotected/index.html b/reference/routing/useisroutematchprotected/index.html index 3aee203e2..4481386a0 100644 --- a/reference/routing/useisroutematchprotected/index.html +++ b/reference/routing/useisroutematchprotected/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/routing/userenderednavigationitems/index.html b/reference/routing/userenderednavigationitems/index.html index 1f05245b5..1c56a4f24 100644 --- a/reference/routing/userenderednavigationitems/index.html +++ b/reference/routing/userenderednavigationitems/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/routing/useroutematch/index.html b/reference/routing/useroutematch/index.html index 7fbebda6c..1284b4b28 100644 --- a/reference/routing/useroutematch/index.html +++ b/reference/routing/useroutematch/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/runtime/runtime-class/index.html b/reference/runtime/runtime-class/index.html index a74fdc921..737547b46 100644 --- a/reference/runtime/runtime-class/index.html +++ b/reference/runtime/runtime-class/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/runtime/runtime-context/index.html b/reference/runtime/runtime-context/index.html index 58b833c01..7d2129187 100644 --- a/reference/runtime/runtime-context/index.html +++ b/reference/runtime/runtime-context/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/runtime/uselogger/index.html b/reference/runtime/uselogger/index.html index e25612a72..53743f12d 100644 --- a/reference/runtime/uselogger/index.html +++ b/reference/runtime/uselogger/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/runtime/usenavigationitems/index.html b/reference/runtime/usenavigationitems/index.html index 5cbfb3768..38aa77261 100644 --- a/reference/runtime/usenavigationitems/index.html +++ b/reference/runtime/usenavigationitems/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/runtime/useroutes/index.html b/reference/runtime/useroutes/index.html index cf3652991..8abce37f5 100644 --- a/reference/runtime/useroutes/index.html +++ b/reference/runtime/useroutes/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/runtime/useruntime/index.html b/reference/runtime/useruntime/index.html index 1ad5b19aa..2720fe6f1 100644 --- a/reference/runtime/useruntime/index.html +++ b/reference/runtime/useruntime/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/runtime/usesession/index.html b/reference/runtime/usesession/index.html index 9b5412aa1..5b8a658e8 100644 --- a/reference/runtime/usesession/index.html +++ b/reference/runtime/usesession/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/session/useisauthenticated/index.html b/reference/session/useisauthenticated/index.html index c1469e39b..b4a8bc168 100644 --- a/reference/session/useisauthenticated/index.html +++ b/reference/session/useisauthenticated/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/webpack/definebuildhostconfig/index.html b/reference/webpack/definebuildhostconfig/index.html index 39aa6e751..f27ca6d93 100644 --- a/reference/webpack/definebuildhostconfig/index.html +++ b/reference/webpack/definebuildhostconfig/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/webpack/definebuildremotemoduleconfig/index.html b/reference/webpack/definebuildremotemoduleconfig/index.html index d2eb0a60c..55f93692d 100644 --- a/reference/webpack/definebuildremotemoduleconfig/index.html +++ b/reference/webpack/definebuildremotemoduleconfig/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/webpack/definedevhostconfig/index.html b/reference/webpack/definedevhostconfig/index.html index b2dd8cc81..d7fc5defd 100644 --- a/reference/webpack/definedevhostconfig/index.html +++ b/reference/webpack/definedevhostconfig/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/reference/webpack/definedevremotemoduleconfig/index.html b/reference/webpack/definedevremotemoduleconfig/index.html index a76b9cdd8..2798c8587 100644 --- a/reference/webpack/definedevremotemoduleconfig/index.html +++ b/reference/webpack/definedevremotemoduleconfig/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + + diff --git a/resources/js/config.js b/resources/js/config.js index 9c985e284..22842185f 100644 --- a/resources/js/config.js +++ b/resources/js/config.js @@ -1 +1 @@ -var __DOCS_CONFIG__ = {"id":"i2BG1193nuEq2aRylG3btyvNHZVav0LN1RG","key":"T1DCRuu9H/t1qLYEMM5vlo5e8mBA8Y1+O2dKxL/SyC8.dYQxPzKeILunwdDpsa0iC56AASLusPNYFiRGHpZt5HX/PQGGFGj8cBuSuEzF1tALqixa+U/UZ49p/02oVCFM4g.300118","base":"/wl-squide/","host":"gsoft-inc.github.io","version":"1.0.0","useRelativePaths":true,"documentName":"index.html","appendDocumentName":false,"trailingSlash":true,"preloadSearch":false,"cacheBustingToken":"3.5.0.755028289776","cacheBustingStrategy":"query","sidebarFilterPlaceholder":"Filter","toolbarFilterPlaceholder":"Filter","showSidebarFilter":true,"filterNotFoundMsg":"No member names found containing the query \"{query}\"","maxHistoryItems":15,"homeIcon":"","access":[{"value":"public","label":"Public"},{"value":"protected","label":"Protected"}],"toolbarLinks":[{"id":"fields","label":"Fields"},{"id":"properties","label":"Properties"},{"id":"methods","label":"Methods"},{"id":"events","label":"Events"}],"sidebar":[{"n":"getting-started","l":"Getting started","o":true,"i":[{"n":"create-host","l":"Create an host app"},{"n":"create-remote-module","l":"Create a remote module"},{"n":"create-local-module","l":"Create a local module"},{"n":"learn-the-api","l":"Learn the API"},{"n":"deploy","l":"Deploy"}],"s":""},{"n":"guides","l":"Guides","o":true,"i":[{"n":"setup-msw","l":"Setup Mock Service Worker"},{"n":"fetch-initial-data","l":"Fetch initial data"},{"n":"fetch-page-data","l":"Fetch page data"},{"n":"manage-shared-state","l":"Manage shared state"},{"n":"isolate-module-failures","l":"Isolate module failures"},{"n":"add-authentication","l":"Add authentication"},{"n":"add-a-public-page","l":"Add a public page"},{"n":"override-the-host-layout","l":"Override the host layout"},{"n":"federated-tabs","l":"Federated tabs"},{"n":"use-feature-flags","l":"Use feature flags"},{"n":"setup-localization","l":"Setup localization"},{"n":"develop-a-module-in-isolation","l":"Develop a module in isolation"},{"n":"override-a-react-context","l":"Override a React context"},{"n":"add-a-shared-dependency","l":"Add a shared dependency"},{"n":"implement-a-custom-logger","l":"Implement a custom logger"},{"n":"migrate-from-a-monolith","l":"Migrate from a monolith"}],"s":""},{"n":"reference","l":"Reference","o":true,"i":[{"n":"packages","l":"Packages"},{"n":"runtime","l":"Runtime","c":false,"i":[{"n":"runtime-class","l":"Firefly​Runtime class"},{"n":"runtime-context","l":"Runtime​Context"},{"n":"useruntime","l":"use​Runtime"},{"n":"useroutes","l":"use​Routes"},{"n":"usenavigationitems","l":"use​Navigation​Items"},{"n":"uselogger","l":"use​Logger"},{"n":"usesession","l":"use​Session"}]},{"n":"registration","l":"Registration","c":false,"i":[{"n":"registerlocalmodules","l":"register​Local​Modules"},{"n":"registerremotemodules","l":"register​Remote​Modules"},{"n":"completemoduleregistrations","l":"complete​Module​Registrations"},{"n":"completelocalmoduleregistrations","l":"complete​Local​Module​Registrations"},{"n":"completeremotemoduleregistrations","l":"complete​Remote​Module​Registrations"},{"n":"usearemodulesregistered","l":"use​Are​Modules​Registered"},{"n":"usearemodulesready","l":"use​Are​Modules​Ready"}]},{"n":"routing","l":"Routing","c":false,"i":[{"n":"approuter","l":"App​Router"},{"n":"managedroutes","l":"Managed​Routes"},{"n":"useisroutematchprotected","l":"use​Is​Route​Match​Protected"},{"n":"userenderednavigationitems","l":"use​Rendered​Navigation​Items"},{"n":"useroutematch","l":"use​Route​Match"}]},{"n":"logging","l":"Logging","c":false,"i":[{"n":"consolelogger","l":"Console​Logger"},{"n":"logger","l":"Logger"}]},{"n":"messaging","l":"Messaging","c":false,"i":[{"n":"eventbus","l":"Event​Bus"},{"n":"useeventbusdispatcher","l":"use​Event​Bus​Dispatcher"},{"n":"useeventbuslistener","l":"use​Event​Bus​Listener"}]},{"n":"session","l":"Session","c":false,"i":[{"n":"useisauthenticated","l":"use​Is​Authenticated"}]},{"n":"plugins","l":"Plugins","c":false,"i":[{"n":"plugin","l":"Plugin"}]},{"n":"webpack","c":false,"i":[{"n":"definedevhostconfig","l":"define​Dev​Host​Config"},{"n":"definedevremotemoduleconfig","l":"define​Dev​Remote​Module​Config"},{"n":"definebuildhostconfig","l":"define​Build​Host​Config"},{"n":"definebuildremotemoduleconfig","l":"define​Build​Remote​Module​Config"}]},{"n":"msw","l":"Mock Service Worker","c":false,"i":[{"n":"setmswasstarted","l":"set​Msw​As​Started"},{"n":"useismswready","l":"use​Is​Msw​Ready"}]},{"n":"i18next","l":"i​18​next","c":false,"i":[{"n":"i18nextplugin","l":"i​18​next​Plugin"},{"n":"geti18nextplugin","l":"get​I​18​next​Plugin"},{"n":"usechangelanguage","l":"use​Change​Language"},{"n":"usecurrentlanguage","l":"use​Current​Language"},{"n":"usei18nextinstance","l":"use​I​18​next​Instance"},{"n":"i18nextnavigationitemlabel","l":"I​18​next​Navigation​Item​Label"}]},{"n":"fakes","l":"Fakes","c":false,"i":[{"n":"localstoragesessionmanager","l":"Local​Storage​Session​Manager"},{"n":"readonlysessionlocalstorage","l":"Readonly​Session​Local​Storage"}]}],"s":""},{"n":"troubleshooting","l":"Troubleshooting","s":""},{"n":"samples","l":"Samples","s":""},{"n":"about","l":"About","v":false}],"search":{"mode":0,"minChars":2,"maxResults":20,"placeholder":"Search","hotkeys":["k"],"noResultsFoundMsg":"Sorry, no results found.","recognizeLanguages":true,"languages":[0],"preload":false},"resources":{"History_Title_Label":"History","History_ClearLink_Label":"Clear","History_NoHistory_Label":"No history items","API_AccessFilter_Label":"Access","API_ParameterSection_Label":"PARAMETERS","API_SignatureSection_Label":"SIGNATURE","API_CopyHint_Label":"Copy","API_CopyNameHint_Label":"Copy name","API_CopyLinkHint_Label":"Copy link","API_CopiedAckHint_Label":"Copied!","API_MoreOverloads_Label":"more","API_MoreDropdownItems_Label":"More","API_OptionalParameter_Label":"optional","API_DefaultParameterValue_Label":"Default value","API_InheritedFilter_Label":"Inherited","Search_Input_Placeholder":"Search","Toc_Contents_Label":"Contents","Toc_RelatedClasses_Label":"Related Classes","History_JustNowTime_Label":"just now","History_AgoTime_Label":"ago","History_YearTime_Label":"y","History_MonthTime_Label":"mo","History_DayTime_Label":"d","History_HourTime_Label":"h","History_MinuteTime_Label":"m","History_SecondTime_Label":"s"}}; +var __DOCS_CONFIG__ = {"id":"IyfhGc0XNWHl5oEXqLp/qstk119wfeR4KVu","key":"wE1pu2bE91sjL3BLBzZdSkFGYccFx5lib3ugB8oh7Jc.K05cpZ62vA5n0bxKnEQ7Vn+7Th5kEAVkEssSDt4JLGg4uptfZB7iDpPPq8uSQeZUm8HTOd03O/efjE1bnIkGVg.300112","base":"/wl-squide/","host":"gsoft-inc.github.io","version":"1.0.0","useRelativePaths":true,"documentName":"index.html","appendDocumentName":false,"trailingSlash":true,"preloadSearch":false,"cacheBustingToken":"3.5.0.755035382199","cacheBustingStrategy":"query","sidebarFilterPlaceholder":"Filter","toolbarFilterPlaceholder":"Filter","showSidebarFilter":true,"filterNotFoundMsg":"No member names found containing the query \"{query}\"","maxHistoryItems":15,"homeIcon":"","access":[{"value":"public","label":"Public"},{"value":"protected","label":"Protected"}],"toolbarLinks":[{"id":"fields","label":"Fields"},{"id":"properties","label":"Properties"},{"id":"methods","label":"Methods"},{"id":"events","label":"Events"}],"sidebar":[{"n":"getting-started","l":"Getting started","o":true,"i":[{"n":"create-host","l":"Create an host app"},{"n":"create-remote-module","l":"Create a remote module"},{"n":"create-local-module","l":"Create a local module"},{"n":"learn-the-api","l":"Learn the API"},{"n":"deploy","l":"Deploy"}],"s":""},{"n":"guides","l":"Guides","o":true,"i":[{"n":"setup-msw","l":"Setup Mock Service Worker"},{"n":"fetch-initial-data","l":"Fetch initial data"},{"n":"fetch-page-data","l":"Fetch page data"},{"n":"manage-shared-state","l":"Manage shared state"},{"n":"isolate-module-failures","l":"Isolate module failures"},{"n":"add-authentication","l":"Add authentication"},{"n":"add-a-public-page","l":"Add a public page"},{"n":"override-the-host-layout","l":"Override the host layout"},{"n":"federated-tabs","l":"Federated tabs"},{"n":"use-feature-flags","l":"Use feature flags"},{"n":"setup-localization","l":"Setup localization"},{"n":"develop-a-module-in-isolation","l":"Develop a module in isolation"},{"n":"override-a-react-context","l":"Override a React context"},{"n":"add-a-shared-dependency","l":"Add a shared dependency"},{"n":"implement-a-custom-logger","l":"Implement a custom logger"},{"n":"migrate-from-a-monolith","l":"Migrate from a monolith"}],"s":""},{"n":"reference","l":"Reference","o":true,"i":[{"n":"packages","l":"Packages"},{"n":"runtime","l":"Runtime","c":false,"i":[{"n":"runtime-class","l":"Firefly​Runtime class"},{"n":"runtime-context","l":"Runtime​Context"},{"n":"useruntime","l":"use​Runtime"},{"n":"useroutes","l":"use​Routes"},{"n":"usenavigationitems","l":"use​Navigation​Items"},{"n":"uselogger","l":"use​Logger"},{"n":"usesession","l":"use​Session"}]},{"n":"registration","l":"Registration","c":false,"i":[{"n":"registerlocalmodules","l":"register​Local​Modules"},{"n":"registerremotemodules","l":"register​Remote​Modules"},{"n":"completemoduleregistrations","l":"complete​Module​Registrations"},{"n":"completelocalmoduleregistrations","l":"complete​Local​Module​Registrations"},{"n":"completeremotemoduleregistrations","l":"complete​Remote​Module​Registrations"},{"n":"usearemodulesregistered","l":"use​Are​Modules​Registered"},{"n":"usearemodulesready","l":"use​Are​Modules​Ready"}]},{"n":"routing","l":"Routing","c":false,"i":[{"n":"approuter","l":"App​Router"},{"n":"managedroutes","l":"Managed​Routes"},{"n":"useisroutematchprotected","l":"use​Is​Route​Match​Protected"},{"n":"userenderednavigationitems","l":"use​Rendered​Navigation​Items"},{"n":"useroutematch","l":"use​Route​Match"}]},{"n":"logging","l":"Logging","c":false,"i":[{"n":"consolelogger","l":"Console​Logger"},{"n":"logger","l":"Logger"}]},{"n":"messaging","l":"Messaging","c":false,"i":[{"n":"eventbus","l":"Event​Bus"},{"n":"useeventbusdispatcher","l":"use​Event​Bus​Dispatcher"},{"n":"useeventbuslistener","l":"use​Event​Bus​Listener"}]},{"n":"session","l":"Session","c":false,"i":[{"n":"useisauthenticated","l":"use​Is​Authenticated"}]},{"n":"plugins","l":"Plugins","c":false,"i":[{"n":"plugin","l":"Plugin"}]},{"n":"webpack","c":false,"i":[{"n":"definedevhostconfig","l":"define​Dev​Host​Config"},{"n":"definedevremotemoduleconfig","l":"define​Dev​Remote​Module​Config"},{"n":"definebuildhostconfig","l":"define​Build​Host​Config"},{"n":"definebuildremotemoduleconfig","l":"define​Build​Remote​Module​Config"}]},{"n":"msw","l":"Mock Service Worker","c":false,"i":[{"n":"setmswasstarted","l":"set​Msw​As​Started"},{"n":"useismswready","l":"use​Is​Msw​Ready"}]},{"n":"i18next","l":"i​18​next","c":false,"i":[{"n":"i18nextplugin","l":"i​18​next​Plugin"},{"n":"geti18nextplugin","l":"get​I​18​next​Plugin"},{"n":"usechangelanguage","l":"use​Change​Language"},{"n":"usecurrentlanguage","l":"use​Current​Language"},{"n":"usei18nextinstance","l":"use​I​18​next​Instance"},{"n":"i18nextnavigationitemlabel","l":"I​18​next​Navigation​Item​Label"}]},{"n":"fakes","l":"Fakes","c":false,"i":[{"n":"localstoragesessionmanager","l":"Local​Storage​Session​Manager"},{"n":"readonlysessionlocalstorage","l":"Readonly​Session​Local​Storage"}]}],"s":""},{"n":"troubleshooting","l":"Troubleshooting","s":""},{"n":"samples","l":"Samples","s":""},{"n":"about","l":"About","v":false}],"search":{"mode":0,"minChars":2,"maxResults":20,"placeholder":"Search","hotkeys":["k"],"noResultsFoundMsg":"Sorry, no results found.","recognizeLanguages":true,"languages":[0],"preload":false},"resources":{"History_Title_Label":"History","History_ClearLink_Label":"Clear","History_NoHistory_Label":"No history items","API_AccessFilter_Label":"Access","API_ParameterSection_Label":"PARAMETERS","API_SignatureSection_Label":"SIGNATURE","API_CopyHint_Label":"Copy","API_CopyNameHint_Label":"Copy name","API_CopyLinkHint_Label":"Copy link","API_CopiedAckHint_Label":"Copied!","API_MoreOverloads_Label":"more","API_MoreDropdownItems_Label":"More","API_OptionalParameter_Label":"optional","API_DefaultParameterValue_Label":"Default value","API_InheritedFilter_Label":"Inherited","Search_Input_Placeholder":"Search","Toc_Contents_Label":"Contents","Toc_RelatedClasses_Label":"Related Classes","History_JustNowTime_Label":"just now","History_AgoTime_Label":"ago","History_YearTime_Label":"y","History_MonthTime_Label":"mo","History_DayTime_Label":"d","History_HourTime_Label":"h","History_MinuteTime_Label":"m","History_SecondTime_Label":"s"}}; diff --git a/resources/js/search.json b/resources/js/search.json index 0518e2698..18d99ebc2 100644 --- a/resources/js/search.json +++ b/resources/js/search.json @@ -1 +1 @@ -[[{"l":"Getting started","p":["Welcome to Squide (yes \uD83E\uDD91 with an \"e\"), a shell for Workleap federated applications. In this getting started section, you'll find an overview of the shell and a quick start guide to create a new federated application from scratch."]},{"i":"why-a-shell","l":"Why a shell?","p":["We have built this shell to facilitate the adoption of federated applications at Workleap by enforcing patterns that we believe will help feature teams successfully implement a distributed architecture.","The shell itself is a lightweight API layer built on top of Module Federation and React Router, with the goal of maximizing the strength of both libraries while interfering as little as possible with their functionality."]},{"l":"Module Federation","p":["We have identified 2 major challenges with frontend federated applications:","How can we prevent loading the same large dependencies twice when switching between modules?","How can we offer a cohesive experience that doesn't feel modular?","To address the first challenge, we believe that Module Federation provides a solution by offering a mecanism capable of deduping common dependencies shared between the host application and the remote modules at runtime.","With this mecanism in place, all federated parts of an application can now be loaded in the same browsing context instead of nested browsing contexts such as iframes.","By sharing the same browsing context (e.g. the same Document object, the same Window object, and the same DOM), federated parts now form a unified and cohesive single application, addressing the second challenge.","With Module Federation, we hope to develop federated applications that provide the same user experience as monolithic applications \uD83D\uDE80."]},{"l":"React Router","p":["React Router nested routes feature is ideal for federated applications as it enables highly composable and decoupled UI. For a more in-depth explanation, refer to this article."]},{"l":"Module registration","p":["The most distinctive aspect of this shell is the conventions it enforces for loading and registering remote modules. Here's a brief overview of the flow:","During bootstrap, the host application attempts to load predefined modules and calls a registration function with a specific name and signature for each successfully loaded module.","During registration, a module receives the shared services of the federation application and use them to dynamically register its routes and navigation items.","Once all the modules are registered, the host application will create a React Router instance with the registered routes and renders a navigation menu with the registered navigation items.","That's a nutshell overview. Of course, there is more to it, but these are the main ideas."]},{"l":"Guiding principles","p":["While developing the API of Squide, we kept a few guiding principles in mind. Those principles are not settled stones, you might want to diverge from them from time to time, but adhering to those will make your experience more enjoyable:","A module should always correspond to a subdomain of the application's business domain and should only export pages.","A module should be fully autonomous. It shouldn't have to coordinate with other parts of the application for things as trivial as navigation links.","A federated application should feel cohesive. Different parts of a federation application should have the ability to communicate with each others and react to changes happening outside of their boundaries.","Data and state should never be shared between parts of a federated application. Even if two parts require the same data or the same state values, they should load, store and manage them independently."]},{"l":"Limitations","p":["Module Federation comes with a few manageable limitations that are important to consider when architecting your distributed application:","A shared dependency cannot be tree-shaken. Since remote modules are loaded at runtime, ModuleFederationPlugin cannot infer which parts of a shared dependency will be used by the application modules. Therefore, tree-shaking is disabled for shared dependencies.","Module Federation does not support React Fast Refresh. However, it does support Hot Module Replacement.","These limitations are not specific to Squide, they are specific to Module Federation."]},{"l":"Create your project","p":["To get started, follow the quick start guide to create a new federated application from scratch."]}],[{"l":"Create an host application","p":["Let's begin by creating the application that will serve as the entry point for our federated application and host the application modules."]},{"l":"Install the packages","p":["Create a new application (we'll refer to ours as host), then open a terminal at the root of the new solution and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the application","p":["First, create the following files:","Then, ensure that you are developing your application using ESM syntax by specifying type: module in your package.json file:","Finally, use a dynamic import to add an async boundary:","To learn more about this async boundary and the bootstrap.tsx file, read the following article."]},{"l":"Module registration","p":["Next, to register the modules, instanciate a shell FireflyRuntime instance and register the remote module with the registerRemoteModules function (the configuration of the remote module will be covered in the next section):","Then, render the AppRouter component. The AppRouter component will render a React Router browser instance configured with the registered routes:"]},{"l":"Navigation items","p":["Next, create a layout component to render the navigation items:"]},{"l":"Homepage","p":["Next, create the HomePage component that will serve as the homepage for this application:","Then, add a local module at the root of the host application to register the homepage:","And an hoisted route to render the RootLayout and the ManagedRoutes placeholder:","The ManagedRoutes placeholder indicates where routes that are neither hoisted or nested with a parentPath or parentName option will be rendered. In this example, the homepage route is considered a managed route and will be rendered under the ManagedRoutes placeholder.","Finally, update the bootstrapping code to register the newly created local module:"]},{"l":"Configure webpack","p":["Squide webpack configuration is built on top of @workleap/webpack-configs, @workleap/browserslist-config and @workleap/swc-configs. If you are having issues with the configuration of these tools, refer to the tools documentation websites.","First, open the public/index.html file created at the beginning of this guide and copy/paste the following HtmlWebpackPlugin template:","Then, open the .browserslist file and copy/paste the following content:"]},{"l":"Development configuration","p":["To configure webpack for a development environment, first open the swc.dev.js file and copy/paste the following code:","Then, open the webpack.dev.js file and use the defineDevHostConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Build configuration","p":["To configure webpack for a build environment, first open the swc.build.js file and copy/paste the following code:","Then, open the webpack.build.js file and use the defineBuildHostConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Add CLI scripts","p":["To initiate the development server, add the following script to the application package.json file:","To build the application, add the following script to the application package.json file:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script. You should see the home page. Even if the remote module application is not yet available, the host application will gracefully load."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] Found 4 local modules to register.","[squide] 1/4 Registering local module.","[squide] 1/4 Local module registration completed.","[squide] Found 1 remote module to register.","[squide] 1/1 Loading module ./register from container remote1 of remote http://localhost:8081/remoteEntry.js.","[squide] 1/1 Container remote1 of remote http://localhost:8081/remoteEntry.js registration completed.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Create a remote module","p":["Remote modules are modules that are not included in the host application build but are instead loaded at runtime from a remote server. They provide a way for teams to be fully autonomous by independently deploying their modules without relying on the other parts of the application.","Let's add our first remote module!"]},{"l":"Install the packages","p":["Create a new application (we'll refer to ours as remote-module), then open a terminal at the root of the new solution and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the application","p":["First, create the following files:","Then, ensure that you are developing your module using ESM syntax by specifying type: module in your package.json file:"]},{"l":"Routes registration","p":["Next, register the remote module routes and navigation items with the registerRoute and registerNavigationItem functions:","Then, create the Page component:"]},{"l":"Configure webpack","p":["Squide webpack configuration is built on top of @workleap/webpack-configs, @workleap/browserslist-config and @workleap/swc-configs. If you are having issues with the configuration of these tools, refer to the tools documentation websites."]},{"l":"Development configuration","p":["To configure webpack for a development environment, first open the swc.dev.js file and copy/paste the following code:","Then, open the webpack.dev.js file and use the the defineDevRemoteModuleConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Build configuration","p":["To configure webpack for a build environment, first open the swc.build.js file and copy/paste the following code:","Then, open the webpack.build.js file and use the the defineBuildRemoteModuleConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Add CLI scripts","p":["To initiate the development server, add the following script to the application package.json file:","To build the module, add the following script to the application package.json file:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the host and the remote-module applications in development mode using the dev script. You should notice an additional link labelled Remote/Page in the navigation menu. Click on the link to navigate to the page of your new remote module!"]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] The following route has been registered. Newly registered item: ...","[squide] The following navigation item has been registered to the root menu for a total of 2 items. Newly registered item: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Create a local module","p":["Local modules are regular modules that are part of the host application build. They are independent modules that expose a registration function to the host application's bootstrapping code. A local module can be a standalone package, a sibling project (in a monorepo setup), or even a local folder within the host application.","Local modules are useful when migrating from a monolithic application to a distributed application or when launching a new product with an unrefined business domain.","Let's add a local module to demonstrate how it's done!","Loading remote modules at runtime with Module Federation is the primary focus of this shell and our recommended approach. It empowers teams to be fully autonomous by deploying their modules independently from the other parts of the application.","However, we recognize that teams working on mature products may prefer to gradually migrate to a distributed architecture by first extracting subdomains into independent modules within their current monolithic setup before fully committing to remote modules loaded at runtime.","To facilitate this transition, this shell also supports local modules that are loaded at build time.","Both remote and local modules can be used within same application as this shell supports dual bootstrapping. For example, an application can be configured to load a few remote modules at runtime while also loading a few local modules."]},{"l":"Install the packages","p":["Create a new application (we'll refer to ours as local-module), then open a terminal at the root of the new solution and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommend that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the application","p":["First, create the following files:","Then, ensure that you are developing your module using ESM syntax by specifying type: module in your package.json file:","Finally, configure the package to be shareable by adding the name, version, and export fields to the package.json file:"]},{"l":"Routes registration","p":["Next, register the local module routes and navigation items with registerRoute and registerNavigationItem functions:","Then, create the Page component:"]},{"l":"Register the local module","p":["Go back to the host application add a dependency to the @sample/local-module package in the host application package.json file:","Then, register the local module with the registerLocalModule function:"]},{"l":"Configure tsup","p":["If you are having issues with the tsup configuration, refer to the @workleap/tsup-configs documentation."]},{"l":"Development configuration","p":["To configure tsup for a development environment, open the tsup.dev.ts file and copy/paste the following code:"]},{"l":"Build configuration","p":["To configure tsup for a build environment, open the tsup.build.ts file and copy/paste the following code:"]},{"l":"Add CLI scripts","p":["To initiate the development server, add the following script to the application package.json file:","To build the module, add the following script to the application package.json file:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the host, remote-module and local-module applications in development mode using the dev script. You should notice an additional link labelled Local/Page in the navigation menu. Click on the link to navigate to the page of your new local module!"]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] The following route has been registered. Newly registered item: ...","[squide] The following navigation item has been registered to the root menu for a total of 2 items. Newly registered item: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Learn the API","p":["Now that we've created a host application, loaded a few modules and registered routes and navigation items, let's delve into the APIs provided by this shell."]},{"l":"Runtime mode","p":["In an effort to optimize the development experience, Squide can be bootstrapped in development or production mode:","By default, the Runtime mode is development."]},{"l":"Logging","p":["Squide includes a built-in logging feature that integrates with the FireflyRuntime class and the useLogger hook.","First, register your own custom logger by implementing the Logger interface or register Squide built-in ConsoleLogger:","Then, log entries from any parts of your federated application with the useLogger hook:","The logger is also available from the FireflyRuntime instance."]},{"l":"Messaging","p":["It's crucial that the parts of a federated application remains loosely coupled. To help with that, Squide offers a built-in Event Bus.","First, listen to an event with the useEventBusListener hook:","Then, dispatch an event from anywhere with the useEventBusDispatcher hook:","You can use the event bus to enable various communication scenarios, such as notifying components of state changes, broadcasting messages across modules, or triggering actions based on specific events.","The event bus is also available from the FireflyRuntime instance."]},{"l":"Session","p":["Most of our applications (if not all) will eventually require the user to authenticate. To facilitate this process, the Squide FireflyRuntime class accepts a sessionAccessor function. Once the shell registration flow is completed, the function will be made accessible to every module of the application.","First, define a sessionAccessor function:","Our security department reminds you to refrain from using a fake LocalStorageSessionManager in a production application \uD83D\uDE0A","Then register the accessor function:","Finally, access the session from any parts of the application with the useSession hook:","Or determine whether or not the user is authenticated with the useIsAuthenticated hook:","The session is also available from the FireflyRuntime instance."]},{"l":"Plugins","p":["To keep Squide lightweight, not all functionalities should be integrated as a core functionality. However, to accommodate a broad range of technologies, a plugin system has been implemented to fill the gap.","Plugins can be registered at bootstrapping with the FireflyRuntime instance:","Then, the plugins can be accessed anywhere from the FireflyRuntime instance:"]},{"l":"Fakes","p":["Take a look at the fake implementations. These implementations are designed to facilitate the set up of a module isolated environment."]},{"l":"Guides","p":["Explore the guides section to learn about Squide advanced features.","Be sure to read, at a minimum, the following guides:","Setup Mock Service Worker","Fetch initial data","Fetch page data","Manage shared state","Isolate module failures","Add authentication","Add a shared dependency"]},{"l":"Reference","p":["For a comprehensive list of the Squide API, refer to the reference section."]}],[{"l":"Deploy","p":["The deployment process for a federated application can vary depending on various factors, including the chosen hosting provider. Therefore, we do not recommend any specific deployment setup.","However, there are a few essential configurations that need to be made regardless of your deployment choices."]},{"l":"Add a default redirect","p":["To enable support for direct page hits, add the following redirect rule to your host application's hosting provider:"]},{"l":"Set the remote URL","p":["Configure the remote modules production URL:"]},{"l":"Update the runtime mode","p":["Don't forget to change the FireflyRuntime mode to production:"]},{"l":"Remove the console logger","p":["Remove the ConsoleLogger from the production build:"]}],[{"l":"Guides","p":["Add a public page","Add a shared dependency","Add authentication","Develop a module in isolation","Federated tabs","Fetch initial data","Fetch page data","Implement a custom logger","Isolate module failures","Manage shared state","Migrate from a monolithic application","Override a React context","Override the host layout","Setup localization","Setup Mock Service Worker","Use feature flags"]}],[{"l":"Setup Mock Service Worker","p":["To speed up frontend development and encourage an API first approach, Squide has built-in support for Mock Service Worker(MSW). MSW offers an API to host fake endpoints directly in the browser. This means that unlike alternative solutions, it doesn't require running an additional process to host fake endpoints."]},{"l":"Setup the host application","p":["First, open a terminal at the root of the host application and install the msw package:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Add an environment variable","p":["Then, update the dev PNPM script to define with cross-env an USE_MSW environment variable that will conditionally include MSW code into the application bundles:","Then, update the development webpack configuration file to include the USE_MSW environment variable into the application bundles:","For more information about the environmentVariables predefined option, refer to the webpack configuration documentation.","Don't forget to define the USE_MSW environment variable for the build script and webpack configuration as well."]},{"l":"Start the service","p":["With the newly added USE_MSW environment variable, the host application bootstrapping code can now be updated to conditionally start MSW when all the request handlers has been registered.","First, define a function to start MSW:","Then, update the bootstrapping code to start the service and mark MSW as started if MSW is enabled:"]},{"l":"Delay routes rendering until the service is started","p":["Finally, update the host application code to delay the rendering of the routes until MSW is started. This is done by setting the waitForMsw property of the AppRouter component to true:"]},{"l":"Setup a remote module","p":["First, open a terminal at the root of the remote module application and install the msw package:","Then, define a request handler:","Finally, register the request handler with the FireflyRuntime instance:","Don't forget to mark the registration function as async since there's a dynamic import."]},{"l":"Setup a local module","p":["Follow the same steps as for a remote module."]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Update a page component code to fetch the /api/character/1 fake API endpoint, then start the application in development mode using the dev script. You should notice that the data has been fetched from the request handler.","In Chrome devtools, the status code for a successful network call that has been handled by an MSW request handler will be 200 OK (from service worker)."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each request handlers registration that occurs and error messages if something went wrong:","[squide] The following MSW request handlers has been registered: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Fetch initial data","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker guide.","Retrieving the initial data of an application is a crucial aspect that isn't always straightforward to implement. That's why we encourage feature teams to build their initial data fetching strategy on top of the Squide AppRouter component."]},{"l":"Challenges with initial data","p":["At first glance, one might wonder what could be so complicated about fetching the initial data of an application. It's just fetches, right...? Well, there are several concerns to take into account for a Squide application:","When in development, the initial data cannot be fetched until the Mock Service Worker (MSW) request handlers are registered and MSW is started.","To register the MSW request handlers, the remote modules must be registered first.","If the requested page is public, only the initial public data should be fetched.","If the requested page is protected, both the initial public and protected data should be fetched.","The requested page cannot be rendered until the initial data has been fetched.","A unique loading spinner should be displayed to the user while the modules are being registered, the MSW request handlers are being registered and the initial data is being fetched, ensuring no flickering due to different spinners being rendered.","To help manage those concerns, Squide offer an AppRouter component that takes care of setuping Squide federated primitive and orchestrating the different states."]},{"l":"Fetch public data"},{"l":"Add an endpoint","p":["First, define an MSW request handler that returns the number of times it has been fetched:","Then, register the request handler using the module registration function:"]},{"l":"Create a shared context","p":["Then, in a shared project, create a React context named FetchCountContext:","Ensure that the shared project is configured as a shared dependency."]},{"l":"Fetch the data","p":["Finally, open the host application code and update the App component to utilize the AppRouter component's onLoadPublicData handler. This handler will fetch the count and forward the retrieved value through FetchCountContext:","The onLoadPublicData handler must return a Promise object. When the async keyword is included in a function signature, the function will automatically return a Promise object."]},{"l":"Use the endpoint data","p":["Now, create a InitialDataLayout component that utilizes the count retrieved from FetchCountContext and render pages with a green background color if the value is odd:","Then, create a Page component:","Finally, register both components:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script and navigate to the /initial-data page. Refresh the page a few times, the background color should alternate between transparent and green."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]},{"l":"Fetch protected data","p":["Now, let's load protected data. The process is similar to fetching public data, but this time, we'll use the onLoadProtectedData handler of the AppRouter component instead."]},{"i":"add-an-endpoint-1","l":"Add an endpoint","p":["First, define a MSW request handler that returns a user tenant subscription data:","If you've registered the public data request handler, the newly created request handler should be automatically registered.","In the previous code sample, for the sake of simplicity, we haven't secured the request handler or implemented session management. However, please be aware that you should do it for a real Workleap application."]},{"i":"create-a-shared-context-1","l":"Create a shared context","p":["Then, in a shared project, create a SubscriptionContext:","Ensure that the shared project is configured as a shared dependency."]},{"i":"fetch-the-data-1","l":"Fetch the data","p":["Finally, open the host application code and update the App component to utilize the AppRouter component's onLoadProtectedData handler. This handler will fetch the user tenant subscription and forward the retrieved value through SubscriptionContext:"]},{"i":"use-the-endpoint-data-1","l":"Use the endpoint data","p":["Now, update the InitialDataLayout component that was previously created for the public data example to render the user tenant subscription status:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script and navigate to the /initial-data page. You should notice the subscription status."]},{"i":"troubleshoot-issues-1","l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Fetch page data","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker guide.","There are various approaches to fetching data for pages. At Workleap, our preference is usually to develop a dedicated endpoint per page, returning a denormalized document specifically tailored for that page. We rely on server state as our singular source of truth and leverage React Query to manage data fetching and ensure our data remains up-to-date.","Although this approach works well, a few adjustments are necessary when transitioning from a monolithic application to a federated application."]},{"l":"Install React Query","p":["First, open a terminal at the root of the module and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the query client","p":["Then, instanciate a QueryClient instance in the module registration function and wrap the routes element with a QueryClientProvider:","To minimize unexpected situations and faciliate maintenance, the React Query cache shouldn't be shared between the host application and the modules. As the React Query cache is located in the QueryClient, both the host application and the modules should instantiate their own QueryClient instance."]},{"l":"Create a component for providers","p":["If the module register multiple routes, to prevent duplicating registration code, you can create a Providers component:"]},{"l":"Setup the development tools","p":["To faciliate development, React Query provides devtools to help visualize all of the inner workings of React Query.","However, the React Query devtools are not been developed to handle a federated application with multiple QueryClient instances. To use the devtools, you have to define a ReactQueryDevtools component for each QueryClient instance:","Then, depending on which page of the application has been rendered, a distinct devtools instance will be accessible. For a better experience, we recommend activating the React Query devtools exclusively when developing a module in isolation:"]},{"l":"Fetch the page data","p":["Now, let's fetch some data. First, add a Mock Service Worker(MSW) request handler to the local module:","Then, register the request handler using the module registration function:","Then, update the Page component to fetch and render the data:"]},{"l":"Define a fallback element","p":["The previous code sample uses useSuspenseQuery instead of useQuery. This enables an application to leverage a React Suspense boundary to render a fallback element in a layout component while the data is being fetched:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the local module in a development environment using the dev-isolated script. If you haven't completed the develop a module in isolation guide, use the dev script instead and skip the part about React Query devtools. Then, navigate to the /page page.","You should notice that the character's data is being fetch from the MSW request handler and rendered on the page. Additionally, you should notice that the React Query devtools are available (a ribbon at the bottom right corner)."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Manage shared state","p":["Effective management of shared state is a crucial aspect of a federated application and can become problematic if not handled carefully. As a general rule, a host application and its modules should never share state."]},{"l":"Forward the initial data","p":["However, at certain points in the lifecycle of a federated application, the host will need to fetch initial data that must be fordwarded to the modules. Such examples include a user session and a user tenant subscription status:","To forward a user session object, a built-in sessionAccessor function is available.","To forward other types of initial data, such as a user tenant subscription, as shown in the fetch initial data guide, the data can be forwarded to modules through a React context."]},{"l":"React Query","p":["Lastly, as detailed in the fetch page data guide, the React Query cache should not be shared between the host and its modules. To do so, both the host application and every module should instantiate their own QueryClient instance."]}],[{"l":"Isolate module failures","p":["One of the key characteristics of micro-frontends implementations like iframes and subdomains is the ability to isolate failures within individual modules, preventing them from breaking the entire application.","However, in a Module Federation implementation, this is not the case as all the modules share the same browsing context (e.g. the same Document object, the same Window object, and the same DOM). A failure in one module can potentially breaks the entire application.","Nevertheless, an application can get very close to iframes failure isolation by utilizing React Router's Outlet component and the errorElement property of a React Router's routes."]},{"l":"Create an error boundary","p":["First, define an error boundary to catch module errors. For this example we'll name it RootErrorBoundary:"]},{"l":"Register the error boundary","p":["Then, update the host application registerHost function to declare the RootErrorBoundary component below the RootLayout but above the routes of the modules. By doing so, if a module encounters an unhandled error, the error boundary will only replace the section rendered by the Outlet component within the RootLayout rather than the entire page:","By implementing this mechanism, the level of failure isolation achieved is comparable to that of an iframes or subdomains implementation. With this mechanism, failure isolation is as good as with an iframes or subdomains implementation."]},{"l":"Hoisted pages","p":["If your application is hoisting pages, it's important to note that they will be rendered outside of the host application's root error boundary. To prevent breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's errorElement property for each hoisted page:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script. Update any of your application routes that is rendered under the newly created error boundary (e.g. that is not hoisted) and throw an Error. The error should be handled by the error boundary instead of breaking the whole application."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Add authentication","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker and fetch initial data guides.","Most of our applications (if not all) will eventually requires the user to authenticate. To facilitate this process, the Squide FireflyRuntime class accepts a sessionAccessor function. Once the application registration flow is completed, the function will be made accessible to every module of the application.","When combined with a React Router authentication boundary and a login page, the shared sessionAccessor function is of great help to manage authentication concerns."]},{"l":"Add a login page","p":["First, open a terminal at the root of the host application and install the @squide/fakes package:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM.","Then, add a Mock Service Worker(MSW) request handler to authenticate a user:","In the previous code sample, the endpoint attempts to authenticate the provided credentials against existing users. If there's a match, the user session is stored in the local storage using a LocalStorageSessionManager instance, and a 200 status code is returned.","Our security department reminds you to refrain from using a fake LocalStorageSessionManager in a production application \uD83D\uDE0A","Next, register the request handler using the host application registration function:","Then, create a login page:","After the user logs in, the application is reloaded. This is a requirement of the AppRouter component's onLoadPublicData and onLoadProtectedData handlers. Nevertheless, it's not a significant concern because Workleap applications utilize a third-party service for authentication which requires a full refresh of the application."]},{"l":"Create a session accessor function","p":["Next, create a shared type for the session and the session manager:","Then, define a sessionAccessor function wrapping an InMemorySessionManager instance:","Finally, create the FireflyRuntime instance with the new sessionAccessor function:"]},{"l":"Fetch the session","p":["Now, let's create an MSW request handler that returns a session object if a user is authenticated:","Then, update the host application App component to load the session when a user navigate to a protected page for the first time:"]},{"l":"Add an authentication boundary","p":["Next, create a new React Router authentication boundary component using the useIsAuthenticated hook:","Internally, the useIsAuthenticated hook utilize the sessionAccessor function that we created previously to determine whether or not the user is authenticated."]},{"l":"Define an authenticated layout","p":["Now that authentication is in place, thanks to the AuthenticationBoundary, we can expect to render the navigation items exclusively for authenticated users.","First, add a MSW request handler to log out a user:","Then, introduce a new AuthenticatedLayout displaying the name of the logged-in user along with a logout button:","By creating a new AuthenticatedLayout, much of the layout code has been transferred from the RootLayout to the AuthenticatedLayout, leaving the root layout responsible only for styling the outer wrapper of the application for now:"]},{"l":"Setup the routes","p":["Finally, assemble everything:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application using the dev script and attempt navigating to the root page (/). You will be redirected to the /login page. Login with temp/ temp, you will be redirected to the root page."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Add a public page","p":["By default, when a route is registered with the registerRoute function, the route is considered as \"protected\". This doesn't imply that the route becomes inacessible to unauthenticated users thought; as this protection is typically granted by an authentication boundary. What it means is that if the AppRouter component's onLoadPublicData and onLoadProtectedData handlers are defined, both handlers will be executed when the route is requested (assuming it is the initial request).","Therefore, if a route and its layout do not rely on the initial protected data of the application, the route should be declared as public using the $visibility option:"]}],[{"l":"Override the host layout"},{"l":"Define a root layout","p":["In many applications, multiple pages often share a common layout that includes elements such as a navigation bar, a user profile menu, and a main content section. In a React Router application, this shared layout is commonly referred to as a RootLayout:","In the previous code sample, the RootLayout serves as the default layout for the homepage as well as for every page (route) registered by a module that are not nested under a parent route with either the parentPath or the parentName option.","For most pages, this is the behavior expected by the author. However, for pages such as a login, the default RootLayout isn't suitable because the page is not bound to a user session (the user is not even authenticated yet).","To accomodate pages that require a different layout, a mechanism is needed to move their route declaration at the root of the React Router router instance, before the RootLayout is declared."]},{"l":"Hoist a module pages","p":["Package managers supporting workspaces such as Yarn and NPM call this mechanism \"hoisting\", which means \"raise (something) by means of ropes and pulleys\". This is exactly what we are trying to achieve here.","Squide has a built-in hoist functionality capable of raising module routes marked as hoist at the root of the routes array, before the RootLayout declaration. Thus, an hoisted page will not be wrapped by the RootLayout(or the AuthenticationBoundary) and will have full control over its rendering.","To hoist module pages, add the hoist option to the route registration options and optionally use a different layout:","By declaring a page as hoisted, other parts of the application will not be isolated anymore from this page's failures as the page will be rendered outside of the host application's root error boundary. To avoid breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's errorElement property for each hoisted page.","By declaring a page as hoisted, the page will be rendered at the root of the router, therefore, most certainly outside the authenticated boundary of the application. If the hoisted page requires an authentication, make sure to wrap the page with an authentication boundary or to handle the authentication within the page."]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script and navigate to the /login page. The page should be displayed even if you are not authenticated."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Federated tabs","p":["While it's typically recommended for a Squide application to maintain the boundary of a page within a single domain (e.g. module), there are situations where enhancing the user experience necessitates rendering a page with parts from multiple domains, or at the very least, simulating it \uD83D\uDE0A.","For this guide, we'll take as an example a page for which the parts that are owned by different domains are organized by tabs (federated tabs):","Tab 1: Registered by Remote Module 1","Tab 2: Registered by Remote Module 2","Tab 3: Registered by Local Module","Anatomy of a page rendering federated tabs"]},{"l":"Define a nested layout","p":["To construct this page while adhering to Squide constraint of exclusively permitting route exports from modules to maintain a high degree of decoupling in the application, let's begin by defining a React Router nested layout. This nested layout will be responsible for rendering all the tab headers and the content of the active tab:","In the previous code sample, the FederatedTabsLayout is similar to the RootLayout introduced in previous guides. However, the key distinction is that this layout is nested under the /federated-tabs URL segment. By nesting the layout under a specific path, it will only render when the user navigates to one of the federated tab pages (e.g. /federated-tabs/tab-1, /federated-tabs/tab-2, /federated-tabs/tab-3).","To register the newly created layout as a nested layout, use the registerRoute function:","With this nested layout in place, thanks to the React Router Outlet component, the content of the tabs can now reside in distinct pages(registered by different modules) while still delivering a cohesive user experience. Whenever a user navigates between the tabs, the URL will be updated, and the tab content will change, but the shared portion of the layout will remain consistent.","As a bonus, each individual tab will have its own dedicated URL! \uD83E\uDD73","It is recommended to define the shared layouts in a standalone package as it's done for the endpoints sample layouts project."]},{"l":"Create the tab routes","p":["Next, let's add the actual tab pages to the modules. To do so, we'll use the parentPath option of the registerRoute function to register the routes under the FederatedTabsLayout:","Now that the tabs have been registered, ensure that all four modules (including remote-module-3) are registered in the host application. Start the development servers using the dev script. Navigate to the /federated-tabs page, and you should see the tab headers. Click on each tab header to confirm that the content renders correctly."]},{"l":"Decouple the navigation items","p":["Althought it's functional, there are still a few configurations needed since the modules are currently coupled by hardcoded URLs within the FederatedTabsLayout.","To decouple the navigation items, similar to what is done for regular federated pages, we'll utilize the registerNavigationItem function. In this case, we'll also use the menuId option. Defining the menuId option will enable the FederatedTabsLayout to retrieve navigation items exclusively for the federated tab component.","First, let's register the navigation items with the menuId option. For this example the menuId will be /federated-tabs(it can be anything):","Then, update the FederatedTabsLayout to render the registered navigation items instead of the hardcoded URLs:"]},{"l":"Change the display order of the tabs","p":["Similarly to how the display order of regular navigation items can be configured, a federated tab position can be affected with the priority property.","To force Tab 3 to be positioned first, we'll give him a priority of 999:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["To ensure everything is still working correctly, start the development servers using the dev script and navigate to the /federated-tabs page. You should see all three tabs, and you should be able to switch between them by clicking on the tab headers."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] The following route has been registered as a children of the /federated-tabs route. Newly registered item: ...","[squide] The following navigation item has been registered to the /federated-tabs menu for a total of 1 item. Newly registered item: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Use feature flags","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker and fetch initial data guides.","To continuously deliver value to our customers, Workleap has adopted a feature flags system that allows to activate or deactivate functionalities without requiring code deployment. While \"in-page\" feature flags are straightforward to implement, feature flags that conditionally register routes require an advanced deferred registration mecanism."]},{"l":"Add an endpoint","p":["First, define a MSW request handler that returns the feature flags:","Then, register the request handler using the module registration function:"]},{"l":"Create a shared context","p":["Then, in a shared project, create a FeatureFlagsContext:","Ensure that the shared project is configured as a shared dependency."]},{"l":"Fetch the feature flags","p":["Finally, open the host application code and update the App component to utilize the AppRouter component's onLoadPublicData handler to fetch the feature flags data:"]},{"l":"Conditionally render a page section","p":["Now, let's use the featureA flag from FeatureFlagsContext to conditionally render a section of the Page component:","In the previous cpde sample, the section of the Page component will only be rendered if featureA is activated."]},{"l":"Conditionally register a route","p":["Since the application hasn't been bootstrapped yet during the route registration phase, the feature flags cannot be retrieved from FeatureFlagsContext to conditionally register a route.","To address this, Squide offers a deferred registration mechanism in two-phases:","The first phase allows modules to register their routes and navigation items that are not dependent on initial data.","The second phase enables modules to register routes and navigation items that are dependent on initial data. We refer to this second phase as deferred registrations.","To defer a registration to the second phase, a module registration function can return an anonymous function. Once the modules are registered and the completeLocalModuleRegistrations function is called, the deferred registration functions will be executed.","First, let's update the module registration function to return an anonymous function that will receive the feature flags:","Finally, open the host application code again and update the App component to utilize the AppRouter component's onCompleteRegistrations handler to complete the module registrations with the feature flags:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application using the dev and navigate to the /page page. The page should render with the conditonal section. Now, disable the featureA flag in the endpoint and refresh the page. You shouldn't see the conditonal section anymore. Finally, disable the featureB flag in the endpoint and refresh the page. The page shouldn't be available anymore.","If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Setup localization","p":["Most of the applications that forms the Workleap platform are either already bilingual or will be in the future. To help feature teams with localized resources, Squide provides a native plugin designed to adapt the i18next library for federated applications.","The examples in this guide load all the resources from single localized resources files. For a real Workleap application, you probably want to spread the resources into multiple files and load the files with a i18next backend plugin."]},{"l":"Setup the host application","p":["Let's start by configuring the host application. First, open a terminal at the root of the host application and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Register the i18nextPlugin","p":["Then, update the host application boostrapping code to register an instance of the i18nextplugin with the FireflyRuntime instance:","In the previous code sample, upon creating an i18nextPlugin instance, the user's language is automatically detected using the plugin.detectUserLanguage function. Applications should always detect the user's language at bootstrapping, even if the current language is expected to be overriden by a preferred language setting once the user session has been loaded."]},{"l":"Define the localized resources","p":["Next, create the localized resource files for the en-US and fr-CA locales:"]},{"l":"Register an i18next instance","p":["Then, update the local module register function to create and register an instance of i18next with the i18nextPlugin plugin instance:","In the previous code sample, notice that the i18next instance has been initialized with the current language of the i18nextPlugin instance by providing the lng option. If the user's language has been detected during bootstrapping, the i18next instance will be initialized with the user's language which has been deduced from either a ?language querystring parameter or the user's navigator language settings. Otherwise, the application the instance will be initialized with the fallback language."]},{"l":"Localize the home page resources","p":["Then, update the HomePage component to use the newly created localized resource:"]},{"l":"Update the webpack configurations","p":["Finally, update the webpack development and build configurations to activate the i18next feature:"]},{"l":"Setup a remote module","p":["First, open a terminal at the root of the remote module application and install the following packages:"]},{"i":"define-the-localized-resources-1","l":"Define the localized resources","p":["Then, create the localized resource files for the en-US and fr-CA locales:","Notice that this time, a standard navigationItems namespace has been added to the resource files. The resources in the navigationItems namespace will be used later on to localize the navigation items labels."]},{"i":"register-an-i18next-instance-1","l":"Register an i18next instance","p":["Then, update the local module register function to create and register an instance of i18next with the i18nextPlugin plugin instance:"]},{"l":"Localize the navigation item labels","p":["Then, localize the navigation items labels using the I18nextNavigationItemLabel component. Since the resources are in the navigationItems namespace, there's no need to specify a namespace property on the components:"]},{"l":"Localize the page resources","p":["Then, update the HomePage component to use the newly created localized resource:"]},{"i":"update-the-webpack-configurations-1","l":"Update the webpack configurations","p":["Finally, update the webpack development and build configurations to activate the i18next feature:"]},{"l":"Setup a local module","p":["Follow the same steps as for a remote module."]},{"l":"Integrate a backend language setting","p":["For many applications, the displayed language is expected to be derived from an application specific user's \"preferred language\" setting, which is stored in an database on the backend. Therefore, the frontend remains unaware of this setting's value until the user session is loaded.","Hence, the strategy to select the displayed language should be as follow:","Utilize the language detected at bootstrapping for anonymous users (with the detectUserLanguage function).","Upon user authentication and session loading, if a \"preferred language\" setting is available from the session data, update the displayed language to reflect this preference.","To implement this strategy, utilize the useChangeLanguage hook and the onLoadProtectedData handler of the AppRouter component:"]},{"l":"Use the Trans component","p":["The Trans component is valuable for scenarios that involve interpolation to render a localized resource. To use the Trans component with Squide, pair the component with an i18next instance retrieved from useI18nextInstance hook:","The Trans component can also be used without the t function by including a namespace to the i18nKey property value:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the development servers using the dev script. The home page and the navigation items should render the english ( en-US) resources. Then append ?language=fr-CA to the URL. The home page and the navigation items should now render the french ( fr-CA) resources."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each i18next instance that is being registered and another log everytime the language is changed:","[squide] Registered a new i18next instance with key remote-module: ...","[squide] The language has been changed to fr-CA.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Develop a module in isolation","p":["To develop their own independent module, a team shouldn't be required to install the host application or any other modules of the application that they do not own. However, they should have a means to integrate their module with the application shell ( RootLayout, RootErrorBoundary, etc..) while working on their module in isolation.","To achieve this, the first step is to extract the application shell from the host application. There are several approaches to accomplish this, but in this guide, we'll transform the host application into a monorepo and introduce a new local package named @sample/shell for this purpose:"]},{"l":"Create a shell package","p":["The implementation details of the RootLayout and RootErrorBoundary won't be covered by this guide as it already has been covered many times by other guides.","First, create a new package (we'll refer to ours as shell) and add the following fields to the package.json file:","Then, install the package dependencies and configure the new package with tsup.","Then, create a AppRouter component in the shell package to provide a reusable router configuration that can be utilized by both the host application and the isolated modules. The new AppRouter component should be based on the @squide/firefly AppRouter component:","Finally, create a local module to register the application shell that will also be utilized by the host application and the isolated modules:","This guide only covers the RootLayout and RootErrorBoundary but the same goes for other shell assets such as an AuthenticationBoundary."]},{"l":"Update the host application","p":["Now, let's revisit the host application by first adding a dependency to the new @sample/shell package:","Then, incorporate the newly introduced AppRouter component:","And the registerShell function to setup the RootLayout, the RootErrorBoundary and any other shell assets:"]},{"l":"Setup a remote module","p":["With the new shell package in place, we can now configure the remote module to be developed in isolation. The goal is to start the module development server and render the module pages with the same layout and functionalities as if it was rendered by the host application.","To begin, let's start by adding a dependency to the @sample/shell package:","Then, create the following files in the remote module application:"]},{"i":"indextsx","l":"index.tsx","p":["The index.tsx file is similar to the bootstrap.tsx file of an host application but, tailored for an isolated module. The key distinctions are that, since the project is set up for isolated development, the module is registered with the registerLocalModules function instead of the registerRemoteModules function, and a new registerDev function is introduced to register the development homepage (which will be covered in an upcoming section):"]},{"i":"apptsx","l":"App.tsx","p":["The App.tsx file uses the newly created AppRouter component to setup React Router:"]},{"i":"devhometsx","l":"DevHome.tsx","p":["The DevHome component purpose is strictly to serve as an index page when developing the remote module in isolation.","To register the development homepage, let's create a new local module specifically for registering what is needed to develop the module in isolation:"]},{"l":"Add a new CLI script","p":["Next, add a new dev-isolated script to the package.json file to start the local development server in \"isolation\":","The dev-isolated script is similar to the dev script but introduces a ISOLATED environment variable. This new environment variable will be utilized by the webpack.dev.js file to conditionally setup the development server for development in isolation or to be consumed by a host application through the /remoteEntry.js entry point:"]},{"l":"Configure webpack","p":["If you are having issues configuring webpack, refer to the @workleap/webpack-configs documentation website.","First, open the public/index.html file created at the beginning of this guide and copy/paste the following HtmlWebpackPlugin template:","Then, open the .browserslist file and copy/paste the following content:"]},{"l":"Isolated environment configuration","p":["To configure webpack, open the webpack.dev.js file and update the configuration to incorporate the ISOLATED environment variable and the defineDevHostConfig function:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the remote module in isolation by running the dev-isolated script. The application shell should wrap the pages of the module and the default page should be DevHome."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]},{"l":"Setup a local module","p":["Similarly to remote modules, the same isolated setup can be achieved for local modules. The main difference is that the webpack.config.js file of a local module serves the sole purpose of starting a development server for isolated development. Typically, local modules do not rely on webpack and Module Federation.","First, open a terminal at the root of the local module application and install the @squide/webpack-configs package and its dependencies:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM.","Then, add a peer dependency and a dev dependency to the @sample/shell package:","Then, create the following files in the local module application:"]},{"i":"indextsx-1","l":"index.tsx","p":["This file is similar to the index.tsx file of the remote module."]},{"i":"apptsx-1","l":"App.tsx","p":["This file is similar to the App.tsx file of the remote module."]},{"i":"devhometsx-and-registerdev","l":"DevHome.tsx and registerDev","p":["These files are similar to the dev/DevHome.tsx and dev/register.tsx files of the remote module."]},{"i":"configure-webpack-1","l":"Configure webpack","p":["If you are having issues configuring webpack, refer to the @workleap/webpack-configs documentation website.","First, open the public/index.html file and copy/paste the following HtmlWebpackPlugin template:","Then, open the .browserslist file and copy/paste the following content:","Then, open the swc.config.js file and copy/paste the following code:","Finally, open the webpack.config.js file and use the the defineDevHostConfig function to configure webpack:"]},{"i":"add-a-new-cli-script-1","l":"Add a new CLI script","p":["Next, add a new dev-isolated script to the package.json file to start the local development server:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the remote module in isolation by running the dev-isolated script. The application shell should wrap the pages of the module and the default page should be DevHome."]},{"i":"troubleshoot-issues-1","l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Override a React context","p":["In a federated application using Module Federation, it's typical to configure various global React contexts at the root of the host application. These contexts are usually consumed down the line by the layouts and pages of the remote modules.","Let's explore a simple example using a BackgroundColorContext:","In the previous code samples, the host application provides a value for the BackgroundColorContext, and the ColoredPage component of the remote module utilizes this value to set its background color (in this example, the background color is set to blue)."]},{"l":"Override the context for the remote module","p":["Now, suppose the requirements change, and one remote module's pages need to have a red background. The context can be overriden for the remote module by declaring a new provider directly in the routes registration:"]},{"l":"Extract an utility component","p":["Since there are multiple routes to setup with the new provider, an utility component can be extracted:"]},{"l":"Update a singleton dependency version","p":["Let's consider a more specific use case where the host application declares a ThemeContext from Workleap's new design system, Hopper:","In this scenario, Hopper's components are used throughout the entire federated application, including the remote modules. Moreover, @hopper/components is defined as a singleton shared dependency:","Now, consider a situation where Hopper releases a new version of the package that includes breaking changes, without a \"compatibility\" package to ensure backward compatility with the previous version.","To update the host application without breaking the remote modules, the recommended approach is to temporary \"break\" the singleton shared dependency by loading two versions of the dependency in parallel (one for the host application and one for the remote modules that have not been updated yet).","As @hopper/components expose the ThemeContext, the context must be re-declared in each remote module until every part of the federated application has been updated to the latest version of Hopper:","Thankfully, React Router makes it very easy to declare contexts in a remote module."]}],[{"l":"Add a shared dependency","p":["Shared dependencies represent one of the most powerful concepts within Module Federation. However, mastering its configuration can be quite challenging. Failure to configure shared dependencies properly in a federated application using Module Federation can significantly impact both user and developer experiences.","Squide aims to simplify the configuration of shared dependencies by abstracting the shared dependencies necessary for building an application with React and React Router. Nevertheless, every federated application will inevitably have to configure additional custom shared dependencies.","For a more comprehensive documentation of the Module Federation APIs, their functionality, and their benefits, please refer to this article."]},{"l":"Understanding singleton dependencies","p":["A singleton shared dependency does exactly what its name suggests: it loads only a single instance of the dependency. This means that the dependency will be included in just one bundle file of the federated application."]},{"l":"strictVersion","p":["Sometimes, a singleton shared dependency is paired with the strictVersion option:","When specified, the strictVersion option will generate a runtime error if a module attempts to load a version of the dependency that is incompatible with the specified version. It's often unnecessary to use a strict version, and omitting it provides greater flexibility when it comes time to update the shared dependency version."]},{"l":"Expected behaviors"},{"l":"Minor or patch version","p":["When the version difference between a host application and a remote module is a minor or patch version, the higher version of the dependency will be loaded. For example:","If the host application is on 10.1.0 and a remote module is on 10.3.1-> 10.3.1 will be loaded","If the host application is on 10.3.1 and a remote module is on 10.1.0-> 10.3.1 will be loaded"]},{"l":"Major version","p":["If the version difference between a host application and a remote module is a major version, once again, the higher version of the dependency will be loaded. However, a warning will also be issued. For example:","If the host application is on 11.0.0 and a remote module is on 10.3.1-> 11.0.0 will be loaded","If the host application is on 10.3.1 and a remote module is on 11.0.0-> 11.0.0 will be loaded"]},{"i":"what-should-be-configured-as-a-shared-dependency","l":"What should be configured as a shared dependency?","p":["Libraries matching the following criterias are strong candidates to be configured as shared dependencies:","Medium to large libraries that are used by multiple modules.","Libraries that requires a single instance to work properly (like react).","Libraries exporting React contexts."]},{"l":"Understanding eager dependencies","p":["An eager shared dependency becomes available as soon as the host application starts. In simple terms, it is included in the host application bundle rather than being loaded lazily when it is first requested.","The key point to remember about eager dependencies is that only one application or remote module should configure a shared dependency as eager. Otherwise, the dependency will be included in the bundle of the host application and of every remote module that set the dependency as eager."]},{"i":"what-should-be-configured-as-an-eager-dependency","l":"What should be configured as an eager dependency?","p":["Any shared dependency that must be loaded to bootstrap the application."]},{"l":"Default shared dependencies","p":["Since Squide has dependencies on React and React Router, the define* functions automatically configure shared dependencies for these packages by default, in addition to Squide own packages. The following shared dependencies are set as eager singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github.","You can extend or override the default shared dependencies configuration."]},{"l":"Add custom shared dependencies","p":["To configure shared dependencies, use the sharedDependencies option of any define* function:","When a dependency is shared between a host application and a remote module, the sharing options must be configured on both ends:"]},{"l":"React context limitations","p":["For a React context to be provided by the host application and consumed by the remote modules, the library exporting the React context must be set as a singleton.","To troubleshoot a React context issue or find more information about the limitations, refer to the troubleshooting page."]},{"l":"React dependencies requirements","p":["react and react-dom dependencies must be configured as a singleton, otherwise either an error will be thrown at bootstrapping if the loaded react versions are incompatible, or features like useState will not work.","The react-router-dom dependency must as well be configured as a singleton because it relies on a global React context that needs to be declared in the host application and is consumed by remote modules."]},{"l":"Learn more","p":["To learn more about Module Federation shared dependencies read this article about the shared APIs and refer to this POC on GitHub."]}],[{"l":"Implement a custom logger","p":["Many applications must integrate with specific remote logging solutions such as Honeycomb and Azure Application Insights. To facilitate this integration, the shell runtime accepts any custom loggers implementing the Logger interface."]},{"l":"Create a custom logger class","p":["First, let's define a custom logger:","Then create a FireflyRuntime instance configured with an instance of the new CustomLogger:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the applications and open the developer tools, then, refresh the page. You should see the following console log message:"]}],[{"l":"Migrate from a monolithic application","p":["Transforming an existing monolithic application into a distributed architecture is often more challenging than building a new federated application from scratch.","However, it's also a bad idea to start a new application with a distributed architecture since teams typically lack sufficient understanding of the business domain at that stage. Therefore, for most applications, it makes sense to begin as monolithic application and transition to a distributed architecture later.","With the introduction of local modules, Squide offers an alternative approach that lies between prior solutions. Instead of immediately embracing Team Topology's stream-aligned teams and striving for full team autonomy across the board, local modules allow teams to start with a monorepo setup and add independent local packages (modules) for each expected value stream.","Since adding/deleting local packages in a monorepo setup is pretty cheap, teams can freely reorganize their value streams along the way and won't preemptively invest into a distributed CI/CD infrastructure as local modules are part of the host application build. With independent but local value streams, teams will be well-positioned to transition toward a federated application once they can justify the cost.","If your project is already a monolithic application with a polyrepo setup and you aim to migrate to a distributed architecture, we recommend a decoupling-first strategy using local modules and a monorepo setup."]},{"l":"Decoupling first strategy","p":["The primary challenge to migrate to a distributed architecture is coupling. Thus, for most applications, starting by decoupling the monolith into composable value streams could be the right strategy. It's a great way to get into the migration without the immediate need to update the CI/CD infrastructure or preemptively change developers' habits.","We recommend the following steps:","Transform the codebase into a monorepo setup.","Create independent local packages (modules) for each identified value stream.","Refactor the monolithic application code into the corresponding value stream local packages and ensure that each value stream can be developed independently(e.g., without the need to start the entire application).","Import and register the local packages(modules) into the host application.","Finally, transition from local modules to remote modules and update your CI/CD pipelines to enable independent deployment of modules.","By following these steps, you can gradually decouple your monolithic application, create modular value streams, and prepare the foundation for a distributed architecture."]}],[{"l":"Reference"},{"l":"Artefacts","p":["Packages"]},{"l":"API"},{"l":"Runtime","p":["FireflyRuntime class","RuntimeContext","useRuntime","useRoutes","useNavigationItems","useLogger","useSession"]},{"l":"Registration","p":["registerLocalModules","registerRemoteModules","completeModuleRegistrations","completeLocalModuleRegistrations","completeRemoteModuleRegistrations","useAreModulesRegistered","useAreModulesReady"]},{"l":"Routing","p":["AppRouter","ManagedRoutes","useRenderedNavigationItems","useRouteMatch","useIsRouteMatchProtected"]},{"l":"Logging","p":["Logger","ConsoleLogger"]},{"l":"Messaging","p":["EventBus","useEventBusDispatcher","useEventBusListener"]},{"l":"Session","p":["useIsAuthenticated"]},{"l":"Plugins","p":["Plugin"]},{"l":"webpack","p":["defineDevHostConfig","defineDevRemoteModuleConfig","defineBuildHostConfig","defineBuildRemoteModuleConfig"]},{"l":"Mock Service Worker","p":["useIsMswReady","setMswAsStarted"]},{"l":"i18next","p":["i18nextPlugin","getI18nextPlugin","useChangeLanguage","useCurrentLanguage","useI18nextInstance","I18nextNavigationItemLabel"]},{"l":"Fakes","p":["Squide offers a collection of fake implementations designed to facilitate the set up of a module isolated environment.","LocalStorageSessionManager","ReadonlySessionLocalStorage"]}],[{"l":"Packages","p":["@squide/core","@squide/fakes","@squide/firefly","@squide/i18next","@squide/msw","@squide/react-router","@squide/webpack-configs","@squide/webpack-module-federation","A collection of fake implementations to facilitate the development of federated modules.","Add support for i18next.","Add support for Module Federation.","Add support for MSW.","Core functionalities of Squide.","Description","Helpers to facilitate the creation of a shell package with Squide firefly technology stack.","Name","NPM","npm version","Specific implementation of the core functionalities to support React Router.","Utilities to configure Webpack."]}],[{"l":"FireflyRuntime class","p":["A runtime instance give modules access to functionalities such as routing, navigation, request handlers and logging."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","mode: An optional mode to optimize Squide for production. Values are development(default) and production.","loggers: An optional array of Logger instances.","plugins: An optional array of custom plugin instances.","sessionAccessor: An optional function returning the current session."]},{"l":"Usage"},{"l":"Create a runtime instance"},{"l":"Change the runtime mode"},{"l":"Register routes","p":["route: accept any properties of a React Router Route component with the addition of:","$name: An optional name for the route.","$visibility: An optional visibility indicator for the route. Accepted values are public or protected.","options: An optional object literal of options:","hoist: An optional boolean value to register the route at the root of the router. The default value is false.","parentPath: An optional path of a parent route to register this new route under.","parentName: An optional name of a parent route to register this new route under."]},{"l":"Register an hoisted route","p":["Unlike a regular page, a hoisted page is added at the root of the router, outside of the host application's root layout, root error boundary and even root authentication boundary. This means that a hoisted page has full control over its rendering. To mark a route as hoisted, provide an hoist property to the route options.","By declaring a page as hoisted, other parts of the application will not be isolated anymore from this page's failures and the page will not be protected anymore by the application authenticated boundary.","To avoid breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's errorElement property for each hoisted page.","If the hoisted page requires an authentication, make sure to wrap the page with an authentication boundary or to handle the authentication within the page."]},{"l":"Register a route with a different layout","p":["Learn more about overriding the host application layout"]},{"l":"Register a public route","p":["When registering a route, a hint can be provided, indicating if the route is intended to be displayed as a public or protected route. This is especially useful when dealing with code that conditionally fetch data for protected routes (e.g. a session).","A nested route can also have a visibility hint:","If the route is nested under an authentication boundary, don't forget to either mark the route as hoisted or to nest the route under a public parent.","A $visibility hint only takes effect if your application is using the useIsRouteMatchProtected hook. When no $visibility hint is provided, a route is considered protected."]},{"l":"Register a named route","p":["The registerRoute function accepts a parentName property, allowing a route to be nested under an existing parent route. When searching for the parent route matching the parentName property, the parentName will be matched against the $name property of every route.","A $name property should only be defined for routes that doesn't have a path like an error boundary or an authentication boundary.","A nested route can also be named:"]},{"l":"Register nested routes under an existing route","p":["React router nested routes enable applications to render nested layouts at various points within the router tree. This is quite helpful for federated applications as it enables composable and decoupled UI.","To fully harness the power of nested routes, the registerRoute function allows a route to be registered under any previously registered route, even if that route was registered by another module. The only requirement is that the parent route must have been registered with the registerRoute function.","When registering a new route with the registerRoute function, to render the route under a parent route, specify a parentPath property that matches the parent route's path property:","Or a parentName property that matches the parent route's name property:","Learn more about using nested routes for federated tabs","Likewise any other React Router routes, the path property of a page rendered under an existing parent route must be an absolute path. For example, if a parent route path is /layout, the path property of a page rendered under that parent route and responding to the /page-1 url, should be /layout/page-1."]},{"l":"Retrieve routes","p":["A federated application routes are accessible from a FireflyRuntime instance, but keep in mind that the preferred way to retrieve the routes is with the useRoutes hook."]},{"l":"Register navigation items","p":["item: NavigationSection | NavigationLink.","options: An optional object literal of options:","menuId: An optional menu id to associate the item with.","A Squide navigation item can either be a NavigationLink or a NavigationSection. Both types can be intertwined to create a multi-level menu hierarchy. A NavigationSection item is used to setup a new level while a NavigationLink define a link.","NavigationSection accept the following properties:","$label: The section text.","$priority: An order priority affecting the position of the item in the menu (higher first)","$addiltionalProps: Additional properties to be forwarded to the section renderer.","children: The section content.","NavigationLink accept any properties of a React Router Link component with the addition of:","$label: The link text.","$additionalProps: Additional properties to be forwarded to the link renderer.","Setup the host application to render navigation items"]},{"l":"Register nested navigation items"},{"l":"Sort registered navigation items","p":["A $priority property can be added to a navigation item to affect it's position in the menu. The sorting algorithm is as follow:","By default a navigation item have a priority of 0.","If no navigation item have a priority, the items are positioned according to their registration order.","If an item have a priority 0, the item will be positioned before any other items with a lower priority (or without an explicit priority value).","If an item have a priority 0, the item will be positioned after any other items with a higher priority (or without an explicit priority value)."]},{"l":"Use a React element as navigation item label"},{"l":"Style a navigation item"},{"l":"Open a navigation link in a new tab"},{"l":"Render additional props on a navigation item"},{"l":"Register navigation items for a specific menu","p":["By default, every navigation item registered with the registerNavigationItem function is registered as part of the root navigation menu. To register a navigation item for a different navigation menu, specify a menuId property when registering the items."]},{"l":"Retrieve navigation items","p":["A federated application navigation items are accessible from a FireflyRuntime instance, but keep in mind that the preferred way to retrieve the navigation items is with the useNavigationItems hook.","By default, the getNavigationItems will return the navigation items for the root menu:","To retrieve the navigation items for a specific navigation menu, provide a menuId:"]},{"l":"Register request handlers","p":["The registered handlers must be Mock Service Worker request handlers:","Learn more about setuping Mock Service Worker"]},{"l":"Retrieve request handlers"},{"l":"Use the logger"},{"l":"Use the event bus"},{"l":"Register a plugin","p":["Learn more about plugins"]},{"l":"Retrieve a plugin","p":["Learn more about plugins"]},{"l":"Retrieve the current session"}],[{"l":"RuntimeContext","p":["React context to share a FireflyRuntime instance between an host application and the modules."]},{"l":"Reference"},{"l":"Properties","p":["value: A FireflyRuntime instance."]},{"l":"Usage"},{"l":"Provide a runtime instance"},{"l":"Retrieve a runtime instance"}],[{"l":"useRuntime","p":["Retrieve a FireflyRuntime instance.","When possible, prefer useRoutes, useNavigationItems, useLogger, useSession to useRuntime."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A FireflyRuntime instance."]},{"l":"Usage"}],[{"l":"useRoutes","p":["Retrieve the registered routes from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["An array of Route."]},{"l":"Usage"}],[{"l":"useNavigationItems","p":["Retrieve the registered navigation items from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["menuId: An optional id to retrieve the navigation menu for a specific menu."]},{"l":"Returns","p":["An array of NavigationItem."]},{"l":"Usage"},{"l":"Retrieve the items for the root menu"},{"l":"Retrieve the items for a specific menu"}],[{"l":"useLogger","p":["Retrieve a RuntimeLogger instance from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A RuntimeLogger instance."]},{"l":"Usage"}],[{"l":"useSession","p":["Retrieve the current session from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A custom session object."]},{"l":"Usage"}],[{"l":"registerLocalModules","p":["Register one or many local module(s). During the registration process, the specified registration function will be invoked with a FireflyRuntime instance and an optional context object. To defer the registration of specific routes or navigation items, a registration function can return an anonymous function.","A local module is a regular module that is part of the host application build and is bundled at build time, as opposed to remote module which is loaded at runtime from a remote server. Local modules are particularly valuable when undergoing a migration from a monolithic application to a federated application or when launching a new product with an evolving business domain."]},{"l":"Reference"},{"l":"Parameters","p":["registerFunctions: An array of ModuleRegisterFunction.","runtime: A FireflyRuntime instance.","options: An optional object literal of options:","context: An optional context object that will be pass to the registration function."]},{"l":"Returns","p":["A Promise object with an array of LocalModuleRegistrationError if any error happens during the registration.","LocalModuleRegistrationError:","error: The original error object."]},{"l":"Usage"},{"l":"Register a local module"},{"l":"Defer the registration of routes or navigation items","p":["Sometimes, data must be fetched to determine which routes or navigation items should be registered by a given module. To address this, Squide offers a two-phase registration mechanism:","The first phase allows modules to register their routes and navigation items that are not dependent on initial data (in addition to their MSW request handlers when fake endpoints are available).","The second phase enables modules to register routes and navigation items that are dependent on initial data. Such a use case would be determining whether a route should be registered based on a feature flag. We refer to this second phase as deferred registrations.","To defer a registration to the second phase, a module registration function can return an anonymous function. Once the modules are registered and the completeLocalModuleRegistrations function is called, the deferred registration functions will be executed.","completeLocalModuleRegistrations"]},{"l":"Handle the registration errors"}],[{"l":"registerRemoteModules","p":["Register one or many remote module(s). During the registration process, the module register function will be invoked with a FireflyRuntime instance and an optional context object. To defer the registration of specific routes or navigation items, a registration function can return an anonymous function.","A remote module is a module that is not part of the current build but is loaded at runtime from a remote server."]},{"l":"Reference"},{"l":"Parameters","p":["remotes: An array of RemoteDefinition(view the Remote definition section).","runtime: A FireflyRuntime instance.","options: An optional object literal of options:","context: An optional context object that will be pass to the registration function."]},{"l":"Returns","p":["A Promise object with an array of RemoteModuleRegistrationError if any error happens during the registration.","RemoteModuleRegistrationError:","url: The URL of the module federation remote that failed to load.","containerName: The name of the dynamic container that Squide attempted to recover.","moduleName: The name of the module that Squide attempted to recover.","error: The original error object."]},{"l":"Usage"},{"l":"Register a remote module"},{"l":"Defer the registration of routes or navigation items","p":["Sometimes, data must be fetched to determine which routes or navigation items should be registered by a given module. To address this, Squide offers a two-phase registration mechanism:","The first phase allows modules to register their routes and navigation items that are not dependent on initial data (in addition to their MSW request handlers when fake endpoints are available).","The second phase enables modules to register routes and navigation items that are dependent on initial data. Such a use case would be determining whether a route should be registered based on a feature flag. We refer to this second phase as deferred registrations.","To defer a registration to the second phase, a module registration function can return an anonymous function. Once the modules are registered and the completeRemoteModuleRegistrations function is called, the deferred registration functions will be executed.","completeRemoteModuleRegistrations"]},{"l":"Handle the registration errors"},{"l":"Remote definition","p":["To ease the configuration of remote modules, make sure that you first import the RemoteDefinition type and assign it to your remote definitions array declaration."]},{"l":"name","p":["The name property of a remote definition must match the name property defined in the remote module ModuleFederationPlugin configuration.","If you are relying on either the Squide defineDevRemoteModuleConfig or defineBuildRemoteModuleConfig function to add the ModuleFederationPlugin to the remote module webpack configuration object, then the remote module name is the second argument of the function.","In the following exemple, the remote module name is remote1."]},{"l":"url","p":["The url property of a remote definition must match the publicPath of the remote module webpack configuration object.","In the following exemple, the remote module publicPath is http://localhost:8081.","In development mode, the publicPath is built from the provided host and port values. Therefore, if the port value is 8081, then the generated publicPath would be http://localhost:8081/:","In build mode, the publicPath is the third argument of the defineBuildRemoteModuleConfig function:"]}],[{"l":"completeModuleRegistrations","p":["Completes the registration process for modules that have been registred using registerLocalModules and registerRemoteModules by executing the registered deferred registration functions.","This function should only be used by applications that support deferred registrations."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A FireflyRuntime instance.","data: An optional object with data to forward to the deferred registration functions."]},{"l":"Returns","p":["A Promise object with the following properties:","localModuleErrors: An array of LocalModuleRegistrationError if any error happens during the completion of the local modules registration process.","remoteModuleErrors: An array of RemoteModuleRegistrationError if any error happens during the completion of the remote modules registration process."]},{"l":"Usage"},{"l":"Complete module registrations"},{"l":"Handle the completion errors"}],[{"l":"completeLocalModuleRegistrations","p":["Completes the registration process for modules that have been registred using registerLocalModules by executing the registered deferred registration functions.","This function should only be used by applications that support deferred registrations."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A FireflyRuntime instance.","data: An optional object with data to forward to the deferred registration functions."]},{"l":"Returns","p":["A Promise object with an array of LocalModuleRegistrationError if any error happens during the completion of the local modules registration process.","LocalModuleRegistrationError:","error: The original error object."]},{"l":"Usage"},{"l":"Complete local module registrations"},{"l":"Handle the completion errors"}],[{"l":"completeRemoteModuleRegistrations","p":["Completes the registration process for modules that have been registred using registerRemoteModules by executing the registered deferred registration functions.","This function should only be used by applications that support deferred registrations."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A FireflyRuntime instance.","data: An optional object with data to forward to the deferred registration functions."]},{"l":"Returns","p":["A Promise object with an array of RemoteModuleRegistrationError if any error happens during the completion of the remote modules registration process.","RemoteModuleRegistrationError:","url: The URL of the module federation remote that failed to load.","containerName: The name of the dynamic container that Squide attempted to recover.","moduleName: The name of the module that Squide attempted to recover.","error: The original error object."]},{"l":"Usage"},{"l":"Complete remote module registrations"},{"l":"Handle the completion errors"}],[{"l":"useAreModulesRegistered","p":["Force the application to re-render once all the modules are registered (but not ready).","If your application is using the AppRouter component, you shouldn't use this hook.","This hook should only be used by applications that support deferred registrations and should be pair with the useAreModulesReady hook."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","interval: The interval in milliseconds at which the hook is validating if the registration process is completed."]},{"l":"Returns","p":["A boolean indicating if the modules are registered."]},{"l":"Usage"}],[{"l":"useAreModulesReady","p":["Force the application to re-render once the registration process has been completed for all the modules. Without this hook, the page is rendered with an empty router as it happens before the remote modules registered their routes and navigation items.","If your application is using the AppRouter component, you shouldn't use this hook.","If your application supports deferred registrations, make sure to pair this hook with the useAreModulesRegistered hook."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","interval: The interval in milliseconds at which the hook is validating if the registration process is completed."]},{"l":"Returns","p":["A boolean indicating if the registration process is completed."]},{"l":"Usage"}],[{"l":"AppRouter","p":["A component that sets up and orchestrate Squide federated primitives and render a React Router instance."]},{"l":"Reference"},{"l":"Properties","p":["fallbackElement: A React element to render while the application is being bootstrapped.","errorElement: A React element to render when there's an unmanaged error during the bootstrapping of the application.","waitForMsw: Whether or not the application bootstrapping sequence should wait for MSW to be started before loading the data and rendering the active route.","onLoadPublicData: An optional handler to load the initial public data after the modules are registered and MSW is started(if enabled). This handler is called the first time a user navigate to a public route. Such public data could include feature flags.","onLoadProtectedData: An optional handler to load the initial protected data after the modules are registered and MSW is started(if enabled). This handler is called the first time a user navigate to a protected route (any route that has no $visibility: public hint). Such protected data could include a user session.","onCompleteRegistrations: An optional handler to complete the deferred registrations.","routerProvidersProps: An optional object of createBrowserRouter options."]},{"l":"Usage"},{"l":"Define a loading component"},{"l":"Define an error component","p":["An error component receives the current error as a prop."]},{"l":"Load public data","p":["The handler must return a Promise, and the consumer application must handle the loaded public data, as the AppRouter component will ignore any data resolved by the returned Promise object."]},{"l":"Load protected data","p":["The handler must return a Promise, and the consumer application must handle the loaded protected data, as the AppRouter component will ignore any data resolved by the returned Promise object."]},{"l":"Complete deferred registrations","p":["For more information about deferred registrations, refer to the registerRemoteModules and completeModuleRegistrations documentation."]},{"l":"Specify additional router options"}],[{"l":"ManagedRoutes","p":["A placeholder indicating where in the routing tree should the managed routes be rendered. The ManagedRoutes placeholder concept is similar to React Router's outlet, it's a pipeline to inject routes at a predetermined location.","A managed route is a route that is neither hoisted or nested with a parentPath or parentName option."]},{"l":"Reference"},{"l":"Properties","p":["None"]},{"l":"Usage","p":["The route including the ManagedRoutes placeholder must be hoisted; otherwise, there will be an infinite loop as the ManagedRoutes placeholder will render within itself."]}],[{"l":"useIsRouteMatchProtected","p":["Execute React Router's matching algorithm against the registered routes and a given location to determine if any route match the location and whether or not that matching route is protected.","To take advantage of this hook, make sure to add a $visibility hint to your public pages."]},{"l":"Reference"},{"l":"Parameters","p":["locationArg: The location to match the route paths against.","options: An optional object literal of options:","throwWhenThereIsNoMatch: Whether or not to throw an Error if no route match locationArg."]},{"l":"Returns","p":["A boolean value indicating whether or not the matching route is protected. If throwWhenThereIsNoMatch is enabled and no route match the given location, an Error is thrown."]},{"l":"Usage"},{"l":"Using useLocation"},{"i":"using-windowlocation","l":"Using window.location"}],[{"l":"useRenderedNavigationItems","p":["Recursively parse a navigation items structure to transform the items into React Elements.","The useNavigationItems hook returns the navigation items tree structure as is, meaning the consumer has to recursively parse the structure to transform the items into actual React Elements.","As it's a non-trivial process, the shell provides this utility hook to help with that."]},{"l":"Reference"},{"l":"Parameters","p":["navigationItems: An array of NavigationItem to render.","renderItem: A function to render a single link from a navigation item","renderSection: A function to render a section from a collection of items."]},{"l":"Returns","p":["An array of ReactElement."]},{"l":"Usage"}],[{"l":"useRouteMatch","p":["Execute React Router's matching algorithm against the registered routes and a given location to determine if any route match the location."]},{"l":"Reference"},{"l":"Parameters","p":["locationArg: The location to match the route paths against."]},{"l":"Returns","p":["A Route object if there's a matching route, otherwise an Error is thrown."]},{"l":"Usage"},{"l":"Using useLocation"},{"i":"using-windowlocation","l":"Using window.location"}],[{"l":"ConsoleLogger","p":["A basic console logger."]},{"l":"Reference"},{"l":"Parameters","p":["logLevel: An optional minimum level for the logger to output a log entry to the console (default is LogLevel.debug)."]},{"l":"Usage"},{"l":"Log everything"},{"l":"Only log errors","p":["To restrict the logs to error or critical, change the minimum log level to error:"]}],[{"l":"Logger","p":["An abstract base class to define a logger."]},{"l":"Usage"},{"l":"Define a custom logger"}],[{"l":"EventBus","p":["A basic implementation of a pub/sub mecanism enabling loosely coupled between the host application and the modules."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","logger: An optional logger to facilitate debugging."]},{"l":"Usage"},{"l":"Create an event bus instance"},{"l":"Add a listener","p":["When possible, prefer useEventBusListener to eventBus.addListener."]},{"l":"Remove a listener"},{"l":"Dispatch an event","p":["When possible, prefer useEventBusDispatcher to eventBus.dispatch."]}],[{"l":"useEventBusDispatcher","p":["Retrieve an EventBus instance from the FireflyRuntime instance provided by RuntimeContext and provide a function to dispatch an event."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A dispatch function."]},{"l":"Usage"}],[{"l":"useEventBusListener"},{"l":"Reference"},{"l":"Parameters","p":["eventName: The name of the event to listen for.","callback: A function to be executed when a event matching the provided name is dispatched.","options: An optional object literal of options:","once: Whether or not the event listener should be automatically removed once an event as been handled."]},{"l":"Returns","p":["Nothing"]},{"l":"Usage"}],[{"l":"useIsAuthenticated","p":["Indicate whether or not the user is authenticated.","If the sessionAccessor function return a non null/ undefined value, a user is considered as authenticated."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A boolean value."]},{"l":"Usage"}],[{"l":"Plugin","p":["An abstract base class to define a plugin."]},{"l":"Usage"},{"l":"Define a plugin"},{"l":"Register a plugin"},{"l":"Retrieve a plugin from a runtime instance"},{"l":"Retrieve a plugin with a custom function","p":["We recommend pairing a plugin definition with a custom function to retrieve the plugin from a runtime instance.","Retrieving a plugin with a custom function doesn't require the consumer to remember the plugin name, and has the upside of inferring the typings."]}],[{"l":"defineDevHostConfig","p":["Creates a webpack configuration object that is adapted for a Squide host application in development mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineDevConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The host application name.","port: The host application port.","options: An optional object literal of options:","Accepts most of webpack definedDevConfig predefined options.","htmlWebpackPluginOptions: An optional object literal accepting any property of the HtmlWebpackPlugin.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide host application in development mode."]},{"l":"Default shared dependencies","p":["The defineDevHostConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate optional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineHostModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]}],[{"l":"defineDevRemoteModuleConfig","p":["Creates a webpack configuration object that is adapted for a Squide remote module application in development mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineDevConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The remote module application name.","port: The remote module application port.","options: An optional object literal of options:","Accepts most of webpack definedDevConfig predefined options.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide remote module application in development mode."]},{"l":"Conventions","p":["To fulfill Squide remote module requirements, the defineDevRemoteModuleConfig function will pre-configure the ModuleFederationPlugin with the following filename and exposes properties.","If the remote module port is 8081, the remote module bundle is available at http://localhost:8081/remoteEntry.js."]},{"l":"Default shared dependencies","p":["The defineDevRemoteModuleConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate additional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineRemoteModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]},{"l":"Expose an additional module"}],[{"l":"defineBuildHostConfig","p":["Creates a webpack configuration object that is adapted for a Squide host application in build mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineBuildConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The host application name.","options: An optional object literal of options:","Accepts most of webpack definedBuildConfig predefined options.","htmlWebpackPluginOptions: An optional object literal accepting any property of the HtmlWebpackPlugin.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide host application in build mode."]},{"l":"Default shared dependencies","p":["The defineBuildHostConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on GitHub."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate additional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineHostModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]}],[{"l":"defineBuildRemoteModuleConfig","p":["Creates a webpack configuration object that is adapted for a Squide remote module application in build mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineBuildConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The remote module application name.","options: An optional object literal of options:","Accepts most of webpack definedDevConfig predefined options.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide remote module application in build mode."]},{"l":"Conventions","p":["To fulfill Squide remote module requirements, the defineBuildRemoteModuleConfig function will pre-configure the ModuleFederationPlugin with the following filename and exposes properties.","If the remote module publicPath is http://localhost/8081, the remote module bundle is available at http://localhost:8081/remoteEntry.js."]},{"l":"Default shared dependencies","p":["The defineBuildRemoteModuleConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate additional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineRemoteModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]},{"l":"Expose an additional module"}],[{"l":"setMswAsStarted","p":["Indicates to the useIsMswStarted hook that Mock Service Worker(MSW) is started and the application can safely be rendered.","This hook should be used in pair with either the useIsMswReady hook or the AppRouter component."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["Nothing"]},{"l":"Usage"}],[{"l":"useIsMswReady","p":["Force the application to re-render once Mock Service Worker(MSW) is started. Without this hook, the page is rendered before all the request handlers are registered to MSW which could results in 404 errors.","If your application is using the AppRouter component, you shouldn't use this hook."]},{"l":"Reference"},{"l":"Parameters","p":["enabled: Whether or not MSW is currently enabled for the application. This is especially useful to ensure the application is not waiting for MSW when in production.","options: An optional object literal of options:","interval: The interval in milliseconds at which the hook is validating if MSW is started."]},{"l":"Returns","p":["A boolean indicating if MSW is started."]},{"l":"Usage","p":["Also take a look at the setIsMswAsStarted function"]}],[{"l":"i18nextPlugin","p":["A plugin to faciliate the integration of i18next in a federated application."]},{"l":"Reference"},{"l":"Parameters","p":["supportedLanguages: An array of languages supported by the application.","fallbackLanguage: The language to default to if none of the detected user's languages match any supported language.","queryStringKey: The querystring parameter lookup when detecting the user's language.","options: An optional object literal of options:","detection: An optional object literal accepting any LanguageDetector options."]},{"l":"Usage"},{"l":"Register the plugin"},{"l":"Retrieve the plugin instance","p":["Prefer using getI18nextPlugin when possible"]},{"l":"Register a i18next instance"},{"l":"Retrieve a i18next instance"},{"l":"Detect the user language","p":["Whenever a plugin instance is created, the user's language should always be detected immediatly using the detectUserLanguage function."]},{"l":"Retrieve the current language"},{"l":"Change the current language"},{"l":"Change the language detection order","p":["By default, the detection of the user's language is done first from the specified URL querystring parameter (?language in this example), then from the user's navigator language settings. The detection order can be changed by specifying a new value for the order detection option:"]},{"l":"Add an additional detection source"}],[{"l":"getI18nextPlugin","p":["Return an instance of i18nextPlugin from the list of plugins registered with a Runtime instance."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A runtime instance."]},{"l":"Returns","p":["An i18nextPlugin instance if the plugin has been registered, otherwise an Error is thrown."]},{"l":"Usage"}],[{"l":"useChangeLanguage","p":["Provide a function to change the current language of every i18next instance registered with the i18nextPlugin instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A function to change the current language of an i18nextPlugin instance."]},{"l":"Usage"}],[{"l":"useCurrentLanguage","p":["Retrieve the current language of the i18nextPlugin instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["The current language of the i18nextPlugin instance."]},{"l":"Usage","p":["Or with a typed language:"]}],[{"l":"useI18nextInstance","p":["Retrieve a registered i18next instance from the i18nextPlugin instance."]},{"l":"Reference"},{"l":"Parameters","p":["key: An instance key."]},{"l":"Returns","p":["An i18next instance."]},{"l":"Usage"},{"l":"Retrieve an instance"},{"l":"Use with the useTranslation hook"},{"l":"Use with the Trans component","p":["Or without the t function:"]}],[{"l":"I18nextNavigationItemLabel","p":["A React component to localize the label of Squide navigation items."]},{"l":"Reference"},{"l":"Properties","p":["i18next: An i18next instance.","namespace: An optional namespace for the localized resource. If no namespace is provided, the default namespace is navigationItems.","resourceKey: A localized resource key."]},{"l":"Usage","p":["Or with a difference resources namespace:"]}],[{"l":"LocalStorageSessionManager","p":["A local storage session manager (strictly for development purpose)."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","key: An optional key identifying the session in localStorage."]},{"l":"Usage"},{"l":"Create a manager instance"},{"l":"Set a session"},{"l":"Get the current session"},{"l":"Clear the current session"},{"l":"Integrate with a runtime instance"}],[{"l":"ReadonlySessionLocalStorage","p":["Read a session object from the local storage (strictly for development purpose)."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","key: An optional key identifying the session in localStorage."]},{"l":"Usage"},{"l":"Create an accessor instance"},{"l":"Get the current session"}],[{"l":"Troubleshooting"},{"l":"Set the runtime mode to development","p":["In an effort to optimize the development experience, Squide can be bootstrapped in development or production mode. To troubleshoot a persistent issue, make sure that the runtime mode is development:"]},{"l":"React context values are undefined","p":["If you are encountering undefined values when providing a React context from the host application and consuming the context in modules, it is likely due to two possible reasons: either you have two instances of React, or you have multiple instances of that React context.","To resolve this issue:","Ensure that the react and react-dom dependencies are shared as singletons between the host application and the remote modules. A React context value cannot be shared between different parts of an application that use different instances of React.","Confirm that the shared React context resides in a library shared as a singleton.","If you are using eager shared dependencies, ensure that ONLY the host application configures these dependencies as eager.","If the issue persists, update your host application and remote module's webpack build configuration with the optimize: false option. Afterward, build the bundles and serve them. Open a web browser, access the DevTools, switch to the Network tab (ensure that JS files are listed), navigate to the application's homepage, and inspect the downloaded bundle files. The problematic React context definition should appear only once; otherwise, you may have multiple instances of the React context.","For additional information on shared dependency versioning, please refer to the add a shared dependency guide and https://github.com/patricklafrance/wmf-versioning."]}],[{"l":"Samples"},{"i":"squide-basic-sample","l":"Squide \"basic\" sample","p":["Host application","Remote module","Another remote module","Local module","Shared application shell","Shared library","Host sample"]},{"i":"squide-sample-with-endpoints","l":"Squide sample with \"endpoints\"","p":["Host application","Remote module","Local module","Shared application shell","Layouts library","i18next library","Shared library","Host sample","Isolated remote module sample"]}],[{"l":"About","p":["To ask a question or propose an idea, feel free to start a new discussion on Github. If you found a bug, please open an issue on Github."]},{"l":"Contributing","p":["Have a look at the contributor's documentation."]},{"l":"License","p":["See the LICENSE on Github."]}]] \ No newline at end of file +[[{"l":"Getting started","p":["Welcome to Squide (yes \uD83E\uDD91 with an \"e\"), a shell for Workleap federated applications. In this getting started section, you'll find an overview of the shell and a quick start guide to create a new federated application from scratch."]},{"i":"why-a-shell","l":"Why a shell?","p":["We have built this shell to facilitate the adoption of federated applications at Workleap by enforcing patterns that we believe will help feature teams successfully implement a distributed architecture.","The shell itself is a lightweight API layer built on top of Module Federation and React Router, with the goal of maximizing the strength of both libraries while interfering as little as possible with their functionality."]},{"l":"Module Federation","p":["We have identified 2 major challenges with frontend federated applications:","How can we prevent loading the same large dependencies twice when switching between modules?","How can we offer a cohesive experience that doesn't feel modular?","To address the first challenge, we believe that Module Federation provides a solution by offering a mecanism capable of deduping common dependencies shared between the host application and the remote modules at runtime.","With this mecanism in place, all federated parts of an application can now be loaded in the same browsing context instead of nested browsing contexts such as iframes.","By sharing the same browsing context (e.g. the same Document object, the same Window object, and the same DOM), federated parts now form a unified and cohesive single application, addressing the second challenge.","With Module Federation, we hope to develop federated applications that provide the same user experience as monolithic applications \uD83D\uDE80."]},{"l":"React Router","p":["React Router nested routes feature is ideal for federated applications as it enables highly composable and decoupled UI. For a more in-depth explanation, refer to this article."]},{"l":"Module registration","p":["The most distinctive aspect of this shell is the conventions it enforces for loading and registering remote modules. Here's a brief overview of the flow:","During bootstrap, the host application attempts to load predefined modules and calls a registration function with a specific name and signature for each successfully loaded module.","During registration, a module receives the shared services of the federation application and use them to dynamically register its routes and navigation items.","Once all the modules are registered, the host application will create a React Router instance with the registered routes and renders a navigation menu with the registered navigation items.","That's a nutshell overview. Of course, there is more to it, but these are the main ideas."]},{"l":"Guiding principles","p":["While developing the API of Squide, we kept a few guiding principles in mind. Those principles are not settled stones, you might want to diverge from them from time to time, but adhering to those will make your experience more enjoyable:","A module should always correspond to a subdomain of the application's business domain and should only export pages.","A module should be fully autonomous. It shouldn't have to coordinate with other parts of the application for things as trivial as navigation links.","A federated application should feel cohesive. Different parts of a federation application should have the ability to communicate with each others and react to changes happening outside of their boundaries.","Data and state should never be shared between parts of a federated application. Even if two parts require the same data or the same state values, they should load, store and manage them independently."]},{"l":"Limitations","p":["Module Federation comes with a few manageable limitations that are important to consider when architecting your distributed application:","A shared dependency cannot be tree-shaken. Since remote modules are loaded at runtime, ModuleFederationPlugin cannot infer which parts of a shared dependency will be used by the application modules. Therefore, tree-shaking is disabled for shared dependencies.","Module Federation does not support React Fast Refresh. However, it does support Hot Module Replacement.","These limitations are not specific to Squide, they are specific to Module Federation."]},{"l":"Create your project","p":["To get started, follow the quick start guide to create a new federated application from scratch."]}],[{"l":"Create an host application","p":["Let's begin by creating the application that will serve as the entry point for our federated application and host the application modules."]},{"l":"Install the packages","p":["Create a new application (we'll refer to ours as host), then open a terminal at the root of the new solution and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the application","p":["First, create the following files:","Then, ensure that you are developing your application using ESM syntax by specifying type: module in your package.json file:","Finally, use a dynamic import to add an async boundary:","To learn more about this async boundary and the bootstrap.tsx file, read the following article."]},{"l":"Module registration","p":["Next, to register the modules, instanciate a shell FireflyRuntime instance and register the remote module with the registerRemoteModules function (the configuration of the remote module will be covered in the next section):","Then, render the AppRouter component. The AppRouter component will render a React Router browser instance configured with the registered routes:"]},{"l":"Navigation items","p":["Next, create a layout component to render the navigation items. In many applications, multiple pages often share a common layout that includes elements such as a navigation bar, a user profile menu, and a main content section. In a React Router application, this shared layout is commonly referred to as a RootLayout:","In the previous code sample, the RootLayout will serves as the default layout for the homepage as well as for every page (route) registered by a module that are not nested under a parent route with either the parentPath or the parentName option."]},{"l":"Homepage","p":["Next, create the HomePage component that will serve as the homepage for this application:","Then, add a local module at the root of the host application to register the homepage:","And an hoisted route to render the RootLayout and the ManagedRoutes placeholder:","The ManagedRoutes placeholder indicates where routes that are neither hoisted or nested with a parentPath or parentName option will be rendered. In this example, the homepage route is considered a managed route and will be rendered under the ManagedRoutes placeholder.","Finally, update the bootstrapping code to register the newly created local module:"]},{"l":"Configure webpack","p":["Squide webpack configuration is built on top of @workleap/webpack-configs, @workleap/browserslist-config and @workleap/swc-configs. If you are having issues with the configuration of these tools, refer to the tools documentation websites.","First, open the public/index.html file created at the beginning of this guide and copy/paste the following HtmlWebpackPlugin template:","Then, open the .browserslist file and copy/paste the following content:"]},{"l":"Development configuration","p":["To configure webpack for a development environment, first open the swc.dev.js file and copy/paste the following code:","Then, open the webpack.dev.js file and use the defineDevHostConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Build configuration","p":["To configure webpack for a build environment, first open the swc.build.js file and copy/paste the following code:","Then, open the webpack.build.js file and use the defineBuildHostConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Add CLI scripts","p":["To initiate the development server, add the following script to the application package.json file:","To build the application, add the following script to the application package.json file:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script. You should see the home page. Even if the remote module application is not yet available, the host application will gracefully load."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] Found 4 local modules to register.","[squide] 1/4 Registering local module.","[squide] 1/4 Local module registration completed.","[squide] Found 1 remote module to register.","[squide] 1/1 Loading module ./register from container remote1 of remote http://localhost:8081/remoteEntry.js.","[squide] 1/1 Container remote1 of remote http://localhost:8081/remoteEntry.js registration completed.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Create a remote module","p":["Remote modules are modules that are not included in the host application build but are instead loaded at runtime from a remote server. They provide a way for teams to be fully autonomous by independently deploying their modules without relying on the other parts of the application.","Let's add our first remote module!"]},{"l":"Install the packages","p":["Create a new application (we'll refer to ours as remote-module), then open a terminal at the root of the new solution and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the application","p":["First, create the following files:","Then, ensure that you are developing your module using ESM syntax by specifying type: module in your package.json file:"]},{"l":"Routes registration","p":["Next, register the remote module routes and navigation items with the registerRoute and registerNavigationItem functions:","Then, create the Page component:"]},{"l":"Configure webpack","p":["Squide webpack configuration is built on top of @workleap/webpack-configs, @workleap/browserslist-config and @workleap/swc-configs. If you are having issues with the configuration of these tools, refer to the tools documentation websites."]},{"l":"Development configuration","p":["To configure webpack for a development environment, first open the swc.dev.js file and copy/paste the following code:","Then, open the webpack.dev.js file and use the the defineDevRemoteModuleConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Build configuration","p":["To configure webpack for a build environment, first open the swc.build.js file and copy/paste the following code:","Then, open the webpack.build.js file and use the the defineBuildRemoteModuleConfig function to configure webpack:","If you are having issues with the wepack configuration that are not related to module federation, refer to the @workleap/webpack-configs documentation."]},{"l":"Add CLI scripts","p":["To initiate the development server, add the following script to the application package.json file:","To build the module, add the following script to the application package.json file:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the host and the remote-module applications in development mode using the dev script. You should notice an additional link labelled Remote/Page in the navigation menu. Click on the link to navigate to the page of your new remote module!"]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] The following route has been registered. Newly registered item: ...","[squide] The following navigation item has been registered to the root menu for a total of 2 items. Newly registered item: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Create a local module","p":["Local modules are regular modules that are part of the host application build. They are independent modules that expose a registration function to the host application's bootstrapping code. A local module can be a standalone package, a sibling project (in a monorepo setup), or even a local folder within the host application.","Local modules are useful when migrating from a monolithic application to a distributed application or when launching a new product with an unrefined business domain.","Let's add a local module to demonstrate how it's done!","Loading remote modules at runtime with Module Federation is the primary focus of this shell and our recommended approach. It empowers teams to be fully autonomous by deploying their modules independently from the other parts of the application.","However, we recognize that teams working on mature products may prefer to gradually migrate to a distributed architecture by first extracting subdomains into independent modules within their current monolithic setup before fully committing to remote modules loaded at runtime.","To facilitate this transition, this shell also supports local modules that are loaded at build time.","Both remote and local modules can be used within same application as this shell supports dual bootstrapping. For example, an application can be configured to load a few remote modules at runtime while also loading a few local modules."]},{"l":"Install the packages","p":["Create a new application (we'll refer to ours as local-module), then open a terminal at the root of the new solution and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommend that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the application","p":["First, create the following files:","Then, ensure that you are developing your module using ESM syntax by specifying type: module in your package.json file:","Finally, configure the package to be shareable by adding the name, version, and export fields to the package.json file:"]},{"l":"Routes registration","p":["Next, register the local module routes and navigation items with registerRoute and registerNavigationItem functions:","Then, create the Page component:"]},{"l":"Register the local module","p":["Go back to the host application add a dependency to the @sample/local-module package in the host application package.json file:","Then, register the local module with the registerLocalModule function:"]},{"l":"Configure tsup","p":["If you are having issues with the tsup configuration, refer to the @workleap/tsup-configs documentation."]},{"l":"Development configuration","p":["To configure tsup for a development environment, open the tsup.dev.ts file and copy/paste the following code:"]},{"l":"Build configuration","p":["To configure tsup for a build environment, open the tsup.build.ts file and copy/paste the following code:"]},{"l":"Add CLI scripts","p":["To initiate the development server, add the following script to the application package.json file:","To build the module, add the following script to the application package.json file:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the host, remote-module and local-module applications in development mode using the dev script. You should notice an additional link labelled Local/Page in the navigation menu. Click on the link to navigate to the page of your new local module!"]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] The following route has been registered. Newly registered item: ...","[squide] The following navigation item has been registered to the root menu for a total of 2 items. Newly registered item: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Learn the API","p":["Now that we've created a host application, loaded a few modules and registered routes and navigation items, let's delve into the APIs provided by this shell."]},{"l":"Runtime mode","p":["In an effort to optimize the development experience, Squide can be bootstrapped in development or production mode:","By default, the Runtime mode is development."]},{"l":"Logging","p":["Squide includes a built-in logging feature that integrates with the FireflyRuntime class and the useLogger hook.","First, register your own custom logger by implementing the Logger interface or register Squide built-in ConsoleLogger:","Then, log entries from any parts of your federated application with the useLogger hook:","The logger is also available from the FireflyRuntime instance."]},{"l":"Messaging","p":["It's crucial that the parts of a federated application remains loosely coupled. To help with that, Squide offers a built-in Event Bus.","First, listen to an event with the useEventBusListener hook:","Then, dispatch an event from anywhere with the useEventBusDispatcher hook:","You can use the event bus to enable various communication scenarios, such as notifying components of state changes, broadcasting messages across modules, or triggering actions based on specific events.","The event bus is also available from the FireflyRuntime instance."]},{"l":"Session","p":["Most of our applications (if not all) will eventually require the user to authenticate. To facilitate this process, the Squide FireflyRuntime class accepts a sessionAccessor function. Once the shell registration flow is completed, the function will be made accessible to every module of the application.","First, define a sessionAccessor function:","Our security department reminds you to refrain from using a fake LocalStorageSessionManager in a production application \uD83D\uDE0A","Then register the accessor function:","Finally, access the session from any parts of the application with the useSession hook:","Or determine whether or not the user is authenticated with the useIsAuthenticated hook:","The session is also available from the FireflyRuntime instance."]},{"l":"Plugins","p":["To keep Squide lightweight, not all functionalities should be integrated as a core functionality. However, to accommodate a broad range of technologies, a plugin system has been implemented to fill the gap.","Plugins can be registered at bootstrapping with the FireflyRuntime instance:","Then, the plugins can be accessed anywhere from the FireflyRuntime instance:"]},{"l":"Fakes","p":["Take a look at the fake implementations. These implementations are designed to facilitate the set up of a module isolated environment."]},{"l":"Guides","p":["Explore the guides section to learn about Squide advanced features.","Be sure to read, at a minimum, the following guides:","Setup Mock Service Worker","Fetch initial data","Fetch page data","Manage shared state","Isolate module failures","Add authentication","Add a shared dependency"]},{"l":"Reference","p":["For a comprehensive list of the Squide API, refer to the reference section."]}],[{"l":"Deploy","p":["The deployment process for a federated application can vary depending on various factors, including the chosen hosting provider. Therefore, we do not recommend any specific deployment setup.","However, there are a few essential configurations that need to be made regardless of your deployment choices."]},{"l":"Add a default redirect","p":["To enable support for direct page hits, add the following redirect rule to your host application's hosting provider:"]},{"l":"Set the remote URL","p":["Configure the remote modules production URL:"]},{"l":"Update the runtime mode","p":["Don't forget to change the FireflyRuntime mode to production:"]},{"l":"Remove the console logger","p":["Remove the ConsoleLogger from the production build:"]}],[{"l":"Guides","p":["Add a public page","Add a shared dependency","Add authentication","Develop a module in isolation","Federated tabs","Fetch initial data","Fetch page data","Implement a custom logger","Isolate module failures","Manage shared state","Migrate from a monolithic application","Override a React context","Override the host layout","Setup localization","Setup Mock Service Worker","Use feature flags"]}],[{"l":"Setup Mock Service Worker","p":["To speed up frontend development and encourage an API first approach, Squide has built-in support for Mock Service Worker(MSW). MSW offers an API to host fake endpoints directly in the browser. This means that unlike alternative solutions, it doesn't require running an additional process to host fake endpoints."]},{"l":"Setup the host application","p":["First, open a terminal at the root of the host application and install the msw package:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Add an environment variable","p":["Then, update the dev PNPM script to define with cross-env an USE_MSW environment variable that will conditionally include MSW code into the application bundles:","Then, update the development webpack configuration file to include the USE_MSW environment variable into the application bundles:","For more information about the environmentVariables predefined option, refer to the webpack configuration documentation.","Don't forget to define the USE_MSW environment variable for the build script and webpack configuration as well."]},{"l":"Start the service","p":["With the newly added USE_MSW environment variable, the host application bootstrapping code can now be updated to conditionally start MSW when all the request handlers has been registered.","First, define a function to start MSW:","Then, update the bootstrapping code to start the service and mark MSW as started if MSW is enabled:"]},{"l":"Delay routes rendering until the service is started","p":["Finally, update the host application code to delay the rendering of the routes until MSW is started. This is done by setting the waitForMsw property of the AppRouter component to true:"]},{"l":"Setup a remote module","p":["First, open a terminal at the root of the remote module application and install the msw package:","Then, define a request handler:","Finally, register the request handler with the FireflyRuntime instance:","Don't forget to mark the registration function as async since there's a dynamic import."]},{"l":"Setup a local module","p":["Follow the same steps as for a remote module."]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Update a page component code to fetch the /api/character/1 fake API endpoint, then start the application in development mode using the dev script. You should notice that the data has been fetched from the request handler.","In Chrome devtools, the status code for a successful network call that has been handled by an MSW request handler will be 200 OK (from service worker)."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each request handlers registration that occurs and error messages if something went wrong:","[squide] The following MSW request handlers has been registered: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Fetch initial data","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker guide.","Retrieving the initial data of an application is a crucial aspect that isn't always straightforward to implement. That's why we encourage feature teams to build their initial data fetching strategy on top of the Squide AppRouter component."]},{"l":"Challenges with initial data","p":["At first glance, one might wonder what could be so complicated about fetching the initial data of an application. It's just fetches, right...? Well, there are several concerns to take into account for a Squide application:","When in development, the initial data cannot be fetched until the Mock Service Worker (MSW) request handlers are registered and MSW is started.","To register the MSW request handlers, the remote modules must be registered first.","If the requested page is public, only the initial public data should be fetched.","If the requested page is protected, both the initial public and protected data should be fetched.","The requested page cannot be rendered until the initial data has been fetched.","A unique loading spinner should be displayed to the user while the modules are being registered, the MSW request handlers are being registered and the initial data is being fetched, ensuring no flickering due to different spinners being rendered.","To help manage those concerns, Squide offer an AppRouter component that takes care of setuping Squide federated primitive and orchestrating the different states."]},{"l":"Fetch public data"},{"l":"Add an endpoint","p":["First, define an MSW request handler that returns the number of times it has been fetched:","Then, register the request handler using the module registration function:"]},{"l":"Create a shared context","p":["Then, in a shared project, create a React context named FetchCountContext:","Ensure that the shared project is configured as a shared dependency."]},{"l":"Fetch the data","p":["Finally, open the host application code and update the App component to utilize the AppRouter component's onLoadPublicData handler. This handler will fetch the count and forward the retrieved value through FetchCountContext:","The onLoadPublicData handler must return a Promise object. When the async keyword is included in a function signature, the function will automatically return a Promise object."]},{"l":"Use the endpoint data","p":["Now, create a InitialDataLayout component that utilizes the count retrieved from FetchCountContext and render pages with a green background color if the value is odd:","Then, create a Page component:","Finally, register both components:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script and navigate to the /initial-data page. Refresh the page a few times, the background color should alternate between transparent and green."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]},{"l":"Fetch protected data","p":["Now, let's load protected data. The process is similar to fetching public data, but this time, we'll use the onLoadProtectedData handler of the AppRouter component instead."]},{"i":"add-an-endpoint-1","l":"Add an endpoint","p":["First, define a MSW request handler that returns a user tenant subscription data:","If you've registered the public data request handler, the newly created request handler should be automatically registered.","In the previous code sample, for the sake of simplicity, we haven't secured the request handler or implemented session management. However, please be aware that you should do it for a real Workleap application."]},{"i":"create-a-shared-context-1","l":"Create a shared context","p":["Then, in a shared project, create a SubscriptionContext:","Ensure that the shared project is configured as a shared dependency."]},{"i":"fetch-the-data-1","l":"Fetch the data","p":["Finally, open the host application code and update the App component to utilize the AppRouter component's onLoadProtectedData handler. This handler will fetch the user tenant subscription and forward the retrieved value through SubscriptionContext:"]},{"i":"use-the-endpoint-data-1","l":"Use the endpoint data","p":["Now, update the InitialDataLayout component that was previously created for the public data example to render the user tenant subscription status:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script and navigate to the /initial-data page. You should notice the subscription status."]},{"i":"troubleshoot-issues-1","l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Fetch page data","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker guide.","There are various approaches to fetching data for pages. At Workleap, our preference is usually to develop a dedicated endpoint per page, returning a denormalized document specifically tailored for that page. We rely on server state as our singular source of truth and leverage React Query to manage data fetching and ensure our data remains up-to-date.","Although this approach works well, a few adjustments are necessary when transitioning from a monolithic application to a federated application."]},{"l":"Install React Query","p":["First, open a terminal at the root of the module and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Setup the query client","p":["Then, instanciate a QueryClient instance in the module registration function and wrap the routes element with a QueryClientProvider:","To minimize unexpected situations and faciliate maintenance, the React Query cache shouldn't be shared between the host application and the modules. As the React Query cache is located in the QueryClient, both the host application and the modules should instantiate their own QueryClient instance."]},{"l":"Create a component for providers","p":["If the module register multiple routes, to prevent duplicating registration code, you can create a Providers component:"]},{"l":"Setup the development tools","p":["To faciliate development, React Query provides devtools to help visualize all of the inner workings of React Query.","However, the React Query devtools are not been developed to handle a federated application with multiple QueryClient instances. To use the devtools, you have to define a ReactQueryDevtools component for each QueryClient instance:","Then, depending on which page of the application has been rendered, a distinct devtools instance will be accessible. For a better experience, we recommend activating the React Query devtools exclusively when developing a module in isolation:"]},{"l":"Fetch the page data","p":["Now, let's fetch some data. First, add a Mock Service Worker(MSW) request handler to the local module:","Then, register the request handler using the module registration function:","Then, update the Page component to fetch and render the data:"]},{"l":"Define a fallback element","p":["The previous code sample uses useSuspenseQuery instead of useQuery. This enables an application to leverage a React Suspense boundary to render a fallback element in a layout component while the data is being fetched:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the local module in a development environment using the dev-isolated script. If you haven't completed the develop a module in isolation guide, use the dev script instead and skip the part about React Query devtools. Then, navigate to the /page page.","You should notice that the character's data is being fetch from the MSW request handler and rendered on the page. Additionally, you should notice that the React Query devtools are available (a ribbon at the bottom right corner)."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Manage shared state","p":["Effective management of shared state is a crucial aspect of a federated application and can become problematic if not handled carefully. As a general rule, a host application and its modules should never share state."]},{"l":"Forward the initial data","p":["However, at certain points in the lifecycle of a federated application, the host will need to fetch initial data that must be fordwarded to the modules. Such examples include a user session and a user tenant subscription status:","To forward a user session object, a built-in sessionAccessor function is available.","To forward other types of initial data, such as a user tenant subscription, as shown in the fetch initial data guide, the data can be forwarded to modules through a React context."]},{"l":"React Query","p":["Lastly, as detailed in the fetch page data guide, the React Query cache should not be shared between the host and its modules. To do so, both the host application and every module should instantiate their own QueryClient instance."]}],[{"l":"Isolate module failures","p":["One of the key characteristics of micro-frontends implementations like iframes and subdomains is the ability to isolate failures within individual modules, preventing them from breaking the entire application.","However, in a Module Federation implementation, this is not the case as all the modules share the same browsing context (e.g. the same Document object, the same Window object, and the same DOM). A failure in one module can potentially breaks the entire application.","Nevertheless, an application can get very close to iframes failure isolation by utilizing React Router's Outlet component and the errorElement property of a React Router's routes."]},{"l":"Create an error boundary","p":["First, define an error boundary to catch module errors. For this example we'll name it RootErrorBoundary:"]},{"l":"Register the error boundary","p":["Then, update the host application registerHost function to declare the RootErrorBoundary component below the RootLayout but above the routes of the modules. By doing so, if a module encounters an unhandled error, the error boundary will only replace the section rendered by the Outlet component within the RootLayout rather than the entire page:","By implementing this mechanism, the level of failure isolation achieved is comparable to that of an iframes or subdomains implementation. With this mechanism, failure isolation is as good as with an iframes or subdomains implementation."]},{"l":"Hoisted pages","p":["If your application is hoisting pages, it's important to note that they will be rendered outside of the host application's root error boundary. To prevent breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's errorElement property for each hoisted page:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script. Update any of your application routes that is rendered under the newly created error boundary (e.g. that is not hoisted) and throw an Error. The error should be handled by the error boundary instead of breaking the whole application."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Add authentication","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker and fetch initial data guides.","Most of our applications (if not all) will eventually requires the user to authenticate. To facilitate this process, the Squide FireflyRuntime class accepts a sessionAccessor function. Once the application registration flow is completed, the function will be made accessible to every module of the application.","When combined with a React Router authentication boundary and a login page, the shared sessionAccessor function is of great help to manage authentication concerns."]},{"l":"Add a login page","p":["First, open a terminal at the root of the host application and install the @squide/fakes package:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM.","Then, add a Mock Service Worker(MSW) request handler to authenticate a user:","In the previous code sample, the endpoint attempts to authenticate the provided credentials against existing users. If there's a match, the user session is stored in the local storage using a LocalStorageSessionManager instance, and a 200 status code is returned.","Our security department reminds you to refrain from using a fake LocalStorageSessionManager in a production application \uD83D\uDE0A","Next, register the request handler using the host application registration function:","Then, create a login page:","After the user logs in, the application is reloaded. This is a requirement of the AppRouter component's onLoadPublicData and onLoadProtectedData handlers. Nevertheless, it's not a significant concern because Workleap applications utilize a third-party service for authentication which requires a full refresh of the application."]},{"l":"Create a session accessor function","p":["Next, create a shared type for the session and the session manager:","Then, define a sessionAccessor function wrapping an InMemorySessionManager instance:","Finally, create the FireflyRuntime instance with the new sessionAccessor function:"]},{"l":"Fetch the session","p":["Now, let's create an MSW request handler that returns a session object if a user is authenticated:","Then, update the host application App component to load the session when a user navigate to a protected page for the first time:"]},{"l":"Add an authentication boundary","p":["Next, create a new React Router authentication boundary component using the useIsAuthenticated hook:","Internally, the useIsAuthenticated hook utilize the sessionAccessor function that we created previously to determine whether or not the user is authenticated."]},{"l":"Define an authenticated layout","p":["Now that authentication is in place, thanks to the AuthenticationBoundary, we can expect to render the navigation items exclusively for authenticated users.","First, add a MSW request handler to log out a user:","Then, introduce a new AuthenticatedLayout displaying the name of the logged-in user along with a logout button:","By creating a new AuthenticatedLayout, much of the layout code has been transferred from the RootLayout to the AuthenticatedLayout, leaving the root layout responsible only for styling the outer wrapper of the application for now:"]},{"l":"Setup the routes","p":["Finally, assemble everything:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application using the dev script and attempt navigating to the root page (/). You will be redirected to the /login page. Login with temp/ temp, you will be redirected to the root page."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Add a public page","p":["By default, when a route is registered with the registerRoute function, the route is considered as \"protected\". This doesn't imply that the route becomes inacessible to unauthenticated users thought; as this protection is typically granted by an authentication boundary. What it means is that if the AppRouter component's onLoadPublicData and onLoadProtectedData handlers are defined, both handlers will be executed when the route is requested (assuming it is the initial request).","Therefore, if a route and its layout do not rely on the initial protected data of the application, the route should be declared as public using the $visibility option:"]}],[{"l":"Override the host layout","p":["The RootLayout component that as been defined in the create an host application starting guide serves as the default layout for the homepage as well as for every page (route) registered by a module that are not nested under a parent route with either the parentPath or the parentName option.","For most pages, this is the behavior expected by the author. However, for pages such as a login, the default RootLayout isn't suitable because the page is not bound to a user session (the user is not even authenticated yet).","To accomodate pages that require a different layout, a mechanism is needed to move their route declaration at the root of the React Router router instance, before the RootLayout is declared."]},{"l":"Hoist a module pages","p":["Package managers supporting workspaces such as Yarn and NPM call this mechanism \"hoisting\", which means \"raise (something) by means of ropes and pulleys\". This is exactly what we are trying to achieve here.","Squide has a built-in hoist functionality capable of raising module routes marked as hoist at the root of the routes array, before the RootLayout declaration. Thus, an hoisted page will not be wrapped by the RootLayout(or the AuthenticationBoundary) and will have full control over its rendering.","To hoist module pages, add the hoist option to the route registration options and optionally use a different layout:","By declaring a page as hoisted, other parts of the application will not be isolated anymore from this page's failures as the page will be rendered outside of the host application's root error boundary. To avoid breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's errorElement property for each hoisted page.","By declaring a page as hoisted, the page will be rendered at the root of the router, therefore, most certainly outside the authenticated boundary of the application. If the hoisted page requires an authentication, make sure to wrap the page with an authentication boundary or to handle the authentication within the page."]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application in a development environment using the dev script and navigate to the /login page. The page should be displayed even if you are not authenticated."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Federated tabs","p":["While it's typically recommended for a Squide application to maintain the boundary of a page within a single domain (e.g. module), there are situations where enhancing the user experience necessitates rendering a page with parts from multiple domains, or at the very least, simulating it \uD83D\uDE0A.","For this guide, we'll take as an example a page for which the parts that are owned by different domains are organized by tabs (federated tabs):","Tab 1: Registered by Remote Module 1","Tab 2: Registered by Remote Module 2","Tab 3: Registered by Local Module","Anatomy of a page rendering federated tabs"]},{"l":"Define a nested layout","p":["To construct this page while adhering to Squide constraint of exclusively permitting route exports from modules to maintain a high degree of decoupling in the application, let's begin by defining a React Router nested layout. This nested layout will be responsible for rendering all the tab headers and the content of the active tab:","In the previous code sample, the FederatedTabsLayout is similar to the RootLayout introduced in previous guides. However, the key distinction is that this layout is nested under the /federated-tabs URL segment. By nesting the layout under a specific path, it will only render when the user navigates to one of the federated tab pages (e.g. /federated-tabs/tab-1, /federated-tabs/tab-2, /federated-tabs/tab-3).","To register the newly created layout as a nested layout, use the registerRoute function:","With this nested layout in place, thanks to the React Router Outlet component, the content of the tabs can now reside in distinct pages(registered by different modules) while still delivering a cohesive user experience. Whenever a user navigates between the tabs, the URL will be updated, and the tab content will change, but the shared portion of the layout will remain consistent.","As a bonus, each individual tab will have its own dedicated URL! \uD83E\uDD73","It is recommended to define the shared layouts in a standalone package as it's done for the endpoints sample layouts project."]},{"l":"Create the tab routes","p":["Next, let's add the actual tab pages to the modules. To do so, we'll use the parentPath option of the registerRoute function to register the routes under the FederatedTabsLayout:","Now that the tabs have been registered, ensure that all four modules (including remote-module-3) are registered in the host application. Start the development servers using the dev script. Navigate to the /federated-tabs page, and you should see the tab headers. Click on each tab header to confirm that the content renders correctly."]},{"l":"Decouple the navigation items","p":["Althought it's functional, there are still a few configurations needed since the modules are currently coupled by hardcoded URLs within the FederatedTabsLayout.","To decouple the navigation items, similar to what is done for regular federated pages, we'll utilize the registerNavigationItem function. In this case, we'll also use the menuId option. Defining the menuId option will enable the FederatedTabsLayout to retrieve navigation items exclusively for the federated tab component.","First, let's register the navigation items with the menuId option. For this example the menuId will be /federated-tabs(it can be anything):","Then, update the FederatedTabsLayout to render the registered navigation items instead of the hardcoded URLs:"]},{"l":"Change the display order of the tabs","p":["Similarly to how the display order of regular navigation items can be configured, a federated tab position can be affected with the priority property.","To force Tab 3 to be positioned first, we'll give him a priority of 999:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["To ensure everything is still working correctly, start the development servers using the dev script and navigate to the /federated-tabs page. You should see all three tabs, and you should be able to switch between them by clicking on the tab headers."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong:","[squide] The following route has been registered as a children of the /federated-tabs route. Newly registered item: ...","[squide] The following navigation item has been registered to the /federated-tabs menu for a total of 1 item. Newly registered item: ...","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Use feature flags","p":["Before going forward with this guide, make sure that you completed the setup Mock Service Worker and fetch initial data guides.","To continuously deliver value to our customers, Workleap has adopted a feature flags system that allows to activate or deactivate functionalities without requiring code deployment. While \"in-page\" feature flags are straightforward to implement, feature flags that conditionally register routes require an advanced deferred registration mecanism."]},{"l":"Add an endpoint","p":["First, define a MSW request handler that returns the feature flags:","Then, register the request handler using the module registration function:"]},{"l":"Create a shared context","p":["Then, in a shared project, create a FeatureFlagsContext:","Ensure that the shared project is configured as a shared dependency."]},{"l":"Fetch the feature flags","p":["Finally, open the host application code and update the App component to utilize the AppRouter component's onLoadPublicData handler to fetch the feature flags data:"]},{"l":"Conditionally render a page section","p":["Now, let's use the featureA flag from FeatureFlagsContext to conditionally render a section of the Page component:","In the previous cpde sample, the section of the Page component will only be rendered if featureA is activated."]},{"l":"Conditionally register a route","p":["Since the application hasn't been bootstrapped yet during the route registration phase, the feature flags cannot be retrieved from FeatureFlagsContext to conditionally register a route.","To address this, Squide offers a deferred registration mechanism in two-phases:","The first phase allows modules to register their routes and navigation items that are not dependent on initial data.","The second phase enables modules to register routes and navigation items that are dependent on initial data. We refer to this second phase as deferred registrations.","To defer a registration to the second phase, a module registration function can return an anonymous function. Once the modules are registered and the completeLocalModuleRegistrations function is called, the deferred registration functions will be executed.","First, let's update the module registration function to return an anonymous function that will receive the feature flags:","Finally, open the host application code again and update the App component to utilize the AppRouter component's onCompleteRegistrations handler to complete the module registrations with the feature flags:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the application using the dev and navigate to the /page page. The page should render with the conditonal section. Now, disable the featureA flag in the endpoint and refresh the page. You shouldn't see the conditonal section anymore. Finally, disable the featureB flag in the endpoint and refresh the page. The page shouldn't be available anymore.","If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs (including MSW request handlers) and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Setup localization","p":["Most of the applications that forms the Workleap platform are either already bilingual or will be in the future. To help feature teams with localized resources, Squide provides a native plugin designed to adapt the i18next library for federated applications.","The examples in this guide load all the resources from single localized resources files. For a real Workleap application, you probably want to spread the resources into multiple files and load the files with a i18next backend plugin."]},{"l":"Setup the host application","p":["Let's start by configuring the host application. First, open a terminal at the root of the host application and install the following packages:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM."]},{"l":"Register the i18nextPlugin","p":["Then, update the host application boostrapping code to register an instance of the i18nextplugin with the FireflyRuntime instance:","In the previous code sample, upon creating an i18nextPlugin instance, the user's language is automatically detected using the plugin.detectUserLanguage function. Applications should always detect the user's language at bootstrapping, even if the current language is expected to be overriden by a preferred language setting once the user session has been loaded."]},{"l":"Define the localized resources","p":["Next, create the localized resource files for the en-US and fr-CA locales:"]},{"l":"Register an i18next instance","p":["Then, update the local module register function to create and register an instance of i18next with the i18nextPlugin plugin instance:","In the previous code sample, notice that the i18next instance has been initialized with the current language of the i18nextPlugin instance by providing the lng option. If the user's language has been detected during bootstrapping, the i18next instance will be initialized with the user's language which has been deduced from either a ?language querystring parameter or the user's navigator language settings. Otherwise, the application the instance will be initialized with the fallback language."]},{"l":"Localize the home page resources","p":["Then, update the HomePage component to use the newly created localized resource:"]},{"l":"Update the webpack configurations","p":["Finally, update the webpack development and build configurations to activate the i18next feature:"]},{"l":"Setup a remote module","p":["First, open a terminal at the root of the remote module application and install the following packages:"]},{"i":"define-the-localized-resources-1","l":"Define the localized resources","p":["Then, create the localized resource files for the en-US and fr-CA locales:","Notice that this time, a standard navigationItems namespace has been added to the resource files. The resources in the navigationItems namespace will be used later on to localize the navigation items labels."]},{"i":"register-an-i18next-instance-1","l":"Register an i18next instance","p":["Then, update the local module register function to create and register an instance of i18next with the i18nextPlugin plugin instance:"]},{"l":"Localize the navigation item labels","p":["Then, localize the navigation items labels using the I18nextNavigationItemLabel component. Since the resources are in the navigationItems namespace, there's no need to specify a namespace property on the components:"]},{"l":"Localize the page resources","p":["Then, update the HomePage component to use the newly created localized resource:"]},{"i":"update-the-webpack-configurations-1","l":"Update the webpack configurations","p":["Finally, update the webpack development and build configurations to activate the i18next feature:"]},{"l":"Setup a local module","p":["Follow the same steps as for a remote module."]},{"l":"Integrate a backend language setting","p":["For many applications, the displayed language is expected to be derived from an application specific user's \"preferred language\" setting, which is stored in an database on the backend. Therefore, the frontend remains unaware of this setting's value until the user session is loaded.","Hence, the strategy to select the displayed language should be as follow:","Utilize the language detected at bootstrapping for anonymous users (with the detectUserLanguage function).","Upon user authentication and session loading, if a \"preferred language\" setting is available from the session data, update the displayed language to reflect this preference.","To implement this strategy, utilize the useChangeLanguage hook and the onLoadProtectedData handler of the AppRouter component:"]},{"l":"Use the Trans component","p":["The Trans component is valuable for scenarios that involve interpolation to render a localized resource. To use the Trans component with Squide, pair the component with an i18next instance retrieved from useI18nextInstance hook:","The Trans component can also be used without the t function by including a namespace to the i18nKey property value:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the development servers using the dev script. The home page and the navigation items should render the english ( en-US) resources. Then append ?language=fr-CA to the URL. The home page and the navigation items should now render the french ( fr-CA) resources."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this guide:","Open the DevTools console. You'll find a log entry for each i18next instance that is being registered and another log everytime the language is changed:","[squide] Registered a new i18next instance with key remote-module: ...","[squide] The language has been changed to fr-CA.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Develop a module in isolation","p":["To develop their own independent module, a team shouldn't be required to install the host application or any other modules of the application that they do not own. However, they should have a means to integrate their module with the application shell ( RootLayout, RootErrorBoundary, etc..) while working on their module in isolation.","To achieve this, the first step is to extract the application shell from the host application. There are several approaches to accomplish this, but in this guide, we'll transform the host application into a monorepo and introduce a new local package named @sample/shell for this purpose:"]},{"l":"Create a shell package","p":["The implementation details of the RootLayout and RootErrorBoundary won't be covered by this guide as it already has been covered many times by other guides.","First, create a new package (we'll refer to ours as shell) and add the following fields to the package.json file:","Then, install the package dependencies and configure the new package with tsup.","Then, create a AppRouter component in the shell package to provide a reusable router configuration that can be utilized by both the host application and the isolated modules. The new AppRouter component should be based on the @squide/firefly AppRouter component:","Finally, create a local module to register the application shell that will also be utilized by the host application and the isolated modules:","This guide only covers the RootLayout and RootErrorBoundary but the same goes for other shell assets such as an AuthenticationBoundary."]},{"l":"Update the host application","p":["Now, let's revisit the host application by first adding a dependency to the new @sample/shell package:","Then, incorporate the newly introduced AppRouter component:","And the registerShell function to setup the RootLayout, the RootErrorBoundary and any other shell assets:"]},{"l":"Setup a remote module","p":["With the new shell package in place, we can now configure the remote module to be developed in isolation. The goal is to start the module development server and render the module pages with the same layout and functionalities as if it was rendered by the host application.","To begin, let's start by adding a dependency to the @sample/shell package:","Then, create the following files in the remote module application:"]},{"i":"indextsx","l":"index.tsx","p":["The index.tsx file is similar to the bootstrap.tsx file of an host application but, tailored for an isolated module. The key distinctions are that, since the project is set up for isolated development, the module is registered with the registerLocalModules function instead of the registerRemoteModules function, and a new registerDev function is introduced to register the development homepage (which will be covered in an upcoming section):"]},{"i":"apptsx","l":"App.tsx","p":["The App.tsx file uses the newly created AppRouter component to setup React Router:"]},{"i":"devhometsx","l":"DevHome.tsx","p":["The DevHome component purpose is strictly to serve as an index page when developing the remote module in isolation.","To register the development homepage, let's create a new local module specifically for registering what is needed to develop the module in isolation:"]},{"l":"Add a new CLI script","p":["Next, add a new dev-isolated script to the package.json file to start the local development server in \"isolation\":","The dev-isolated script is similar to the dev script but introduces a ISOLATED environment variable. This new environment variable will be utilized by the webpack.dev.js file to conditionally setup the development server for development in isolation or to be consumed by a host application through the /remoteEntry.js entry point:"]},{"l":"Configure webpack","p":["If you are having issues configuring webpack, refer to the @workleap/webpack-configs documentation website.","First, open the public/index.html file created at the beginning of this guide and copy/paste the following HtmlWebpackPlugin template:","Then, open the .browserslist file and copy/paste the following content:"]},{"l":"Isolated environment configuration","p":["To configure webpack, open the webpack.dev.js file and update the configuration to incorporate the ISOLATED environment variable and the defineDevHostConfig function:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the remote module in isolation by running the dev-isolated script. The application shell should wrap the pages of the module and the default page should be DevHome."]},{"l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]},{"l":"Setup a local module","p":["Similarly to remote modules, the same isolated setup can be achieved for local modules. The main difference is that the webpack.config.js file of a local module serves the sole purpose of starting a development server for isolated development. Typically, local modules do not rely on webpack and Module Federation.","First, open a terminal at the root of the local module application and install the @squide/webpack-configs package and its dependencies:","While you can use any package manager to develop an application with Squide, it is highly recommended that you use PNPM as the guides has been developed and tested with PNPM.","Then, add a peer dependency and a dev dependency to the @sample/shell package:","Then, create the following files in the local module application:"]},{"i":"indextsx-1","l":"index.tsx","p":["This file is similar to the index.tsx file of the remote module."]},{"i":"apptsx-1","l":"App.tsx","p":["This file is similar to the App.tsx file of the remote module."]},{"i":"devhometsx-and-registerdev","l":"DevHome.tsx and registerDev","p":["These files are similar to the dev/DevHome.tsx and dev/register.tsx files of the remote module."]},{"i":"configure-webpack-1","l":"Configure webpack","p":["If you are having issues configuring webpack, refer to the @workleap/webpack-configs documentation website.","First, open the public/index.html file and copy/paste the following HtmlWebpackPlugin template:","Then, open the .browserslist file and copy/paste the following content:","Then, open the swc.config.js file and copy/paste the following code:","Finally, open the webpack.config.js file and use the the defineDevHostConfig function to configure webpack:"]},{"i":"add-a-new-cli-script-1","l":"Add a new CLI script","p":["Next, add a new dev-isolated script to the package.json file to start the local development server:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the remote module in isolation by running the dev-isolated script. The application shell should wrap the pages of the module and the default page should be DevHome."]},{"i":"troubleshoot-issues-1","l":"Troubleshoot issues","p":["If you are experiencing issues with this section of the guide:","Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.","Refer to a working example on GitHub.","Refer to the troubleshooting page."]}],[{"l":"Override a React context","p":["In a federated application using Module Federation, it's typical to configure various global React contexts at the root of the host application. These contexts are usually consumed down the line by the layouts and pages of the remote modules.","Let's explore a simple example using a BackgroundColorContext:","In the previous code samples, the host application provides a value for the BackgroundColorContext, and the ColoredPage component of the remote module utilizes this value to set its background color (in this example, the background color is set to blue)."]},{"l":"Override the context for the remote module","p":["Now, suppose the requirements change, and one remote module's pages need to have a red background. The context can be overriden for the remote module by declaring a new provider directly in the routes registration:"]},{"l":"Extract an utility component","p":["Since there are multiple routes to setup with the new provider, an utility component can be extracted:"]},{"l":"Update a singleton dependency version","p":["Let's consider a more specific use case where the host application declares a ThemeContext from Workleap's new design system, Hopper:","In this scenario, Hopper's components are used throughout the entire federated application, including the remote modules. Moreover, @hopper/components is defined as a singleton shared dependency:","Now, consider a situation where Hopper releases a new version of the package that includes breaking changes, without a \"compatibility\" package to ensure backward compatility with the previous version.","To update the host application without breaking the remote modules, the recommended approach is to temporary \"break\" the singleton shared dependency by loading two versions of the dependency in parallel (one for the host application and one for the remote modules that have not been updated yet).","As @hopper/components expose the ThemeContext, the context must be re-declared in each remote module until every part of the federated application has been updated to the latest version of Hopper:","Thankfully, React Router makes it very easy to declare contexts in a remote module."]}],[{"l":"Add a shared dependency","p":["Shared dependencies represent one of the most powerful concepts within Module Federation. However, mastering its configuration can be quite challenging. Failure to configure shared dependencies properly in a federated application using Module Federation can significantly impact both user and developer experiences.","Squide aims to simplify the configuration of shared dependencies by abstracting the shared dependencies necessary for building an application with React and React Router. Nevertheless, every federated application will inevitably have to configure additional custom shared dependencies.","For a more comprehensive documentation of the Module Federation APIs, their functionality, and their benefits, please refer to this article."]},{"l":"Understanding singleton dependencies","p":["A singleton shared dependency does exactly what its name suggests: it loads only a single instance of the dependency. This means that the dependency will be included in just one bundle file of the federated application."]},{"l":"strictVersion","p":["Sometimes, a singleton shared dependency is paired with the strictVersion option:","When specified, the strictVersion option will generate a runtime error if a module attempts to load a version of the dependency that is incompatible with the specified version. It's often unnecessary to use a strict version, and omitting it provides greater flexibility when it comes time to update the shared dependency version."]},{"l":"Expected behaviors"},{"l":"Minor or patch version","p":["When the version difference between a host application and a remote module is a minor or patch version, the higher version of the dependency will be loaded. For example:","If the host application is on 10.1.0 and a remote module is on 10.3.1-> 10.3.1 will be loaded","If the host application is on 10.3.1 and a remote module is on 10.1.0-> 10.3.1 will be loaded"]},{"l":"Major version","p":["If the version difference between a host application and a remote module is a major version, once again, the higher version of the dependency will be loaded. However, a warning will also be issued. For example:","If the host application is on 11.0.0 and a remote module is on 10.3.1-> 11.0.0 will be loaded","If the host application is on 10.3.1 and a remote module is on 11.0.0-> 11.0.0 will be loaded"]},{"i":"what-should-be-configured-as-a-shared-dependency","l":"What should be configured as a shared dependency?","p":["Libraries matching the following criterias are strong candidates to be configured as shared dependencies:","Medium to large libraries that are used by multiple modules.","Libraries that requires a single instance to work properly (like react).","Libraries exporting React contexts."]},{"l":"Understanding eager dependencies","p":["An eager shared dependency becomes available as soon as the host application starts. In simple terms, it is included in the host application bundle rather than being loaded lazily when it is first requested.","The key point to remember about eager dependencies is that only one application or remote module should configure a shared dependency as eager. Otherwise, the dependency will be included in the bundle of the host application and of every remote module that set the dependency as eager."]},{"i":"what-should-be-configured-as-an-eager-dependency","l":"What should be configured as an eager dependency?","p":["Any shared dependency that must be loaded to bootstrap the application."]},{"l":"Default shared dependencies","p":["Since Squide has dependencies on React and React Router, the define* functions automatically configure shared dependencies for these packages by default, in addition to Squide own packages. The following shared dependencies are set as eager singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github.","You can extend or override the default shared dependencies configuration."]},{"l":"Add custom shared dependencies","p":["To configure shared dependencies, use the sharedDependencies option of any define* function:","When a dependency is shared between a host application and a remote module, the sharing options must be configured on both ends:"]},{"l":"React context limitations","p":["For a React context to be provided by the host application and consumed by the remote modules, the library exporting the React context must be set as a singleton.","To troubleshoot a React context issue or find more information about the limitations, refer to the troubleshooting page."]},{"l":"React dependencies requirements","p":["react and react-dom dependencies must be configured as a singleton, otherwise either an error will be thrown at bootstrapping if the loaded react versions are incompatible, or features like useState will not work.","The react-router-dom dependency must as well be configured as a singleton because it relies on a global React context that needs to be declared in the host application and is consumed by remote modules."]},{"l":"Learn more","p":["To learn more about Module Federation shared dependencies read this article about the shared APIs and refer to this POC on GitHub."]}],[{"l":"Implement a custom logger","p":["Many applications must integrate with specific remote logging solutions such as Honeycomb and Azure Application Insights. To facilitate this integration, the shell runtime accepts any custom loggers implementing the Logger interface."]},{"l":"Create a custom logger class","p":["First, let's define a custom logger:","Then create a FireflyRuntime instance configured with an instance of the new CustomLogger:"]},{"i":"try-it","l":"Try it \uD83D\uDE80","p":["Start the applications and open the developer tools, then, refresh the page. You should see the following console log message:"]}],[{"l":"Migrate from a monolithic application","p":["Transforming an existing monolithic application into a distributed architecture is often more challenging than building a new federated application from scratch.","However, it's also a bad idea to start a new application with a distributed architecture since teams typically lack sufficient understanding of the business domain at that stage. Therefore, for most applications, it makes sense to begin as monolithic application and transition to a distributed architecture later.","With the introduction of local modules, Squide offers an alternative approach that lies between prior solutions. Instead of immediately embracing Team Topology's stream-aligned teams and striving for full team autonomy across the board, local modules allow teams to start with a monorepo setup and add independent local packages (modules) for each expected value stream.","Since adding/deleting local packages in a monorepo setup is pretty cheap, teams can freely reorganize their value streams along the way and won't preemptively invest into a distributed CI/CD infrastructure as local modules are part of the host application build. With independent but local value streams, teams will be well-positioned to transition toward a federated application once they can justify the cost.","If your project is already a monolithic application with a polyrepo setup and you aim to migrate to a distributed architecture, we recommend a decoupling-first strategy using local modules and a monorepo setup."]},{"l":"Decoupling first strategy","p":["The primary challenge to migrate to a distributed architecture is coupling. Thus, for most applications, starting by decoupling the monolith into composable value streams could be the right strategy. It's a great way to get into the migration without the immediate need to update the CI/CD infrastructure or preemptively change developers' habits.","We recommend the following steps:","Transform the codebase into a monorepo setup.","Create independent local packages (modules) for each identified value stream.","Refactor the monolithic application code into the corresponding value stream local packages and ensure that each value stream can be developed independently(e.g., without the need to start the entire application).","Import and register the local packages(modules) into the host application.","Finally, transition from local modules to remote modules and update your CI/CD pipelines to enable independent deployment of modules.","By following these steps, you can gradually decouple your monolithic application, create modular value streams, and prepare the foundation for a distributed architecture."]}],[{"l":"Reference"},{"l":"Artefacts","p":["Packages"]},{"l":"API"},{"l":"Runtime","p":["FireflyRuntime class","RuntimeContext","useRuntime","useRoutes","useNavigationItems","useLogger","useSession"]},{"l":"Registration","p":["registerLocalModules","registerRemoteModules","completeModuleRegistrations","completeLocalModuleRegistrations","completeRemoteModuleRegistrations","useAreModulesRegistered","useAreModulesReady"]},{"l":"Routing","p":["AppRouter","ManagedRoutes","useRenderedNavigationItems","useRouteMatch","useIsRouteMatchProtected"]},{"l":"Logging","p":["Logger","ConsoleLogger"]},{"l":"Messaging","p":["EventBus","useEventBusDispatcher","useEventBusListener"]},{"l":"Session","p":["useIsAuthenticated"]},{"l":"Plugins","p":["Plugin"]},{"l":"webpack","p":["defineDevHostConfig","defineDevRemoteModuleConfig","defineBuildHostConfig","defineBuildRemoteModuleConfig"]},{"l":"Mock Service Worker","p":["useIsMswReady","setMswAsStarted"]},{"l":"i18next","p":["i18nextPlugin","getI18nextPlugin","useChangeLanguage","useCurrentLanguage","useI18nextInstance","I18nextNavigationItemLabel"]},{"l":"Fakes","p":["Squide offers a collection of fake implementations designed to facilitate the set up of a module isolated environment.","LocalStorageSessionManager","ReadonlySessionLocalStorage"]}],[{"l":"Packages","p":["@squide/core","@squide/fakes","@squide/firefly","@squide/i18next","@squide/msw","@squide/react-router","@squide/webpack-configs","@squide/webpack-module-federation","A collection of fake implementations to facilitate the development of federated modules.","Add support for i18next.","Add support for Module Federation.","Add support for MSW.","Core functionalities of Squide.","Description","Helpers to facilitate the creation of a shell package with Squide firefly technology stack.","Name","NPM","npm version","Specific implementation of the core functionalities to support React Router.","Utilities to configure Webpack."]}],[{"l":"FireflyRuntime class","p":["A runtime instance give modules access to functionalities such as routing, navigation, request handlers and logging."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","mode: An optional mode to optimize Squide for production. Values are development(default) and production.","loggers: An optional array of Logger instances.","plugins: An optional array of custom plugin instances.","sessionAccessor: An optional function returning the current session."]},{"l":"Usage"},{"l":"Create a runtime instance"},{"l":"Change the runtime mode"},{"l":"Register routes","p":["route: accept any properties of a React Router Route component with the addition of:","$name: An optional name for the route.","$visibility: An optional visibility indicator for the route. Accepted values are public or protected.","options: An optional object literal of options:","hoist: An optional boolean value to register the route at the root of the router. The default value is false.","parentPath: An optional path of a parent route to register this new route under.","parentName: An optional name of a parent route to register this new route under."]},{"l":"Register an hoisted route","p":["Unlike a regular page, a hoisted page is added at the root of the router, outside of the host application's root layout, root error boundary and even root authentication boundary. This means that a hoisted page has full control over its rendering. To mark a route as hoisted, provide an hoist property to the route options.","By declaring a page as hoisted, other parts of the application will not be isolated anymore from this page's failures and the page will not be protected anymore by the application authenticated boundary.","To avoid breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's errorElement property for each hoisted page.","If the hoisted page requires an authentication, make sure to wrap the page with an authentication boundary or to handle the authentication within the page."]},{"l":"Register a route with a different layout","p":["Learn more about overriding the host application layout"]},{"l":"Register a public route","p":["When registering a route, a hint can be provided, indicating if the route is intended to be displayed as a public or protected route. This is especially useful when dealing with code that conditionally fetch data for protected routes (e.g. a session).","A nested route can also have a visibility hint:","If the route is nested under an authentication boundary, don't forget to either mark the route as hoisted or to nest the route under a public parent.","A $visibility hint only takes effect if your application is using the useIsRouteMatchProtected hook. When no $visibility hint is provided, a route is considered protected."]},{"l":"Register a named route","p":["The registerRoute function accepts a parentName property, allowing a route to be nested under an existing parent route. When searching for the parent route matching the parentName property, the parentName will be matched against the $name property of every route.","A $name property should only be defined for routes that doesn't have a path like an error boundary or an authentication boundary.","A nested route can also be named:"]},{"l":"Register nested routes under an existing route","p":["React router nested routes enable applications to render nested layouts at various points within the router tree. This is quite helpful for federated applications as it enables composable and decoupled UI.","To fully harness the power of nested routes, the registerRoute function allows a route to be registered under any previously registered route, even if that route was registered by another module. The only requirement is that the parent route must have been registered with the registerRoute function.","When registering a new route with the registerRoute function, to render the route under a parent route, specify a parentPath property that matches the parent route's path property:","Or a parentName property that matches the parent route's name property:","Learn more about using nested routes for federated tabs","Likewise any other React Router routes, the path property of a page rendered under an existing parent route must be an absolute path. For example, if a parent route path is /layout, the path property of a page rendered under that parent route and responding to the /page-1 url, should be /layout/page-1."]},{"l":"Retrieve routes","p":["A federated application routes are accessible from a FireflyRuntime instance, but keep in mind that the preferred way to retrieve the routes is with the useRoutes hook."]},{"l":"Register navigation items","p":["item: NavigationSection | NavigationLink.","options: An optional object literal of options:","menuId: An optional menu id to associate the item with.","A Squide navigation item can either be a NavigationLink or a NavigationSection. Both types can be intertwined to create a multi-level menu hierarchy. A NavigationSection item is used to setup a new level while a NavigationLink define a link.","NavigationSection accept the following properties:","$label: The section text.","$priority: An order priority affecting the position of the item in the menu (higher first)","$addiltionalProps: Additional properties to be forwarded to the section renderer.","children: The section content.","NavigationLink accept any properties of a React Router Link component with the addition of:","$label: The link text.","$additionalProps: Additional properties to be forwarded to the link renderer.","Setup the host application to render navigation items"]},{"l":"Register nested navigation items"},{"l":"Sort registered navigation items","p":["A $priority property can be added to a navigation item to affect it's position in the menu. The sorting algorithm is as follow:","By default a navigation item have a priority of 0.","If no navigation item have a priority, the items are positioned according to their registration order.","If an item have a priority 0, the item will be positioned before any other items with a lower priority (or without an explicit priority value).","If an item have a priority 0, the item will be positioned after any other items with a higher priority (or without an explicit priority value)."]},{"l":"Use a React element as navigation item label"},{"l":"Style a navigation item"},{"l":"Open a navigation link in a new tab"},{"l":"Render additional props on a navigation item"},{"l":"Register navigation items for a specific menu","p":["By default, every navigation item registered with the registerNavigationItem function is registered as part of the root navigation menu. To register a navigation item for a different navigation menu, specify a menuId property when registering the items."]},{"l":"Retrieve navigation items","p":["A federated application navigation items are accessible from a FireflyRuntime instance, but keep in mind that the preferred way to retrieve the navigation items is with the useNavigationItems hook.","By default, the getNavigationItems will return the navigation items for the root menu:","To retrieve the navigation items for a specific navigation menu, provide a menuId:"]},{"l":"Register request handlers","p":["The registered handlers must be Mock Service Worker request handlers:","Learn more about setuping Mock Service Worker"]},{"l":"Retrieve request handlers"},{"l":"Use the logger"},{"l":"Use the event bus"},{"l":"Register a plugin","p":["Learn more about plugins"]},{"l":"Retrieve a plugin","p":["Learn more about plugins"]},{"l":"Retrieve the current session"}],[{"l":"RuntimeContext","p":["React context to share a FireflyRuntime instance between an host application and the modules."]},{"l":"Reference"},{"l":"Properties","p":["value: A FireflyRuntime instance."]},{"l":"Usage"},{"l":"Provide a runtime instance"},{"l":"Retrieve a runtime instance"}],[{"l":"useRuntime","p":["Retrieve a FireflyRuntime instance.","When possible, prefer useRoutes, useNavigationItems, useLogger, useSession to useRuntime."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A FireflyRuntime instance."]},{"l":"Usage"}],[{"l":"useRoutes","p":["Retrieve the registered routes from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["An array of Route."]},{"l":"Usage"}],[{"l":"useNavigationItems","p":["Retrieve the registered navigation items from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["menuId: An optional id to retrieve the navigation menu for a specific menu."]},{"l":"Returns","p":["An array of NavigationItem."]},{"l":"Usage"},{"l":"Retrieve the items for the root menu"},{"l":"Retrieve the items for a specific menu"}],[{"l":"useLogger","p":["Retrieve a RuntimeLogger instance from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A RuntimeLogger instance."]},{"l":"Usage"}],[{"l":"useSession","p":["Retrieve the current session from the FireflyRuntime instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A custom session object."]},{"l":"Usage"}],[{"l":"registerLocalModules","p":["Register one or many local module(s). During the registration process, the specified registration function will be invoked with a FireflyRuntime instance and an optional context object. To defer the registration of specific routes or navigation items, a registration function can return an anonymous function.","A local module is a regular module that is part of the host application build and is bundled at build time, as opposed to remote module which is loaded at runtime from a remote server. Local modules are particularly valuable when undergoing a migration from a monolithic application to a federated application or when launching a new product with an evolving business domain."]},{"l":"Reference"},{"l":"Parameters","p":["registerFunctions: An array of ModuleRegisterFunction.","runtime: A FireflyRuntime instance.","options: An optional object literal of options:","context: An optional context object that will be pass to the registration function."]},{"l":"Returns","p":["A Promise object with an array of LocalModuleRegistrationError if any error happens during the registration.","LocalModuleRegistrationError:","error: The original error object."]},{"l":"Usage"},{"l":"Register a local module"},{"l":"Defer the registration of routes or navigation items","p":["Sometimes, data must be fetched to determine which routes or navigation items should be registered by a given module. To address this, Squide offers a two-phase registration mechanism:","The first phase allows modules to register their routes and navigation items that are not dependent on initial data (in addition to their MSW request handlers when fake endpoints are available).","The second phase enables modules to register routes and navigation items that are dependent on initial data. Such a use case would be determining whether a route should be registered based on a feature flag. We refer to this second phase as deferred registrations.","To defer a registration to the second phase, a module registration function can return an anonymous function. Once the modules are registered and the completeLocalModuleRegistrations function is called, the deferred registration functions will be executed.","completeLocalModuleRegistrations"]},{"l":"Handle the registration errors"}],[{"l":"registerRemoteModules","p":["Register one or many remote module(s). During the registration process, the module register function will be invoked with a FireflyRuntime instance and an optional context object. To defer the registration of specific routes or navigation items, a registration function can return an anonymous function.","A remote module is a module that is not part of the current build but is loaded at runtime from a remote server."]},{"l":"Reference"},{"l":"Parameters","p":["remotes: An array of RemoteDefinition(view the Remote definition section).","runtime: A FireflyRuntime instance.","options: An optional object literal of options:","context: An optional context object that will be pass to the registration function."]},{"l":"Returns","p":["A Promise object with an array of RemoteModuleRegistrationError if any error happens during the registration.","RemoteModuleRegistrationError:","url: The URL of the module federation remote that failed to load.","containerName: The name of the dynamic container that Squide attempted to recover.","moduleName: The name of the module that Squide attempted to recover.","error: The original error object."]},{"l":"Usage"},{"l":"Register a remote module"},{"l":"Defer the registration of routes or navigation items","p":["Sometimes, data must be fetched to determine which routes or navigation items should be registered by a given module. To address this, Squide offers a two-phase registration mechanism:","The first phase allows modules to register their routes and navigation items that are not dependent on initial data (in addition to their MSW request handlers when fake endpoints are available).","The second phase enables modules to register routes and navigation items that are dependent on initial data. Such a use case would be determining whether a route should be registered based on a feature flag. We refer to this second phase as deferred registrations.","To defer a registration to the second phase, a module registration function can return an anonymous function. Once the modules are registered and the completeRemoteModuleRegistrations function is called, the deferred registration functions will be executed.","completeRemoteModuleRegistrations"]},{"l":"Handle the registration errors"},{"l":"Remote definition","p":["To ease the configuration of remote modules, make sure that you first import the RemoteDefinition type and assign it to your remote definitions array declaration."]},{"l":"name","p":["The name property of a remote definition must match the name property defined in the remote module ModuleFederationPlugin configuration.","If you are relying on either the Squide defineDevRemoteModuleConfig or defineBuildRemoteModuleConfig function to add the ModuleFederationPlugin to the remote module webpack configuration object, then the remote module name is the second argument of the function.","In the following exemple, the remote module name is remote1."]},{"l":"url","p":["The url property of a remote definition must match the publicPath of the remote module webpack configuration object.","In the following exemple, the remote module publicPath is http://localhost:8081.","In development mode, the publicPath is built from the provided host and port values. Therefore, if the port value is 8081, then the generated publicPath would be http://localhost:8081/:","In build mode, the publicPath is the third argument of the defineBuildRemoteModuleConfig function:"]}],[{"l":"completeModuleRegistrations","p":["Completes the registration process for modules that have been registred using registerLocalModules and registerRemoteModules by executing the registered deferred registration functions.","This function should only be used by applications that support deferred registrations."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A FireflyRuntime instance.","data: An optional object with data to forward to the deferred registration functions."]},{"l":"Returns","p":["A Promise object with the following properties:","localModuleErrors: An array of LocalModuleRegistrationError if any error happens during the completion of the local modules registration process.","remoteModuleErrors: An array of RemoteModuleRegistrationError if any error happens during the completion of the remote modules registration process."]},{"l":"Usage"},{"l":"Complete module registrations"},{"l":"Handle the completion errors"}],[{"l":"completeLocalModuleRegistrations","p":["Completes the registration process for modules that have been registred using registerLocalModules by executing the registered deferred registration functions.","This function should only be used by applications that support deferred registrations."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A FireflyRuntime instance.","data: An optional object with data to forward to the deferred registration functions."]},{"l":"Returns","p":["A Promise object with an array of LocalModuleRegistrationError if any error happens during the completion of the local modules registration process.","LocalModuleRegistrationError:","error: The original error object."]},{"l":"Usage"},{"l":"Complete local module registrations"},{"l":"Handle the completion errors"}],[{"l":"completeRemoteModuleRegistrations","p":["Completes the registration process for modules that have been registred using registerRemoteModules by executing the registered deferred registration functions.","This function should only be used by applications that support deferred registrations."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A FireflyRuntime instance.","data: An optional object with data to forward to the deferred registration functions."]},{"l":"Returns","p":["A Promise object with an array of RemoteModuleRegistrationError if any error happens during the completion of the remote modules registration process.","RemoteModuleRegistrationError:","url: The URL of the module federation remote that failed to load.","containerName: The name of the dynamic container that Squide attempted to recover.","moduleName: The name of the module that Squide attempted to recover.","error: The original error object."]},{"l":"Usage"},{"l":"Complete remote module registrations"},{"l":"Handle the completion errors"}],[{"l":"useAreModulesRegistered","p":["Force the application to re-render once all the modules are registered (but not ready).","If your application is using the AppRouter component, you shouldn't use this hook.","This hook should only be used by applications that support deferred registrations and should be pair with the useAreModulesReady hook."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","interval: The interval in milliseconds at which the hook is validating if the registration process is completed."]},{"l":"Returns","p":["A boolean indicating if the modules are registered."]},{"l":"Usage"}],[{"l":"useAreModulesReady","p":["Force the application to re-render once the registration process has been completed for all the modules. Without this hook, the page is rendered with an empty router as it happens before the remote modules registered their routes and navigation items.","If your application is using the AppRouter component, you shouldn't use this hook.","If your application supports deferred registrations, make sure to pair this hook with the useAreModulesRegistered hook."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","interval: The interval in milliseconds at which the hook is validating if the registration process is completed."]},{"l":"Returns","p":["A boolean indicating if the registration process is completed."]},{"l":"Usage"}],[{"l":"AppRouter","p":["A component that sets up and orchestrate Squide federated primitives and render a React Router instance."]},{"l":"Reference"},{"l":"Properties","p":["fallbackElement: A React element to render while the application is being bootstrapped.","errorElement: A React element to render when there's an unmanaged error during the bootstrapping of the application.","waitForMsw: Whether or not the application bootstrapping sequence should wait for MSW to be started before loading the data and rendering the active route.","onLoadPublicData: An optional handler to load the initial public data after the modules are registered and MSW is started(if enabled). This handler is called the first time a user navigate to a public route. Such public data could include feature flags.","onLoadProtectedData: An optional handler to load the initial protected data after the modules are registered and MSW is started(if enabled). This handler is called the first time a user navigate to a protected route (any route that has no $visibility: public hint). Such protected data could include a user session.","onCompleteRegistrations: An optional handler to complete the deferred registrations.","routerProvidersProps: An optional object of createBrowserRouter options."]},{"l":"Usage"},{"l":"Define a loading component"},{"l":"Define an error component","p":["An error component receives the current error as a prop."]},{"l":"Load public data","p":["The handler must return a Promise, and the consumer application must handle the loaded public data, as the AppRouter component will ignore any data resolved by the returned Promise object."]},{"l":"Load protected data","p":["The handler must return a Promise, and the consumer application must handle the loaded protected data, as the AppRouter component will ignore any data resolved by the returned Promise object."]},{"l":"Complete deferred registrations","p":["For more information about deferred registrations, refer to the registerRemoteModules and completeModuleRegistrations documentation."]},{"l":"Specify additional router options"}],[{"l":"ManagedRoutes","p":["A placeholder indicating where in the routing tree should the managed routes be rendered. The ManagedRoutes placeholder concept is similar to React Router's outlet, it's a pipeline to inject routes at a predetermined location.","A managed route is a route that is neither hoisted or nested with a parentPath or parentName option."]},{"l":"Reference"},{"l":"Properties","p":["None"]},{"l":"Usage","p":["The route including the ManagedRoutes placeholder must be hoisted; otherwise, there will be an infinite loop as the ManagedRoutes placeholder will render within itself."]}],[{"l":"useIsRouteMatchProtected","p":["Execute React Router's matching algorithm against the registered routes and a given location to determine if any route match the location and whether or not that matching route is protected.","To take advantage of this hook, make sure to add a $visibility hint to your public pages."]},{"l":"Reference"},{"l":"Parameters","p":["locationArg: The location to match the route paths against.","options: An optional object literal of options:","throwWhenThereIsNoMatch: Whether or not to throw an Error if no route match locationArg."]},{"l":"Returns","p":["A boolean value indicating whether or not the matching route is protected. If throwWhenThereIsNoMatch is enabled and no route match the given location, an Error is thrown."]},{"l":"Usage"},{"l":"Using useLocation"},{"i":"using-windowlocation","l":"Using window.location"}],[{"l":"useRenderedNavigationItems","p":["Recursively parse a navigation items structure to transform the items into React Elements.","The useNavigationItems hook returns the navigation items tree structure as is, meaning the consumer has to recursively parse the structure to transform the items into actual React Elements.","As it's a non-trivial process, the shell provides this utility hook to help with that."]},{"l":"Reference"},{"l":"Parameters","p":["navigationItems: An array of NavigationItem to render.","renderItem: A function to render a single link from a navigation item","renderSection: A function to render a section from a collection of items."]},{"l":"Returns","p":["An array of ReactElement."]},{"l":"Usage"}],[{"l":"useRouteMatch","p":["Execute React Router's matching algorithm against the registered routes and a given location to determine if any route match the location."]},{"l":"Reference"},{"l":"Parameters","p":["locationArg: The location to match the route paths against."]},{"l":"Returns","p":["A Route object if there's a matching route, otherwise an Error is thrown."]},{"l":"Usage"},{"l":"Using useLocation"},{"i":"using-windowlocation","l":"Using window.location"}],[{"l":"ConsoleLogger","p":["A basic console logger."]},{"l":"Reference"},{"l":"Parameters","p":["logLevel: An optional minimum level for the logger to output a log entry to the console (default is LogLevel.debug)."]},{"l":"Usage"},{"l":"Log everything"},{"l":"Only log errors","p":["To restrict the logs to error or critical, change the minimum log level to error:"]}],[{"l":"Logger","p":["An abstract base class to define a logger."]},{"l":"Usage"},{"l":"Define a custom logger"}],[{"l":"EventBus","p":["A basic implementation of a pub/sub mecanism enabling loosely coupled between the host application and the modules."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","logger: An optional logger to facilitate debugging."]},{"l":"Usage"},{"l":"Create an event bus instance"},{"l":"Add a listener","p":["When possible, prefer useEventBusListener to eventBus.addListener."]},{"l":"Remove a listener"},{"l":"Dispatch an event","p":["When possible, prefer useEventBusDispatcher to eventBus.dispatch."]}],[{"l":"useEventBusDispatcher","p":["Retrieve an EventBus instance from the FireflyRuntime instance provided by RuntimeContext and provide a function to dispatch an event."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A dispatch function."]},{"l":"Usage"}],[{"l":"useEventBusListener"},{"l":"Reference"},{"l":"Parameters","p":["eventName: The name of the event to listen for.","callback: A function to be executed when a event matching the provided name is dispatched.","options: An optional object literal of options:","once: Whether or not the event listener should be automatically removed once an event as been handled."]},{"l":"Returns","p":["Nothing"]},{"l":"Usage"}],[{"l":"useIsAuthenticated","p":["Indicate whether or not the user is authenticated.","If the sessionAccessor function return a non null/ undefined value, a user is considered as authenticated."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A boolean value."]},{"l":"Usage"}],[{"l":"Plugin","p":["An abstract base class to define a plugin."]},{"l":"Usage"},{"l":"Define a plugin"},{"l":"Register a plugin"},{"l":"Retrieve a plugin from a runtime instance"},{"l":"Retrieve a plugin with a custom function","p":["We recommend pairing a plugin definition with a custom function to retrieve the plugin from a runtime instance.","Retrieving a plugin with a custom function doesn't require the consumer to remember the plugin name, and has the upside of inferring the typings."]}],[{"l":"defineDevHostConfig","p":["Creates a webpack configuration object that is adapted for a Squide host application in development mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineDevConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The host application name.","port: The host application port.","options: An optional object literal of options:","Accepts most of webpack definedDevConfig predefined options.","htmlWebpackPluginOptions: An optional object literal accepting any property of the HtmlWebpackPlugin.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide host application in development mode."]},{"l":"Default shared dependencies","p":["The defineDevHostConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate optional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineHostModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]}],[{"l":"defineDevRemoteModuleConfig","p":["Creates a webpack configuration object that is adapted for a Squide remote module application in development mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineDevConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The remote module application name.","port: The remote module application port.","options: An optional object literal of options:","Accepts most of webpack definedDevConfig predefined options.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide remote module application in development mode."]},{"l":"Conventions","p":["To fulfill Squide remote module requirements, the defineDevRemoteModuleConfig function will pre-configure the ModuleFederationPlugin with the following filename and exposes properties.","If the remote module port is 8081, the remote module bundle is available at http://localhost:8081/remoteEntry.js."]},{"l":"Default shared dependencies","p":["The defineDevRemoteModuleConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate additional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineRemoteModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]},{"l":"Expose an additional module"}],[{"l":"defineBuildHostConfig","p":["Creates a webpack configuration object that is adapted for a Squide host application in build mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineBuildConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The host application name.","options: An optional object literal of options:","Accepts most of webpack definedBuildConfig predefined options.","htmlWebpackPluginOptions: An optional object literal accepting any property of the HtmlWebpackPlugin.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide host application in build mode."]},{"l":"Default shared dependencies","p":["The defineBuildHostConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on GitHub."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate additional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineHostModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]}],[{"l":"defineBuildRemoteModuleConfig","p":["Creates a webpack configuration object that is adapted for a Squide remote module application in build mode.","This function is a wrapper built on top of @workleap/web-configs. Make sure to read the defineBuildConfig documentation first."]},{"l":"Reference"},{"l":"Parameters","p":["swcConfig: An SWC configuration object.","applicationName: The remote module application name.","options: An optional object literal of options:","Accepts most of webpack definedDevConfig predefined options.","features: An optional object literal of feature switches to define additional shared dependencies.","i18next: Whether or not to add @squide/i18next as a shared dependency.","sharedDependencies: An optional object literal of additional (or updated) module federation shared dependencies.","moduleFederationPluginOptions: An optional object literal of ModuleFederationPlugin options."]},{"l":"Returns","p":["A webpack configuration object tailored for a Squide remote module application in build mode."]},{"l":"Conventions","p":["To fulfill Squide remote module requirements, the defineBuildRemoteModuleConfig function will pre-configure the ModuleFederationPlugin with the following filename and exposes properties.","If the remote module publicPath is http://localhost/8081, the remote module bundle is available at http://localhost:8081/remoteEntry.js."]},{"l":"Default shared dependencies","p":["The defineBuildRemoteModuleConfig function will add the following shared dependencies as singleton by default:","react","react-dom","react-router-dom","@squide/core","@squide/react-router","@squide/webpack-module-federation","@squide/msw","For the full shared dependencies configuration, have a look at the defineConfig.ts file on Github."]},{"l":"Usage"},{"l":"Define a webpack config"},{"l":"Activate additional features","p":["Features must be activated on the host application as well as every remote module."]},{"l":"Specify additional shared dependencies","p":["Additional shared dependencies must be configured on the host application as well as every remote module."]},{"l":"Extend a default shared dependency","p":["In the previous code sample, the react shared dependency will be augmented with the newly provided strictVersion option. The resulting shared dependency will be:"]},{"l":"Override a default shared dependency","p":["In the previous code sample, the react shared dependency singleton option will be overrided by the newly provided value. The resulting shared dependency will be:"]},{"l":"Customize module federation configuration","p":["While you could customize the ModuleFederationPlugin configuration by providing your own object literal through the moduleFederationPluginOptions option, we recommend using the defineRemoteModuleFederationPluginOptions(applicationName, options) function as it will take care of merging the custom options with the default plugin options.","applicationName: The host application name.","moduleFederationPluginOptions: An object literal of ModuleFederationPlugin options."]},{"l":"Expose an additional module"}],[{"l":"setMswAsStarted","p":["Indicates to the useIsMswStarted hook that Mock Service Worker(MSW) is started and the application can safely be rendered.","This hook should be used in pair with either the useIsMswReady hook or the AppRouter component."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["Nothing"]},{"l":"Usage"}],[{"l":"useIsMswReady","p":["Force the application to re-render once Mock Service Worker(MSW) is started. Without this hook, the page is rendered before all the request handlers are registered to MSW which could results in 404 errors.","If your application is using the AppRouter component, you shouldn't use this hook."]},{"l":"Reference"},{"l":"Parameters","p":["enabled: Whether or not MSW is currently enabled for the application. This is especially useful to ensure the application is not waiting for MSW when in production.","options: An optional object literal of options:","interval: The interval in milliseconds at which the hook is validating if MSW is started."]},{"l":"Returns","p":["A boolean indicating if MSW is started."]},{"l":"Usage","p":["Also take a look at the setIsMswAsStarted function"]}],[{"l":"i18nextPlugin","p":["A plugin to faciliate the integration of i18next in a federated application."]},{"l":"Reference"},{"l":"Parameters","p":["supportedLanguages: An array of languages supported by the application.","fallbackLanguage: The language to default to if none of the detected user's languages match any supported language.","queryStringKey: The querystring parameter lookup when detecting the user's language.","options: An optional object literal of options:","detection: An optional object literal accepting any LanguageDetector options."]},{"l":"Usage"},{"l":"Register the plugin"},{"l":"Retrieve the plugin instance","p":["Prefer using getI18nextPlugin when possible"]},{"l":"Register a i18next instance"},{"l":"Retrieve a i18next instance"},{"l":"Detect the user language","p":["Whenever a plugin instance is created, the user's language should always be detected immediatly using the detectUserLanguage function."]},{"l":"Retrieve the current language"},{"l":"Change the current language"},{"l":"Change the language detection order","p":["By default, the detection of the user's language is done first from the specified URL querystring parameter (?language in this example), then from the user's navigator language settings. The detection order can be changed by specifying a new value for the order detection option:"]},{"l":"Add an additional detection source"}],[{"l":"getI18nextPlugin","p":["Return an instance of i18nextPlugin from the list of plugins registered with a Runtime instance."]},{"l":"Reference"},{"l":"Parameters","p":["runtime: A runtime instance."]},{"l":"Returns","p":["An i18nextPlugin instance if the plugin has been registered, otherwise an Error is thrown."]},{"l":"Usage"}],[{"l":"useChangeLanguage","p":["Provide a function to change the current language of every i18next instance registered with the i18nextPlugin instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["A function to change the current language of an i18nextPlugin instance."]},{"l":"Usage"}],[{"l":"useCurrentLanguage","p":["Retrieve the current language of the i18nextPlugin instance."]},{"l":"Reference"},{"l":"Parameters","p":["None"]},{"l":"Returns","p":["The current language of the i18nextPlugin instance."]},{"l":"Usage","p":["Or with a typed language:"]}],[{"l":"useI18nextInstance","p":["Retrieve a registered i18next instance from the i18nextPlugin instance."]},{"l":"Reference"},{"l":"Parameters","p":["key: An instance key."]},{"l":"Returns","p":["An i18next instance."]},{"l":"Usage"},{"l":"Retrieve an instance"},{"l":"Use with the useTranslation hook"},{"l":"Use with the Trans component","p":["Or without the t function:"]}],[{"l":"I18nextNavigationItemLabel","p":["A React component to localize the label of Squide navigation items."]},{"l":"Reference"},{"l":"Properties","p":["i18next: An i18next instance.","namespace: An optional namespace for the localized resource. If no namespace is provided, the default namespace is navigationItems.","resourceKey: A localized resource key."]},{"l":"Usage","p":["Or with a difference resources namespace:"]}],[{"l":"LocalStorageSessionManager","p":["A local storage session manager (strictly for development purpose)."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","key: An optional key identifying the session in localStorage."]},{"l":"Usage"},{"l":"Create a manager instance"},{"l":"Set a session"},{"l":"Get the current session"},{"l":"Clear the current session"},{"l":"Integrate with a runtime instance"}],[{"l":"ReadonlySessionLocalStorage","p":["Read a session object from the local storage (strictly for development purpose)."]},{"l":"Reference"},{"l":"Parameters","p":["options: An optional object literal of options:","key: An optional key identifying the session in localStorage."]},{"l":"Usage"},{"l":"Create an accessor instance"},{"l":"Get the current session"}],[{"l":"Troubleshooting"},{"l":"Set the runtime mode to development","p":["In an effort to optimize the development experience, Squide can be bootstrapped in development or production mode. To troubleshoot a persistent issue, make sure that the runtime mode is development:"]},{"l":"React context values are undefined","p":["If you are encountering undefined values when providing a React context from the host application and consuming the context in modules, it is likely due to two possible reasons: either you have two instances of React, or you have multiple instances of that React context.","To resolve this issue:","Ensure that the react and react-dom dependencies are shared as singletons between the host application and the remote modules. A React context value cannot be shared between different parts of an application that use different instances of React.","Confirm that the shared React context resides in a library shared as a singleton.","If you are using eager shared dependencies, ensure that ONLY the host application configures these dependencies as eager.","If the issue persists, update your host application and remote module's webpack build configuration with the optimize: false option. Afterward, build the bundles and serve them. Open a web browser, access the DevTools, switch to the Network tab (ensure that JS files are listed), navigate to the application's homepage, and inspect the downloaded bundle files. The problematic React context definition should appear only once; otherwise, you may have multiple instances of the React context.","For additional information on shared dependency versioning, please refer to the add a shared dependency guide and https://github.com/patricklafrance/wmf-versioning."]}],[{"l":"Samples"},{"i":"squide-basic-sample","l":"Squide \"basic\" sample","p":["Host application","Remote module","Another remote module","Local module","Shared application shell","Shared library","Host sample"]},{"i":"squide-sample-with-endpoints","l":"Squide sample with \"endpoints\"","p":["Host application","Remote module","Local module","Shared application shell","Layouts library","i18next library","Shared library","Host sample","Isolated remote module sample"]}],[{"l":"About","p":["To ask a question or propose an idea, feel free to start a new discussion on Github. If you found a bug, please open an issue on Github."]},{"l":"Contributing","p":["Have a look at the contributor's documentation."]},{"l":"License","p":["See the LICENSE on Github."]}]] \ No newline at end of file diff --git a/samples/index.html b/samples/index.html index 039b65708..9dd2ad772 100644 --- a/samples/index.html +++ b/samples/index.html @@ -4,7 +4,7 @@ - + @@ -32,11 +32,11 @@ - + - + - + diff --git a/sitemap.xml.gz b/sitemap.xml.gz index f3f46fcb9b7808eb9df356218b3b42b4fb2976b4..c67618e9f3346465d796f8bd2e81975fd3b93c71 100644 GIT binary patch literal 1090 zcmV-I1ikwoiwFP!000001Js($Zrd;n$M1cLAa}+2Xt$Y|v@J}QJc&-) zJ^d)#N!@({0~I(Z5IKpQ{!Ej9Qj{M*JlAG+03WooPxIAcIiG>eozk{^nt%TEQQXfz z>~0=gZvx zX7Q}yd>OEIYPVjlH)6FG%iB-u<->CQaCiHK<_+KWa~rRn*Z8tqF77vv+4*fG$_AwW zLM!8I;NW`CiUo*ti$y@`5flgCyW0eIBd2Q{QHLW3;oUj`VF8#|G)beDMktALf~Y^; z?c9Sz5S0ta0=wdbMeo|ZflxUYp{ED<2Wxt$9SsD~8WthCy8tx(0D0||FdLBG3amiL zMo*7+tOt!OflmxLG3DO1JW-$WRx4;llNXT7f>l(7PS9;%3uNDtS?lvCaw3?xFNlWf z0iAc6%0hadfYi|%%F|Joo?>AojC^5I+9bdQDDb2UN}$~Hct8qF1qU#$Auky8OIy(^ z8kaDnpl$;REfrWKw4yMwdHX4l zje(l-7!l`fKv$EY%M$!#iECX(O%R1A));cu8Jayk-q(cI$owlM2A&9Gt!aad_n!wW zQ856e4(R)^@83{}b?ew(ZCT(qtP1-N`cBC>;aQbz1W&a=>~6@WiHAIzkPLPx8Y!)H zusoJ!4YuFHT6xfA=O3{leJ#{{IU+Uec@e>vh&QREZsfwl;dRi`Utp@rlF^QZICnAY zk)Vyz$an%z6khU5Qe6Yo8{N+9@Qo`g*>Pf zxhNI=Uku+@Sv)(28`f=^U#;JFX%%D1k{Fn5twV=fOUx^(3=wjs?b!>kzml^6r{Rs& zRph0bB{;)bY^CL$JZMdYitJF~02cQxOUSJg^}%0h$#GA>N{1%8FOQusif*DV+i8QJ zO4&~4Mq4~Q0+))qF)bN83?mC^qK3#3_OTn5DJZlB1&6o~K{{7xmZvB#hCN<7Fybd_ zj=aU+x7w)dx&Xbpw{i78jEsJMIg2xUdZJ!+e2y&u(jWullXod`JFTVzwk6j(QBUgR z#U&uWBPVJ%oS4bTc_nQLhF)5xZSG4zx1K)3E2AC`DW{`kdS*B^^)SBeAsD#_GiCZh zex-GH*Ln)*eB7n$E5LqX&Qq%k literal 1086 zcmV-E1i||siwFP!000001Js(`ZreHx$M5?TLGBgjYgI7S5Mh$SZmivf;yQx!BM9a~@wRtj)VK z&ztU?)qcOvx^L%)huJLtQXJ{Xxcm_32Rfk)u0Ww}z9=95er_!>C4zOZ5eBHdyUPajzVh+kJwYTuU2oaHymQ6`_$OdXli};EcQqlPIJ% zt>>volJH8Ugls9wE$Un%m8H{0y?6n+ELcTF=mg#Ntv>cGnYF%-A}4~0`+{hwcF}pK zsVtgV&I7|)|xiRc>j685)}hb z>VUov`~D4uShtSt)s_W*!>X_!(05A43D2rzBY3I}Vkbs6O+4h$gk-Qo(MV~ngXOU- zYq0$m*2jLy@XXEO97#aQib{1#$^hCYt_!wINq(KJ82k%nic3MpbY)h_nqMp>ni%UR$ zM^4mkxGS27}Loo6PX3F%1{6XvP zuJshq`M68hSAhM(oTpYz${tnES@Sw@k1vG<{`U=>ZlFAIc=!nb0RR630Bh;SAD%A& E080}aJpcdz diff --git a/troubleshooting/index.html b/troubleshooting/index.html index 2654392c4..1187459df 100644 --- a/troubleshooting/index.html +++ b/troubleshooting/index.html @@ -4,7 +4,7 @@ - + @@ -32,12 +32,12 @@ - + - + - - + +