Skip to content

Commit 162b749

Browse files
committed
fix: typing URL with custom paths
1 parent 56e72ef commit 162b749

File tree

4 files changed

+151
-126
lines changed

4 files changed

+151
-126
lines changed

package.json

Lines changed: 75 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,78 @@
11
{
2-
"name": "brouther",
3-
"type": "module",
4-
"version": "5.2.0",
5-
"source": "./src/index.ts",
6-
"types": "./dist/index.d.ts",
7-
"main": "./dist/index.js",
8-
"module": "./dist/index.cjs",
9-
"exports": {
10-
".": {
11-
"default": {
12-
"type": "./dist/index.d.ts",
13-
"import": "./dist/index.cjs",
14-
"default": "./dist/index.js",
15-
"require": "./dist/index.cjs"
16-
},
17-
"types": {
18-
"import": "./dist/index.d.ts",
19-
"require": "./dist/index.d.ts",
20-
"default": "./dist/index.d.ts"
21-
}
22-
}
23-
},
24-
"scripts": {
25-
"build": "NODE_ENV=production vite build; tsc",
26-
"dev": "vite build --watch",
27-
"pack": "npm pack --dry-run --pack-destination out out/brouther",
28-
"prepack": "npm run build",
29-
"prepare": "npm run build",
30-
"start": "vite build --watch",
31-
"test": "vitest run && npm run test:types",
32-
"test:types": "tsc -p ./tsconfig.test.json",
33-
"test:watch": "vitest watch",
34-
"cy": "cypress open",
35-
"cy:chrome": "cypress run --browser chrome",
36-
"cy:electron": "cypress run --browser electron",
37-
"cy:run": "npm run cy:chrome && npm cy:electron",
38-
"cy:browser": "start-server-and-test start http://localhost:5173 cy:run",
39-
"typedoc": "typedoc --out docs src/index.ts"
40-
},
41-
"dependencies": {
42-
"history": "5.3.0",
43-
"qs": "6.13.0",
44-
"react-error-boundary": "4.0.13",
45-
"ts-toolbelt": "9.6.0"
46-
},
47-
"peerDependencies": {
48-
"react": ">=16.8.3"
49-
},
50-
"devDependencies": {
51-
"@types/node": "20.12.13",
52-
"@types/qs": "6.9.15",
53-
"@types/react": "18.3.3",
54-
"cypress": "13.10.0",
55-
"prettier": "3.2.5",
56-
"start-server-and-test": "2.0.3",
57-
"ts-node": "10.9.2",
58-
"tslib": "2.6.2",
59-
"typescript": "5.4.5",
60-
"vite": "5.2.12",
61-
"vitest": "1.6.0"
62-
},
63-
"browserslist": {
64-
"production": [
65-
">0.2%",
66-
"not dead",
67-
"not op_mini all"
68-
],
69-
"development": [
70-
"last 1 chrome version",
71-
"last 1 firefox version",
72-
"last 1 safari version"
73-
]
74-
},
75-
"volta": {
76-
"node": "18.13.0"
2+
"name": "brouther",
3+
"type": "module",
4+
"version": "5.2.0",
5+
"source": "./src/index.ts",
6+
"types": "./dist/index.d.ts",
7+
"main": "./dist/index.js",
8+
"module": "./dist/index.cjs",
9+
"exports": {
10+
".": {
11+
"default": {
12+
"type": "./dist/index.d.ts",
13+
"import": "./dist/index.cjs",
14+
"default": "./dist/index.js",
15+
"require": "./dist/index.cjs"
16+
},
17+
"types": {
18+
"import": "./dist/index.d.ts",
19+
"require": "./dist/index.d.ts",
20+
"default": "./dist/index.d.ts"
21+
}
7722
}
23+
},
24+
"scripts": {
25+
"build": "NODE_ENV=production vite build; tsc",
26+
"dev": "vite build --watch",
27+
"pack": "npm pack --dry-run --pack-destination out out/brouther",
28+
"prepack": "npm run build",
29+
"prepare": "npm run build",
30+
"start": "vite build --watch",
31+
"test": "vitest run && npm run test:types",
32+
"test:types": "tsc -p ./tsconfig.test.json",
33+
"test:watch": "vitest watch",
34+
"cy": "cypress open",
35+
"cy:chrome": "cypress run --browser chrome",
36+
"cy:electron": "cypress run --browser electron",
37+
"cy:run": "npm run cy:chrome && npm cy:electron",
38+
"cy:browser": "start-server-and-test start http://localhost:5173 cy:run",
39+
"typedoc": "typedoc --out docs src/index.ts"
40+
},
41+
"dependencies": {
42+
"history": "5.3.0",
43+
"qs": "6.13.0",
44+
"react-error-boundary": "4.0.13",
45+
"ts-toolbelt": "9.6.0"
46+
},
47+
"peerDependencies": {
48+
"react": ">=16.8.3"
49+
},
50+
"devDependencies": {
51+
"@types/node": "20.12.13",
52+
"@types/qs": "6.9.15",
53+
"@types/react": "18.3.3",
54+
"cypress": "13.10.0",
55+
"prettier": "3.2.5",
56+
"start-server-and-test": "2.0.3",
57+
"ts-node": "10.9.2",
58+
"tslib": "2.6.2",
59+
"typescript": "5.4.5",
60+
"vite": "5.2.12",
61+
"vitest": "1.6.0"
62+
},
63+
"browserslist": {
64+
"production": [
65+
">0.2%",
66+
"not dead",
67+
"not op_mini all"
68+
],
69+
"development": [
70+
"last 1 chrome version",
71+
"last 1 firefox version",
72+
"last 1 safari version"
73+
]
74+
},
75+
"volta": {
76+
"node": "18.13.0"
77+
}
7878
}

src/index.ts

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,58 @@
11
export type {
2-
ActionProps,
3-
Actions,
4-
AnyJson,
5-
AnyJsonArray,
6-
ConfiguredRoute,
7-
HttpMethods,
8-
Loader,
9-
LoaderProps,
10-
PathFormat,
11-
Route,
12-
RouteData,
13-
WithoutGet,
14-
InferRouter,
15-
BroutherFlags,
2+
ActionProps,
3+
Actions,
4+
AnyJson,
5+
AnyJsonArray,
6+
ConfiguredRoute,
7+
HttpMethods,
8+
Loader,
9+
LoaderProps,
10+
PathFormat,
11+
Route,
12+
RouteData,
13+
WithoutGet,
14+
InferRouter,
15+
BroutherFlags,
1616
} from "./types";
1717
export type { LinkProps } from "./router/link";
1818
export type { Paths } from "./types/paths";
1919
export type { QueryString } from "./types/query-string";
2020
export type { RouterNavigator } from "./router/router-navigator";
2121
export {
22-
Brouther,
23-
Outlet,
24-
useBasename,
25-
useBeforeUnload,
26-
useBrouther,
27-
useDataLoader,
28-
useErrorPage,
29-
useFlags,
30-
useFormActions,
31-
useHref,
32-
useLoadingState,
33-
useNavigation,
34-
usePage,
35-
usePageStats,
36-
usePaths,
37-
useQueryString,
38-
useRouteError,
39-
useURL,
40-
useUrlSearchParams,
22+
Brouther,
23+
Outlet,
24+
useBasename,
25+
useBeforeUnload,
26+
useBrouther,
27+
useDataLoader,
28+
useErrorPage,
29+
useFlags,
30+
useFormActions,
31+
useHref,
32+
useLoadingState,
33+
useNavigation,
34+
usePage,
35+
usePageStats,
36+
usePaths,
37+
useQueryString,
38+
useQueryStringState,
39+
useRouteError,
40+
useURL,
41+
useUrlSearchParams,
4142
} from "./brouther/brouther";
4243
export { BroutherError, NotFoundRoute } from "./utils/errors";
4344
export { parsePath, pathsToValue, transformParams } from "./utils/mappers";
4445
export { Form } from "./form/form";
4546
export { Link } from "./router/link";
4647
export {
47-
asyncActions,
48-
asyncComponent,
49-
asyncLoader,
50-
createMappedRouter as createRecordRouter,
51-
createMappedRouter as createRouterMap,
52-
createMappedRouter as createRouter,
53-
createMappedRouter,
54-
createRoute,
48+
asyncActions,
49+
asyncComponent,
50+
asyncLoader,
51+
createMappedRouter as createRecordRouter,
52+
createMappedRouter as createRouterMap,
53+
createMappedRouter as createRouter,
54+
createMappedRouter,
55+
createRoute,
5556
} from "./router/router";
5657
export { urlEntity, mergeUrlEntities, createHref, qsToString, transformData, createPaths, type GetPaths } from "./utils/utils";
5758
export { urlSearchParamsToJson, jsonToURLSearchParams, formToJson } from "./form/form-data-api";

src/types/paths.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Function, Number, Object, String, Union } from "ts-toolbelt";
2-
import type { QueryString } from "./query-string";
32
import type { Router } from "./index";
3+
import type { QueryString } from "./query-string";
44

55
type RecordMap = Record<string, string>;
66

@@ -25,8 +25,8 @@ export namespace Paths {
2525
? X
2626
: null
2727
: T extends `:${infer ID}`
28-
? `${ID}:string`
29-
: null;
28+
? `${ID}:string`
29+
: null;
3030

3131
type Filter<T extends readonly string[], Acc extends string[] = [], I extends number = 0> = T["length"] extends I
3232
? Acc
@@ -42,11 +42,12 @@ export namespace Paths {
4242

4343
export type Map<_Router extends Function.Narrow<Router>> = NonNullable<{ [_ in keyof _Router[string]]: _Router[string]["path"] }["path"]>;
4444

45-
export type PathsQs<Path extends string> = Parse<Pathname<Path>> extends null
46-
? QueryString.Has<Path> extends true
47-
? QueryString.Parse<Path>
48-
: Parse<Pathname<Path>>
49-
: QueryString.Parse<Path>;
45+
export type PathsQs<Path extends string> =
46+
Parse<Pathname<Path>> extends null
47+
? QueryString.Has<Path> extends true
48+
? QueryString.Parse<Path>
49+
: Parse<Pathname<Path>>
50+
: QueryString.Parse<Path>;
5051

5152
export type Assign<Path extends string, Params extends {}, I extends number = 0> = I extends ToArray<Params>["length"]
5253
? Path
@@ -58,5 +59,5 @@ export namespace Paths {
5859
Number.Add<I, 1>
5960
>;
6061

61-
export type Has<T extends string> = T extends `${string}/:${string}` ? true : T extends `${string}/<:${string}` ? true : false;
62+
export type Has<T extends string> = T extends `${string}/:${string}` ? true : T extends `${string}<${infer _}:${infer __}>${string}` ? true : false;
6263
}

tests/typings.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Fragment } from "react";
2-
import { createRouterMap, Link, ParseSerializable } from "../src";
2+
import { createRouterMap, Link, ParseSerializable, Paths } from "../src";
33

44
const equals = <A extends any, B extends A>(a: A, b: B): a is B => a === b;
55

@@ -8,6 +8,10 @@ type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
88
type Merge<T> = { [K in keyof T]: T[K] } & {};
99

1010
const map = createRouterMap({
11+
customPattern: {
12+
path: "/<region:string>?lang=string",
13+
element: <Fragment />,
14+
},
1115
atLeastOnlyQs: {
1216
path: "/:region?lang=string&discount=number&array=number[]&date=date!",
1317
element: <Fragment />,
@@ -18,6 +22,8 @@ const map = createRouterMap({
1822
},
1923
});
2024

25+
console.log(map.link(map.links.customPattern, { region: "string" }, { lang: "123" }));
26+
2127
console.log(map.link(map.links.atLeast, { region: "Brazil" }, { lang: "cool" }));
2228
const r = map.link(map.links.atLeast, { region: "Brazil" } as const, {
2329
lang: "lang",
@@ -61,6 +67,23 @@ const TestLinkWithCustomEventHandler = (
6167
</Link>
6268
);
6369

70+
type A = Paths.Has<"/users/<id:string>">;
71+
72+
const TestWithCustomPatternForParams = (
73+
<Link
74+
paths={{ id: "UUID" }}
75+
query={{ sort: "sort" }}
76+
href="/users/<id:string>"
77+
onClick={(e, queryPaths) => {
78+
e.preventDefault();
79+
console.log(queryPaths.query.sort);
80+
console.log(queryPaths.paths.id);
81+
}}
82+
>
83+
Link
84+
</Link>
85+
);
86+
6487
type WillBeParsed = Merge<
6588
ParseSerializable<{
6689
date: Date;

0 commit comments

Comments
 (0)