Feature request: generatePath for better consistency
Your bundle size is impressive! The only thing I’m missing is generatePath, similar to React Router’s utility. It’s incredibly helpful for maintaining consistent links in large projects, especially when routes change over time.
@max-mykhailenko you can just grab a copy from packages/react-router/lib/router/utils.ts:859 and paste it into your projects:
export function generatePath<Path extends string>(
originalPath: Path,
params: {
[key in PathParam<Path>]: string | null;
} = {} as any
): string {
let path: string = originalPath;
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
warning(
false,
`Route path "${path}" will be treated as if it were ` +
`"${path.replace(/\*$/, "/*")}" because the \`*\` character must ` +
`always follow a \`/\` in the pattern. To get rid of this warning, ` +
`please change the route path to "${path.replace(/\*$/, "/*")}".`
);
path = path.replace(/\*$/, "/*") as Path;
}
// ensure `/` is added at the beginning if the path is absolute
const prefix = path.startsWith("/") ? "/" : "";
const stringify = (p: any) =>
p == null ? "" : typeof p === "string" ? p : String(p);
const segments = path
.split(/\/+/)
.map((segment, index, array) => {
const isLastSegment = index === array.length - 1;
// only apply the splat if it's the last segment
if (isLastSegment && segment === "*") {
const star = "*" as PathParam<Path>;
// Apply the splat
return stringify(params[star]);
}
const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
if (keyMatch) {
const [, key, optional] = keyMatch;
let param = params[key as PathParam<Path>];
invariant(optional === "?" || param != null, `Missing ":${key}" param`);
return stringify(param);
}
// Remove any optional markers from optional static segments
return segment.replace(/\?$/g, "");
})
// Remove empty segments
.filter((segment) => !!segment);
return prefix + segments.join("/");
}
You'll only gonna need to introduce your own PathParam type, plus warning and invariant functions. Or remove those from snippet entirely.
In fact you could even micro improve it by moving stringify outside and do segments in a single iteration
Thank you for the suggestion. Only one thing that doesn't allow me to do that is the bigger possible options of how I can build the path with your library. That's why I've started this discussion. If you know any library reversed to your current parser, it would be awesome.
Hey, sorry for the late reply. The regexparam library (which wouter uses internally for pattern matching) has an inject() function that does something similar:
import { inject } from 'regexparam';
inject('/users/:id', { id: '123' }); // => '/users/123'
Since it's already a dependency, you could use it directly. Not sure if it covers all the edge cases from React Router's implementation though.