diff --git a/AGENTS.md b/AGENTS.md index 8657979..35f028e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,7 +52,7 @@ Where `RouteInfo` contains: #### Reactive Properties - `routeStatus`: Per-route match status and extracted parameters -- `noMatches`: Boolean indicating NO routes matched (excluding `ignoreForFallback` routes) +- `fallback`: Boolean indicating NO routes matched (excluding `ignoreForFallback` routes) ### Component Architecture @@ -72,12 +72,12 @@ universe they belong to. #### Fallback Component - Shows content when no routes match - Props: - - `when?: WhenPredicate`: Override default `noMatches` behavior + - `when?: WhenPredicate`: Override default `fallback` behavior - `children`: Content snippet Render logic: ```svelte -{#if (router && when?.(router.routeStatus, router.noMatches)) || (!when && router?.noMatches)} +{#if (router && when?.(router.routeStatus, router.fallback)) || (!when && router?.fallback)} {@render children?.(router.state, router.routeStatus)} {/if} ``` @@ -224,8 +224,8 @@ test("Should hide content when routes match.", () => { }); // ❌ Bad - Test internal implementation -test("Should call router.noMatches.", () => { - const spy = vi.spyOn(router, 'noMatches'); +test("Should call router.fallback.", () => { + const spy = vi.spyOn(router, 'fallback'); render(Component, { props, context }); expect(spy).toHaveBeenCalled(); // Testing implementation detail }); @@ -240,7 +240,7 @@ test("Component renders when condition is met.", () => { }); // ✅ Router tests focus on router logic (separate file) -test("Router calculates noMatches correctly.", () => { +test("Router calculates fallback correctly.", () => { // Test router's internal logic }); ``` @@ -691,7 +691,7 @@ test("Should react to state changes.", () => { **Purpose**: Render content when no routes match, with override capability **Key behaviors to test**: -1. Shows when `router.noMatches` is true +1. Shows when `router.fallback` is true 2. Hides when routes are matching 3. Respects `when` predicate override 4. Works across all routing universes diff --git a/src/lib/Fallback/Fallback.svelte b/src/lib/Fallback/Fallback.svelte index 40abd2a..7af883b 100644 --- a/src/lib/Fallback/Fallback.svelte +++ b/src/lib/Fallback/Fallback.svelte @@ -53,8 +53,8 @@ /** * Renders the children of the component. * - * This rendering is conditioned to the parent router engine's `noMatches` property being `true`. This means - * that the children will only be rendered when no route matches the current location. + * This rendering is conditioned to the parent router engine's `fallback` property being `true`. This means + * that the children will only be rendered when no routes match the current location. * @param context The component's context available to children. */ children?: Snippet<[RouterChildrenContext]>; @@ -68,6 +68,6 @@ const router = getRouterContext(resolvedHash); -{#if (router && when?.(router.routeStatus, router.noMatches)) || (!when && router?.noMatches)} +{#if (router && when?.(router.routeStatus, router.fallback)) || (!when && router?.fallback)} {@render children?.({ state: router.state, rs: router.routeStatus })} {/if} diff --git a/src/lib/Fallback/README.md b/src/lib/Fallback/README.md index 16d0664..e7ab0fc 100644 --- a/src/lib/Fallback/README.md +++ b/src/lib/Fallback/README.md @@ -3,7 +3,7 @@ The `Fallback` component can be thought about as a `Route` component that only render its children if there are no other routes in the parent router engine that match. -Internally, it checks the parent router engine's `noMatches` value, which is a reactive value calculated when all other +Internally, it checks the parent router engine's `fallback` value, which is a reactive value calculated when all other route status data is calculated. ## Props diff --git a/src/lib/kernel/RouterEngine.svelte.test.ts b/src/lib/kernel/RouterEngine.svelte.test.ts index 030e668..3249a45 100644 --- a/src/lib/kernel/RouterEngine.svelte.test.ts +++ b/src/lib/kernel/RouterEngine.svelte.test.ts @@ -666,13 +666,13 @@ ROUTING_UNIVERSES.forEach(universe => { }); }); - describe('noMatches', () => { + describe('fallback', () => { test("Should be true whenever there are no routes registered.", () => { // Act. const router = new RouterEngine({ hash: universe.hash }); // Assert. - expect(router.noMatches).toBe(true); + expect(router.fallback).toBe(true); }); test("Should be true whenever there are no matching routes.", () => { @@ -686,7 +686,7 @@ ROUTING_UNIVERSES.forEach(universe => { }; // Assert. - expect(router.noMatches).toBe(true); + expect(router.fallback).toBe(true); }); test.each([ @@ -714,7 +714,7 @@ ROUTING_UNIVERSES.forEach(universe => { addRoutes(router, { matching: routeCount, nonMatching: nonMatchingCount }); // Assert. - expect(router.noMatches).toBe(false); + expect(router.fallback).toBe(false); }); test.each([ @@ -732,7 +732,7 @@ ROUTING_UNIVERSES.forEach(universe => { }); // Assert. - expect(router.noMatches).toBe(true); + expect(router.fallback).toBe(true); }); }); }); diff --git a/src/lib/kernel/RouterEngine.svelte.ts b/src/lib/kernel/RouterEngine.svelte.ts index f37baef..e70d969 100644 --- a/src/lib/kernel/RouterEngine.svelte.ts +++ b/src/lib/kernel/RouterEngine.svelte.ts @@ -47,7 +47,7 @@ export class RouterEngine { #routeHelper; #cleanup = false; #parent: RouterEngine | undefined; - #resolvedHash: Hash; + resolvedHash: Hash; /** * Gets or sets the router's identifier. This is displayed by the `RouterTracer` component. */ @@ -86,17 +86,17 @@ export class RouterEngine { #routeStatusData = $derived.by(() => { const routeStatus = {} as Record; - let noMatches = true; + let fallback = true; for (let routeKey of Object.keys(this.routes)) { const pattern = this.#routePatterns.get(routeKey)!; const [match, routeParams] = this.#routeHelper.testRoute(pattern); - noMatches = noMatches && (pattern.ignoreForFallback ? true : !match); + fallback = fallback && (pattern.ignoreForFallback ? true : !match); routeStatus[routeKey] = { match, routeParams, }; } - return [routeStatus, noMatches] as const; + return [routeStatus, fallback] as const; }); /** * Gets a a record of route statuses where the keys are the route keys, and the values are @@ -105,9 +105,9 @@ export class RouterEngine { routeStatus = $derived(this.#routeStatusData[0]); /** * Gets a boolean value that indicates whether the current URL matches none of the route - * patterns. + * patterns, therefore enabling fallback behavior. */ - noMatches = $derived(this.#routeStatusData[1]); + fallback = $derived(this.#routeStatusData[1]); /** * Initializes a new instance of this class with the specified options. */ @@ -121,24 +121,24 @@ export class RouterEngine { throw new Error("The routing library hasn't been initialized. Execute init() before creating routers."); } if (isRouterEngine(parentOrOpts)) { - this.#resolvedHash = parentOrOpts.#resolvedHash; + this.resolvedHash = parentOrOpts.resolvedHash; this.#parent = parentOrOpts; } else { this.#parent = parentOrOpts?.parent; - this.#resolvedHash = this.#parent && parentOrOpts?.hash === undefined ? this.#parent.#resolvedHash : resolveHashValue(parentOrOpts?.hash); - if (this.#parent && this.#resolvedHash !== this.#parent.#resolvedHash) { + this.resolvedHash = this.#parent && parentOrOpts?.hash === undefined ? this.#parent.resolvedHash : resolveHashValue(parentOrOpts?.hash); + if (this.#parent && this.resolvedHash !== this.#parent.resolvedHash) { throw new Error("The parent router's hash mode must match the child router's hash mode."); } - if (routingOptions.hashMode === 'multi' && this.#resolvedHash && typeof this.#resolvedHash !== 'string') { + if (routingOptions.hashMode === 'multi' && this.resolvedHash && typeof this.resolvedHash !== 'string') { throw new Error("The specified hash value is not valid for the 'multi' hash mode. Either don't specify a hash for path routing, or correct the hash value."); } - if (routingOptions.hashMode !== 'multi' && typeof this.#resolvedHash === 'string') { + if (routingOptions.hashMode !== 'multi' && typeof this.resolvedHash === 'string') { throw new Error("A hash path ID was given, but is only allowed when the library's hash mode has been set to 'multi'."); } } - assertAllowedRoutingMode(this.#resolvedHash); - this.#routeHelper = new RouteHelper(this.#resolvedHash); + assertAllowedRoutingMode(this.resolvedHash); + this.#routeHelper = new RouteHelper(this.resolvedHash); if (traceOptions.routerHierarchy) { registerRouter(this); this.#cleanup = true; @@ -158,7 +158,7 @@ export class RouterEngine { * This is a shortcut for `location.state`. */ get state() { - return location.getState(this.#resolvedHash); + return location.getState(this.resolvedHash); } /** * Gets or sets the router's base path. diff --git a/src/lib/types.ts b/src/lib/types.ts index 126d4e7..044fd74 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -428,10 +428,10 @@ export type ActiveState = { * Defines the type of function accepted by the `Fallback` component via its `when` property. * * @param routeStatus The current route status data from the parent router. - * @param noMatches The value that the parent router has calculated as per standard fallback logic. + * @param fallback The value that the parent router has calculated as per standard fallback logic. * @returns `true` if the fallback content should be shown; `false` to prevent content from being shown. */ -export type WhenPredicate = (routeStatus: Record, noMatches: boolean) => boolean; +export type WhenPredicate = (routeStatus: Record, fallback: boolean) => boolean; /** * Defines the shape of logger objects that can be given to this library during initialization.