svelte-navigator-lite 1.1.2 → 1.1.4
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 +15 -13
- package/dist/router.svelte.d.ts +2 -3
- package/dist/router.svelte.js +11 -26
- package/dist/router.test.js +48 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,32 +110,30 @@ Add `optional: true` to make a trailing segment optional. Optional segments must
|
|
|
110
110
|
|
|
111
111
|
## Search Params
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
Search params are automatically captured into `router.searchParams` when present in the URL. No configuration is needed — they never affect whether a route matches.
|
|
114
114
|
|
|
115
115
|
```ts
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
segments: [],
|
|
119
|
-
searchParams: ['token'],
|
|
120
|
-
}
|
|
121
|
-
// /password-reset?token=abc → router.params.token === 'abc'
|
|
122
|
-
// /password-reset → router.params === {}
|
|
116
|
+
// /password-reset?token=abc → router.searchParams.token === 'abc'
|
|
117
|
+
// /password-reset → router.searchParams === {}
|
|
123
118
|
```
|
|
124
119
|
|
|
125
120
|
---
|
|
126
121
|
|
|
127
122
|
## Navigating
|
|
128
123
|
|
|
129
|
-
### `router.navigate(route, params?)`
|
|
124
|
+
### `router.navigate(route, params?, searchParams?)`
|
|
130
125
|
|
|
131
|
-
Navigate to a named route.
|
|
126
|
+
Navigate to a named route. Path params fill dynamic segments; pass search params separately as the third argument.
|
|
132
127
|
|
|
133
128
|
```ts
|
|
134
129
|
router.navigate('event', { eventId: '123' });
|
|
135
130
|
// → /event/123
|
|
136
131
|
|
|
137
|
-
router.navigate('password-reset', { token: 'abc123' });
|
|
132
|
+
router.navigate('password-reset', undefined, { token: 'abc123' });
|
|
138
133
|
// → /password-reset?token=abc123
|
|
134
|
+
|
|
135
|
+
router.navigate('event', { eventId: '123' }, { tab: 'details' });
|
|
136
|
+
// → /event/123?tab=details
|
|
139
137
|
```
|
|
140
138
|
|
|
141
139
|
Throws if a required param is missing.
|
|
@@ -201,7 +199,11 @@ The label of the currently matched route. Falls back to `defaultRoute` if no rou
|
|
|
201
199
|
|
|
202
200
|
### `router.params`
|
|
203
201
|
|
|
204
|
-
An object containing
|
|
202
|
+
An object containing the captured path param values for the current route.
|
|
203
|
+
|
|
204
|
+
### `router.searchParams`
|
|
205
|
+
|
|
206
|
+
An object containing the search params present in the current URL. Empty object when none are present.
|
|
205
207
|
|
|
206
208
|
### `router.notFound`
|
|
207
209
|
|
|
@@ -215,7 +217,7 @@ An object containing all captured values — path params, required search params
|
|
|
215
217
|
{/if}
|
|
216
218
|
```
|
|
217
219
|
|
|
218
|
-
### `router.navigate(route, params?)`
|
|
220
|
+
### `router.navigate(route, params?, searchParams?)`
|
|
219
221
|
|
|
220
222
|
Navigate to a named route, applying guards and building the URL from the route definition.
|
|
221
223
|
|
package/dist/router.svelte.d.ts
CHANGED
|
@@ -11,8 +11,6 @@ export type Route = {
|
|
|
11
11
|
enforceVal?: string;
|
|
12
12
|
optional?: boolean;
|
|
13
13
|
})[];
|
|
14
|
-
searchParams?: string[];
|
|
15
|
-
optionalSearchParams?: string[];
|
|
16
14
|
routeGuards?: RouteGuard[];
|
|
17
15
|
};
|
|
18
16
|
export type RouteGuard = {
|
|
@@ -24,12 +22,13 @@ export type Router = ReturnType<typeof _createRouter>;
|
|
|
24
22
|
export declare function _createRouter(routeList?: RouteList): {
|
|
25
23
|
readonly route: string;
|
|
26
24
|
readonly params: Record<string, string>;
|
|
25
|
+
readonly searchParams: Record<string, string>;
|
|
27
26
|
readonly notFound: boolean;
|
|
28
27
|
rootRoute: string;
|
|
29
28
|
is(route: string): boolean;
|
|
30
29
|
matches(routes: string[]): boolean;
|
|
31
30
|
parseUrl: (url: string) => void;
|
|
32
|
-
navigate(route: string, params?: Record<string, string>): Promise<void>;
|
|
31
|
+
navigate(route: string, params?: Record<string, string>, searchParams?: Record<string, string>): Promise<void>;
|
|
33
32
|
registerRoute(name: string, route: Route): void;
|
|
34
33
|
};
|
|
35
34
|
export declare let router: Router;
|
package/dist/router.svelte.js
CHANGED
|
@@ -38,6 +38,7 @@ export function _createRouter(routeList = {}) {
|
|
|
38
38
|
let state = $state({
|
|
39
39
|
current: '',
|
|
40
40
|
params: {},
|
|
41
|
+
searchParams: {},
|
|
41
42
|
notFound: false,
|
|
42
43
|
});
|
|
43
44
|
let routes = {};
|
|
@@ -75,32 +76,17 @@ export function _createRouter(routeList = {}) {
|
|
|
75
76
|
params[routeSegment.name] = urlSegment;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
if (match && route.searchParams) {
|
|
79
|
-
for (const key of route.searchParams) {
|
|
80
|
-
const val = searchParams.get(key);
|
|
81
|
-
if (val === null) {
|
|
82
|
-
match = false;
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
params[key] = val;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (match && route.optionalSearchParams) {
|
|
89
|
-
for (const key of route.optionalSearchParams) {
|
|
90
|
-
const val = searchParams.get(key);
|
|
91
|
-
if (val !== null)
|
|
92
|
-
params[key] = val;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
79
|
if (match) {
|
|
96
80
|
state.current = routeName;
|
|
97
81
|
state.params = params;
|
|
82
|
+
state.searchParams = Object.fromEntries(searchParams.entries());
|
|
98
83
|
state.notFound = false;
|
|
99
84
|
return;
|
|
100
85
|
}
|
|
101
86
|
}
|
|
102
87
|
state.current = rootRoute;
|
|
103
88
|
state.params = {};
|
|
89
|
+
state.searchParams = {};
|
|
104
90
|
state.notFound = true;
|
|
105
91
|
}
|
|
106
92
|
return {
|
|
@@ -111,6 +97,9 @@ export function _createRouter(routeList = {}) {
|
|
|
111
97
|
get params() {
|
|
112
98
|
return state.params;
|
|
113
99
|
},
|
|
100
|
+
get searchParams() {
|
|
101
|
+
return state.searchParams;
|
|
102
|
+
},
|
|
114
103
|
get notFound() {
|
|
115
104
|
return state.notFound;
|
|
116
105
|
},
|
|
@@ -124,7 +113,7 @@ export function _createRouter(routeList = {}) {
|
|
|
124
113
|
return routes.includes(state.current);
|
|
125
114
|
},
|
|
126
115
|
parseUrl,
|
|
127
|
-
async navigate(route, params) {
|
|
116
|
+
async navigate(route, params, searchParams) {
|
|
128
117
|
let path = routes[route].rootPath;
|
|
129
118
|
for (const guard of routes[route].routeGuards || []) {
|
|
130
119
|
if (guard.fn()) {
|
|
@@ -143,14 +132,10 @@ export function _createRouter(routeList = {}) {
|
|
|
143
132
|
path += `/${params[segment.name]}`;
|
|
144
133
|
}
|
|
145
134
|
}
|
|
146
|
-
if (
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
searchParams.set(param, params[param]);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
path += `?${searchParams.toString()}`;
|
|
135
|
+
if (searchParams) {
|
|
136
|
+
const qs = new URLSearchParams(searchParams).toString();
|
|
137
|
+
if (qs)
|
|
138
|
+
path += `?${qs}`;
|
|
154
139
|
}
|
|
155
140
|
await goto(path);
|
|
156
141
|
},
|
package/dist/router.test.js
CHANGED
|
@@ -129,46 +129,40 @@ describe('parseUrl — optional segments', () => {
|
|
|
129
129
|
expect(r.notFound).toBe(true);
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
|
-
describe('parseUrl —
|
|
132
|
+
describe('parseUrl — search params', () => {
|
|
133
133
|
const routes = {
|
|
134
134
|
'password-reset': {
|
|
135
135
|
rootPath: 'password-reset',
|
|
136
136
|
segments: [],
|
|
137
|
-
searchParams: ['token'],
|
|
138
137
|
},
|
|
139
138
|
fallback: { rootPath: 'fallback', segments: [] },
|
|
140
139
|
};
|
|
141
|
-
it('
|
|
140
|
+
it('captures a search param when present', () => {
|
|
142
141
|
const r = makeRouter(routes, 'fallback');
|
|
143
142
|
r.parseUrl('http://localhost/password-reset?token=abc123');
|
|
144
143
|
expect(r.route).toBe('password-reset');
|
|
145
|
-
expect(r.
|
|
144
|
+
expect(r.searchParams).toEqual({ token: 'abc123' });
|
|
146
145
|
});
|
|
147
|
-
it('
|
|
146
|
+
it('still matches when no search params are present', () => {
|
|
148
147
|
const r = makeRouter(routes, 'fallback');
|
|
149
148
|
r.parseUrl('http://localhost/password-reset');
|
|
150
|
-
expect(r.
|
|
149
|
+
expect(r.route).toBe('password-reset');
|
|
150
|
+
expect(r.searchParams).toEqual({});
|
|
151
151
|
});
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
r.parseUrl('http://localhost/
|
|
164
|
-
expect(r.
|
|
165
|
-
expect(r.
|
|
166
|
-
});
|
|
167
|
-
it('still matches when optional search param is absent', () => {
|
|
168
|
-
const r = makeRouter(routes, 'signup');
|
|
169
|
-
r.parseUrl('http://localhost/signup');
|
|
170
|
-
expect(r.route).toBe('signup');
|
|
171
|
-
expect(r.params).toEqual({});
|
|
152
|
+
it('captures multiple search params', () => {
|
|
153
|
+
const r = makeRouter({
|
|
154
|
+
items: { rootPath: 'items', segments: [] },
|
|
155
|
+
}, 'items');
|
|
156
|
+
r.parseUrl('http://localhost/items?page=2&sort=asc');
|
|
157
|
+
expect(r.searchParams).toEqual({ page: '2', sort: 'asc' });
|
|
158
|
+
});
|
|
159
|
+
it('captures search params alongside path params', () => {
|
|
160
|
+
const r = makeRouter({
|
|
161
|
+
event: { rootPath: 'event', segments: [{ name: 'eventId' }] },
|
|
162
|
+
}, 'event');
|
|
163
|
+
r.parseUrl('http://localhost/event/123?tab=details');
|
|
164
|
+
expect(r.params).toEqual({ eventId: '123' });
|
|
165
|
+
expect(r.searchParams).toEqual({ tab: 'details' });
|
|
172
166
|
});
|
|
173
167
|
});
|
|
174
168
|
describe('parseUrl — fallback and notFound', () => {
|
|
@@ -249,17 +243,38 @@ describe('navigate()', () => {
|
|
|
249
243
|
}, 'event');
|
|
250
244
|
await expect(r.navigate('event')).rejects.toThrow('Missing parameter eventId');
|
|
251
245
|
});
|
|
252
|
-
it('appends search params
|
|
246
|
+
it('appends search params when provided', async () => {
|
|
253
247
|
const r = makeRouter({
|
|
254
|
-
'password-reset': {
|
|
255
|
-
rootPath: 'password-reset',
|
|
256
|
-
segments: [],
|
|
257
|
-
searchParams: ['token'],
|
|
258
|
-
},
|
|
248
|
+
'password-reset': { rootPath: 'password-reset', segments: [] },
|
|
259
249
|
}, 'password-reset');
|
|
260
|
-
await r.navigate('password-reset', { token: 'abc' });
|
|
250
|
+
await r.navigate('password-reset', undefined, { token: 'abc' });
|
|
261
251
|
expect(history.pushState).toHaveBeenCalledWith({}, '', '/password-reset?token=abc');
|
|
262
252
|
});
|
|
253
|
+
it('appends multiple search params', async () => {
|
|
254
|
+
const r = makeRouter({
|
|
255
|
+
items: { rootPath: 'items', segments: [] },
|
|
256
|
+
}, 'items');
|
|
257
|
+
await r.navigate('items', undefined, { page: '2', sort: 'asc' });
|
|
258
|
+
const call = history.pushState.mock.calls[0][2];
|
|
259
|
+
const url = new URL(call, 'http://localhost');
|
|
260
|
+
expect(url.pathname).toBe('/items');
|
|
261
|
+
expect(url.searchParams.get('page')).toBe('2');
|
|
262
|
+
expect(url.searchParams.get('sort')).toBe('asc');
|
|
263
|
+
});
|
|
264
|
+
it('navigates with both path params and search params', async () => {
|
|
265
|
+
const r = makeRouter({
|
|
266
|
+
event: { rootPath: 'event', segments: [{ name: 'eventId' }] },
|
|
267
|
+
}, 'event');
|
|
268
|
+
await r.navigate('event', { eventId: '42' }, { tab: 'details' });
|
|
269
|
+
expect(history.pushState).toHaveBeenCalledWith({}, '', '/event/42?tab=details');
|
|
270
|
+
});
|
|
271
|
+
it('does not append search params when not provided', async () => {
|
|
272
|
+
const r = makeRouter({
|
|
273
|
+
event: { rootPath: 'event', segments: [{ name: 'eventId' }] },
|
|
274
|
+
}, 'event');
|
|
275
|
+
await r.navigate('event', { eventId: '42' });
|
|
276
|
+
expect(history.pushState).toHaveBeenCalledWith({}, '', '/event/42');
|
|
277
|
+
});
|
|
263
278
|
it('redirects when a guard fn returns true', async () => {
|
|
264
279
|
const r = makeRouter({
|
|
265
280
|
login: { rootPath: 'login', segments: [] },
|