diff --git a/.prettierrc b/.prettierrc index a7056f5..7acf4f0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,4 @@ { - "semi": false -} \ No newline at end of file + "semi": false, + "printWidth": 100 +} diff --git a/package.json b/package.json index 07e5956..8ac0ab2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "route-fn", - "version": "1.3.0", + "version": "1.3.1", "description": "A simple utility function for typesafe urls.", "license": "MIT", "type": "module", diff --git a/src/index.ts b/src/index.ts index 61a2650..4f1c3e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,14 +15,8 @@ export function createRouteFn(routes: Routes) { type DynamicRouteId = ExtractDynamicRouteIds[number] type StaticRouteId = ExtractStaticRouteIds[number] - function fn( - id: Id, - params: RouteParams - ): string - function fn( - id: Id, - params?: RouteParams - ): string + function fn(id: Id, params: RouteParams): string + function fn(id: Id, params?: RouteParams): string function fn( id: Id, params?: RouteParams | SearchParams @@ -70,9 +64,7 @@ export function createRouteFn(routes: Routes) { return 0 }) - const patterns = sortedRoutes.map( - (route) => new URLPattern({ pathname: route }) - ) + const patterns = sortedRoutes.map((route) => new URLPattern({ pathname: route })) for (const pattern of patterns) { const patternResult = pattern.exec(input) @@ -86,13 +78,11 @@ export function createRouteFn(routes: Routes) { fn.test = function ( url: string, - routeIds: - | StaticRouteId - | DynamicRouteId - | (StaticRouteId | DynamicRouteId)[] + routeIds: StaticRouteId | DynamicRouteId | (StaticRouteId | DynamicRouteId)[] ): boolean { - const routes = typeof routeIds === "string" ? [routeIds] : routeIds - for (const route of routes) { + const matchingRoutes = typeof routeIds === "string" ? [routeIds] : routeIds + + const matchedRoutes = routes.filter((route) => { const urlWithOrigin = new URL(url, fakeOrigin).href const input = urlWithOrigin.split("?")[0] @@ -101,8 +91,17 @@ export function createRouteFn(routes: Routes) { if (pattern.test(input)) { return true } - } - return false + + return false + }) + + // make sure some dynamic params are not conflicting + return ( + matchedRoutes.length > 0 && + matchedRoutes.every((route) => + matchingRoutes.includes(route as StaticRouteId | DynamicRouteId) + ) + ) } fn.list = function (): Routes { diff --git a/tests/index.test.ts b/tests/index.test.ts index 65c76e9..1997aff 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -24,23 +24,15 @@ describe("route-fn", () => { it("should return the correct route with search params", () => { const route = createRouteFn(["/user/:id"]) - expect(route("/user/:id", { id: 1, searchParams: { page: 0 } })).toBe( - "/user/1?page=0" - ) - expect(route("/user/:id", { id: 1, searchParams: { page: 2 } })).toBe( - "/user/1?page=2" - ) + expect(route("/user/:id", { id: 1, searchParams: { page: 0 } })).toBe("/user/1?page=0") + expect(route("/user/:id", { id: 1, searchParams: { page: 2 } })).toBe("/user/1?page=2") }) it("should strip null or undefined search params", () => { const route = createRouteFn(["/user/:id"]) - expect(route("/user/:id", { id: 1, searchParams: { page: null } })).toBe( - "/user/1" - ) - expect( - route("/user/:id", { id: 1, searchParams: { page: undefined } }) - ).toBe("/user/1") + expect(route("/user/:id", { id: 1, searchParams: { page: null } })).toBe("/user/1") + expect(route("/user/:id", { id: 1, searchParams: { page: undefined } })).toBe("/user/1") }) it("should show type error when a param is missing", () => { @@ -74,13 +66,11 @@ describe("route-fn.params", () => { }) }) -describe("route-fn.match", () => { +describe("route-fn.test", () => { it("should match the same url", () => { const route = createRouteFn(["/user/:id/settings/:page"]) - expect( - route.test("/user/1/settings/2", "/user/:id/settings/:page") - ).toEqual(true) + expect(route.test("/user/1/settings/2", "/user/:id/settings/:page")).toEqual(true) }) it("should match one of multiple test routes", () => { @@ -96,15 +86,18 @@ describe("route-fn.match", () => { }) it("should not match different route", () => { - const route = createRouteFn(["/user/:id/settings/:page"]) + const route = createRouteFn([ + "/:org", + "/:org/settings", + "/:org/settings/billing", + "/:org/:project", + "/:org/:project/posts", + "/:org/:project/settings", + ]) - expect(route.test("/user", "/user/:id/settings/:page")).toEqual(false) - expect(route.test("/user/1", "/user/:id/settings/:page")).toEqual(false) - expect(route.test("/user/1/settings", "/user/:id/settings/:page")).toEqual( - false - ) - expect( - route.test("/user/1/settings/2/posts", "/user/:id/settings/:page") - ).toEqual(false) + expect(route.test("/apple", "/:org/:project")).toEqual(false) + expect(route.test("/apple/settings", "/:org/:project")).toEqual(false) + expect(route.test("/apple/settings/billing", "/:org/:project/posts")).toEqual(false) + expect(route.test("/apple/settings/billing", "/:org/:project/settings")).toEqual(false) }) })