diff --git a/README.md b/README.md index abe3ba2..96e1c52 100644 --- a/README.md +++ b/README.md @@ -221,9 +221,9 @@ useRoute("/orders/*?"); // regex for matching complex patterns, // matches "/hello:123" -useRoute(/[/]([a-z]+):([0-9]+)[/]?/); +useRoute(/^[/]([a-z]+):([0-9]+)[/]?$/); // and with named capture groups -useRoute(/[/](?[a-z]+):(?[0-9]+)[/]?/); +useRoute(/^[/](?[a-z]+):(?[0-9]+)[/]?$/); ``` The second item in the pair `params` is an object with parameters or null if there was no match. For wildcard segments the parameter name is `"*"`: @@ -335,7 +335,7 @@ const User = () => { params[0]; // "1" }; -[0-9]+)[/]?/} component={User}> /> +[0-9]+)[/]?$/} component={User}> /> ``` ### `useSearch`: query strings @@ -442,9 +442,10 @@ If you call `useLocation()` inside the last route, it will return `/orders` and ``` -**Note:** The `nest` prop has no effect on regexes passed in. -It will only determine if nested routes will match the rest of path or match against the same path. -To make a strict path regex, use regex techniques like `[/]?$` (this matches an optional end slash and the end of the string). +**Note:** The `nest` prop does not alter the regex passed into regex paths. +Instead, the `nest` prop will only determine if nested routes will match against the rest of path or the same path. +To make a strict path regex, use a regex pattern like `/^[/](your pattern)[/]?$/` (this matches an optional end slash and the end of the string). +To make a nestable regex, use a regex pattern like `/^[/](your pattern)(?=$|[/])/` (this matches either the end of the string or a slash for future segments). ### `` diff --git a/packages/wouter-preact/types/index.d.ts b/packages/wouter-preact/types/index.d.ts index 543e5f8..f8de84f 100644 --- a/packages/wouter-preact/types/index.d.ts +++ b/packages/wouter-preact/types/index.d.ts @@ -10,6 +10,7 @@ import { import { Path, + PathPattern, BaseLocationHook, HookReturnValue, HookNavigationOptions, @@ -59,7 +60,7 @@ export interface RouteComponentProps { export interface RouteProps< T extends DefaultParams | undefined = undefined, - RoutePath extends Path | RegExp = Path | RegExp + RoutePath extends PathPattern = PathPattern > { children?: | (( @@ -85,7 +86,7 @@ export interface RouteProps< export function Route< T extends DefaultParams | undefined = undefined, - RoutePath extends Path | RegExp = Path | RegExp + RoutePath extends PathPattern = PathPattern >(props: RouteProps): ReturnType; /* @@ -155,7 +156,7 @@ export function useRouter(): RouterObject; export function useRoute< T extends DefaultParams | undefined = undefined, - RoutePath extends Path | RegExp = Path | RegExp + RoutePath extends PathPattern = PathPattern >( pattern: RoutePath ): Match< diff --git a/packages/wouter-preact/types/location-hook.d.ts b/packages/wouter-preact/types/location-hook.d.ts index ab3ca90..202ccb2 100644 --- a/packages/wouter-preact/types/location-hook.d.ts +++ b/packages/wouter-preact/types/location-hook.d.ts @@ -4,6 +4,8 @@ export type Path = string; +export type PathPattern = string | RegExp; + export type SearchString = string; // the base useLocation hook type. Any custom hook (including the diff --git a/packages/wouter/src/index.js b/packages/wouter/src/index.js index 4d63505..99262c3 100644 --- a/packages/wouter/src/index.js +++ b/packages/wouter/src/index.js @@ -80,12 +80,10 @@ export const useSearch = () => { const matchRoute = (parser, route, path, loose) => { // if the input is a regexp, skip parsing - const { pattern, keys } = (() => { - if (route instanceof RegExp) { - return { keys: false, pattern: route }; - } - return parser(route || "*", loose); - })(); + const { pattern, keys } = + route instanceof RegExp + ? { keys: false, pattern: route } + : parser(route || "*", loose); // array destructuring loses keys, so this is done in two steps const result = pattern.exec(path) || []; diff --git a/packages/wouter/test/router.test-d.tsx b/packages/wouter/test/router.test-d.tsx index 6604554..790f735 100644 --- a/packages/wouter/test/router.test-d.tsx +++ b/packages/wouter/test/router.test-d.tsx @@ -1,6 +1,13 @@ import { ComponentProps } from "react"; import { it, expectTypeOf } from "vitest"; -import { Router, Route, BaseLocationHook, useRouter } from "wouter"; +import { + Router, + Route, + BaseLocationHook, + useRouter, + Parser, + Path, +} from "wouter"; it("should have at least one child", () => { // @ts-expect-error @@ -64,6 +71,17 @@ it("accepts `hrefs` function for transforming href strings", () => { ; }); +it("accepts `parser` function for generating regular expressions", () => { + const parser: Parser = (path: Path, loose?: boolean) => { + return { + pattern: new RegExp(`^${path}${loose === true ? "(?=$|[/])" : "[/]$"}`), + keys: [], + }; + }; + + this is a valid router; +}); + it("does not accept other props", () => { const router = useRouter(); diff --git a/packages/wouter/types/index.d.ts b/packages/wouter/types/index.d.ts index d66ed92..c9c0faf 100644 --- a/packages/wouter/types/index.d.ts +++ b/packages/wouter/types/index.d.ts @@ -14,6 +14,7 @@ import { import { Path, + PathPattern, BaseLocationHook, HookReturnValue, HookNavigationOptions, @@ -63,7 +64,7 @@ export interface RouteComponentProps { export interface RouteProps< T extends DefaultParams | undefined = undefined, - RoutePath extends Path | RegExp = Path | RegExp + RoutePath extends PathPattern = PathPattern > { children?: | (( @@ -89,7 +90,7 @@ export interface RouteProps< export function Route< T extends DefaultParams | undefined = undefined, - RoutePath extends Path | RegExp = Path | RegExp + RoutePath extends PathPattern = PathPattern >(props: RouteProps): ReturnType; /* @@ -162,7 +163,7 @@ export function useRouter(): RouterObject; export function useRoute< T extends DefaultParams | undefined = undefined, - RoutePath extends Path | RegExp = Path | RegExp + RoutePath extends PathPattern = PathPattern >( pattern: RoutePath ): Match< diff --git a/packages/wouter/types/location-hook.d.ts b/packages/wouter/types/location-hook.d.ts index 9bcfc3c..0d1aac1 100644 --- a/packages/wouter/types/location-hook.d.ts +++ b/packages/wouter/types/location-hook.d.ts @@ -4,6 +4,8 @@ export type Path = string; +export type PathPattern = string | RegExp; + export type SearchString = string; // the base useLocation hook type. Any custom hook (including the