Releases: cat394/link-generator
v4.0.0
Announcement of Version 4.0.0
link-generator has finally reached the highest level.
All issues from previous versions have been resolved, with stricter typing and improved performance.
Here, we will explain the problems that previous versions had and what has changed from version 3.0.0 in two sections.
Previous Issues
- Handling Search Parameters
In previous versions, there was a problem with handling query parameters. This was caused by query parameters included in the parent route path being inherited by child routes.
For example, consider the following route configuration object:
const routeConfig = {
products: {
path: '/products/?size&color',
children: {
product: {
path: '/:productid'
}
}
}
} as const satisfies RouteConfig;In this case, the path for the child route is not generated. This is because the child route inherits the path of the parent route. When generating paths, the string /? is searched first, and if found, all query parameters after it are deleted. Therefore, previously only the last child route could have query parameters. This was completely incorrect and should have been tested.
link('products/product', { productid: '1' }); // => /productsIn version 4.0.0, this has been resolved, and child routes no longer inherit query parameters included in the parent route path.
link('products/product', { productid: '1' }); // => /products/1This change required the creation of additional types. Previously, the FlattedRouteConfig type allowed you to obtain the type of the flattened route configuration object, but from version 4.0.0 onward, please use the FlatRoutes type. This is not just a name change; a new type that removes query parameters included in the parent route path from FlattedRouteConfig has been implemented.
type FlatConfig = FlatRoutes<typeof routeConfig>;
/* ^
* {
* 'products': '/products?size&color',
* 'products/product': '/products/:productid'
* }
* */- Type Safety When Parameters Are Present
The link function's second and third arguments have been modified to be dynamically generated types. The previous link function kept the types simple by making the second and third arguments optional objects. This worked well for generating paths, but it was a problem that no type error occurred when generating paths requiring path parameters. This was one of the tasks I had been putting off. Dynamically generating function parameters with TypeScript is a bit tricky.
link('products/product'); // This does not cause a type error.However, from version 4.0.0, a type error occurs unless you pass the type of the parameters in the second argument for paths with defined parameters. Search parameters remain optional. This means that in paths that include search parameters, no type error will occur if you do not pass a search parameter object in the third argument.
link('products', undefined, { size: 'large' });
// or
link('products'); // No type error will occur if you do not set search parameters.Note that undefined is set as the second argument. Before version 3.0.0, you could set null if query parameters were present.
// Before version 3
// Both are OK
link('products', null, { size: 'large' });
link('products', undefined, { size: 'large' });However, from version 4 onward, if the path does not have path parameters and only includes search parameters, the second argument can only accept undefined.
link('products', null, { size: 'large' }); // ❌null is type error!
link('products', undefined, { size: 'large' }); // ✅Correct!These are the main changes in version 4!
Migration from Version 3
- Review Route Configuration Objects
Migration from version 3 to version 4 may require some changes to the route configuration object. This is because previous versions did not treat paths with parameters as mandatory. For example, let's say you wrote the route configuration object like this:
const routeConfig = {
users: "/users/:user"
} as const satisfies RouteConfig;In that case, the users route could generate two paths, /users and /users/some-userid, without causing a type error.
link('users'); // Type is OK
// or
link('users', { userid: '1' }); // Type is OKHowever, from version 4 onward, a type error occurs unless you explicitly set path parameters for paths with path parameters.
link('users'); // TypeError: Path parameters must be set!In this case, you need to create a separate route for paths with path parameters, as shown below.
const routeConfig = {
users: {
path: '/users',
children: {
user: {
path: '/:userid'
}
}
}
} as const satisfies RouteConfig;
link('users'); // => /users
link('users/user', { userid: '1' }); // => /users/1- Change of Type Names
You only need to read this section if you are building something using the types published by my package.
In version 4, types have been carefully reviewed one by one and renamed to more clear and understandable names.
- DefaultParameterType => DefaultParamValue
- Parameter => Param
- Param => PathParam
- The
FlattenRouteConfigtype is no longer published. Instead, use theFlatRoutestype. This represents the return value offlatRouteConfigwith the search parameters included in the parent route removed.
Others
The following information is about the bugs fixed and performance improvements in this update.
-
In versions prior to version 3, setting
falseas a path parameter caused that path parameter segment to be omitted, which has been fixed. -
Clear documentation has been added to the type definition files to help anyone understand the type inference being done in this package.
v3.0.0
Breaking Change!!
We've changed the way absolute paths are handled, greatly simplifying the code implementation and resulting in a much improved developer experience and performance.
Migrating from Version 2
To migrate from version 2.0.0 to version 3.0.0, prepend / to all paths in your RouteConfig. This change enhances consistency and clarity in defining routes.
Why this change?
In previous versions, there was inconsistency in how paths were defined. Some paths started with / while others didn't, leading to confusion. Version 3.0.0 standardizes this by requiring all paths to be absolute, making the route definitions more intuitive and easier to understand.
Migration Steps
-
Update Route Definitions
In version 3.0.0, ensure all paths start with
/.Before version 2.0.0:
const routeConfig = { home: { path: '/' }, users: { path: 'users/:userid' } products: { path: 'products', children: { search: { path: 'search/?size&color' } } } } as const satisfies RouteConfig;
Version 3.0.0:
const routeConfig = { home: { path: '/', }, users: { path: '/users/:userid', }, products: { path: '/products', children: { search: { path: '/search/?size&color', }, }, }, } as const satisfies RouteConfig;
Now, all paths are prefixed with
/. -
Handling Absolute Paths
This migration is optional and will continue to work as expected with previous versions.
In previous versions, absolute paths required the Route ID to be prefixed with
*. In version 3.0.0, this is no longer necessary, though the*prefix will still work for compatibility.Before version 2.0.0:
const routeConfig = { '*external': { path: 'https://', children: { youtube: { path: 'youtube.com', children: { watch: '/watch?videoid', }, }, }, }, };
Version 3.0.0:
const routeConfig = { 'external': { path: 'https://', children: { youtube: { path: 'youtube.com', children: { watch: '/watch?videoid', }, }, }, }, };
Summary
Make sure you precede all relative paths in RouteConfig with a '/'.
v2.0.0
Breaking Change!!
Due to my lack of understanding of query parameters, I used to generate the query parameters with the characters ?q= prepended, but I realized this was wrong and changed it to start with ?.
Example:
Previous:
const routeConfig = {
productsSearch: {
path: 'products/search/?q=size'
}
}
// ... create link generator
link('productsSearch', null, { size: 'large' }) // result: '/products/search?q=size'Now:
const routeConfig = {
productsSearch: {
path: 'products/search/?size'
}
}
link('productsSearch', null, { size: 'large'}) // result: '/products/search?size=largev1.5.2
I was upgraded from version 1.4.2 to 1.5.2 because I forgot to reset the patch version.
What's new?
Version 1.5.x added support for boolean values in union types for constraint fields.
Example:
Previous:
const routeConfig = {
route1: {
path: '/route1/:id<(a|10|true)'
}
} as const satisfies RouteConfig;
// ...flatten route route config
link('route1', { id: 'true' }) // id type is 'a' or 10 or 'true'Now:
const routeConfig = {
route1: {
path: '/route1/:id<(a|10|true)'
}
} as const satisfies RouteConfig;
// ...flatten route route config
link('route1', { id: true }) // id type is 'a' or 10 or true!v1.4.2
Changes
In this release, the ExtractRouteData type has been fixed from a string type to a string literal type.
Previous:
const routeConfig = {
users: {
path: "users/:userid",
},
news: {
path: "news/?q=is_archived<boolean>",
},
} as const satisfies RouteConfig;
const flatRouteConfig = flattenRouteConfig(routeConfig);
type RouteData = ExtractRouteData<typeof flatRouteConfig>;
// ^
// {
// users: {
// path: string; // path property type is string...
// params: Record<"userid", DefaultParameterType>;
// search: never;
// };
// news: {
// path: string;
// params: never;
// search: Record<"is_archived", boolean>;
// };
// }v1.4.2
type RouteData = ExtractRouteData<typeof flatRouteConfig>;
// ^
// {
// users: {
// path: "users/:userid"; // path property type is string literal!!
// params: Record<"userid", DefaultParameterType>;
// search: never;
// };
// news: {
// path: "news/?q=is_archived<boolean>";
// params: never;
// search: Record<"is_archived", boolean>;
// };
// }v1.4.0
What's new?
-
The default types for path parameters and query parameters have been exported as a package.
-
Path parameters and query parameters can now be set to
booleanvalues by default without causing a type error. -
Constraint fields now allow the use of
<boolean>constraints.
Example
Previous:
const routeConfig = {
route1: {
path: "route1/:param1"
}
} as const satisfies RouteConfig;
// ...create link generator
link("route1", { param1: true }); // ⚠This actually worked but threw a type error...v.1.4.x
link("route1", { param1: true )}; // 😊No type error!
// You can also use constraint fields to narrow down types to boolean values.
const routeConfig = {
route1: {
path: "route1/:param1<boolean>"
}
} as const satisfies RouteConfig;
link("route1", { param1: 'alice' }); // string parameter is a type error because param1 is a boolean type.v.1.3.0
v1.2.0
What's new?
Fixed Unwanted Query Parameter in Generated Paths
Previously, when all properties of the search parameter object in the link function had values of undefined, the generated path would end with ?q=. This was an undesirable outcome in most cases. Therefore, we have changed this behavior to generate a clean path. As a result, the trailing ?q= will be removed in such cases.
Example:
Previous:
const routeConfig = {
products: {
path: 'products/?q=size&color'
}
} as const satisfies RouteConfig;
const flatConfig = flattenConfig(routeConfig);
const link = createLinkGeneratro(flatConfig);
const productspage = link("products", null, { size: undefined, color: undefined }); // => 😥 /products?q=Now:
const productspage = link("products", null, { size: undefined, color: undefined }) // => 😊 /productsv.1.2.3
v1.1.0
What's new?
Path Parameter and Query Parameter Type Flexibility Update
Previously, the types for parameters and query parameters were string by default.
However, we found it cumbersome to use constraint fields to handle number types.
Therefore, we have changed it to accept the broader type string | number.
This update simplifies the process by allowing you to directly specify number types without additional constraints.
Also, if you want to accept only string types, we have added a feature that allows you to strictly enforce the type using a constraint field, similar to how it works for number types.
Example:
Previous:
const routeConfig = {
users: {
path: 'users/:userid'
}
} as const satisfies RouteConfig;
const flatConfig = flattenConfig(routeConfig);
const link = createLinkGeneratro(flatConfig);
const userpage = link("users", { userid: 1 }); // 😥userid parameter type is string! so, this cause type error!Now:
const userpage = link("users", { userid: 1 }) // 😊userid paramter type is string|number! so, this does not cause type error!
// If you want the userid parameter to be of type string instead, use a constraint field:
const routeConfig = {
users: {
path: 'users/:userid<string>' // Write 'string' in the constraint field!
}
} as const satisfies RouteConfig;
const userpage = link("users", { userid: 1 }) // ❌ userid parameter type is string!