svelte-navigator-lite 1.1.3 → 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 -2
- package/dist/router.svelte.js +9 -16
- package/dist/router.test.js +45 -21
- 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,7 +11,6 @@ export type Route = {
|
|
|
11
11
|
enforceVal?: string;
|
|
12
12
|
optional?: boolean;
|
|
13
13
|
})[];
|
|
14
|
-
searchParams?: string[];
|
|
15
14
|
routeGuards?: RouteGuard[];
|
|
16
15
|
};
|
|
17
16
|
export type RouteGuard = {
|
|
@@ -23,12 +22,13 @@ export type Router = ReturnType<typeof _createRouter>;
|
|
|
23
22
|
export declare function _createRouter(routeList?: RouteList): {
|
|
24
23
|
readonly route: string;
|
|
25
24
|
readonly params: Record<string, string>;
|
|
25
|
+
readonly searchParams: Record<string, string>;
|
|
26
26
|
readonly notFound: boolean;
|
|
27
27
|
rootRoute: string;
|
|
28
28
|
is(route: string): boolean;
|
|
29
29
|
matches(routes: string[]): boolean;
|
|
30
30
|
parseUrl: (url: string) => void;
|
|
31
|
-
navigate(route: string, params?: Record<string, string>): Promise<void>;
|
|
31
|
+
navigate(route: string, params?: Record<string, string>, searchParams?: Record<string, string>): Promise<void>;
|
|
32
32
|
registerRoute(name: string, route: Route): void;
|
|
33
33
|
};
|
|
34
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,22 +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)
|
|
82
|
-
params[key] = val;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
79
|
if (match) {
|
|
86
80
|
state.current = routeName;
|
|
87
81
|
state.params = params;
|
|
82
|
+
state.searchParams = Object.fromEntries(searchParams.entries());
|
|
88
83
|
state.notFound = false;
|
|
89
84
|
return;
|
|
90
85
|
}
|
|
91
86
|
}
|
|
92
87
|
state.current = rootRoute;
|
|
93
88
|
state.params = {};
|
|
89
|
+
state.searchParams = {};
|
|
94
90
|
state.notFound = true;
|
|
95
91
|
}
|
|
96
92
|
return {
|
|
@@ -101,6 +97,9 @@ export function _createRouter(routeList = {}) {
|
|
|
101
97
|
get params() {
|
|
102
98
|
return state.params;
|
|
103
99
|
},
|
|
100
|
+
get searchParams() {
|
|
101
|
+
return state.searchParams;
|
|
102
|
+
},
|
|
104
103
|
get notFound() {
|
|
105
104
|
return state.notFound;
|
|
106
105
|
},
|
|
@@ -114,7 +113,7 @@ export function _createRouter(routeList = {}) {
|
|
|
114
113
|
return routes.includes(state.current);
|
|
115
114
|
},
|
|
116
115
|
parseUrl,
|
|
117
|
-
async navigate(route, params) {
|
|
116
|
+
async navigate(route, params, searchParams) {
|
|
118
117
|
let path = routes[route].rootPath;
|
|
119
118
|
for (const guard of routes[route].routeGuards || []) {
|
|
120
119
|
if (guard.fn()) {
|
|
@@ -133,14 +132,8 @@ export function _createRouter(routeList = {}) {
|
|
|
133
132
|
path += `/${params[segment.name]}`;
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
|
-
if (
|
|
137
|
-
const
|
|
138
|
-
for (const param of routes[route].searchParams) {
|
|
139
|
-
if (params && params[param]) {
|
|
140
|
-
searchParams.set(param, params[param]);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const qs = searchParams.toString();
|
|
135
|
+
if (searchParams) {
|
|
136
|
+
const qs = new URLSearchParams(searchParams).toString();
|
|
144
137
|
if (qs)
|
|
145
138
|
path += `?${qs}`;
|
|
146
139
|
}
|
package/dist/router.test.js
CHANGED
|
@@ -129,12 +129,11 @@ 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
|
};
|
|
@@ -142,24 +141,28 @@ describe('parseUrl — required searchParams', () => {
|
|
|
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('still matches when
|
|
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
149
|
expect(r.route).toBe('password-reset');
|
|
151
|
-
expect(r.
|
|
150
|
+
expect(r.searchParams).toEqual({});
|
|
152
151
|
});
|
|
153
|
-
it('captures multiple search params
|
|
152
|
+
it('captures multiple search params', () => {
|
|
154
153
|
const r = makeRouter({
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
r
|
|
162
|
-
|
|
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' });
|
|
163
166
|
});
|
|
164
167
|
});
|
|
165
168
|
describe('parseUrl — fallback and notFound', () => {
|
|
@@ -240,17 +243,38 @@ describe('navigate()', () => {
|
|
|
240
243
|
}, 'event');
|
|
241
244
|
await expect(r.navigate('event')).rejects.toThrow('Missing parameter eventId');
|
|
242
245
|
});
|
|
243
|
-
it('appends search params
|
|
246
|
+
it('appends search params when provided', async () => {
|
|
244
247
|
const r = makeRouter({
|
|
245
|
-
'password-reset': {
|
|
246
|
-
rootPath: 'password-reset',
|
|
247
|
-
segments: [],
|
|
248
|
-
searchParams: ['token'],
|
|
249
|
-
},
|
|
248
|
+
'password-reset': { rootPath: 'password-reset', segments: [] },
|
|
250
249
|
}, 'password-reset');
|
|
251
|
-
await r.navigate('password-reset', { token: 'abc' });
|
|
250
|
+
await r.navigate('password-reset', undefined, { token: 'abc' });
|
|
252
251
|
expect(history.pushState).toHaveBeenCalledWith({}, '', '/password-reset?token=abc');
|
|
253
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
|
+
});
|
|
254
278
|
it('redirects when a guard fn returns true', async () => {
|
|
255
279
|
const r = makeRouter({
|
|
256
280
|
login: { rootPath: 'login', segments: [] },
|