Skip to content

Commit

Permalink
Release 1.0.4:
Browse files Browse the repository at this point in the history
 * add type constraints for parametrized urls;

 * changed name casing of UrlBuilderImpl to urlBuilderImpl
  • Loading branch information
tomas-light committed Feb 2, 2023
1 parent c8e64bf commit 3a5a7b1
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 26 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NiceWebRoutesNode.ts
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NiceWebRoutesNode.ts
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const routes = createNiceWebRoutes({
avatar: {},
private_info: {},
}),
// typed parameter
form: (form: 'create' | 'edit') => ({}),
},
});

Expand All @@ -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
```

### <a name="react-router"></a> Using with react-router
Expand Down Expand Up @@ -127,12 +134,12 @@ routes.user.userId('18').url(); // '/user/argument_18'

#### <a name="config"></a> FactoryConfig

| Property | Type | Description | Default value |
|-----------|-----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| `getSegmentValue` | `GetSegmentValue` => `(segmentName: string, segmentValue: string &#124; 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' &#124; '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 &#124; 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' &#124; '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 | `{}` |


#### <a name="strategies"></a> Creating strategies
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/FactoryConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CreatingStrategyVariant } from './creatingStrategies';

export type FactoryConfig = {
getSegmentValue?: GetSegmentValue;
UrlBuilderImpl?: UrlBuilderConstructor;
urlBuilderImpl?: UrlBuilderConstructor;
creatingStrategy?: CreatingStrategyVariant;

snakeTransformation?: {
Expand Down
4 changes: 2 additions & 2 deletions src/strategies/createObjectNiceWebRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const createObjectNiceWebRoutes: CreatingStrategy = (config = {}) =>
> {
const {
getSegmentValue = defaultSegmentValueGetter,
UrlBuilderImpl = DefaultUrlBuilder,
urlBuilderImpl = DefaultUrlBuilder,
snakeTransformation = {
disableForSegmentName: false,
disableForSegmentValue: false,
Expand All @@ -40,7 +40,7 @@ export const createObjectNiceWebRoutes: CreatingStrategy = (config = {}) =>
url: function <Search extends Record<string, string>>(
searchParams?: Search | string
) {
return new UrlBuilderImpl()
return new urlBuilderImpl()
.addPathnameIfExists(routePath)
.addSearchParamsIfExists(searchParams)
.build();
Expand Down
4 changes: 2 additions & 2 deletions src/strategies/createProxyNiceWebRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const createProxyNiceWebRoutes: CreatingStrategy = (config = {}) =>
> {
const {
getSegmentValue = defaultSegmentValueGetter,
UrlBuilderImpl = DefaultUrlBuilder,
urlBuilderImpl = DefaultUrlBuilder,
snakeTransformation = {
disableForSegmentName: false,
disableForSegmentValue: false,
Expand All @@ -42,7 +42,7 @@ export const createProxyNiceWebRoutes: CreatingStrategy = (config = {}) =>
url: function <Search extends Record<string, string>>(
searchParams?: Search | string
) {
return new UrlBuilderImpl()
return new urlBuilderImpl()
.addPathnameIfExists(routePath)
.addSearchParamsIfExists(searchParams)
.build();
Expand Down
35 changes: 21 additions & 14 deletions src/types/NiceWebRoutesNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ type NiceWebRoutesNode<
> = {
[propertyName in keyof RoutesDescription]: RoutesDescription[propertyName] extends () => infer NestedRoutes
? ParametrizedNiceWebRoute<NestedRoutes>
: NestedNiceWebRoutes<RoutesDescription[propertyName]>;
: RoutesDescription[propertyName] extends (parameter: infer Parameter) => infer NestedRoutes
? ParametrizedNiceWebRoute<NestedRoutes, Parameter>
: NestedNiceWebRoutes<RoutesDescription[propertyName]>;
} & NiceWebRouteUrls;

type ParametrizedNiceWebRoute<NestedRoutes> = (
value?: string
type ParametrizedNiceWebRoute<NestedRoutes, Parameter = string> = (
parameter?: Parameter
) => NestedNiceWebRoutes<NestedRoutes>;

type NestedNiceWebRoutes<NestedRouteDescription> =
NestedRouteDescription extends NiceWebRoutesDescription<
infer DescriptionShape
>
NestedRouteDescription extends NiceWebRoutesDescription<infer DescriptionShape>
? NiceWebRoutesNode<DescriptionShape>
: never;

Expand All @@ -26,23 +26,30 @@ type ForbiddenNames =
type FilterReservedNames<RouteSegment> = 'url' extends RouteSegment
? ForbiddenNames
: 'relativeUrl' extends RouteSegment
? ForbiddenNames
: true;
? ForbiddenNames
: true;

type NiceWebRoutesDescription<DescriptionShape extends object> = {
[routeSegment in keyof DescriptionShape]: FilterReservedNames<routeSegment> extends ForbiddenNames
? ForbiddenNames
: DescriptionShape[routeSegment] extends infer MaybeObjectOrFunction
? NiceWebRoutesDescriptionValue<MaybeObjectOrFunction>
: DescriptionShape[routeSegment];
? NiceWebRoutesDescriptionValue<MaybeObjectOrFunction>
: DescriptionShape[routeSegment];
};

type NiceWebRoutesDescriptionValue<MaybeObjectOrFunction> =
MaybeObjectOrFunction extends () => infer FunctionResult
MaybeObjectOrFunction extends (...parameter: infer Parameter) => infer FunctionResult
? FunctionResult extends object
? () => NiceWebRoutesDescription<FunctionResult>
: 'function description has to return an object'
: MaybeObjectOrFunction extends object
? Parameter[0] extends string
? (...parameter: Parameter) => NiceWebRoutesDescription<FunctionResult>

: Parameter[0] extends undefined
? () => NiceWebRoutesDescription<FunctionResult>
: 'parametrized argument has to be a string'

: 'function description has to return an object'

: MaybeObjectOrFunction extends object
? NiceWebRoutesDescription<MaybeObjectOrFunction>
: 'route description has to be an object or function';

Expand Down

0 comments on commit 3a5a7b1

Please sign in to comment.