@uxf/router 11.68.0 → 11.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -19
- package/package.json +3 -2
- package/router.d.ts +47 -17
- package/router.js +70 -13
- package/router.test.js +122 -13
- package/sitemap-generator.d.ts +6 -5
- package/sitemap-generator.test.js +22 -4
- package/superstruct/array.d.ts +7 -0
- package/superstruct/array.js +14 -0
- package/superstruct/boolean.d.ts +1 -0
- package/superstruct/boolean.js +8 -0
- package/superstruct/index.d.ts +4 -0
- package/superstruct/index.js +24 -0
- package/superstruct/integer.d.ts +1 -0
- package/superstruct/integer.js +12 -0
- package/types.d.ts +5 -0
package/README.md
CHANGED
|
@@ -14,27 +14,53 @@ yarn add @uxf/router
|
|
|
14
14
|
|
|
15
15
|
import { createRouter } from "@uxf/router";
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
17
|
+
export default createRouter(
|
|
18
|
+
{
|
|
19
|
+
index: {
|
|
20
|
+
path: "/"
|
|
21
|
+
},
|
|
22
|
+
"admin/index": {
|
|
23
|
+
path: "/admin",
|
|
24
|
+
schema: object({
|
|
25
|
+
param1: optional(number())
|
|
26
|
+
})
|
|
27
|
+
},
|
|
28
|
+
"blog/detail": {
|
|
29
|
+
path: "/blog/[id]",
|
|
30
|
+
schema: object({
|
|
31
|
+
id: number()
|
|
32
|
+
})
|
|
33
|
+
},
|
|
34
|
+
"localized-route": {
|
|
35
|
+
path: {
|
|
36
|
+
en: "/en/home",
|
|
37
|
+
cs: "/cs/domu"
|
|
38
|
+
},
|
|
39
|
+
schema: object({
|
|
40
|
+
term: optional(string()),
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
} as const,
|
|
44
|
+
{
|
|
45
|
+
locales: ["cs", "en"],
|
|
46
|
+
baseUrl: "https://www.uxf.cz"
|
|
47
|
+
} as const
|
|
48
|
+
);
|
|
28
49
|
```
|
|
29
50
|
|
|
30
51
|
```ts
|
|
31
52
|
// routes/index.ts
|
|
32
53
|
|
|
33
|
-
import router
|
|
54
|
+
import router from "./routes";
|
|
34
55
|
import { UxfGetServerSideProps, UxfGetStaticProps } from "@uxf/router";
|
|
35
56
|
import { PreviewData as NextPreviewData } from "next/types";
|
|
36
57
|
|
|
37
|
-
export const {
|
|
58
|
+
export const {
|
|
59
|
+
routeToUrl,
|
|
60
|
+
sitemapGenerator,
|
|
61
|
+
useQueryParams,
|
|
62
|
+
useQueryParamsStatic
|
|
63
|
+
} = router;
|
|
38
64
|
|
|
39
65
|
export type GetStaticProps<
|
|
40
66
|
Route extends keyof RouteList,
|
|
@@ -67,12 +93,15 @@ Add configuration to `tsconfig.json`
|
|
|
67
93
|
## useQueryParams
|
|
68
94
|
|
|
69
95
|
```tsx
|
|
70
|
-
import {useQueryParams} from "@app-routes";
|
|
71
|
-
import {queryParamToNumber} from "./helper";
|
|
96
|
+
import { useQueryParams } from "@app-routes";
|
|
97
|
+
import { queryParamToNumber } from "./helper";
|
|
72
98
|
|
|
73
|
-
|
|
99
|
+
// can be used on SSR pages
|
|
100
|
+
const [query, { push, replace }] = useQueryParams("route-name");
|
|
74
101
|
|
|
75
|
-
|
|
102
|
+
// must be used on static pages, because router is not ready on first render
|
|
103
|
+
// query is null if router is not ready
|
|
104
|
+
const [query, { push, replace }] = useQueryParamsStatic("route-name");
|
|
76
105
|
```
|
|
77
106
|
|
|
78
107
|
## Next Link
|
|
@@ -81,10 +110,10 @@ const id = queryParamToNumber(params.id);
|
|
|
81
110
|
// pages/index.js
|
|
82
111
|
|
|
83
112
|
import Link from "next/link";
|
|
84
|
-
import {
|
|
113
|
+
import { routeToUrl } from "@app-routes";
|
|
85
114
|
|
|
86
115
|
export default () => (
|
|
87
|
-
<Link href={
|
|
116
|
+
<Link href={routeToUrl("blog/detail", { id: 12 })}>
|
|
88
117
|
Hello world
|
|
89
118
|
</Link>
|
|
90
119
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uxf/router",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.72.0",
|
|
4
4
|
"description": "UXF Router",
|
|
5
5
|
"author": "UXFans <dev@uxf.cz>",
|
|
6
6
|
"homepage": "https://gitlab.com/uxf-npm/router#readme",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"url": "https://gitlab.com/uxf-npm/router/issues"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"qs": "6.13.0"
|
|
26
|
+
"qs": "6.13.0",
|
|
27
|
+
"superstruct": "2.0.2"
|
|
27
28
|
},
|
|
28
29
|
"peerDependencies": {
|
|
29
30
|
"next": ">= 12"
|
package/router.d.ts
CHANGED
|
@@ -1,22 +1,52 @@
|
|
|
1
1
|
import { LinkProps } from "next/link";
|
|
2
|
+
import { Infer, Struct } from "superstruct";
|
|
2
3
|
import { SitemapGeneratorOptions, SitemapGeneratorType } from "./sitemap-generator";
|
|
3
|
-
import { QueryParams } from "./types";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { QueryParams, RoutesDefinition } from "./types";
|
|
5
|
+
type ExtractSchema<T> = T extends {
|
|
6
|
+
schema: Struct<infer U, any>;
|
|
7
|
+
} ? U : null;
|
|
8
|
+
type Options = {
|
|
9
|
+
shouldBeAbsolute?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type LocaleOptions<Locales extends string[]> = {
|
|
12
|
+
locale: Locales[number];
|
|
13
|
+
};
|
|
14
|
+
export type FunctionParametersGenerator<Locales extends string[], RouteList extends RoutesDefinition<Locales>> = {
|
|
15
|
+
[K in keyof RouteList]: RouteList[K]["path"] extends string ? RouteList[K] extends {
|
|
16
|
+
schema: undefined;
|
|
17
|
+
} ? [K, null?, Options?] : [K, ExtractSchema<RouteList[K]>?, Options?] : RouteList[K] extends {
|
|
18
|
+
schema: undefined;
|
|
19
|
+
} ? [K, null, Options & LocaleOptions<Locales>] : [K, ExtractSchema<RouteList[K]>, Options & LocaleOptions<Locales>];
|
|
6
20
|
}[keyof RouteList];
|
|
7
|
-
type RouteFunction<RouteList
|
|
8
|
-
type RouteToUrlFunction<RouteList
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
type RouteFunction<Locales extends string[], RouteList extends RoutesDefinition<Locales>> = (...args: FunctionParametersGenerator<Locales, RouteList>) => LinkProps["href"];
|
|
22
|
+
type RouteToUrlFunction<Locales extends string[], RouteList extends RoutesDefinition<Locales>> = (...args: FunctionParametersGenerator<Locales, RouteList>) => string;
|
|
23
|
+
type QueryParamsResult<Nullable extends boolean, T extends keyof RouteList, Locales extends string[], RouteList extends RoutesDefinition<Locales>> = [
|
|
24
|
+
Nullable extends true ? Infer<NonNullable<RouteList[T]["schema"]>> | null : Infer<NonNullable<RouteList[T]["schema"]>>,
|
|
25
|
+
{
|
|
26
|
+
push: (params: Infer<NonNullable<RouteList[T]["schema"]>>) => Promise<boolean>;
|
|
27
|
+
replace: (params: Infer<NonNullable<RouteList[T]["schema"]>>) => Promise<boolean>;
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
type Router<Locales extends string[], RouteList extends RoutesDefinition<Locales>> = {
|
|
31
|
+
route: RouteFunction<Locales, RouteList>;
|
|
32
|
+
routeToUrl: RouteToUrlFunction<Locales, RouteList>;
|
|
33
|
+
createSitemapGenerator: (options?: SitemapGeneratorOptions) => SitemapGeneratorType<Locales, RouteList>;
|
|
34
|
+
routes: RouteList;
|
|
35
|
+
useActiveRoute: () => ActiveRoute<Locales, RouteList>;
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated use useQueryParamsStatic or useQueryParams instead
|
|
38
|
+
*/
|
|
39
|
+
useQueryParamsDeprecated: <T extends keyof RouteList>() => QueryParams<RouteList, T>;
|
|
40
|
+
useQueryParams: <T extends keyof RouteList>(routeName: T) => QueryParamsResult<false, T, Locales, RouteList>;
|
|
41
|
+
useQueryParamsStatic: <T extends keyof RouteList>(routeName: T) => QueryParamsResult<true, T, Locales, RouteList>;
|
|
42
|
+
};
|
|
43
|
+
type RouterOptions<L extends string[]> = {
|
|
44
|
+
baseUrl?: string;
|
|
45
|
+
locales?: L;
|
|
46
|
+
};
|
|
47
|
+
type ActiveRoute<Locales extends string[], RouteList extends RoutesDefinition<Locales>> = {
|
|
48
|
+
route: keyof RouteList;
|
|
49
|
+
locale: Locales[number] | null;
|
|
18
50
|
};
|
|
19
|
-
export declare function createRouter<RouteList
|
|
20
|
-
[key in keyof RouteList]: string;
|
|
21
|
-
}): Router<RouteList>;
|
|
51
|
+
export declare function createRouter<Locales extends string[], RouteList extends RoutesDefinition<Locales>>(routes: RouteList, routerOptions: RouterOptions<Locales>): Router<Locales, RouteList>;
|
|
22
52
|
export {};
|
package/router.js
CHANGED
|
@@ -2,18 +2,38 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createRouter = createRouter;
|
|
4
4
|
const empty_object_1 = require("@uxf/core/constants/empty-object");
|
|
5
|
+
const throw_error_1 = require("@uxf/core/utils/throw-error");
|
|
5
6
|
const router_1 = require("next/router");
|
|
6
7
|
const qs_1 = require("qs");
|
|
8
|
+
const superstruct_1 = require("superstruct");
|
|
7
9
|
const sitemap_generator_1 = require("./sitemap-generator");
|
|
8
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Arguments can be:
|
|
12
|
+
*
|
|
13
|
+
* [routeName, params, options]
|
|
14
|
+
*/
|
|
15
|
+
function decodeArgs(args) {
|
|
16
|
+
return { route: args[0], params: args[1], options: args[2] };
|
|
17
|
+
}
|
|
18
|
+
function createRouter(routes, routerOptions) {
|
|
9
19
|
return {
|
|
10
|
-
route
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
route(...args) {
|
|
21
|
+
var _a;
|
|
22
|
+
const { route, params, options } = decodeArgs(args);
|
|
23
|
+
return {
|
|
24
|
+
pathname: typeof routes[route].path === "string"
|
|
25
|
+
? routes[route].path
|
|
26
|
+
: routes[route].path[(_a = options === null || options === void 0 ? void 0 : options.locale) !== null && _a !== void 0 ? _a : (0, throw_error_1.throwError)("Locale is required")],
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
28
|
+
query: params !== null && params !== void 0 ? params : null,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
routeToUrl(...args) {
|
|
32
|
+
var _a;
|
|
33
|
+
const { route, params, options } = decodeArgs(args);
|
|
34
|
+
let pathname = typeof routes[route].path === "string"
|
|
35
|
+
? routes[route].path
|
|
36
|
+
: routes[route].path[(_a = options === null || options === void 0 ? void 0 : options.locale) !== null && _a !== void 0 ? _a : (0, throw_error_1.throwError)("Locale is required")];
|
|
17
37
|
if (!pathname) {
|
|
18
38
|
throw new Error(`Route '${String(route)}' not found.`);
|
|
19
39
|
}
|
|
@@ -61,18 +81,55 @@ function createRouter(routes) {
|
|
|
61
81
|
if (Object.keys(restParams).length > 0) {
|
|
62
82
|
pathname = `${pathname}?${(0, qs_1.stringify)(restParams, { arrayFormat: "repeat" })}`;
|
|
63
83
|
}
|
|
64
|
-
return pathname;
|
|
84
|
+
return (options === null || options === void 0 ? void 0 : options.shouldBeAbsolute) ? `${routerOptions.baseUrl}${pathname}` : pathname;
|
|
65
85
|
},
|
|
66
|
-
useActiveRoute
|
|
86
|
+
useActiveRoute() {
|
|
87
|
+
var _a;
|
|
67
88
|
const router = (0, router_1.useRouter)();
|
|
68
|
-
const activeRoute = Object.keys(routes).find((route) => routes[route] ===
|
|
89
|
+
const activeRoute = Object.keys(routes).find((route) => typeof routes[route].path === "string"
|
|
90
|
+
? routes[route].path === router.pathname
|
|
91
|
+
: Object.values(routes[route].path).includes(router.pathname));
|
|
69
92
|
if (!activeRoute) {
|
|
70
93
|
throw new Error("Active route not found.");
|
|
71
94
|
}
|
|
72
|
-
return
|
|
95
|
+
return {
|
|
96
|
+
route: activeRoute,
|
|
97
|
+
locale: ((_a = router.locale) !== null && _a !== void 0 ? _a : null),
|
|
98
|
+
};
|
|
73
99
|
},
|
|
74
100
|
createSitemapGenerator: sitemap_generator_1.createSitemapGenerator,
|
|
75
101
|
routes,
|
|
76
|
-
|
|
102
|
+
useQueryParamsDeprecated: () => (0, router_1.useRouter)().query,
|
|
103
|
+
useQueryParams(routeName) {
|
|
104
|
+
const router = (0, router_1.useRouter)();
|
|
105
|
+
const schema = routes[routeName].schema;
|
|
106
|
+
if (!schema) {
|
|
107
|
+
throw new Error(`Route '${String(routeName)}' has no schema.`);
|
|
108
|
+
}
|
|
109
|
+
if (router.isReady) {
|
|
110
|
+
throw new Error("Router is not ready. Use useQueryParamsStatic instead of useQueryParams.");
|
|
111
|
+
}
|
|
112
|
+
return [
|
|
113
|
+
(0, superstruct_1.mask)(router.query, schema),
|
|
114
|
+
{
|
|
115
|
+
push: (params) => router.push(this.routeToUrl(routeName, params, {})),
|
|
116
|
+
replace: (params) => router.replace(this.routeToUrl(routeName, params, {})),
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
},
|
|
120
|
+
useQueryParamsStatic(routeName) {
|
|
121
|
+
const router = (0, router_1.useRouter)();
|
|
122
|
+
const schema = routes[routeName].schema;
|
|
123
|
+
if (!schema) {
|
|
124
|
+
throw new Error(`Route '${String(routeName)}' has no schema.`);
|
|
125
|
+
}
|
|
126
|
+
return [
|
|
127
|
+
router.isReady ? (0, superstruct_1.mask)(router.query, schema) : null,
|
|
128
|
+
{
|
|
129
|
+
push: (params) => router.push(this.routeToUrl(routeName, params, {})),
|
|
130
|
+
replace: (params) => router.replace(this.routeToUrl(routeName, params, {})),
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
},
|
|
77
134
|
};
|
|
78
135
|
}
|
package/router.test.js
CHANGED
|
@@ -1,20 +1,129 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const superstruct_1 = require("superstruct");
|
|
3
4
|
const router_1 = require("./router");
|
|
5
|
+
const superstruct_2 = require("./superstruct");
|
|
4
6
|
const { routeToUrl } = (0, router_1.createRouter)({
|
|
5
|
-
index:
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
index: {
|
|
8
|
+
path: "/",
|
|
9
|
+
schema: (0, superstruct_1.object)({
|
|
10
|
+
queryParam: (0, superstruct_1.optional)((0, superstruct_1.string)()),
|
|
11
|
+
}),
|
|
12
|
+
},
|
|
13
|
+
catchAllSegments: {
|
|
14
|
+
path: "/catch-all/[...pathParams]",
|
|
15
|
+
schema: (0, superstruct_1.object)({
|
|
16
|
+
pathParams: (0, superstruct_2.array)((0, superstruct_1.string)()),
|
|
17
|
+
queryParam: (0, superstruct_1.optional)((0, superstruct_1.string)()),
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
20
|
+
optionalCatchAllSegments: {
|
|
21
|
+
path: "/catch-all-optional/[[...pathParams]]",
|
|
22
|
+
schema: (0, superstruct_1.object)({
|
|
23
|
+
pathParams: (0, superstruct_1.optional)((0, superstruct_1.nullable)((0, superstruct_1.union)([(0, superstruct_2.array)((0, superstruct_1.string)()), (0, superstruct_1.string)()]))),
|
|
24
|
+
queryParam: (0, superstruct_1.optional)((0, superstruct_1.string)()),
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
routeWithoutParams: {
|
|
28
|
+
path: "/route-without-params",
|
|
29
|
+
},
|
|
30
|
+
localizedRouteWithoutParams: {
|
|
31
|
+
path: {
|
|
32
|
+
cs: "/lokalizovany-odkaz",
|
|
33
|
+
en: "/localized-url",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
localizedRouteWithParams: {
|
|
37
|
+
path: {
|
|
38
|
+
cs: "/lokalizovany-odkaz-s-parametry",
|
|
39
|
+
en: "/localized-url-with-parameters",
|
|
40
|
+
},
|
|
41
|
+
schema: (0, superstruct_1.object)({
|
|
42
|
+
test: (0, superstruct_1.string)(),
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
}, {
|
|
46
|
+
locales: ["cs", "en"],
|
|
47
|
+
baseUrl: "https://www.uxf.cz",
|
|
8
48
|
});
|
|
49
|
+
const DATA = [
|
|
50
|
+
{
|
|
51
|
+
actual: routeToUrl("index", {}),
|
|
52
|
+
expected: "/",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
actual: routeToUrl("index", { queryParam: "value" }),
|
|
56
|
+
expected: "/?queryParam=value",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
actual: routeToUrl("catchAllSegments", { pathParams: ["param1", "param2"] }),
|
|
60
|
+
expected: "/catch-all/param1/param2",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
actual: routeToUrl("catchAllSegments", { pathParams: ["param1", "param2"], queryParam: "value" }),
|
|
64
|
+
expected: "/catch-all/param1/param2?queryParam=value",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
actual: routeToUrl("optionalCatchAllSegments", { pathParams: ["param1", "param2"], queryParam: "value" }),
|
|
68
|
+
expected: "/catch-all-optional/param1/param2?queryParam=value",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
actual: routeToUrl("optionalCatchAllSegments", { pathParams: [] }),
|
|
72
|
+
expected: "/catch-all-optional",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
actual: routeToUrl("optionalCatchAllSegments", { pathParams: null }),
|
|
76
|
+
expected: "/catch-all-optional",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
actual: routeToUrl("optionalCatchAllSegments", { pathParams: undefined }),
|
|
80
|
+
expected: "/catch-all-optional",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
actual: routeToUrl("optionalCatchAllSegments", { pathParams: "" }),
|
|
84
|
+
expected: "/catch-all-optional",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
actual: routeToUrl("optionalCatchAllSegments", { pathParams: "", queryParam: "value" }),
|
|
88
|
+
expected: "/catch-all-optional?queryParam=value",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
actual: routeToUrl("routeWithoutParams"),
|
|
92
|
+
expected: "/route-without-params",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
actual: routeToUrl("localizedRouteWithoutParams", null, { locale: "en" }),
|
|
96
|
+
expected: "/localized-url",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
actual: routeToUrl("localizedRouteWithoutParams", null, { locale: "cs" }),
|
|
100
|
+
expected: "/lokalizovany-odkaz",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
actual: routeToUrl("localizedRouteWithParams", { test: "value" }, { locale: "en" }),
|
|
104
|
+
expected: "/localized-url-with-parameters?test=value",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
actual: routeToUrl("localizedRouteWithParams", { test: "value" }, { locale: "cs" }),
|
|
108
|
+
expected: "/lokalizovany-odkaz-s-parametry?test=value",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
actual: routeToUrl("index", { queryParam: "value" }, { shouldBeAbsolute: true }),
|
|
112
|
+
expected: "https://www.uxf.cz/?queryParam=value",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
actual: routeToUrl("routeWithoutParams", null, { shouldBeAbsolute: true }),
|
|
116
|
+
expected: "https://www.uxf.cz/route-without-params",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
actual: routeToUrl("localizedRouteWithoutParams", null, { locale: "cs", shouldBeAbsolute: true }),
|
|
120
|
+
expected: "https://www.uxf.cz/lokalizovany-odkaz",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
actual: routeToUrl("localizedRouteWithParams", { test: "value" }, { locale: "en", shouldBeAbsolute: true }),
|
|
124
|
+
expected: "https://www.uxf.cz/localized-url-with-parameters?test=value",
|
|
125
|
+
},
|
|
126
|
+
];
|
|
9
127
|
test("routeToUrl", () => {
|
|
10
|
-
|
|
11
|
-
expect(routeToUrl("index", { queryParam: "value" })).toBe("/?queryParam=value");
|
|
12
|
-
expect(routeToUrl("catchAllSegments", { pathParams: ["param1", "param2"] })).toBe("/catch-all/param1/param2");
|
|
13
|
-
expect(routeToUrl("catchAllSegments", { pathParams: ["param1", "param2"], queryParam: "value" })).toBe("/catch-all/param1/param2?queryParam=value");
|
|
14
|
-
expect(routeToUrl("optionalCatchAllSegments", { pathParams: ["param1", "param2"], queryParam: "value" })).toBe("/catch-all-optional/param1/param2?queryParam=value");
|
|
15
|
-
expect(routeToUrl("optionalCatchAllSegments", { pathParams: [] })).toBe("/catch-all-optional");
|
|
16
|
-
expect(routeToUrl("optionalCatchAllSegments", { pathParams: null })).toBe("/catch-all-optional");
|
|
17
|
-
expect(routeToUrl("optionalCatchAllSegments", { pathParams: undefined })).toBe("/catch-all-optional");
|
|
18
|
-
expect(routeToUrl("optionalCatchAllSegments", { pathParams: "" })).toBe("/catch-all-optional");
|
|
19
|
-
expect(routeToUrl("optionalCatchAllSegments", { pathParams: "", queryParam: "value" })).toBe("/catch-all-optional?queryParam=value");
|
|
128
|
+
DATA.map(({ actual, expected }) => expect(actual).toBe(expected));
|
|
20
129
|
});
|
package/sitemap-generator.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RoutesDefinition } from "./types";
|
|
1
2
|
type ChangeFrequency = "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
2
3
|
export type SitemapItem = {
|
|
3
4
|
loc: string;
|
|
@@ -11,10 +12,10 @@ type MissingRoutesError<Routes> = {
|
|
|
11
12
|
__nonExhaustive: never;
|
|
12
13
|
missingRoutes: Routes;
|
|
13
14
|
};
|
|
14
|
-
export type SitemapGeneratorType<RouteList
|
|
15
|
-
add: <Route extends keyof RouteList>(route: Route, resolver: RouteResolver<Route>) => SitemapGeneratorType<Omit<RouteList, Route>>;
|
|
16
|
-
skip: <Route extends keyof RouteList>(route: Route) => SitemapGeneratorType<Omit<RouteList, Route>>;
|
|
17
|
-
exhaustive: keyof RouteList extends never ? () => SitemapGeneratorType<RouteList> : MissingRoutesError<keyof RouteList>;
|
|
15
|
+
export type SitemapGeneratorType<Locales extends string[], RouteList extends RoutesDefinition<Locales>> = {
|
|
16
|
+
add: <Route extends keyof RouteList>(route: Route, resolver: RouteResolver<Route>) => SitemapGeneratorType<Locales, Omit<RouteList, Route>>;
|
|
17
|
+
skip: <Route extends keyof RouteList>(route: Route) => SitemapGeneratorType<Locales, Omit<RouteList, Route>>;
|
|
18
|
+
exhaustive: keyof RouteList extends never ? () => SitemapGeneratorType<Locales, RouteList> : MissingRoutesError<keyof RouteList>;
|
|
18
19
|
toXml: keyof RouteList extends never ? () => Promise<string> : MissingRoutesError<keyof RouteList>;
|
|
19
20
|
toJson: keyof RouteList extends never ? () => Promise<any> : MissingRoutesError<keyof RouteList>;
|
|
20
21
|
};
|
|
@@ -23,5 +24,5 @@ export type SitemapGeneratorOptions = {
|
|
|
23
24
|
defaultPriority?: number;
|
|
24
25
|
defaultChangeFreq?: ChangeFrequency;
|
|
25
26
|
};
|
|
26
|
-
export declare const createSitemapGenerator: <RouteList
|
|
27
|
+
export declare const createSitemapGenerator: <Locales extends string[], RouteList extends RoutesDefinition<Locales>>(options?: SitemapGeneratorOptions) => SitemapGeneratorType<Locales, RouteList>;
|
|
27
28
|
export {};
|
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const superstruct_1 = require("superstruct");
|
|
3
4
|
const router_1 = require("./router");
|
|
4
5
|
const { createSitemapGenerator, routeToUrl } = (0, router_1.createRouter)({
|
|
5
|
-
index:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
index: {
|
|
7
|
+
path: "/",
|
|
8
|
+
schema: (0, superstruct_1.object)({
|
|
9
|
+
queryParam: (0, superstruct_1.optional)((0, superstruct_1.string)()),
|
|
10
|
+
}),
|
|
11
|
+
},
|
|
12
|
+
catchAllSegments: {
|
|
13
|
+
path: "/catch-all/[...pathParams]",
|
|
14
|
+
schema: (0, superstruct_1.object)({
|
|
15
|
+
pathParams: (0, superstruct_1.array)(),
|
|
16
|
+
queryParam: (0, superstruct_1.optional)((0, superstruct_1.string)()),
|
|
17
|
+
}),
|
|
18
|
+
},
|
|
19
|
+
optionalCatchAllSegments: {
|
|
20
|
+
path: "/catch-all-optional/[[...pathParams]]",
|
|
21
|
+
schema: (0, superstruct_1.object)({
|
|
22
|
+
pathParams: (0, superstruct_1.optional)((0, superstruct_1.nullable)((0, superstruct_1.union)([(0, superstruct_1.array)(), (0, superstruct_1.string)()]))),
|
|
23
|
+
queryParam: (0, superstruct_1.optional)((0, superstruct_1.string)()),
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
}, { baseUrl: "https://www.uxf.cz" });
|
|
9
27
|
const BASE_URL = "www.uxf.cz";
|
|
10
28
|
test("routeToUrl", async () => {
|
|
11
29
|
const xml = await createSitemapGenerator({ baseUrl: BASE_URL })
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Infer, Struct } from "superstruct";
|
|
2
|
+
/**
|
|
3
|
+
* NextJS používá následující zápis pole v url `?item=a&item=b&item=c`
|
|
4
|
+
*
|
|
5
|
+
* Není teda schopen rozeznat, zda parametr `?item=a` je pole o jednom prvku nebo pouze string
|
|
6
|
+
*/
|
|
7
|
+
export declare function array<T extends Struct<any>>(inner: T): Struct<Infer<T>[], null>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.array = array;
|
|
4
|
+
const superstruct_1 = require("superstruct");
|
|
5
|
+
/**
|
|
6
|
+
* NextJS používá následující zápis pole v url `?item=a&item=b&item=c`
|
|
7
|
+
*
|
|
8
|
+
* Není teda schopen rozeznat, zda parametr `?item=a` je pole o jednom prvku nebo pouze string
|
|
9
|
+
*/
|
|
10
|
+
function array(inner) {
|
|
11
|
+
return (0, superstruct_1.coerce)((0, superstruct_1.array)(inner), (0, superstruct_1.union)([(0, superstruct_1.string)(), (0, superstruct_1.array)((0, superstruct_1.string)())]), (value) => {
|
|
12
|
+
return Array.isArray(value) ? value : [value];
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const boolean: () => import("superstruct").Struct<boolean, null>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.boolean = void 0;
|
|
4
|
+
const superstruct_1 = require("superstruct");
|
|
5
|
+
const boolean = () => (0, superstruct_1.coerce)((0, superstruct_1.boolean)(), (0, superstruct_1.string)(), (value) => {
|
|
6
|
+
return value === "1" || value === "true" || value === "";
|
|
7
|
+
});
|
|
8
|
+
exports.boolean = boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.string = exports.optional = exports.object = void 0;
|
|
18
|
+
var superstruct_1 = require("superstruct");
|
|
19
|
+
Object.defineProperty(exports, "object", { enumerable: true, get: function () { return superstruct_1.object; } });
|
|
20
|
+
Object.defineProperty(exports, "optional", { enumerable: true, get: function () { return superstruct_1.optional; } });
|
|
21
|
+
Object.defineProperty(exports, "string", { enumerable: true, get: function () { return superstruct_1.string; } });
|
|
22
|
+
__exportStar(require("./array"), exports);
|
|
23
|
+
__exportStar(require("./boolean"), exports);
|
|
24
|
+
__exportStar(require("./integer"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const integer: () => import("superstruct").Struct<number, null>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.integer = void 0;
|
|
4
|
+
const superstruct_1 = require("superstruct");
|
|
5
|
+
const integer = () => (0, superstruct_1.coerce)((0, superstruct_1.integer)(), (0, superstruct_1.string)(), (value) => {
|
|
6
|
+
const parsed = parseInt(value, 10);
|
|
7
|
+
if (isNaN(parsed)) {
|
|
8
|
+
throw new Error("Invalid number string");
|
|
9
|
+
}
|
|
10
|
+
return parsed;
|
|
11
|
+
});
|
|
12
|
+
exports.integer = integer;
|
package/types.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { GetServerSideProps, GetStaticProps, PreviewData as NextPreviewData } from "next";
|
|
2
|
+
import { Struct } from "superstruct";
|
|
3
|
+
export type RoutesDefinition<Locales extends string[]> = Record<string, {
|
|
4
|
+
path: string | Record<Locales[number], string>;
|
|
5
|
+
schema?: Struct<any, any> | null;
|
|
6
|
+
}>;
|
|
2
7
|
export type QueryParams<RouteList, Route extends keyof RouteList> = RouteList[Route] extends null ? never : {
|
|
3
8
|
[key in keyof RouteList[Route]]: string | string[] | undefined;
|
|
4
9
|
};
|