svelte-spa-history-router 3.0.0-next.1 → 3.0.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 +70 -36
- 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,9 +186,23 @@ 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
|
-
(Changed resolver interface in v3.0.0-next.
|
|
205
|
+
(Changed resolver interface in v3.0.0-next.1)
|
|
173
206
|
|
|
174
207
|
### currentURL()
|
|
175
208
|
|
|
@@ -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>.*)",
|
|
@@ -220,16 +253,17 @@ svelte-spa-history-router provides `Route` type to check combination of componen
|
|
|
220
253
|
},
|
|
221
254
|
];
|
|
222
255
|
</script>
|
|
256
|
+
```
|
|
223
257
|
|
|
224
|
-
(Added in v3.0.0-next.
|
|
258
|
+
(Added in v3.0.0-next.1)
|
|
225
259
|
|
|
226
260
|
### Full example:
|
|
227
261
|
|
|
228
|
-
[example](https://github.com/ykrods/svelte-spa-history-router/tree/master/
|
|
262
|
+
[example](https://github.com/ykrods/svelte-spa-history-router/tree/master/src)
|
|
229
263
|
|
|
230
264
|
## ChangeLog
|
|
231
265
|
|
|
232
|
-
### 3.0.0-next.
|
|
266
|
+
### 3.0.0-next.1
|
|
233
267
|
|
|
234
268
|
* *[Breaking change]* Drop Svelte4 support
|
|
235
269
|
|
|
@@ -286,9 +320,9 @@ MIT License.
|
|
|
286
320
|
|
|
287
321
|
## Appendix
|
|
288
322
|
|
|
289
|
-
|
|
323
|
+
A history-based router generally requires server-side routing to support direct links or page reloads.
|
|
290
324
|
|
|
291
|
-
For example,
|
|
325
|
+
For example, the following nginx configuration allows proper routing:
|
|
292
326
|
|
|
293
327
|
```
|
|
294
328
|
location / {
|
|
@@ -296,10 +330,10 @@ location / {
|
|
|
296
330
|
}
|
|
297
331
|
```
|
|
298
332
|
|
|
299
|
-
If you
|
|
333
|
+
If you are considering using firebase hosting for your application, [rewrite](https://firebase.google.com/docs/hosting/full-config#rewrites) may be useful.
|
|
300
334
|
|
|
301
335
|
## Inspired
|
|
302
336
|
|
|
303
337
|
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).
|
|
304
338
|
|
|
305
|
-
If you don't need both history-
|
|
339
|
+
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.0
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "History base router for Svelte SPA",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"svelte": "./dist/index.ts",
|
|
8
10
|
"exports": {
|
|
9
11
|
".": {
|
|
10
|
-
"svelte": "./
|
|
12
|
+
"svelte": "./dist/index.ts",
|
|
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
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { Article } from "../types"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const articles: Article[] = [
|
|
8
|
-
{
|
|
9
|
-
id: '2024/04/01/test-fragment',
|
|
10
|
-
title: 'test fragment',
|
|
11
|
-
date: "2024-04-01",
|
|
12
|
-
html: `<a id="top" href="#bottom">to bottom</a>
|
|
13
|
-
<div style="height:800px">spacer</div>
|
|
14
|
-
<a id="bottom" href="#top">to top</a>`,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
id: '2024/03/01/baz',
|
|
18
|
-
title: 'baz',
|
|
19
|
-
date: "2024-03-01",
|
|
20
|
-
html: `<p>baz!</p>`,
|
|
21
|
-
prev: '2024/02/01/bar',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: '2024/02/01/bar',
|
|
25
|
-
title: 'bar',
|
|
26
|
-
date: "2024-02-01",
|
|
27
|
-
html: `<p>bar!</p>`,
|
|
28
|
-
next: '2024/03/01/baz',
|
|
29
|
-
prev: '2024/01/01/foo',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: '2024/01/01/foo',
|
|
33
|
-
title: 'foo',
|
|
34
|
-
date: "2024-01-01",
|
|
35
|
-
html: `<p>foo!</p>`,
|
|
36
|
-
next: '2024/02/01/bar',
|
|
37
|
-
},
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
export async function getArticles(): Promise<Article[]> {
|
|
41
|
-
await sleep(100);
|
|
42
|
-
return articles;
|
|
43
|
-
}
|