From 1a6f8ecbf56a5a19962b7af7a204c44fd3cc38fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ramirez=20Vargas=2C=20Jos=C3=A9=20Pablo?= Date: Thu, 27 Nov 2025 00:12:03 -0600 Subject: [PATCH] feat!: Simplify RouteInfo type --- src/lib/Redirector.svelte.test.ts | 48 +++++++++---------- src/lib/Route/Route.svelte | 15 +++--- src/lib/Route/Route.svelte.test.ts | 52 ++++++++++---------- src/lib/kernel/RouteHelper.svelte.test.ts | 16 +++---- src/lib/kernel/RouteHelper.svelte.ts | 9 ++-- src/lib/kernel/RouterEngine.svelte.test.ts | 18 +++---- src/lib/kernel/RouterEngine.svelte.ts | 13 +---- src/lib/types.ts | 56 ++++++---------------- 8 files changed, 94 insertions(+), 133 deletions(-) diff --git a/src/lib/Redirector.svelte.test.ts b/src/lib/Redirector.svelte.test.ts index 77b917f..1ee7508 100644 --- a/src/lib/Redirector.svelte.test.ts +++ b/src/lib/Redirector.svelte.test.ts @@ -2,7 +2,7 @@ import { afterAll, afterEach, beforeAll, describe, expect, vi, type MockInstance import { testWithEffect as test } from "$test/testWithEffect.svelte.js"; import { ALL_HASHES, ROUTING_UNIVERSES } from "$test/test-utils.js"; import { init } from "$lib/init.js"; -import type { Hash, PatternRouteInfo, RedirectedRouteInfo } from "$lib/types.js"; +import type { Hash, RouteInfo, RedirectedRouteInfo } from "$lib/types.js"; import { resolveHashValue } from "./kernel/resolveHashValue.js"; import { Redirector } from "./Redirector.svelte.js"; import { location } from "./kernel/Location.js"; @@ -48,27 +48,27 @@ ROUTING_UNIVERSES.forEach((universe) => { })[] = [ { triggerUrl: '/old/path', - pattern: '/old/path', + path: '/old/path', href: '/new/path', expectedPath: '/new/path', text: "Static pattern; static href" }, { - pattern: '/old-path/:id', + path: '/old-path/:id', triggerUrl: '/old-path/123', expectedPath: '/new-path/123', href: (rp) => `/new-path/${rp?.id}`, text: "Parameterized pattern; dynamic href" }, { - pattern: '/old-path/*', + path: '/old-path/*', triggerUrl: '/old-path/any/number/of/segments', expectedPath: '/new-path/any/number/of/segments', href: (rp) => `/new-path${rp?.rest}`, text: "Rest parameter; dynamic href" }, { - pattern: '/conditional/:id', + path: '/conditional/:id', triggerUrl: '/conditional/123', expectedPath: '/allowed/123', href: (rp) => `/allowed/${rp?.id}`, @@ -104,7 +104,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. redirector.redirections.push({ - pattern: '/old-path', + path: '/old-path', href: '/new-path', goTo: true, }); @@ -123,7 +123,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. redirector.redirections.push({ - pattern: '/conditional/:id', + path: '/conditional/:id', href: '/not-allowed', and: (rp) => (rp?.id as number) > 100, }); @@ -143,11 +143,11 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. redirector.redirections.push( { - pattern: '/multi/*', + path: '/multi/*', href: '/first-match', }, { - pattern: '/multi/test', + path: '/multi/test', href: '/second-match', } ); @@ -166,7 +166,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. redirector.redirections.push({ - pattern: '/test-replace', + path: '/test-replace', href: '/replaced', }); flushSync(); @@ -185,7 +185,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. redirector.redirections.push({ - pattern: '/with-options', + path: '/with-options', href: '/target', options: { preserveQuery: true, state: { custom: 'data' } } }); @@ -205,7 +205,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Add initial redirection that won't match redirector.redirections.push({ - pattern: '/different-path', + path: '/different-path', href: '/not-relevant' }); flushSync(); @@ -213,7 +213,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. redirector.redirections.push({ - pattern: '/test-reactivity', + path: '/test-reactivity', href: '/should-redirect' }); flushSync(); @@ -227,14 +227,14 @@ ROUTING_UNIVERSES.forEach((universe) => { location.navigate('/test-reactivity', { hash: universe.hash }); const redirector = new Redirector(universe.hash); redirector.redirections.push({ - pattern: '/different-path', + path: '/different-path', href: '/punch-line' }); flushSync(); navigateSpy.mockClear(); // Act. - (redirector.redirections[0] as PatternRouteInfo).pattern = '/test-reactivity'; + redirector.redirections[0].path = '/test-reactivity'; flushSync(); // Assert. @@ -251,7 +251,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. const redirector = new Redirector(universe.hash, { replace: false }); redirector.redirections.push({ - pattern: '/explicit-hash', + path: '/explicit-hash', href: '/redirected-explicit' }); flushSync(); @@ -273,7 +273,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. const redirector = new Redirector(universe.hash, { replace: true }); redirector.redirections.push({ - pattern: '/hash-resolution-test', + path: '/hash-resolution-test', href: '/hash-resolved' }); flushSync(); @@ -299,7 +299,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. const redirector = new Redirector({ replace: false }); redirector.redirections.push({ - pattern: '/default-hash', + path: '/default-hash', href: '/redirected-default' }); flushSync(); @@ -319,7 +319,7 @@ ROUTING_UNIVERSES.forEach((universe) => { // Act. const redirector = new Redirector({}); // Empty options object redirector.redirections.push({ - pattern: '/minimal-options', + path: '/minimal-options', href: '/redirected-minimal' }); flushSync(); @@ -373,7 +373,7 @@ describe("Options-Only Constructor with Matching Library Defaults", () => { // Act. const redirector = new Redirector({ replace: false }); redirector.redirections.push({ - pattern: '/hash-default-test', + path: '/hash-default-test', href: '/hash-redirected' }); flushSync(); @@ -414,7 +414,7 @@ describe("Options-Only Constructor with Matching Library Defaults", () => { // Act. const redirector = new Redirector({ replace: false }); redirector.redirections.push({ - pattern: '/multi-hash-default-test', + path: '/multi-hash-default-test', href: '/multi-hash-redirected' }); flushSync(); @@ -461,7 +461,7 @@ describe("Cross-universe Redirection", () => { // Act. redirector.redirections.push({ - pattern: '/old-path-route', + path: '/old-path-route', href: '/new-hash-route', options: { hash: true } }); @@ -482,7 +482,7 @@ describe("Cross-universe Redirection", () => { // Act. redirector.redirections.push({ - pattern: '/old-hash-route', + path: '/old-hash-route', href: '/new-path-route', options: { hash: false } // Target path universe }); @@ -555,7 +555,7 @@ describe("Cross-universe Redirection", () => { // Act. redirector.redirections.push({ - pattern: '/old-path-route', + path: '/old-path-route', href: '/new-hash-route', options: { hash: tc.destinationHash } }); diff --git a/src/lib/Route/Route.svelte b/src/lib/Route/Route.svelte index fc321aa..296ef13 100644 --- a/src/lib/Route/Route.svelte +++ b/src/lib/Route/Route.svelte @@ -128,15 +128,12 @@ return; } // svelte-ignore ownership_invalid_mutation - untrack(() => router.routes)[key] = - path instanceof RegExp - ? { regex: path, and: and as AndUntyped, ignoreForFallback } - : { - pattern: path, - and: and as AndUntyped, - ignoreForFallback, - caseSensitive - }; + untrack(() => router.routes)[key] = { + path, + and: and as AndUntyped, + ignoreForFallback, + caseSensitive + }; return () => { // svelte-ignore ownership_invalid_mutation delete untrack(() => router.routes)[key]; diff --git a/src/lib/Route/Route.svelte.test.ts b/src/lib/Route/Route.svelte.test.ts index 9fc4bff..c743efd 100644 --- a/src/lib/Route/Route.svelte.test.ts +++ b/src/lib/Route/Route.svelte.test.ts @@ -7,7 +7,8 @@ import { init } from "$lib/init.js"; import { location } from "$lib/kernel/Location.js"; import TestRouteWithRouter from "$test/TestRouteWithRouter.svelte"; import { resetRoutingOptions, setRoutingOptions } from "$lib/kernel/options.js"; -import type { ExtendedRoutingOptions, RouteChildrenContext } from "$lib/types.js"; +import type { ExtendedRoutingOptions, RouteChildrenContext, RouteParamsRecord } from "$lib/types.js"; +import type { RouterEngine } from "$lib/kernel/RouterEngine.svelte.js"; function basicRouteTests(setup: ReturnType) { beforeEach(() => { @@ -88,7 +89,7 @@ function routePropsTests(setup: ReturnType) { test("Should register string pattern route.", async () => { // Arrange. const { hash, context } = setup; - let routerInstance: any; + let routerInstance: RouterEngine; // Act. render(TestRouteWithRouter, { @@ -103,16 +104,16 @@ function routePropsTests(setup: ReturnType) { }); // Assert. - const route = routerInstance?.routes["pattern-route"]; + const route = routerInstance!?.routes["pattern-route"]; expect(route).toBeDefined(); - expect(route.pattern).toBe("/user/:id"); + expect(route.path).toBe("/user/:id"); }); test("Should register regex route.", async () => { // Arrange. const { hash, context } = setup; const regex = /^\/user\/(?\d+)$/; - let routerInstance: any; + let routerInstance: RouterEngine; // Act. render(TestRouteWithRouter, { @@ -127,9 +128,9 @@ function routePropsTests(setup: ReturnType) { }); // Assert. - const route = routerInstance?.routes["regex-route"]; + const route = routerInstance!?.routes["regex-route"]; expect(route).toBeDefined(); - expect(route.regex).toBe(regex); + expect(route.path).toBe(regex); }); test("Should register route with and function.", async () => { @@ -216,7 +217,7 @@ function routeParamsTests(setup: ReturnType) { test("Should bind route parameters.", async () => { // Arrange. const { hash, context } = setup; - let routerInstance: any; + let routerInstance: RouterEngine; // Act. render(TestRouteWithRouter, { @@ -231,15 +232,15 @@ function routeParamsTests(setup: ReturnType) { }); // Assert - Route should be registered with parameter pattern - const route = routerInstance?.routes["param-route"]; + const route = routerInstance!?.routes["param-route"]; expect(route).toBeDefined(); - expect(route.pattern).toBe("/user/:id"); + expect(route.path).toBe("/user/:id"); }); test("Should handle route with rest parameter.", async () => { // Arrange. const { hash, context } = setup; - let routerInstance: any; + let routerInstance: RouterEngine; // Act. render(TestRouteWithRouter, { @@ -254,9 +255,9 @@ function routeParamsTests(setup: ReturnType) { }); // Assert. - const route = routerInstance?.routes["rest-route"]; + const route = routerInstance!?.routes["rest-route"]; expect(route).toBeDefined(); - expect(route.pattern).toBe("/files/*"); + expect(route.path).toBe("/files/*"); }); } @@ -274,7 +275,7 @@ function routeReactivityTests(setup: ReturnType) { const { hash, context } = setup; const initialPath = "/initial"; const updatedPath = "/updated"; - let routerInstance: any; + let routerInstance: RouterEngine; const { rerender } = render(TestRouteWithRouter, { props: { @@ -287,8 +288,8 @@ function routeReactivityTests(setup: ReturnType) { context }); - const initialRoute = routerInstance?.routes["reactive-route"]; - expect(initialRoute?.pattern).toBe(initialPath); + const initialRoute = routerInstance!?.routes["reactive-route"]; + expect(initialRoute?.path).toBe(initialPath); // Act. await rerender({ @@ -300,8 +301,8 @@ function routeReactivityTests(setup: ReturnType) { }); // Assert. - const updatedRoute = routerInstance?.routes["reactive-route"]; - expect(updatedRoute?.pattern).toBe(updatedPath); + const updatedRoute = routerInstance!?.routes["reactive-route"]; + expect(updatedRoute?.path).toBe(updatedPath); }); test("Should update ignoreForFallback when prop changes (rerender).", async () => { @@ -665,18 +666,17 @@ function routeBindingTestsForUniverse(setup: ReturnType { // Arrange. const { hash, context } = setup; - let capturedParams: any; + let capturedParams: RouteParamsRecord; const paramsSetter = vi.fn((value) => { capturedParams = value; }); // Act. - render(TestRouteWithRouter, { + render(Route, { props: { hash, - routeKey: "test-route", - routePath: "/files/*", + key: "test-route", + path: "/files/*", get params() { return capturedParams; }, set params(value) { paramsSetter(value); }, - children: createTestSnippet('
File path: {params?.rest}
') }, context }); @@ -692,10 +692,10 @@ function routeBindingTestsForUniverse(setup: ReturnType { }); + flushSync(); // Assert. - expect(paramsSetter).toHaveBeenCalled(); + expect(paramsSetter).toHaveBeenCalledTimes(2); // Multi-hash routing (MHR) has different behavior and may not work with simple URLs in tests if (ru.text === 'MHR') { @@ -703,7 +703,7 @@ function routeBindingTestsForUniverse(setup: ReturnType { test("Should set ignoreForFallback to false by default when not provided.", () => { // Arrange - const routeInfo = { pattern: "/test" }; + const routeInfo = { path: "/test" }; // Act const result = routeHelper.parseRoutePattern(routeInfo); @@ -192,7 +192,7 @@ describe("RouteHelper", () => { { pattern: "/api/v1/users", expectedRegex: "^\\/api\\/v1\\/users$", description: "multi-segment path" } ])("Should create correct regex for $description .", ({ pattern, expectedRegex }) => { // Arrange - const routeInfo = { pattern }; + const routeInfo = { path: pattern }; // Act const result = routeHelper.parseRoutePattern(routeInfo); @@ -210,7 +210,7 @@ describe("RouteHelper", () => { { pattern: "/user-:id/profile", expectedRegex: "^\\/user-(?[^/]+)\\/profile$", description: "parameter with prefix and suffix" } ])("Should create correct regex for $description .", ({ pattern, expectedRegex }) => { // Arrange - const routeInfo = { pattern }; + const routeInfo = { path: pattern }; // Act const result = routeHelper.parseRoutePattern(routeInfo); @@ -226,7 +226,7 @@ describe("RouteHelper", () => { { pattern: "/:category?/:id", expectedRegex: "^\\/?(?:(?[^/]+))?\\/(?[^/]+)$", description: "optional then required parameters" } ])("Should create correct regex for $description .", ({ pattern, expectedRegex }) => { // Arrange - const routeInfo = { pattern }; + const routeInfo = { path: pattern }; // Act const result = routeHelper.parseRoutePattern(routeInfo); @@ -241,7 +241,7 @@ describe("RouteHelper", () => { { pattern: "/*", expectedRegex: "^(?.*)$", description: "root rest parameter" } ])("Should create correct regex for $description .", ({ pattern, expectedRegex }) => { // Arrange - const routeInfo = { pattern }; + const routeInfo = { path: pattern }; // Act const result = routeHelper.parseRoutePattern(routeInfo); @@ -255,7 +255,7 @@ describe("RouteHelper", () => { { caseSensitive: false, expectedFlags: "i", description: "case insensitive" } ])("Should create regex with correct flags for $description .", ({ caseSensitive, expectedFlags }) => { // Arrange - const routeInfo = { pattern: "/test", caseSensitive }; + const routeInfo = { path: "/test", caseSensitive }; // Act const result = routeHelper.parseRoutePattern(routeInfo); @@ -288,7 +288,7 @@ describe("RouteHelper", () => { { basePath: "/api/", pattern: "/users/", expectedRegex: "^\\/api\\/users$", description: "trailing slashes handled" } ])("Should join base path correctly for $description .", ({ basePath, pattern, expectedRegex }) => { // Arrange - const routeInfo = { pattern }; + const routeInfo = { path: pattern }; // Act const result = routeHelper.parseRoutePattern(routeInfo, basePath); @@ -310,7 +310,7 @@ describe("RouteHelper", () => { { pattern: "/back\\slash", expectedRegex: "^\\/back\\\\slash$", description: "backslash" } ])("Should escape $description correctly .", ({ pattern, expectedRegex }) => { // Arrange - const routeInfo = { pattern }; + const routeInfo = { path: pattern }; // Act const result = routeHelper.parseRoutePattern(routeInfo); diff --git a/src/lib/kernel/RouteHelper.svelte.ts b/src/lib/kernel/RouteHelper.svelte.ts index 96e58ef..e6d868a 100644 --- a/src/lib/kernel/RouteHelper.svelte.ts +++ b/src/lib/kernel/RouteHelper.svelte.ts @@ -1,5 +1,5 @@ import { joinPaths } from "$lib/public-utils.js"; -import type { AndUntyped, Hash, PatternRouteInfo, RouteParamsRecord } from "$lib/types.js"; +import type { AndUntyped, Hash, RouteInfo, RouteParamsRecord } from "$lib/types.js"; import { noTrailingSlash } from "$lib/utils.js"; import { location } from "./Location.js"; @@ -53,14 +53,15 @@ export class RouteHelper { * @param routeInfo Pattern route information to parse. * @returns An object with the regular expression, the optional predicate function, and the ignoreForFallback flag. */ - parseRoutePattern(routeInfo: PatternRouteInfo, basePath?: string): { regex?: RegExp; and?: AndUntyped; ignoreForFallback: boolean; } { - if (!routeInfo.pattern) { + parseRoutePattern(routeInfo: RouteInfo, basePath?: string): { regex?: RegExp; and?: AndUntyped; ignoreForFallback: boolean; } { + if (typeof routeInfo.path !== 'string') { return { + regex: routeInfo.path, and: routeInfo.and, ignoreForFallback: !!routeInfo.ignoreForFallback } } - const fullPattern = joinPaths(basePath || '/', routeInfo.pattern === '/' ? '' : routeInfo.pattern); + const fullPattern = joinPaths(basePath || '/', routeInfo.path === '/' ? '' : routeInfo.path); const escapedPattern = escapeRegExp(fullPattern); let regexPattern = escapedPattern.replace(identifierRegex, (_match, startingSlash, paramName, optional, offset) => { let regex = paramValueRegex.replace(paramNamePlaceholder, paramName); diff --git a/src/lib/kernel/RouterEngine.svelte.test.ts b/src/lib/kernel/RouterEngine.svelte.test.ts index f2bf8f3..030e668 100644 --- a/src/lib/kernel/RouterEngine.svelte.test.ts +++ b/src/lib/kernel/RouterEngine.svelte.test.ts @@ -3,7 +3,7 @@ import { routePatternsKey, RouterEngine } from "./RouterEngine.svelte.js"; import { init } from "../init.js"; import { registerRouter } from "./trace.svelte.js"; import { location } from "./Location.js"; -import type { State, RouteInfo, ExtendedRoutingOptions, PatternRouteInfo } from "../types.js"; +import type { State, RouteInfo, ExtendedRoutingOptions } from "../types.js"; import { setupBrowserMocks, addRoutes, ROUTING_UNIVERSES, ALL_HASHES } from "$test/test-utils.js"; import { resetRoutingOptions, setRoutingOptions } from "./options.js"; @@ -312,7 +312,7 @@ ROUTING_UNIVERSES.forEach(universe => { // Arrange. const router = new RouterEngine({ hash: universe.hash }); const route: RouteInfo = { - pattern: '/path', + path: '/path', caseSensitive: false, }; expect(Object.keys(router.routes).length).toBe(0); @@ -328,7 +328,7 @@ ROUTING_UNIVERSES.forEach(universe => { // Arrange. const router = new RouterEngine({ hash: universe.hash }); const route: RouteInfo = { - pattern: '/path', + path: '/path', caseSensitive: false, }; router.routes['route'] = route; @@ -345,14 +345,14 @@ ROUTING_UNIVERSES.forEach(universe => { // Arrange. const router = new RouterEngine({ hash: universe.hash }); const route: RouteInfo = { - pattern: '/path', + path: '/path', caseSensitive: false, }; router.routes['route'] = route; expect(Object.keys(router.routes).length).toBe(1); // Act. - router.routes['route'].pattern = '/other'; + router.routes['route'].path = '/other'; // Assert. expect(router[routePatternsKey]().has('route')).toBe(true); @@ -475,7 +475,7 @@ ROUTING_UNIVERSES.forEach(universe => { // Arrange. const router = new RouterEngine({ hash: universe.hash }); const route: RouteInfo = { - pattern, + path: pattern, caseSensitive: false, }; router.routes['route'] = route; @@ -531,7 +531,7 @@ ROUTING_UNIVERSES.forEach(universe => { // Arrange. const router = new RouterEngine({ hash: universe.hash }); const route: RouteInfo = { - pattern, + path: pattern, caseSensitive: false, }; router.routes['route'] = route; @@ -645,7 +645,7 @@ ROUTING_UNIVERSES.forEach(universe => { // Arrange. const router = new RouterEngine({ hash: universe.hash }); const route: RouteInfo = { - pattern, + path: pattern, caseSensitive: false, }; router.routes['route'] = route; @@ -681,7 +681,7 @@ ROUTING_UNIVERSES.forEach(universe => { // Act. router.routes['route'] = { - pattern: '/:one/:two?', + path: '/:one/:two?', caseSensitive: false, }; diff --git a/src/lib/kernel/RouterEngine.svelte.ts b/src/lib/kernel/RouterEngine.svelte.ts index 44611de..f37baef 100644 --- a/src/lib/kernel/RouterEngine.svelte.ts +++ b/src/lib/kernel/RouterEngine.svelte.ts @@ -1,4 +1,4 @@ -import type { AndUntyped, Hash, RegexRouteInfo, RouteInfo, RouteStatus } from "../types.js"; +import type { AndUntyped, Hash, RouteInfo, RouteStatus } from "../types.js"; import { traceOptions, registerRouter, unregisterRouter } from "./trace.svelte.js"; import { location } from "./Location.js"; import { routingOptions } from "./options.js"; @@ -32,10 +32,6 @@ function isRouterEngine(obj: unknown): obj is RouterEngine { return obj instanceof RouterEngine; } -function routeInfoIsRegexInfo(info: unknown): info is RegexRouteInfo { - return (info as RegexRouteInfo).regex instanceof RegExp; -} - /** * Internal key used to access the route patterns of a router engine. */ @@ -75,12 +71,7 @@ export class RouterEngine { * This is done separately so it is memoized based on the route definitions and the base path only. */ #routePatterns = $derived(Object.entries(this.routes).reduce((map, [key, route]) => { - map.set( - key, routeInfoIsRegexInfo(route) ? - { regex: route.regex, and: route.and, ignoreForFallback: !!route.ignoreForFallback } : - this.#routeHelper.parseRoutePattern(route, this.basePath) - ); - return map; + return map.set(key, this.#routeHelper.parseRoutePattern(route, this.basePath)); }, new Map())); [routePatternsKey]() { diff --git a/src/lib/types.ts b/src/lib/types.ts index e689fbf..126d4e7 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -109,8 +109,7 @@ export type RouteChildrenContext = RouterChildre */ export type LinkChildrenContext = Omit & { /** - * Holds the route status data for all routes managed by the parent router, if said parent - * exists. + * Holds the route status data for all routes managed by the parent router, if said parent exists. */ rs?: RouteStatusRecord | undefined; }; @@ -121,60 +120,33 @@ export type LinkChildrenContext = Omit & { export type AndUntyped = (params: RouteParamsRecord | undefined) => boolean; /** - * Defines the core properties of a route definition. + * Defines the shape of a route definition. */ -export type CoreRouteInfo = { - /** - * An optional predicate function that is used to further test if the route should be matched. - */ - and?: AndUntyped; +export type RouteInfo = { /** - * A Boolean value that determines if the route's match status should be ignored for fallback purposes. + * The path to match. It can contain route parameters in the form of `:paramName` when written as a string, or it + * can be a regular expression. */ - ignoreForFallback?: boolean; -} - -/** - * Defines the shape of a route definition that is based on a regular expression. - */ -export type RegexRouteInfo = CoreRouteInfo & { + path?: string | RegExp; /** - * The regular expression that the URL's pathname must match. - * - * Any capturing groups in the regular expression are treated as route parameters. + * Whether the path is case-sensitive (when written as a string only). + * @default false */ - regex: RegExp; -}; - -/** - * Defines the shape of a route definition that is based on a string pattern. - */ -export type PatternRouteInfo = CoreRouteInfo & { + caseSensitive?: boolean; /** - * The pattern that the URL's pathname must match. It can contain route parameters in the form of `:paramName`. + * An optional predicate function that is used to further test if the route should be matched. */ - pattern?: string; + and?: AndUntyped; /** - * Whether the pattern is case-sensitive. - * @default false + * A Boolean value that determines if the route's match status should be ignored for fallback purposes. */ - caseSensitive?: boolean; + ignoreForFallback?: boolean; }; -/** - * Defines the shape of a route definition. - */ -export type RouteInfo = RegexRouteInfo | PatternRouteInfo; - -/** - * Distributes the Omit over unions. - */ -type NoIgnoreForFallback = T extends any ? Omit : never; - /** * Defines the shape of redirection information used by the Redirector class. */ -export type RedirectedRouteInfo = NoIgnoreForFallback & { +export type RedirectedRouteInfo = Omit & { /** * The HREF to navigate to (via `location.navigate()` or `location.goTo()`). It can be a string or a function that * receives the matched route parameters and returns a string.