Skip to content

Commit c31017b

Browse files
committed
feat(router): improve type inference for routing functions
1 parent 781333d commit c31017b

File tree

13 files changed

+74
-61
lines changed

13 files changed

+74
-61
lines changed

demo/App.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
Any: 'Any',
3636
} as const;
3737
38-
type Routes = (typeof RouteName)[keyof typeof RouteName] | string;
38+
type Routes = (typeof RouteName)[keyof typeof RouteName] | (string & {});
3939
4040
let beforeChecked = false;
4141

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
"svelte": ">=5"
122122
},
123123
"dependencies": {
124-
"@dvcol/common-utils": "^1.31.1",
124+
"@dvcol/common-utils": "^1.31.2",
125125
"@dvcol/svelte-utils": "^1.17.1",
126126
"svelte": "^5.32.1"
127127
},

pnpm-lock.yaml

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/action/active.action.svelte.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { getRouter } from '~/router/context.svelte.js';
3232
* ```
3333
*/
3434
export const active: Action<HTMLElement, ActiveOptions | undefined> = (node: HTMLElement, options: ActiveOptions | undefined = {}) => {
35-
const router = getRouter();
35+
const router = options?.router || getRouter();
3636
if (!ensureRouter(node, router)) return {};
3737

3838
let _options = $state(options);

src/lib/action/link.action.svelte.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { Action } from 'svelte/action';
22

3+
import type { RouteName } from '~/models/index.js';
34
import type { LinkNavigateFunction, LinkNavigateOptions } from '~/models/link.model.js';
45

56
import { getLinkNavigateFunction, getResolveFunction, normalizeLinkAttributes } from '~/models/link.model.js';
67
import { Logger } from '~/utils/logger.utils.js';
78

8-
export type LinkActionOptions = LinkNavigateOptions;
9+
export type LinkActionOptions<Name extends RouteName = RouteName, Path extends string = string> = LinkNavigateOptions<Name, Path>;
910

1011
/**
1112
* A svelte action to add to an element to navigate to a new location using the router.

src/lib/action/links.action.svelte.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import type { Action } from 'svelte/action';
22

3+
import type { RouteName } from '~/models/index.js';
34
import type { LinkNavigateFunction, LinkNavigateOptions } from '~/models/link.model.js';
45

56
import { getLinkNavigateFunction, getResolveFunction, parseBooleanAttribute } from '~/models/link.model.js';
67
import { Logger } from '~/utils/logger.utils.js';
78

89
export type NodeConditionFn = (node: HTMLElement) => boolean;
9-
export interface LinksActionOptions {
10+
export interface LinksActionOptions<Name extends RouteName = RouteName, Path extends string = string> {
1011
/**
1112
* Whether the target node should be considered a link.
1213
*/
@@ -18,7 +19,7 @@ export interface LinksActionOptions {
1819
/**
1920
* The navigate options to use for the navigation.
2021
*/
21-
navigate?: LinkNavigateOptions;
22+
navigate?: LinkNavigateOptions<Name, Path>;
2223
}
2324

2425
function isLinkNode(node: HTMLElement, apply?: LinksActionOptions['apply']): boolean {

src/lib/attachment/active.attachment.svelte.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Matcher } from '~/models/index.js';
88
import { getRouter } from '~/router/context.svelte.js';
99

1010
export function useActive(options: ActiveOptions): Attachment {
11-
const router = getRouter();
11+
const router = options?.router || getRouter();
1212

1313
return (element) => {
1414
if (!ensureRouter(element, router)) return;

src/lib/models/action.model.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import type { IRouter } from '~/models/router.model.js';
55
import { Logger } from '~/utils/logger.utils.js';
66

77
export interface ActiveOptions<Name extends RouteName = RouteName> {
8+
/**
9+
* Optional router instance to use for matching.
10+
* If not provided, the router will be extracted from the context.
11+
*/
12+
router?: IRouter<Name>;
813
/**
914
* Route name to match against.
1015
* This takes precedence over the path option.

src/lib/models/link.model.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { CommonRouteNavigation, ResolvedRoute, RouteName, RouteNavigation } from '~/models/route.model.js';
2-
import type { ResolvedRouterLocationSnapshot, RouterNavigationOptions } from '~/models/router.model.js';
2+
import type { IRouter, ResolvedRouterLocationSnapshot, RouterNavigationOptions } from '~/models/router.model.js';
33

44
import { resolveComponent } from '@dvcol/svelte-utils/component';
55

@@ -79,7 +79,7 @@ export type LinkNavigateFunction = <Action extends 'replace' | 'push' | 'resolve
7979
* @throws {MissingRouterContextError} - If the router context is not found
8080
*/
8181
export function getLinkNavigateFunction(options: LinkNavigateOptions = {}): LinkNavigateFunction {
82-
const router = getRouter();
82+
const router = options?.router || getRouter();
8383
if (!router) throw new MissingRouterContextError();
8484

8585
return async (event, node, action) => {
@@ -155,8 +155,13 @@ export function normalizeLinkAttributes(node: HTMLElement, options: LinkNavigate
155155
return { node, options };
156156
}
157157

158-
export interface LinkNavigateOptions<Name extends RouteName = RouteName> extends CommonRouteNavigation,
158+
export interface LinkNavigateOptions<Name extends RouteName = RouteName, Path extends string = string> extends CommonRouteNavigation<Path>,
159159
RouterNavigationOptions {
160+
/**
161+
* Optional router instance to use for matching.
162+
* If not provided, the router will be extracted from the context.
163+
*/
164+
router?: IRouter<Name>;
160165
/**
161166
* Whether to resolve the link on hover or focus.
162167
* If a string is provided, it will be used as the name of the view to resolve instead of the link's target.
@@ -173,7 +178,7 @@ export interface LinkNavigateOptions<Name extends RouteName = RouteName> extends
173178
/**
174179
* The path of the route to navigate to.
175180
*/
176-
path?: string;
181+
path?: Path;
177182
/**
178183
* Whether the link is disabled.
179184
*/

src/lib/models/matcher.model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ export function templateToParams(template: string) {
137137
}
138138

139139
export interface PathParamsResult {
140-
params: Record<string, string | number | boolean | undefined | null>;
141-
wildcards: Record<string, string | number | boolean | undefined | null>;
140+
params: Record<string, string | number | boolean | undefined>;
141+
wildcards: Record<string, string | number | boolean | undefined>;
142142
}
143143
export interface IMatcher {
144144
/**

0 commit comments

Comments
 (0)