From 3a5a7b1dfddedfca587cc7e7b7ff14793a15674b Mon Sep 17 00:00:00 2001 From: Artem Ignatev Date: Thu, 2 Feb 2023 13:45:46 +0100 Subject: [PATCH] Release 1.0.4: * add type constraints for parametrized urls; * changed name casing of UrlBuilderImpl to urlBuilderImpl --- .eslintignore | 1 + .prettierignore | 1 + README.md | 19 +++++++---- package.json | 2 +- src/strategies/FactoryConfig.ts | 2 +- src/strategies/createObjectNiceWebRoutes.ts | 4 +-- src/strategies/createProxyNiceWebRoutes.ts | 4 +-- src/types/NiceWebRoutesNode.ts | 35 ++++++++++++--------- 8 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 .eslintignore create mode 100644 .prettierignore diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..884eef1 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +NiceWebRoutesNode.ts \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..884eef1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +NiceWebRoutesNode.ts \ No newline at end of file diff --git a/README.md b/README.md index 8b6b56d..94db74d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ const routes = createNiceWebRoutes({ avatar: {}, private_info: {}, }), + // typed parameter + form: (form: 'create' | 'edit') => ({}), }, }); @@ -38,6 +40,11 @@ routes.users.statistic.url('/*'); // '/users/statistic/*' routes.user.userId().relativeUrl(); // ':userId' routes.user.userId('18').private_info.url(); // '/user/18/private-info' + +// typed parameter +routes.user.form('create').url(); // '/user/create' +routes.user.form('edit').url(); // '/user/edit' +routes.user.form('something').url(); // error because it violates type constraint of 'create' | 'edit' | undefined ``` ### Using with react-router @@ -127,12 +134,12 @@ routes.user.userId('18').url(); // '/user/argument_18' #### FactoryConfig -| Property | Type | Description | Default value | -|-----------|-----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| -| `getSegmentValue` | `GetSegmentValue` => `(segmentName: string, segmentValue: string | undefined) => string` | It is responsible for displaying parametrized route value | value is displayed as is, and when there is no value it shows as `:segmentName` | -| `UrlBuilderImpl` | `UrlBuilderConstructor` => class that implements `UrlBuilder` interface | You can override how the target url is creating | `DefaultUrlBuilder` - internal implementation | -| `creatingStrategy` | `CreatingStrategyVariant` => `'proxy' | 'object'` | it is about how your routes object is created (see [Creating strategies](#strategies) section bellow) | `object` | -| `snakeTransformation` | `{ disableForSegmentName?: boolean; disableForSegmentValue?: boolean; }` | You can disable transformation of `user_list` segment name or value to `user-list` url part | `{}` | +| Property | Type | Description | Default value | +|-----------------------|-----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| +| `getSegmentValue` | `GetSegmentValue` => `(segmentName: string, segmentValue: string | undefined) => string` | It is responsible for displaying parametrized route value | value is displayed as is, and when there is no value it shows as `:segmentName` | +| `urlBuilderImpl` | `UrlBuilderConstructor` => class that implements `UrlBuilder` interface | You can override how the target url is creating | `DefaultUrlBuilder` - internal implementation | +| `creatingStrategy` | `CreatingStrategyVariant` => `'proxy' | 'object'` | it is about how your routes object is created (see [Creating strategies](#strategies) section bellow) | `object` | +| `snakeTransformation` | `{ disableForSegmentName?: boolean; disableForSegmentValue?: boolean; }` | You can disable transformation of `user_list` segment name or value to `user-list` url part | `{}` | #### Creating strategies diff --git a/package.json b/package.json index ee4e978..d927c8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nice-web-routes", - "version": "1.0.3", + "version": "1.0.4", "description": "Easy way to create nice web routes for you application", "contributors": [ "Artem Ignatev " diff --git a/src/strategies/FactoryConfig.ts b/src/strategies/FactoryConfig.ts index 0429235..05168f4 100644 --- a/src/strategies/FactoryConfig.ts +++ b/src/strategies/FactoryConfig.ts @@ -4,7 +4,7 @@ import { CreatingStrategyVariant } from './creatingStrategies'; export type FactoryConfig = { getSegmentValue?: GetSegmentValue; - UrlBuilderImpl?: UrlBuilderConstructor; + urlBuilderImpl?: UrlBuilderConstructor; creatingStrategy?: CreatingStrategyVariant; snakeTransformation?: { diff --git a/src/strategies/createObjectNiceWebRoutes.ts b/src/strategies/createObjectNiceWebRoutes.ts index e033b98..af87b72 100644 --- a/src/strategies/createObjectNiceWebRoutes.ts +++ b/src/strategies/createObjectNiceWebRoutes.ts @@ -27,7 +27,7 @@ export const createObjectNiceWebRoutes: CreatingStrategy = (config = {}) => > { const { getSegmentValue = defaultSegmentValueGetter, - UrlBuilderImpl = DefaultUrlBuilder, + urlBuilderImpl = DefaultUrlBuilder, snakeTransformation = { disableForSegmentName: false, disableForSegmentValue: false, @@ -40,7 +40,7 @@ export const createObjectNiceWebRoutes: CreatingStrategy = (config = {}) => url: function >( searchParams?: Search | string ) { - return new UrlBuilderImpl() + return new urlBuilderImpl() .addPathnameIfExists(routePath) .addSearchParamsIfExists(searchParams) .build(); diff --git a/src/strategies/createProxyNiceWebRoutes.ts b/src/strategies/createProxyNiceWebRoutes.ts index c0459eb..7ca43d5 100644 --- a/src/strategies/createProxyNiceWebRoutes.ts +++ b/src/strategies/createProxyNiceWebRoutes.ts @@ -28,7 +28,7 @@ export const createProxyNiceWebRoutes: CreatingStrategy = (config = {}) => > { const { getSegmentValue = defaultSegmentValueGetter, - UrlBuilderImpl = DefaultUrlBuilder, + urlBuilderImpl = DefaultUrlBuilder, snakeTransformation = { disableForSegmentName: false, disableForSegmentValue: false, @@ -42,7 +42,7 @@ export const createProxyNiceWebRoutes: CreatingStrategy = (config = {}) => url: function >( searchParams?: Search | string ) { - return new UrlBuilderImpl() + return new urlBuilderImpl() .addPathnameIfExists(routePath) .addSearchParamsIfExists(searchParams) .build(); diff --git a/src/types/NiceWebRoutesNode.ts b/src/types/NiceWebRoutesNode.ts index 3f93005..8eb12dc 100644 --- a/src/types/NiceWebRoutesNode.ts +++ b/src/types/NiceWebRoutesNode.ts @@ -6,17 +6,17 @@ type NiceWebRoutesNode< > = { [propertyName in keyof RoutesDescription]: RoutesDescription[propertyName] extends () => infer NestedRoutes ? ParametrizedNiceWebRoute - : NestedNiceWebRoutes; + : RoutesDescription[propertyName] extends (parameter: infer Parameter) => infer NestedRoutes + ? ParametrizedNiceWebRoute + : NestedNiceWebRoutes; } & NiceWebRouteUrls; -type ParametrizedNiceWebRoute = ( - value?: string +type ParametrizedNiceWebRoute = ( + parameter?: Parameter ) => NestedNiceWebRoutes; type NestedNiceWebRoutes = - NestedRouteDescription extends NiceWebRoutesDescription< - infer DescriptionShape - > + NestedRouteDescription extends NiceWebRoutesDescription ? NiceWebRoutesNode : never; @@ -26,23 +26,30 @@ type ForbiddenNames = type FilterReservedNames = 'url' extends RouteSegment ? ForbiddenNames : 'relativeUrl' extends RouteSegment - ? ForbiddenNames - : true; + ? ForbiddenNames + : true; type NiceWebRoutesDescription = { [routeSegment in keyof DescriptionShape]: FilterReservedNames extends ForbiddenNames ? ForbiddenNames : DescriptionShape[routeSegment] extends infer MaybeObjectOrFunction - ? NiceWebRoutesDescriptionValue - : DescriptionShape[routeSegment]; + ? NiceWebRoutesDescriptionValue + : DescriptionShape[routeSegment]; }; type NiceWebRoutesDescriptionValue = - MaybeObjectOrFunction extends () => infer FunctionResult + MaybeObjectOrFunction extends (...parameter: infer Parameter) => infer FunctionResult ? FunctionResult extends object - ? () => NiceWebRoutesDescription - : 'function description has to return an object' - : MaybeObjectOrFunction extends object + ? Parameter[0] extends string + ? (...parameter: Parameter) => NiceWebRoutesDescription + + : Parameter[0] extends undefined + ? () => NiceWebRoutesDescription + : 'parametrized argument has to be a string' + + : 'function description has to return an object' + + : MaybeObjectOrFunction extends object ? NiceWebRoutesDescription : 'route description has to be an object or function';