diff --git a/.changeset/red-badgers-marry.md b/.changeset/red-badgers-marry.md
new file mode 100644
index 000000000..002295137
--- /dev/null
+++ b/.changeset/red-badgers-marry.md
@@ -0,0 +1,5 @@
+---
+"@squide/react-router": patch
+---
+
+Improve logging when pending routes are detected. The registered routes are now logged, as well as the pending routes.
diff --git a/docs/upgrading/migrate-to-firefly-v9.0.md b/docs/upgrading/migrate-to-firefly-v9.0.md
index c8d024c87..21502a76f 100644
--- a/docs/upgrading/migrate-to-firefly-v9.0.md
+++ b/docs/upgrading/migrate-to-firefly-v9.0.md
@@ -5,6 +5,12 @@ label: Migrate to firefly v9.0
# Migrate to firefly v9.0
+!!!warning
+Although this migration guide is labeled for version `9.0.0`, it is actually intended for migrating from version `8.*` to version `9.2.1`. Therefore, please ensure you upgrade to `@squide/firefly` version `9.2.1` instead of `9.0.0`.
+
+We apologize for the confusion.
+!!!
+
This major version of `@squide/firefly` introduces [TanStack Query](https://tanstack.com/query/latest) as the official library for fetching the global data of a Squide's application and features a complete rewrite of the [AppRouter](../reference/routing/appRouter.md) component, which now uses a state machine to manage the application's bootstrapping flow.
Prior to `v9.0`, Squide applications couldn't use TanStack Query to fetch global data, making it **challenging** for Workleap's applications to **keep** their **global data** in **sync** with the **server state**. With `v9.0`, applications can now leverage [custom wrappers](../guides/fetch-global-data.md) of the TanStack Query's [useQueries](https://tanstack.com/query/latest/docs/framework/react/reference/useQueries) hook to fetch and keep their global data up-to-date with the server state. Additionally, the new [deferred registrations update](../reference/registration/useDeferredRegistrations.md#register-or-update-deferred-registrations) feature allows applications to even **keep** their conditional **navigation items in sync** with the **server state**.
@@ -321,6 +327,36 @@ The `v9.0` release introduces several breaking changes affecting the host applic
9. Convert all deferred routes into static routes. [View example](#removed-support-for-deferred-routes)
10. Add an `$id` option to the navigation item registrations. [View example](#new-id-option-for-navigation-items)
+### `waitForMsw`, `waitForPublicData`, `waitForProtectedData`
+
+The `AppRouter` component accepts the `waitForMsw`, `waitForPublicData`, and `waitForProtectedData` properties. These properties are forwarded directly to the Squide bootstrapping flow state machine, where they are used to determine its initial state.
+
+If the application register MSW [request handlers](https://mswjs.io/docs/concepts/request-handler/) with the [runtime.registerRequestHandlers](../reference/runtime/runtime-class.md#register-request-handlers) function, add the `waitForMsw` property to the `AppRouter` component:
+
+```tsx
+
+ ...
+
+```
+
+If the application uses the [usePublicDataQueries](../reference/tanstack-query/usePublicDataQueries.md), add the `waitForPublicData` property to the `AppRouter` component:
+
+```tsx
+
+ ...
+
+```
+
+If the application uses the [useProtectedDataQueries](../reference/tanstack-query/useProtectedDataQueries.md), add the `waitForProtectedData` property to the `AppRouter` component:
+
+```tsx
+
+ ...
+
+```
+
+Otherwise, don't define any of those three properties on the `AppRouter` component.
+
### Root error boundary
When transitioning to the new `AppRouter` component, make sure to nest the `RootErrorBoundary` component within the `AppRouter` component's render function.
diff --git a/packages/react-router/src/reactRouterRuntime.ts b/packages/react-router/src/reactRouterRuntime.ts
index a087763b5..8edffa8aa 100644
--- a/packages/react-router/src/reactRouterRuntime.ts
+++ b/packages/react-router/src/reactRouterRuntime.ts
@@ -3,6 +3,10 @@ import { NavigationItemDeferredRegistrationScope, NavigationItemDeferredRegistra
import { ProtectedRoutesOutletId, PublicRoutesOutletId } from "./outlets.ts";
import { RouteRegistry, type Route } from "./routeRegistry.ts";
+function indent(text: string, depth: number) {
+ return `${" ".repeat(depth * 4)}${text}`;
+}
+
function translateOutletsParentId(parentId?: string) {
if (parentId === PublicRoutesOutletId) {
return "PublicRoutes";
@@ -15,6 +19,20 @@ function translateOutletsParentId(parentId?: string) {
return parentId;
}
+function logRoutesTree(routes: Route[], depth: number = 0) {
+ let log = "";
+
+ routes.forEach(x => {
+ log += indent(`- ${x.path ?? x.$id ?? (x.index ? "(index route)" : undefined) ?? "(no identifier)"}\r\n`, depth);
+
+ if (x.children) {
+ log += logRoutesTree(x.children, depth + 1);
+ }
+ });
+
+ return log;
+}
+
export class ReactRouterRuntime extends Runtime {
readonly #routeRegistry = new RouteRegistry();
readonly #navigationItemRegistry = new NavigationItemRegistry();
@@ -161,22 +179,26 @@ export class ReactRouterRuntime extends Runtime {
let message = `[squide] ${pendingRoutes.length} route${pendingRoutes.length !== 1 ? "s were" : " is"} expected to be registered but ${pendingRoutes.length !== 1 ? "are" : "is"} missing:\r\n\r\n`;
pendingRoutes.forEach((x, index) => {
- message += `${index}/${pendingRoutes.length} Missing route with the following path or name: "${x}"\r\n`;
- message += " Pending registrations:\r\n";
+ message += `${index + 1}/${pendingRoutes.length} Missing route with the following path or id: "${x}"\r\n`;
+ message += indent("Pending registrations:\r\n", 1);
const pendingRegistrationsForRoute = pendingRegistrations.getPendingRegistrationsForRoute(x);
pendingRegistrationsForRoute.forEach(y => {
- message += ` - "${y.path ?? y.$id ?? "(no identifier)"}"\r\n`;
+ message += indent(`- "${y.path ?? y.$id ?? "(no identifier)"}"\r\n`, 2);
});
message += "\r\n";
});
+ message += "Registered routes:\r\n";
+ message += logRoutesTree(this.#routeRegistry.routes, 1);
+ message += "\r\n";
+
message += `If you are certain that the route${pendingRoutes.length !== 1 ? "s" : ""} has been registered, make sure that the following conditions are met:\r\n`;
message += "- The missing routes \"path\" or \"$id\" option perfectly match the provided \"parentPath\" or \"parentId\" (make sure that there's no leading or trailing \"/\" that differs).\r\n";
- message += "- The missing routes has been registered with the runtime.registerRoute function. A route cannot be registered under a parent route that has not be registered with the runtime.registerRoute function.\r\n";
- message += "For more information about nested routes, refers to https://gsoft-inc.github.io/wl-squide/reference/runtime/runtime-class/#register-nested-routes-under-an-existing-route.\r\n";
+ message += "- The missing routes has been registered with the runtime.registerRoute function. A route cannot be registered under a parent route that has not be registered with the runtime.registerRoute function.\r\n\r\n";
+ message += "For more information about nested routes, refers to https://gsoft-inc.github.io/wl-squide/reference/runtime/runtime-class/#register-nested-routes-under-an-existing-route.\r\n\r\n";
message += "For more information about the PublicRoutes and ProtectedRoutes outlets, refers to https://gsoft-inc.github.io/wl-squide/reference/#routing.";
if (this._mode === "development") {
@@ -197,20 +219,20 @@ export class ReactRouterRuntime extends Runtime {
pendingSectionIds.forEach((x, index) => {
const [menuId, sectionId] = parseSectionIndexKey(x);
- message += `${index}/${pendingSectionIds.length} Missing navigation section "${sectionId}" of the "${menuId}" menu.\r\n`;
- message += " Pending registrations:\r\n";
+ message += `${index + 1}/${pendingSectionIds.length} Missing navigation section "${sectionId}" of the "${menuId}" menu.\r\n`;
+ message += indent("Pending registrations:\r\n", 1);
const pendingItems = pendingRegistrations.getPendingRegistrationsForSection(x);
pendingItems.forEach(y => {
- message += ` - "${y.item.$id ?? y.item.$label ?? y.item.to ?? "(no identifier)"}"\r\n`;
+ message += indent(`- "${y.item.$id ?? y.item.$label ?? y.item.to ?? "(no identifier)"}"\r\n`, 2);
});
message += "\r\n";
});
message += `If you are certain that the navigation section${pendingSectionIds.length !== 1 ? "s" : ""} has been registered, make sure that the following conditions are met:\r\n`;
- message += "- The missing navigation section \"$id\" and \"menuId\" properties perfectly match the provided \"sectionId\" and \"menuId\".\r\n";
+ message += "- The missing navigation section \"$id\" and \"menuId\" properties perfectly match the provided \"sectionId\" and \"menuId\".\r\n\r\n";
message += "For more information about nested navigation items, refers to: https://gsoft-inc.github.io/wl-squide/reference/runtime/runtime-class/#register-nested-navigation-items.\r\n";
if (this._mode === "development") {