diff --git a/packages/routes-gen/src/route.test.ts b/packages/routes-gen/src/route.test.ts index 9c3bb6e..9a10c8e 100644 --- a/packages/routes-gen/src/route.test.ts +++ b/packages/routes-gen/src/route.test.ts @@ -1,16 +1,28 @@ import { route } from "./route"; it("supports optional params (issue 31)", () => { - expect(route("/reports/:type?/:id/", { id: "123" })).toEqual("/reports/123/"); + expect(route('/special?/foo')).toEqual('/foo'); + expect(route('/special/foo')).toEqual('/special/foo'); - expect(route("/reports/:type?/:id/", { id: "123", type: "annual" })).toEqual( - "/reports/annual/123/" + expect(route("/reports/:type?/:id", { id: "123" })).toEqual("/reports/123"); + + expect(route("/reports/:type?/:id", { id: "123", type: "annual" })).toEqual( + "/reports/annual/123" + ); + + + expect(route("/reports/:id/:type?", { id: "123" })).toEqual("/reports/123"); + + expect(route("/reports/:id/:type?", { id: "123", type: "annual" })).toEqual( + "/reports/123/annual" ); - expect(route("/reports/:id/:type?/", { id: "123" })).toEqual("/reports/123/"); + expect(route("/categories?/:category?/products")).toEqual( + "/products" + ); - expect(route("/reports/:id/:type?/", { id: "123", type: "annual" })).toEqual( - "/reports/123/annual/" + expect(route("/categories?/:category?/products", { category: 'tees'})).toEqual( + "/categories/tees/products" ); }); diff --git a/packages/routes-gen/src/route.ts b/packages/routes-gen/src/route.ts index bd6212c..295c219 100644 --- a/packages/routes-gen/src/route.ts +++ b/packages/routes-gen/src/route.ts @@ -1,28 +1,33 @@ -export function route( - path: T, - params?: Record -): T { - if (params) { - const segments = path.split(/\/+/).map((segment) => { - if (segment.startsWith(":")) { - const key = segment.replace(":", "").replace("?", ""); - - if (key in params) { - return params[key]; - } +export function route(path: T, params: Record = {}): T { + if (!path.includes('?') && !path.includes(':')) { + return path; + } - // If the segment is optional and it doesn't exist in params, return null to omit it from the resulting path - if (segment.endsWith("?")) { - return null; - } - } + let realPath = ""; + let currentIndex = path.length; + let lastSegmentHadParam = false; - return segment; - }); + while (currentIndex > 0) { + const startSegmentIndex = path.lastIndexOf('/', currentIndex); + const segment = path.slice(startSegmentIndex, currentIndex + 1); + currentIndex = startSegmentIndex - 1; - // Filter out any null/undefined segments and join remaining segments - return segments.filter((value) => value != null).join("/") as T; + if (segment.startsWith('/:')) { + const paramName = segment.endsWith('?') ? segment.slice(2, -1) : segment.slice(2); + const paramValue = params[paramName]; + if (paramValue !== undefined) { + lastSegmentHadParam = true; + realPath = `/${paramValue}` + realPath; + } + } else if (segment.endsWith('?')) { + if (lastSegmentHadParam) { + realPath = segment.slice(0, -1) + realPath; + } + } else { + lastSegmentHadParam = false; + realPath = segment + realPath; + } } - return path; -} + return realPath as T; +} \ No newline at end of file