Skip to content

Commit

Permalink
fix: segment parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
lifeiscontent committed Apr 1, 2024
1 parent 32c50bb commit 3a18129
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 28 deletions.
24 changes: 18 additions & 6 deletions packages/routes-gen/src/route.test.ts
Original file line number Diff line number Diff line change
@@ -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', undefined, true)).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"
);
});

Expand Down
49 changes: 27 additions & 22 deletions packages/routes-gen/src/route.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
export function route<T extends string>(
path: T,
params?: Record<string, any>
): T {
if (params) {
const segments = path.split(/\/+/).map((segment) => {
if (segment.startsWith(":")) {
const key = segment.replace(":", "").replace("?", "");
export function route<T extends string>(path: T, params: Record<string, any> = {}, includeOptional?: boolean): T {
// If the path doesn't contain any optional segments or parameter placeholders, return the path as is.
if (!path.includes('?') && !path.includes(':')) {
return path;
}

if (key in params) {
return params[key];
}
let realPath = "";
let currentIndex = path.length;
let lastSegmentHadParam = false;

// 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;
}
while (currentIndex > 0) {
const startSegmentIndex = path.lastIndexOf('/', currentIndex);
const segment = path.slice(startSegmentIndex, currentIndex + 1);
currentIndex = startSegmentIndex - 1;
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;
}

return segment;
});

// Filter out any null/undefined segments and join remaining segments
return segments.filter((value) => value != null).join("/") as T;
} else if (segment.endsWith('?')) {
if (lastSegmentHadParam || includeOptional) {
realPath = segment.slice(0, -1) + realPath;
}
} else {
lastSegmentHadParam = false;
realPath = segment + realPath;
}
}

return path;
return realPath as T;
}

0 comments on commit 3a18129

Please sign in to comment.