diff --git a/.changeset/path-matcher-segment-wildcard.md b/.changeset/path-matcher-segment-wildcard.md new file mode 100644 index 00000000000..aedd7fca173 --- /dev/null +++ b/.changeset/path-matcher-segment-wildcard.md @@ -0,0 +1,6 @@ +--- +'@clerk/shared': patch +'@clerk/nextjs': patch +--- + +`createPathMatcher()` and `createRouteMatcher()` route suggestions now use the `:path*` subtree form (e.g. `/dashboard/:path*`) instead of `(.*)`. Unlike `/dashboard(.*)`, which also matches sibling routes such as `/dashboardxyz`, `/dashboard/:path*` matches only `/dashboard` and its path-segment subtree. The new suggestion type is exported as `WithPathSegmentWildcard`; the existing `WithPathPatternWildcard` type is unchanged (now deprecated), and `(.*)` patterns keep working. This only changes the type-level autocomplete suggestion. diff --git a/packages/nextjs/src/server/routeMatcher.ts b/packages/nextjs/src/server/routeMatcher.ts index 428fc3e9dec..15dd32cc8aa 100644 --- a/packages/nextjs/src/server/routeMatcher.ts +++ b/packages/nextjs/src/server/routeMatcher.ts @@ -1,10 +1,10 @@ -import { createPathMatcher, type WithPathPatternWildcard } from '@clerk/shared/pathMatcher'; +import { createPathMatcher, type WithPathSegmentWildcard } from '@clerk/shared/pathMatcher'; import type { Autocomplete } from '@clerk/shared/types'; import type Link from 'next/link'; import type { NextRequest } from 'next/server'; type NextTypedRoute['0']['href']> = T extends string ? T : never; -type RouteMatcherWithNextTypedRoutes = Autocomplete | NextTypedRoute>; +type RouteMatcherWithNextTypedRoutes = Autocomplete | NextTypedRoute>; export type RouteMatcherParam = | Array diff --git a/packages/shared/src/pathMatcher.ts b/packages/shared/src/pathMatcher.ts index d5e6e49f516..dc9c901f4a1 100644 --- a/packages/shared/src/pathMatcher.ts +++ b/packages/shared/src/pathMatcher.ts @@ -1,8 +1,26 @@ import { pathToRegexp } from './pathToRegexp'; import type { Autocomplete } from './types'; +/** + * @deprecated Prefer {@link WithPathSegmentWildcard}; `(.*)` also matches sibling routes + * (e.g. `/dashboard(.*)` matches `/dashboardxyz`). + */ export type WithPathPatternWildcard = `${T & string}(.*)`; -export type PathPattern = Autocomplete; + +type StripTrailingSlash = T extends '/' + ? T + : T extends `${infer Prefix}/` + ? StripTrailingSlash + : T; + +/** + * Suggests the `:path*` subtree form (e.g. `/dashboard/:path*`), which matches on + * path-segment boundaries. `/` is special-cased to `/:path*` to avoid a malformed `//:path*`. + */ +export type WithPathSegmentWildcard = T extends '/' + ? '/:path*' + : `${StripTrailingSlash}/:path*`; +export type PathPattern = Autocomplete; export type PathMatcherParam = Array | RegExp | PathPattern; export class MalformedURLError extends Error {