svelte-spa-history-router 3.0.0-next.2 → 3.0.1
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 +67 -83
- package/dist/Router.svelte.d.ts +26 -0
- package/dist/currentURL.d.ts +1 -0
- package/{src/currentURL.ts → dist/currentURL.js} +3 -6
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/link.d.ts +12 -0
- package/dist/link.js +25 -0
- package/dist/push.d.ts +8 -0
- package/dist/push.js +12 -0
- package/dist/redirect.d.ts +6 -0
- package/dist/redirect.js +7 -0
- package/dist/spa-context.d.ts +3 -0
- package/dist/spa-context.js +8 -0
- package/dist/spa-event.d.ts +1 -0
- package/dist/spa-event.js +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.js +8 -0
- package/package.json +15 -10
- package/.vscode/extensions.json +0 -3
- package/example/App.svelte +0 -117
- package/example/index.html +0 -13
- package/example/lib/getArticle.ts +0 -13
- package/example/lib/getArticles.ts +0 -43
- package/example/main.ts +0 -8
- package/example/pages/Admin.svelte +0 -7
- package/example/pages/Blog.svelte +0 -19
- package/example/pages/BlogPost.svelte +0 -23
- package/example/pages/IntParam.svelte +0 -14
- package/example/pages/Message.svelte +0 -9
- package/example/pages/NotFound.svelte +0 -4
- package/example/pages/Params.svelte +0 -13
- package/example/pages/Query.svelte +0 -14
- package/example/pages/Top.svelte +0 -7
- package/example/types.ts +0 -9
- package/example/vite-env.d.ts +0 -2
- package/playwright-report/index.html +0 -71
- package/playwright.config.js +0 -26
- package/src/index.ts +0 -7
- package/src/link.ts +0 -28
- package/src/push.ts +0 -18
- package/src/redirect.ts +0 -9
- package/src/spa-context.ts +0 -15
- package/src/spa-event.ts +0 -1
- package/src/types.ts +0 -46
- package/test-results/.last-run.json +0 -4
- package/tests/e2e.spec.ts +0 -133
- package/tsconfig.app.json +0 -27
- package/tsconfig.json +0 -7
- package/tsconfig.node.json +0 -24
- package/vite.config.ts +0 -8
- /package/{src → dist}/Router.svelte +0 -0
package/README.md
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
# svelte-spa-history-router
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A history-based router for [Svelte](https://svelte.dev/) Single Page Applications (SPAs).
|
|
4
4
|
|
|
5
5
|
> [!TIP]
|
|
6
|
-
>
|
|
7
|
-
|
|
6
|
+
> Svelte's official routing library is [SvelteKit](https://svelte.dev/docs/kit/introduction). This library is designed for small or simple projects.
|
|
8
7
|
|
|
9
8
|
## Features
|
|
10
9
|
|
|
11
|
-
- History-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
10
|
+
- History-Based Routing
|
|
11
|
+
- Path matching and Variable Capture using regular expressions
|
|
12
|
+
- Resolver for dynamic routing, code splitting, data preloading, etc.
|
|
14
13
|
|
|
15
|
-
## *
|
|
14
|
+
## Features *not* supported
|
|
16
15
|
|
|
17
|
-
- Hash-
|
|
16
|
+
- Hash-based Routing
|
|
18
17
|
- Nested router
|
|
19
|
-
-
|
|
18
|
+
- Server-Side Rendering (SSR)
|
|
20
19
|
|
|
21
20
|
## Install
|
|
22
21
|
|
|
@@ -28,7 +27,7 @@ $ yarn add svelte-spa-history-router
|
|
|
28
27
|
|
|
29
28
|
## Usage
|
|
30
29
|
|
|
31
|
-
Import `Router` and
|
|
30
|
+
Import `Router` and include it in your main component (typically App.svelte).
|
|
32
31
|
|
|
33
32
|
For example:
|
|
34
33
|
|
|
@@ -51,59 +50,76 @@ For example:
|
|
|
51
50
|
<Router {routes}/>
|
|
52
51
|
```
|
|
53
52
|
|
|
54
|
-
* `
|
|
55
|
-
* `routes` is a list of route objects. route object
|
|
53
|
+
* `Router` requires the `routes` parameter.
|
|
54
|
+
* `routes` is a list of route objects. Each route object must have a `path` property, and either a `component` or `resolver` property.
|
|
56
55
|
|
|
57
56
|
* `path` can be a regular expression. `^` and `$` are automatically added when matching.
|
|
58
|
-
* `component` is a Svelte component.
|
|
59
|
-
* `resolver` is a function to
|
|
57
|
+
* `component` is a Svelte component. There are no specific requirements for the component.
|
|
58
|
+
* `resolver` is a function to return a dynamic component and return props of the type expected by the component.
|
|
60
59
|
|
|
61
60
|
* Matching is simply performed in the order defined by `routes`.
|
|
62
61
|
|
|
63
62
|
### Path variable
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
For routes that do not use a resolver, matched parameters are passed to the component via the `params` prop.
|
|
66
65
|
|
|
67
66
|
For example:
|
|
68
67
|
|
|
69
68
|
```html
|
|
70
|
-
#
|
|
69
|
+
# App.svelte
|
|
70
|
+
<script>
|
|
71
|
+
import { Router } from "svelte-spa-history-router";
|
|
72
|
+
import ItemPage from "./ItemPage.svelte";
|
|
73
|
+
|
|
74
|
+
const routes = [
|
|
75
|
+
{ path: "/items/(?<itemId>\\d+)", component: ItemPage },
|
|
76
|
+
];
|
|
77
|
+
</script>
|
|
78
|
+
<Router {routes}/>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
# ItemPage.svelte
|
|
71
83
|
|
|
72
84
|
<script lang="ts">
|
|
73
|
-
let { params }: { params: {
|
|
85
|
+
let { params }: { params: { itemId: string } } = $props();
|
|
74
86
|
|
|
75
|
-
const
|
|
87
|
+
const itemId = $derived(parseInt(params.itemId));
|
|
76
88
|
</script>
|
|
77
89
|
<div>
|
|
78
|
-
{
|
|
90
|
+
{ itemId }
|
|
79
91
|
</div>
|
|
80
92
|
```
|
|
81
93
|
|
|
82
94
|
### Navigation methods
|
|
83
95
|
|
|
84
|
-
To navigate another page, `link` and `push` are available.
|
|
96
|
+
To navigate to another page, `link` and `push` are available.
|
|
85
97
|
|
|
86
|
-
* `link`
|
|
98
|
+
* `link` turns an `<a>` tag into a spa navigation link. For example:
|
|
87
99
|
|
|
88
100
|
```html
|
|
89
|
-
|
|
101
|
+
<script>
|
|
102
|
+
import { link } from 'svelte-spa-history-router';
|
|
103
|
+
</script>
|
|
90
104
|
|
|
91
105
|
<a use:link href="/">Home</a>
|
|
92
106
|
```
|
|
93
107
|
|
|
94
|
-
* `push`
|
|
108
|
+
* `push` navigates to the given path programatically.
|
|
95
109
|
|
|
96
110
|
```html
|
|
97
|
-
|
|
111
|
+
<script>
|
|
112
|
+
import { push } from 'svelte-spa-history-router';
|
|
113
|
+
<script>
|
|
98
114
|
|
|
99
115
|
<button onclick={ () => push('/') }>Go to Home</button>
|
|
100
116
|
```
|
|
101
117
|
|
|
102
118
|
### resolver
|
|
103
119
|
|
|
104
|
-
|
|
120
|
+
A resolver is a mechanism for dynamically determining which component to render. It can be used for various purposes, such as:
|
|
105
121
|
|
|
106
|
-
Example: code
|
|
122
|
+
Example: code splitting (dynamic import)
|
|
107
123
|
|
|
108
124
|
```html
|
|
109
125
|
<script>
|
|
@@ -141,6 +157,9 @@ Example: dynamic routing and pass value to component props.
|
|
|
141
157
|
<Router {routes}/>
|
|
142
158
|
```
|
|
143
159
|
|
|
160
|
+
> [!TIP]
|
|
161
|
+
> This routing mechanism allows preloading data before rendering the component, minimizing layout flicker and improving perceived performance. While skeleton screens are a common modern pattern, this approach can simplify state handling in simple apps.
|
|
162
|
+
|
|
144
163
|
Example: guard
|
|
145
164
|
|
|
146
165
|
```html
|
|
@@ -167,6 +186,20 @@ Example: guard
|
|
|
167
186
|
<Router {routes}/>
|
|
168
187
|
```
|
|
169
188
|
|
|
189
|
+
A resolver must return one of the following types:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
| Component
|
|
193
|
+
| { component: Component, props: ComponentProps }
|
|
194
|
+
| Redirection // return value of `redirect()`
|
|
195
|
+
| Promise<
|
|
196
|
+
| Component
|
|
197
|
+
| { component: Component, props: ComponentProps }
|
|
198
|
+
| Redirection
|
|
199
|
+
| { default: Component } // return value of `import()`
|
|
200
|
+
>
|
|
201
|
+
```
|
|
202
|
+
|
|
170
203
|
(Added in v2.0.0)
|
|
171
204
|
|
|
172
205
|
(Changed resolver interface in v3.0.0-next.1)
|
|
@@ -186,7 +219,7 @@ state to detect URL changes (including query string or hash)
|
|
|
186
219
|
|
|
187
220
|
(Added in v2.1.0)
|
|
188
221
|
|
|
189
|
-
(Replaced
|
|
222
|
+
(Replaced with Svelte5's `$state()` in v3.0.0-next.1)
|
|
190
223
|
|
|
191
224
|
### Typing
|
|
192
225
|
|
|
@@ -204,7 +237,7 @@ svelte-spa-history-router provides `Route` type to check combination of componen
|
|
|
204
237
|
const routes: [
|
|
205
238
|
Route<typeof Top>,
|
|
206
239
|
Route<typeof BlogPost | typeof NotFound>,
|
|
207
|
-
]
|
|
240
|
+
] = [
|
|
208
241
|
{ path: "/", component: Top },
|
|
209
242
|
{
|
|
210
243
|
path: "/blog/posts/(?<slug>.*)",
|
|
@@ -226,60 +259,11 @@ svelte-spa-history-router provides `Route` type to check combination of componen
|
|
|
226
259
|
|
|
227
260
|
### Full example:
|
|
228
261
|
|
|
229
|
-
[example](https://github.com/ykrods/svelte-spa-history-router/tree/
|
|
262
|
+
[example](https://github.com/ykrods/svelte-spa-history-router/tree/main/src)
|
|
230
263
|
|
|
231
264
|
## ChangeLog
|
|
232
265
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
* *[Breaking change]* Drop Svelte4 support
|
|
236
|
-
|
|
237
|
-
* Remove stores of `routeParams` and `currentURL`
|
|
238
|
-
|
|
239
|
-
* *[Breaking change]* Change resolver interface
|
|
240
|
-
* Add `currentURL()` which is rewriten to use `$state()`
|
|
241
|
-
|
|
242
|
-
### 2.2.0
|
|
243
|
-
|
|
244
|
-
* Support Svelte5
|
|
245
|
-
|
|
246
|
-
### 2.2.0-next.1
|
|
247
|
-
|
|
248
|
-
* Fix component type on svelte5 [PR13](https://github.com/ykrods/svelte-spa-history-router/pull/13)
|
|
249
|
-
|
|
250
|
-
### 2.2.0-next.0
|
|
251
|
-
|
|
252
|
-
* Add the way to get routing params to via props [PR12](https://github.com/ykrods/svelte-spa-history-router/pull/12)
|
|
253
|
-
* Refactor (changes: [2.1.2...7b7795b](https://github.com/ykrods/svelte-spa-history-router/compare/2.1.2...7b7795b2675c452a1a189d3931c0c4c9abb04c51) )
|
|
254
|
-
|
|
255
|
-
### 2.1.2 (2024-04-29)
|
|
256
|
-
|
|
257
|
-
* Support types [PR10](https://github.com/ykrods/svelte-spa-history-router/pull/10)
|
|
258
|
-
|
|
259
|
-
### 2.1.1 (2024-01-13)
|
|
260
|
-
|
|
261
|
-
* ~~Support Types~~ Add typecheck [PR9](https://github.com/ykrods/svelte-spa-history-router/pull/9)
|
|
262
|
-
|
|
263
|
-
### 2.1.0 (2021-04-29)
|
|
264
|
-
|
|
265
|
-
* Add `currentURL` store to detect URL changes [PR6](https://github.com/ykrods/svelte-spa-history-router/pull/6)
|
|
266
|
-
|
|
267
|
-
### 2.0.0 (2021-04-15)
|
|
268
|
-
|
|
269
|
-
* [Added] resolver
|
|
270
|
-
* [Removed] guard
|
|
271
|
-
|
|
272
|
-
### 1.1.1 (2021-04-12)
|
|
273
|
-
|
|
274
|
-
* Fix bug with async guard function causing loop
|
|
275
|
-
|
|
276
|
-
### 1.1.0 (2021-03-26)
|
|
277
|
-
|
|
278
|
-
* Add guard
|
|
279
|
-
|
|
280
|
-
### 1.0.2
|
|
281
|
-
|
|
282
|
-
* Fix import error
|
|
266
|
+
[ChangeLog](https://github.com/ykrods/svelte-spa-history-router/tree/main/ChangeLog.md)
|
|
283
267
|
|
|
284
268
|
## License
|
|
285
269
|
|
|
@@ -287,9 +271,9 @@ MIT License.
|
|
|
287
271
|
|
|
288
272
|
## Appendix
|
|
289
273
|
|
|
290
|
-
|
|
274
|
+
A history-based router generally requires server-side routing to support direct links or page reloads.
|
|
291
275
|
|
|
292
|
-
For example,
|
|
276
|
+
For example, the following nginx configuration allows proper routing:
|
|
293
277
|
|
|
294
278
|
```
|
|
295
279
|
location / {
|
|
@@ -297,10 +281,10 @@ location / {
|
|
|
297
281
|
}
|
|
298
282
|
```
|
|
299
283
|
|
|
300
|
-
If you
|
|
284
|
+
If you are considering using firebase hosting for your application, [rewrite](https://firebase.google.com/docs/hosting/full-config#rewrites) may be useful.
|
|
301
285
|
|
|
302
286
|
## Inspired
|
|
303
287
|
|
|
304
288
|
svelte-spa-history-router is inspired by [svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router) and [Svelte Router SPA](https://github.com/jorgegorka/svelte-router).
|
|
305
289
|
|
|
306
|
-
If you don't need both history-
|
|
290
|
+
If you don't need support for both history-based routing and regular expressions, I recommend these powerful routers.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Component } from "svelte";
|
|
2
|
+
import type { Route } from "./types";
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
routes: Route<any>[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Router component
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* <script>
|
|
11
|
+
* import { Router } from "svelte-spa-history-router";
|
|
12
|
+
*
|
|
13
|
+
* import Top from "./Top.svelte"
|
|
14
|
+
* import NotFound from "./NotFound.svelte"
|
|
15
|
+
*
|
|
16
|
+
* const routes = [
|
|
17
|
+
* { path: "/", component: Top },
|
|
18
|
+
* { path: "/posts/(?<postId>.*)", resolver: () => import("./Article.svelte") },
|
|
19
|
+
* { path: ".*", component: NotFound },
|
|
20
|
+
* ];
|
|
21
|
+
* </script>
|
|
22
|
+
* <Router {routes}/>
|
|
23
|
+
*/
|
|
24
|
+
declare const Router: Component<$$ComponentProps, {}, "">;
|
|
25
|
+
type Router = ReturnType<typeof Router>;
|
|
26
|
+
export default Router;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function currentURL(): URL;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// https://svelte.dev/docs/svelte/compiler-warnings#state_referenced_locally
|
|
2
2
|
// https://svelte.dev/docs/svelte/$state#Passing-state-into-functions
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export function currentURL(): URL {
|
|
8
|
-
return getSpaContext().currentURL();
|
|
3
|
+
import { getSpaContext } from "./spa-context";
|
|
4
|
+
export function currentURL() {
|
|
5
|
+
return getSpaContext().currentURL();
|
|
9
6
|
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/link.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte action to make `<a>` work as navigation of svelte-spa-history-router
|
|
3
|
+
*
|
|
4
|
+
* @param node - target `<a>`
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
*
|
|
8
|
+
* <a use:link href="/">top</a>
|
|
9
|
+
*/
|
|
10
|
+
export declare function link(node: HTMLAnchorElement): {
|
|
11
|
+
destroy(): void;
|
|
12
|
+
};
|
package/dist/link.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { push } from "./push";
|
|
2
|
+
/**
|
|
3
|
+
* Svelte action to make `<a>` work as navigation of svelte-spa-history-router
|
|
4
|
+
*
|
|
5
|
+
* @param node - target `<a>`
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
*
|
|
9
|
+
* <a use:link href="/">top</a>
|
|
10
|
+
*/
|
|
11
|
+
export function link(node) {
|
|
12
|
+
function onClick(event) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
var href = node.getAttribute("href");
|
|
15
|
+
if (href) {
|
|
16
|
+
push(href);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
node.addEventListener("click", onClick);
|
|
20
|
+
return {
|
|
21
|
+
destroy: function () {
|
|
22
|
+
node.removeEventListener("click", onClick);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
package/dist/push.d.ts
ADDED
package/dist/push.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as SpaEvent from "./spa-event";
|
|
2
|
+
/**
|
|
3
|
+
* Navigate to next page programmatically
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
*
|
|
7
|
+
* <button onclick={ () => push(`posts/${id}`) }>next</button>
|
|
8
|
+
*/
|
|
9
|
+
export function push(next) {
|
|
10
|
+
var evt = new CustomEvent(SpaEvent.NAVIGATE, { detail: { next: next } });
|
|
11
|
+
window.dispatchEvent(evt);
|
|
12
|
+
}
|
package/dist/redirect.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const NAVIGATE = "spa-navigate";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var NAVIGATE = "spa-navigate";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Component, ComponentProps } from "svelte";
|
|
2
|
+
import * as SpaEvent from "./spa-event";
|
|
3
|
+
export type NavigationEvent = CustomEvent<{
|
|
4
|
+
next: string;
|
|
5
|
+
}>;
|
|
6
|
+
declare global {
|
|
7
|
+
interface WindowEventMap {
|
|
8
|
+
[SpaEvent.NAVIGATE]: NavigationEvent;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export interface Redirection {
|
|
12
|
+
redirect: string;
|
|
13
|
+
}
|
|
14
|
+
export interface Destination<T extends Component<any>> {
|
|
15
|
+
component: T;
|
|
16
|
+
props: ComponentProps<T>;
|
|
17
|
+
}
|
|
18
|
+
type DestMap<T extends Component<any>> = T extends unknown ? Destination<T> : never;
|
|
19
|
+
export type Route<T extends Component<any> = Component> = {
|
|
20
|
+
path: string;
|
|
21
|
+
component?: T;
|
|
22
|
+
resolver?: (params: Record<string, string>) => DestMap<T> | T | Redirection | Promise<DestMap<T> | T | Redirection | {
|
|
23
|
+
default: T;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
export interface SpaContext {
|
|
27
|
+
currentURL(): URL;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
package/dist/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-spa-history-router",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "History base router for Svelte SPA",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"svelte": "./dist/index.js",
|
|
8
10
|
"exports": {
|
|
9
11
|
".": {
|
|
10
|
-
"svelte": "./
|
|
12
|
+
"svelte": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
11
14
|
}
|
|
12
15
|
},
|
|
13
16
|
"scripts": {
|
|
14
17
|
"dev": "vite",
|
|
18
|
+
"build": "svelte-package",
|
|
15
19
|
"test": "playwright test",
|
|
16
20
|
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
|
|
17
21
|
},
|
|
@@ -19,14 +23,15 @@
|
|
|
19
23
|
"svelte": ">5.0.0"
|
|
20
24
|
},
|
|
21
25
|
"devDependencies": {
|
|
22
|
-
"@playwright/test": "1.
|
|
26
|
+
"@playwright/test": "1.52.0",
|
|
27
|
+
"@sveltejs/package": "2.3.11",
|
|
23
28
|
"@sveltejs/vite-plugin-svelte": "5.0.3",
|
|
24
29
|
"@tsconfig/svelte": "5.0.4",
|
|
25
|
-
"playwright": "1.
|
|
26
|
-
"svelte": "5.
|
|
27
|
-
"svelte-check": "4.1.
|
|
28
|
-
"typescript": "5.
|
|
29
|
-
"vite": "6.
|
|
30
|
+
"playwright": "1.52.0",
|
|
31
|
+
"svelte": "5.28.2",
|
|
32
|
+
"svelte-check": "4.1.6",
|
|
33
|
+
"typescript": "5.8.3",
|
|
34
|
+
"vite": "6.3.4"
|
|
30
35
|
},
|
|
31
36
|
"author": "ykrods",
|
|
32
37
|
"license": "MIT",
|
package/.vscode/extensions.json
DELETED
package/example/App.svelte
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Route } from "svelte-spa-history-router"
|
|
3
|
-
import { Router, link, push, redirect } from "svelte-spa-history-router"
|
|
4
|
-
|
|
5
|
-
import type Admin from "./pages/Admin.svelte"
|
|
6
|
-
import type Blog from "./pages/Blog.svelte"
|
|
7
|
-
import type BlogPost from "./pages/BlogPost.svelte"
|
|
8
|
-
import type Query from "./pages/Query.svelte"
|
|
9
|
-
|
|
10
|
-
import IntParam from "./pages/IntParam.svelte"
|
|
11
|
-
import Message from "./pages/Message.svelte"
|
|
12
|
-
import NotFound from "./pages/NotFound.svelte"
|
|
13
|
-
import Params from "./pages/Params.svelte"
|
|
14
|
-
import Top from "./pages/Top.svelte"
|
|
15
|
-
|
|
16
|
-
import { getArticle } from "./lib/getArticle"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const routes: [
|
|
20
|
-
Route,
|
|
21
|
-
Route<typeof Params>,
|
|
22
|
-
Route<typeof IntParam>,
|
|
23
|
-
Route<typeof IntParam | typeof Message>,
|
|
24
|
-
Route<typeof Blog>,
|
|
25
|
-
Route<typeof BlogPost | typeof NotFound>,
|
|
26
|
-
Route<typeof Admin>,
|
|
27
|
-
Route<typeof Query>,
|
|
28
|
-
Route<typeof NotFound>,
|
|
29
|
-
] = [
|
|
30
|
-
{ path: "/", component: Top },
|
|
31
|
-
// path variable via params
|
|
32
|
-
{ path: "/params/(?<slug>.*)", component: Params },
|
|
33
|
-
// typed props
|
|
34
|
-
{
|
|
35
|
-
path: "/int-param/(?<num>\\d+)",
|
|
36
|
-
resolver: (params: Record<"num", string>) => ({
|
|
37
|
-
component: IntParam,
|
|
38
|
-
props: { num: parseInt(params.num) }
|
|
39
|
-
}),
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
path: "/conditional/(?<arg>.+)",
|
|
43
|
-
resolver: (params: Record<"arg", string>) => {
|
|
44
|
-
const arg = parseInt(params.arg)
|
|
45
|
-
if (!Number.isNaN(arg)) {
|
|
46
|
-
return { component: IntParam, props: { num: arg }}
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
component: Message,
|
|
50
|
-
props: { message: `Unexpected param: ${params.arg}` }
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
// spliting
|
|
55
|
-
{
|
|
56
|
-
path: "/blog",
|
|
57
|
-
resolver: () => import("./pages/Blog.svelte"),
|
|
58
|
-
},
|
|
59
|
-
// async resolver with props
|
|
60
|
-
// path variable with slash
|
|
61
|
-
// prefetch
|
|
62
|
-
// spliting
|
|
63
|
-
// return component
|
|
64
|
-
{
|
|
65
|
-
path: "/blog/posts/(?<slug>.*)",
|
|
66
|
-
resolver: async (params: Record<"slug", string>) => {
|
|
67
|
-
const article = await getArticle(params.slug);
|
|
68
|
-
if (article) {
|
|
69
|
-
const component = (await import("./pages/BlogPost.svelte")).default;
|
|
70
|
-
return { component, props: { article } }
|
|
71
|
-
} else {
|
|
72
|
-
return NotFound;
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
// guard
|
|
77
|
-
// redirect
|
|
78
|
-
{
|
|
79
|
-
path: "/admin",
|
|
80
|
-
resolver: () => {
|
|
81
|
-
if (user === "admin") {
|
|
82
|
-
return import("./pages/Admin.svelte");
|
|
83
|
-
} else {
|
|
84
|
-
return redirect("/");
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
path: "/query",
|
|
90
|
-
resolver: () => import("./pages/Query.svelte")
|
|
91
|
-
},
|
|
92
|
-
{ path: ".*", component: NotFound },
|
|
93
|
-
]
|
|
94
|
-
|
|
95
|
-
let user = $state("anonymous");
|
|
96
|
-
</script>
|
|
97
|
-
<main>
|
|
98
|
-
<header>
|
|
99
|
-
<nav>
|
|
100
|
-
<a use:link href="/">Top</a> |
|
|
101
|
-
<a use:link href="/params/foo/bar">params</a> |
|
|
102
|
-
<a use:link href="/int-param/3">int param</a> |
|
|
103
|
-
<a use:link href="/conditional/1">conditional (1)</a> |
|
|
104
|
-
<a use:link href="/conditional/a">conditional (a)</a> |
|
|
105
|
-
<a use:link href="/blog">blog</a> |
|
|
106
|
-
<a use:link href="/admin">admin</a> |
|
|
107
|
-
<a use:link href="/query">query</a> |
|
|
108
|
-
|
|
109
|
-
{#if user === "anonymous"}
|
|
110
|
-
<button id="login" onclick={() => { user = "admin" }}>Login</button>
|
|
111
|
-
{:else}
|
|
112
|
-
user: { user } <button onclick={() => { user = "anonymous"; push("/"); }}>Logout</button>
|
|
113
|
-
{/if}
|
|
114
|
-
</nav>
|
|
115
|
-
</header>
|
|
116
|
-
<Router {routes} />
|
|
117
|
-
</main>
|
package/example/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>Vite + Svelte + TS</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="app"></div>
|
|
11
|
-
<script type="module" src="/main.ts"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Article } from "../types"
|
|
2
|
-
|
|
3
|
-
import { getArticles } from "./getArticles"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export async function getArticle(id: string): Promise<Article | undefined> {
|
|
10
|
-
await sleep(50);
|
|
11
|
-
const articles = await getArticles();
|
|
12
|
-
return articles.find(a => a.id === id);
|
|
13
|
-
}
|