@volpe/astro-svelte-spa 0.1.1 → 0.1.2
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/index.d.ts +31 -0
- package/index.js +68 -14
- package/package.json +5 -17
- package/App.svelte +0 -40
- package/App.svelte.d.ts +0 -5
- package/Link.svelte +0 -32
- package/Link.svelte.d.ts +0 -12
package/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
import type { Component } from "svelte";
|
|
2
3
|
|
|
3
4
|
export interface PluginOptions {
|
|
4
5
|
/**
|
|
@@ -35,3 +36,33 @@ export interface PluginOptions {
|
|
|
35
36
|
export function plugin(options?: PluginOptions): Plugin;
|
|
36
37
|
|
|
37
38
|
export default plugin;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The main App component that renders the router.
|
|
42
|
+
* Use with `client:only="svelte"` in Astro.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```astro
|
|
46
|
+
* ---
|
|
47
|
+
* import { App } from "@volpe/astro-svelte-spa"
|
|
48
|
+
* ---
|
|
49
|
+
* <App client:only="svelte" />
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export const App: Component<{}>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Link component for client-side navigation.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```svelte
|
|
59
|
+
* <script>
|
|
60
|
+
* import { Link } from "@volpe/astro-svelte-spa"
|
|
61
|
+
* </script>
|
|
62
|
+
* <Link href="/about">About</Link>
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export const Link: Component<{
|
|
66
|
+
href: string;
|
|
67
|
+
[key: string]: any;
|
|
68
|
+
}>;
|
package/index.js
CHANGED
|
@@ -4,6 +4,11 @@ import path from "node:path";
|
|
|
4
4
|
const VIRTUAL_MODULE_ID = "virtual:svelte-routes";
|
|
5
5
|
const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
|
|
6
6
|
|
|
7
|
+
const PACKAGE_ID = "@volpe/astro-svelte-spa";
|
|
8
|
+
const RESOLVED_PACKAGE_ID = "\0" + PACKAGE_ID;
|
|
9
|
+
const VIRTUAL_APP_ID = "\0svelte-spa-app.svelte";
|
|
10
|
+
const VIRTUAL_LINK_ID = "\0svelte-spa-link.svelte";
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* @typedef {Object} RouteNode
|
|
9
14
|
* @property {string} path
|
|
@@ -101,15 +106,12 @@ function applyBasePath(routePath, basePath) {
|
|
|
101
106
|
function generateRoutes(node, imports) {
|
|
102
107
|
const routes = [];
|
|
103
108
|
|
|
104
|
-
// Use relative paths (basePath is handled by Router component)
|
|
105
109
|
const routePath = node.path;
|
|
106
110
|
const layoutName = node.layout ? imports.get(node.layout) : null;
|
|
107
111
|
const pageName = node.page ? imports.get(node.page) : null;
|
|
108
112
|
|
|
109
|
-
// Add page route
|
|
110
113
|
if (pageName) {
|
|
111
114
|
if (layoutName) {
|
|
112
|
-
// With layout - use children
|
|
113
115
|
routes.push(`{
|
|
114
116
|
path: "${routePath}",
|
|
115
117
|
component: ${layoutName},
|
|
@@ -118,7 +120,6 @@ function generateRoutes(node, imports) {
|
|
|
118
120
|
]
|
|
119
121
|
}`);
|
|
120
122
|
} else {
|
|
121
|
-
// No layout - simple route with async loading
|
|
122
123
|
routes.push(`{
|
|
123
124
|
path: "${routePath}",
|
|
124
125
|
component: async () => import("${node.page}")
|
|
@@ -126,7 +127,6 @@ function generateRoutes(node, imports) {
|
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
// Add children routes
|
|
130
130
|
for (const child of node.children) {
|
|
131
131
|
routes.push(...generateRoutes(child, imports));
|
|
132
132
|
}
|
|
@@ -144,16 +144,13 @@ function generateRoutesModule(pagesDir, basePath) {
|
|
|
144
144
|
const imports = new Map();
|
|
145
145
|
generateImports(tree, imports);
|
|
146
146
|
|
|
147
|
-
// Import layouts (pages are loaded async)
|
|
148
147
|
const layoutImports = Array.from(imports.entries())
|
|
149
148
|
.filter(([filePath]) => filePath.includes("layout.svelte"))
|
|
150
149
|
.map(([filePath, name]) => `import ${name} from "${filePath}"`)
|
|
151
150
|
.join("\n");
|
|
152
151
|
|
|
153
|
-
// Routes use relative paths - basePath is handled by Router component
|
|
154
152
|
const routes = generateRoutes(tree, imports);
|
|
155
153
|
|
|
156
|
-
// Check for 404.svelte for statuses
|
|
157
154
|
const notFoundPath = path.join(pagesDir, "404.svelte");
|
|
158
155
|
const hasNotFound = fs.existsSync(notFoundPath);
|
|
159
156
|
const notFoundImportStatement = hasNotFound
|
|
@@ -222,11 +219,10 @@ function generatePageFiles(pagesDir) {
|
|
|
222
219
|
fs.mkdirSync(pagesDir, { recursive: true });
|
|
223
220
|
}
|
|
224
221
|
|
|
225
|
-
// Generate [...path].astro if it doesn't exist
|
|
226
222
|
const catchAllPath = path.resolve(pagesDir, "[...path].astro");
|
|
227
223
|
if (!fs.existsSync(catchAllPath)) {
|
|
228
224
|
const catchAllContent = `---
|
|
229
|
-
import App from "@volpe/astro-svelte-spa
|
|
225
|
+
import { App } from "@volpe/astro-svelte-spa"
|
|
230
226
|
---
|
|
231
227
|
|
|
232
228
|
<App client:only="svelte" />
|
|
@@ -234,7 +230,6 @@ import App from "@volpe/astro-svelte-spa/App.svelte"
|
|
|
234
230
|
fs.writeFileSync(catchAllPath, catchAllContent);
|
|
235
231
|
}
|
|
236
232
|
|
|
237
|
-
// Generate page.svelte if it doesn't exist
|
|
238
233
|
const pagePath = path.resolve(pagesDir, "page.svelte");
|
|
239
234
|
if (!fs.existsSync(pagePath)) {
|
|
240
235
|
const pageContent = `<h1>Edit me :)</h1>
|
|
@@ -242,7 +237,6 @@ import App from "@volpe/astro-svelte-spa/App.svelte"
|
|
|
242
237
|
fs.writeFileSync(pagePath, pageContent);
|
|
243
238
|
}
|
|
244
239
|
|
|
245
|
-
// Generate 404.svelte if it doesn't exist
|
|
246
240
|
const notFoundPagePath = path.resolve(pagesDir, "404.svelte");
|
|
247
241
|
if (!fs.existsSync(notFoundPagePath)) {
|
|
248
242
|
const notFoundContent = `<script>
|
|
@@ -283,6 +277,46 @@ import App from "@volpe/astro-svelte-spa/App.svelte"
|
|
|
283
277
|
}
|
|
284
278
|
}
|
|
285
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Generates the virtual App component code
|
|
282
|
+
* @returns {string}
|
|
283
|
+
*/
|
|
284
|
+
function generateAppComponent() {
|
|
285
|
+
return `<script>
|
|
286
|
+
import { Router, StatusCode } from "@mateothegreat/svelte5-router"
|
|
287
|
+
import routes, { basePath, notFoundComponent } from "virtual:svelte-routes"
|
|
288
|
+
|
|
289
|
+
const statuses = notFoundComponent ? {
|
|
290
|
+
[StatusCode.NotFound]: { component: notFoundComponent }
|
|
291
|
+
} : undefined
|
|
292
|
+
</script>
|
|
293
|
+
|
|
294
|
+
<Router {routes} {basePath} {statuses} />
|
|
295
|
+
`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Generates the virtual Link component code
|
|
300
|
+
* @returns {string}
|
|
301
|
+
*/
|
|
302
|
+
function generateLinkComponent() {
|
|
303
|
+
return `<script>
|
|
304
|
+
import { route, goto } from "@mateothegreat/svelte5-router"
|
|
305
|
+
|
|
306
|
+
let { href, children, ...rest } = $props()
|
|
307
|
+
|
|
308
|
+
const handleClick = (e) => {
|
|
309
|
+
e.preventDefault()
|
|
310
|
+
goto(href)
|
|
311
|
+
}
|
|
312
|
+
</script>
|
|
313
|
+
|
|
314
|
+
<a {href} onclick={handleClick} class:active={$route.path === href} {...rest}>
|
|
315
|
+
{@render children?.()}
|
|
316
|
+
</a>
|
|
317
|
+
`;
|
|
318
|
+
}
|
|
319
|
+
|
|
286
320
|
/**
|
|
287
321
|
* Vite plugin for file-based routing with svelte5-router in Astro
|
|
288
322
|
* @param {Object} options - Plugin options
|
|
@@ -290,9 +324,7 @@ import App from "@volpe/astro-svelte-spa/App.svelte"
|
|
|
290
324
|
* @returns {import('vite').Plugin}
|
|
291
325
|
*/
|
|
292
326
|
export function plugin(options = {}) {
|
|
293
|
-
// Normalize basePath, defaults to "/svelte-app"
|
|
294
327
|
const basePath = options.basePath?.replace(/\/$/, "") || "/svelte-app";
|
|
295
|
-
// Directory suffix: "/svelte-app" -> "svelte-app"
|
|
296
328
|
const dirSuffix = basePath.replace(/^\//, "");
|
|
297
329
|
|
|
298
330
|
const rootDir = process.cwd();
|
|
@@ -344,12 +376,34 @@ export function plugin(options = {}) {
|
|
|
344
376
|
if (id === VIRTUAL_MODULE_ID) {
|
|
345
377
|
return RESOLVED_VIRTUAL_MODULE_ID;
|
|
346
378
|
}
|
|
379
|
+
// Intercept main package import (but not /plugin subpath)
|
|
380
|
+
if (id === PACKAGE_ID) {
|
|
381
|
+
return RESOLVED_PACKAGE_ID;
|
|
382
|
+
}
|
|
383
|
+
// Handle virtual component IDs
|
|
384
|
+
if (id === VIRTUAL_APP_ID || id === VIRTUAL_LINK_ID) {
|
|
385
|
+
return id;
|
|
386
|
+
}
|
|
347
387
|
},
|
|
348
388
|
|
|
349
389
|
load(id) {
|
|
350
390
|
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
351
391
|
return generateRoutesModule(pagesDir, basePath);
|
|
352
392
|
}
|
|
393
|
+
// Virtual package module - exports App, Link, and re-exports plugin
|
|
394
|
+
if (id === RESOLVED_PACKAGE_ID) {
|
|
395
|
+
return `
|
|
396
|
+
export { default as App } from "${VIRTUAL_APP_ID}";
|
|
397
|
+
export { default as Link } from "${VIRTUAL_LINK_ID}";
|
|
398
|
+
export { plugin, default } from "@volpe/astro-svelte-spa/plugin";
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
if (id === VIRTUAL_APP_ID) {
|
|
402
|
+
return generateAppComponent();
|
|
403
|
+
}
|
|
404
|
+
if (id === VIRTUAL_LINK_ID) {
|
|
405
|
+
return generateLinkComponent();
|
|
406
|
+
}
|
|
353
407
|
},
|
|
354
408
|
|
|
355
409
|
handleHotUpdate() {
|
package/package.json
CHANGED
|
@@ -1,35 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volpe/astro-svelte-spa",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Vite plugin for file-based routing with svelte5-router in Astro",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
|
-
"svelte": "./index.js",
|
|
9
8
|
"exports": {
|
|
10
9
|
".": {
|
|
11
10
|
"types": "./index.d.ts",
|
|
12
|
-
"svelte": "./index.js",
|
|
13
11
|
"default": "./index.js"
|
|
14
12
|
},
|
|
15
|
-
"./
|
|
16
|
-
"types": "./
|
|
17
|
-
"
|
|
18
|
-
"default": "./App.svelte"
|
|
19
|
-
},
|
|
20
|
-
"./Link.svelte": {
|
|
21
|
-
"types": "./Link.svelte.d.ts",
|
|
22
|
-
"svelte": "./Link.svelte",
|
|
23
|
-
"default": "./Link.svelte"
|
|
13
|
+
"./plugin": {
|
|
14
|
+
"types": "./index.d.ts",
|
|
15
|
+
"default": "./index.js"
|
|
24
16
|
}
|
|
25
17
|
},
|
|
26
18
|
"files": [
|
|
27
19
|
"index.js",
|
|
28
|
-
"index.d.ts"
|
|
29
|
-
"App.svelte",
|
|
30
|
-
"App.svelte.d.ts",
|
|
31
|
-
"Link.svelte",
|
|
32
|
-
"Link.svelte.d.ts"
|
|
20
|
+
"index.d.ts"
|
|
33
21
|
],
|
|
34
22
|
"keywords": [
|
|
35
23
|
"vite",
|
package/App.svelte
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Router, route, StatusCode } from "@mateothegreat/svelte5-router"
|
|
3
|
-
import routes, { basePath, notFoundComponent } from "virtual:svelte-routes"
|
|
4
|
-
|
|
5
|
-
// Build statuses config for 404 handling
|
|
6
|
-
const statuses = notFoundComponent ? {
|
|
7
|
-
[StatusCode.NotFound]: { component: notFoundComponent }
|
|
8
|
-
} : undefined
|
|
9
|
-
|
|
10
|
-
// Action that combines route + prefetch on hover
|
|
11
|
-
function link(node: HTMLAnchorElement) {
|
|
12
|
-
const href = node.getAttribute("href")
|
|
13
|
-
|
|
14
|
-
function preload() {
|
|
15
|
-
// Convert full path to relative path for matching
|
|
16
|
-
const relativePath = href === basePath ? "/" : href?.replace(basePath, "") || "/"
|
|
17
|
-
const r = routes.find(r => r.path === relativePath)
|
|
18
|
-
if (r?.component && typeof r.component === "function") {
|
|
19
|
-
r.component()
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
node.addEventListener("mouseenter", preload)
|
|
24
|
-
node.addEventListener("touchstart", preload, { passive: true })
|
|
25
|
-
|
|
26
|
-
const routeAction = route(node)
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
destroy() {
|
|
30
|
-
node.removeEventListener("mouseenter", preload)
|
|
31
|
-
node.removeEventListener("touchstart", preload)
|
|
32
|
-
routeAction?.destroy?.()
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export { link }
|
|
38
|
-
</script>
|
|
39
|
-
|
|
40
|
-
<Router {routes} {basePath} {statuses} />
|
package/App.svelte.d.ts
DELETED
package/Link.svelte
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { route } from "@mateothegreat/svelte5-router"
|
|
3
|
-
import routes, { basePath, type SvelteAppRoutes } from "virtual:svelte-routes"
|
|
4
|
-
import type { Snippet } from "svelte"
|
|
5
|
-
import type { HTMLAnchorAttributes } from "svelte/elements"
|
|
6
|
-
|
|
7
|
-
type Props = Omit<HTMLAnchorAttributes, "href"> & {
|
|
8
|
-
href: SvelteAppRoutes
|
|
9
|
-
children: Snippet
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
let { href, children, ...rest }: Props = $props()
|
|
13
|
-
|
|
14
|
-
function preload() {
|
|
15
|
-
// Convert full path to relative path for matching
|
|
16
|
-
const relativePath = href === basePath ? "/" : href.replace(basePath, "")
|
|
17
|
-
const r = routes.find(r => r.path === relativePath)
|
|
18
|
-
if (r?.component && typeof r.component === "function") {
|
|
19
|
-
r.component()
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<a
|
|
25
|
-
{href}
|
|
26
|
-
use:route
|
|
27
|
-
onmouseenter={preload}
|
|
28
|
-
ontouchstart={preload}
|
|
29
|
-
{...rest}
|
|
30
|
-
>
|
|
31
|
-
{@render children()}
|
|
32
|
-
</a>
|
package/Link.svelte.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { SvelteComponent } from "svelte"
|
|
2
|
-
import type { SvelteAppRoutes } from "virtual:svelte-routes"
|
|
3
|
-
import type { Snippet } from "svelte"
|
|
4
|
-
import type { HTMLAnchorAttributes } from "svelte/elements"
|
|
5
|
-
|
|
6
|
-
type Props = Omit<HTMLAnchorAttributes, "href"> & {
|
|
7
|
-
href: SvelteAppRoutes
|
|
8
|
-
children: Snippet
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
declare const Link: typeof SvelteComponent<Props>
|
|
12
|
-
export default Link
|