@kasperrt/smolter 0.2.0 → 0.3.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/.generated/client/deployment.ts +161 -0
- package/{src/internal → .generated}/client/index.ts +3 -0
- package/.generated/core/environment.ts +43 -0
- package/{src/internal → .generated}/core/errors.ts +1 -1
- package/{src/internal → .generated}/core/index.ts +1 -0
- package/.generated/core/navigation-router.ts +107 -0
- package/.generated/core/navigation.ts +147 -0
- package/.generated/core/presentation.ts +108 -0
- package/.generated/core/resource-types.ts +44 -0
- package/.generated/core/resources.ts +444 -0
- package/.generated/core/responses.ts +86 -0
- package/{src/internal → .generated}/core/router.ts +136 -63
- package/{src/internal → .generated}/core/serialize.ts +7 -2
- package/.generated/core/store.ts +25 -0
- package/.generated/core/submission.ts +116 -0
- package/{src/internal → .generated}/core/types.ts +18 -1
- package/.generated/core/url.ts +29 -0
- package/.generated/core/utils.ts +5 -0
- package/.generated/preact/await.tsx +41 -0
- package/.generated/preact/client.tsx +93 -0
- package/.generated/preact/context.ts +59 -0
- package/.generated/preact/document.tsx +198 -0
- package/{src/internal → .generated}/preact/fetcher.ts +43 -23
- package/.generated/preact/form.ts +58 -0
- package/{src/internal → .generated}/preact/hooks.ts +25 -28
- package/.generated/preact/index.ts +34 -0
- package/.generated/preact/lazy-route.ts +100 -0
- package/.generated/preact/navigation-state.ts +9 -0
- package/.generated/preact/navigation.tsx +147 -0
- package/.generated/preact/root-context.ts +21 -0
- package/.generated/preact/root-data.ts +45 -0
- package/.generated/preact/route-hooks.tsx +170 -0
- package/.generated/preact/route-types.ts +76 -0
- package/.generated/preact/router.tsx +107 -0
- package/{src/internal → .generated}/preact/routes.ts +17 -9
- package/.generated/react/context.ts +59 -0
- package/.generated/react/data.ts +25 -0
- package/{src/internal → .generated}/react/fetcher.ts +13 -11
- package/.generated/react/form.ts +58 -0
- package/{src/internal → .generated}/react/hooks.ts +9 -14
- package/{src/internal/preact → .generated/react}/index.ts +5 -1
- package/{src/internal → .generated}/react/link.ts +13 -21
- package/.generated/react/resources/components.tsx +404 -0
- package/{src/internal → .generated}/react/routes.ts +17 -9
- package/.generated/server/application.tsx +185 -0
- package/.generated/server/assets.ts +19 -0
- package/.generated/server/handler.ts +69 -0
- package/.generated/server/http.ts +55 -0
- package/{src/internal → .generated}/server/index.ts +3 -3
- package/.generated/server/node.ts +4 -0
- package/.generated/server/preact.ts +7 -0
- package/.generated/vite/application/browser-boundary.ts +42 -0
- package/.generated/vite/application/lazy-files.ts +138 -0
- package/.generated/vite/application/vite.ts +144 -0
- package/.generated/vite/deployment.ts +46 -0
- package/{src/internal → .generated}/vite/index.ts +11 -4
- package/{src/preact-vite.ts → .generated/vite/preact.ts} +14 -3
- package/{src/react-vite.ts → .generated/vite/react.ts} +8 -3
- package/CHANGELOG.md +63 -0
- package/README.md +277 -35
- package/dist/client/deployment.d.ts +12 -0
- package/dist/client/deployment.js +102 -0
- package/dist/client/deserialize.js +10 -0
- package/dist/{internal/client → client}/index.d.ts +2 -0
- package/dist/client/index.js +47 -0
- package/dist/core/environment.d.ts +12 -0
- package/dist/core/environment.js +23 -0
- package/dist/core/errors.js +7 -0
- package/dist/{internal/core → core}/index.d.ts +1 -0
- package/dist/core/match.js +33 -0
- package/dist/core/navigation-router.d.ts +15 -0
- package/dist/core/navigation-router.js +64 -0
- package/dist/core/navigation.d.ts +15 -0
- package/dist/core/navigation.js +92 -0
- package/dist/core/presentation.d.ts +5 -0
- package/dist/core/presentation.js +69 -0
- package/dist/core/resource-types.d.ts +37 -0
- package/dist/core/resources.d.ts +19 -0
- package/dist/core/resources.js +345 -0
- package/dist/core/responses.d.ts +15 -0
- package/dist/core/responses.js +48 -0
- package/dist/{internal/core → core}/router.d.ts +1 -1
- package/dist/core/router.js +204 -0
- package/dist/{internal/core → core}/serialize.d.ts +2 -0
- package/dist/core/serialize.js +17 -0
- package/dist/core/store.d.ts +6 -0
- package/dist/core/store.js +21 -0
- package/dist/core/submission.d.ts +30 -0
- package/dist/core/submission.js +41 -0
- package/dist/{internal/core → core}/types.d.ts +16 -1
- package/dist/core/url.d.ts +7 -0
- package/dist/core/url.js +12 -0
- package/dist/core/utils.d.ts +5 -0
- package/dist/core/utils.js +5 -0
- package/dist/files/index.js +64 -0
- package/dist/preact/await.d.ts +7 -0
- package/dist/preact/await.js +19 -0
- package/dist/preact/client.d.ts +7 -0
- package/dist/preact/client.js +68 -0
- package/dist/preact/context.d.ts +18 -0
- package/dist/preact/context.js +23 -0
- package/dist/preact/document.d.ts +15 -0
- package/dist/preact/document.js +155 -0
- package/dist/{internal/preact → preact}/fetcher.d.ts +9 -5
- package/dist/preact/fetcher.js +45 -0
- package/dist/{internal/preact → preact}/form.d.ts +5 -6
- package/dist/preact/form.js +21 -0
- package/dist/{internal/preact → preact}/hooks.d.ts +7 -6
- package/dist/preact/hooks.js +25 -0
- package/dist/preact/index.d.ts +18 -0
- package/dist/preact/index.js +15 -0
- package/dist/preact/lazy-route.d.ts +19 -0
- package/dist/preact/lazy-route.js +51 -0
- package/dist/preact/navigation-state.d.ts +2 -0
- package/dist/preact/navigation-state.js +9 -0
- package/dist/preact/navigation.d.ts +28 -0
- package/dist/preact/navigation.js +60 -0
- package/dist/preact/root-context.d.ts +13 -0
- package/dist/preact/root-context.js +9 -0
- package/dist/preact/root-data.d.ts +8 -0
- package/dist/preact/root-data.js +28 -0
- package/dist/preact/route-hooks.d.ts +32 -0
- package/dist/preact/route-hooks.js +94 -0
- package/dist/preact/route-types.d.ts +54 -0
- package/dist/preact/router.d.ts +16 -0
- package/dist/preact/router.js +63 -0
- package/dist/{internal/preact → preact}/routes.d.ts +1 -1
- package/dist/preact/routes.js +12 -0
- package/dist/{internal/preact → preact}/runtime.js +1 -1
- package/dist/react/context.d.ts +18 -0
- package/dist/react/context.js +23 -0
- package/dist/react/data.d.ts +5 -0
- package/dist/react/data.js +3 -0
- package/dist/{internal/react → react}/fetcher.d.ts +2 -2
- package/dist/react/fetcher.js +34 -0
- package/dist/{internal/react → react}/form.d.ts +5 -6
- package/dist/react/form.js +21 -0
- package/dist/react/hooks.js +25 -0
- package/dist/{internal/react → react}/index.d.ts +1 -1
- package/dist/react/index.js +10 -0
- package/dist/{internal/react → react}/link.d.ts +2 -2
- package/dist/react/link.js +27 -0
- package/dist/react/resources/components.d.ts +64 -0
- package/dist/react/resources/components.js +239 -0
- package/dist/{internal/react → react}/routes.d.ts +1 -1
- package/dist/react/routes.js +12 -0
- package/dist/react/runtime.js +1 -0
- package/dist/server/application.d.ts +4 -0
- package/dist/server/application.js +119 -0
- package/dist/server/assets.d.ts +5 -0
- package/dist/server/assets.js +7 -0
- package/dist/server/handler.d.ts +19 -0
- package/dist/server/handler.js +22 -0
- package/dist/server/http.d.ts +4 -0
- package/dist/server/http.js +27 -0
- package/dist/server/index.js +76 -0
- package/dist/server/node.d.ts +3 -0
- package/dist/server/node.js +3 -0
- package/dist/server/preact.d.ts +3 -0
- package/dist/server/preact.js +4 -0
- package/dist/vite/application/browser-boundary.d.ts +3 -0
- package/dist/vite/application/browser-boundary.js +35 -0
- package/dist/vite/application/lazy-files.d.ts +2 -0
- package/dist/vite/application/lazy-files.js +88 -0
- package/dist/vite/application/vite.d.ts +9 -0
- package/dist/vite/application/vite.js +101 -0
- package/dist/vite/deployment.d.ts +8 -0
- package/dist/vite/deployment.js +36 -0
- package/dist/vite/index.js +109 -0
- package/dist/{preact-vite.d.ts → vite/preact.d.ts} +6 -3
- package/dist/vite/preact.js +16 -0
- package/dist/{react-vite.d.ts → vite/react.d.ts} +4 -3
- package/dist/vite/react.js +15 -0
- package/dist/vite/transport.js +35 -0
- package/jsr.json +28 -16
- package/package.json +115 -35
- package/dist/client.d.ts +0 -2
- package/dist/client.js +0 -2
- package/dist/core.d.ts +0 -2
- package/dist/core.js +0 -2
- package/dist/files.d.ts +0 -2
- package/dist/files.js +0 -2
- package/dist/internal/client/deserialize.js +0 -11
- package/dist/internal/client/index.js +0 -44
- package/dist/internal/core/errors.js +0 -6
- package/dist/internal/core/index.js +0 -7
- package/dist/internal/core/match.js +0 -27
- package/dist/internal/core/router.js +0 -189
- package/dist/internal/core/serialize.js +0 -13
- package/dist/internal/core/types.js +0 -5
- package/dist/internal/core/url.d.ts +0 -2
- package/dist/internal/core/url.js +0 -6
- package/dist/internal/files/index.js +0 -77
- package/dist/internal/preact/context.d.ts +0 -18
- package/dist/internal/preact/context.js +0 -20
- package/dist/internal/preact/fetcher.js +0 -28
- package/dist/internal/preact/form.js +0 -41
- package/dist/internal/preact/hooks.js +0 -35
- package/dist/internal/preact/index.d.ts +0 -9
- package/dist/internal/preact/index.js +0 -7
- package/dist/internal/preact/link.d.ts +0 -12
- package/dist/internal/preact/link.js +0 -28
- package/dist/internal/preact/routes.js +0 -22
- package/dist/internal/react/context.d.ts +0 -18
- package/dist/internal/react/context.js +0 -20
- package/dist/internal/react/fetcher.js +0 -28
- package/dist/internal/react/form.js +0 -41
- package/dist/internal/react/hooks.js +0 -35
- package/dist/internal/react/index.js +0 -7
- package/dist/internal/react/link.js +0 -28
- package/dist/internal/react/routes.js +0 -22
- package/dist/internal/react/runtime.js +0 -2
- package/dist/internal/server/index.js +0 -69
- package/dist/internal/vite/index.js +0 -132
- package/dist/internal/vite/transport.js +0 -42
- package/dist/preact-client.d.ts +0 -2
- package/dist/preact-client.js +0 -2
- package/dist/preact-server.d.ts +0 -2
- package/dist/preact-server.js +0 -2
- package/dist/preact-vite.js +0 -9
- package/dist/preact.d.ts +0 -2
- package/dist/preact.js +0 -2
- package/dist/react-client.d.ts +0 -2
- package/dist/react-client.js +0 -2
- package/dist/react-server.d.ts +0 -2
- package/dist/react-server.js +0 -2
- package/dist/react-vite.js +0 -9
- package/dist/react.d.ts +0 -2
- package/dist/react.js +0 -2
- package/dist/server.d.ts +0 -2
- package/dist/server.js +0 -2
- package/dist/utils.d.ts +0 -5
- package/dist/utils.js +0 -5
- package/dist/vite.d.ts +0 -2
- package/dist/vite.js +0 -2
- package/src/client.ts +0 -2
- package/src/core.ts +0 -2
- package/src/files.ts +0 -2
- package/src/internal/core/url.ts +0 -6
- package/src/internal/preact/context.ts +0 -46
- package/src/internal/preact/form.ts +0 -79
- package/src/internal/preact/link.ts +0 -46
- package/src/internal/react/context.ts +0 -46
- package/src/internal/react/form.ts +0 -79
- package/src/internal/react/index.ts +0 -13
- package/src/preact-client.ts +0 -2
- package/src/preact-server.ts +0 -2
- package/src/preact.ts +0 -2
- package/src/react-client.ts +0 -2
- package/src/react-server.ts +0 -2
- package/src/react.ts +0 -2
- package/src/server.ts +0 -2
- package/src/utils.ts +0 -5
- package/src/vite.ts +0 -2
- /package/{src/internal → .generated}/client/deserialize.ts +0 -0
- /package/{src/internal → .generated}/core/match.ts +0 -0
- /package/{src/internal → .generated}/files/index.ts +0 -0
- /package/{src/internal → .generated}/preact/runtime.ts +0 -0
- /package/{src/internal → .generated}/react/runtime.ts +0 -0
- /package/{src/internal → .generated}/vite/transport.ts +0 -0
- /package/dist/{internal/client → client}/deserialize.d.ts +0 -0
- /package/dist/{internal/core → core}/errors.d.ts +0 -0
- /package/dist/{internal/core → core}/match.d.ts +0 -0
- /package/dist/{internal/files → files}/index.d.ts +0 -0
- /package/dist/{internal/preact → preact}/runtime.d.ts +0 -0
- /package/dist/{internal/react → react}/hooks.d.ts +0 -0
- /package/dist/{internal/react → react}/runtime.d.ts +0 -0
- /package/dist/{internal/server → server}/index.d.ts +0 -0
- /package/dist/{internal/vite → vite}/index.d.ts +0 -0
- /package/dist/{internal/vite → vite}/transport.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# smolter _
|
|
2
2
|
|
|
3
3
|
<!-- bundlejs:start -->
|
|
4
|
-
[](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.3.1%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&bundle)
|
|
5
|
+
[](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.3.1%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&bundle)
|
|
6
6
|
<!-- bundlejs:end -->
|
|
7
7
|
|
|
8
8
|
A typed data router for React and Preact. **Zero runtime dependencies.**
|
|
@@ -20,6 +20,8 @@ Start with [loaders](https://kasperrt.github.io/smolter/react/docs/loaders/), [a
|
|
|
20
20
|
|
|
21
21
|
## Contents
|
|
22
22
|
|
|
23
|
+
- [Deployment updates](#deployment-updates)
|
|
24
|
+
|
|
23
25
|
- [Install](#install)
|
|
24
26
|
- [Try the four standalone examples](#try-the-four-standalone-examples)
|
|
25
27
|
- [Choose file routing or manual routes](#choose-file-routing-or-manual-routes)
|
|
@@ -29,7 +31,10 @@ Start with [loaders](https://kasperrt.github.io/smolter/react/docs/loaders/), [a
|
|
|
29
31
|
- [Types follow the handler](#types-follow-the-handler)
|
|
30
32
|
- [Server rendering](#server-rendering)
|
|
31
33
|
- [Package entries](#package-entries)
|
|
34
|
+
- [Application integration](#application-integration)
|
|
35
|
+
- [Resource subscriptions](#resource-subscriptions)
|
|
32
36
|
- [Size and checks](#size-and-checks)
|
|
37
|
+
- [Routing without data hooks](#routing-without-data-hooks)
|
|
33
38
|
|
|
34
39
|
## Install
|
|
35
40
|
|
|
@@ -40,7 +45,7 @@ pnpm add @kasperrt/smolter
|
|
|
40
45
|
```
|
|
41
46
|
|
|
42
47
|
```ts
|
|
43
|
-
import {
|
|
48
|
+
import { createDataRouter, useFetcher } from "@kasperrt/smolter/react";
|
|
44
49
|
// Preact: import from "@kasperrt/smolter/preact".
|
|
45
50
|
```
|
|
46
51
|
|
|
@@ -61,7 +66,8 @@ pnpm dev
|
|
|
61
66
|
Each app has its own entrypoints, routes, and data code. Only UI components are
|
|
62
67
|
shared. Preact examples do not import React. Each includes:
|
|
63
68
|
|
|
64
|
-
- `/`:
|
|
69
|
+
- `/`: a short introduction and a link to the docs.
|
|
70
|
+
- `/examples/counter`: fetcher POST, automatic and manual revalidation.
|
|
65
71
|
- `/tasks`: list, create form, and a fetcher-only task preview.
|
|
66
72
|
- `/tasks/:id`: PATCH, DELETE, validation, and skipping no-op revalidation.
|
|
67
73
|
- `/about`: the separation between data and rendering.
|
|
@@ -94,10 +100,10 @@ export default defineConfig({
|
|
|
94
100
|
|
|
95
101
|
```ts
|
|
96
102
|
// app.ts
|
|
97
|
-
import {
|
|
103
|
+
import { createDataRouter } from "@kasperrt/smolter/react";
|
|
98
104
|
import routes from "virtual:smolter/routes";
|
|
99
105
|
|
|
100
|
-
export const router =
|
|
106
|
+
export const router = createDataRouter(routes, { browser: true });
|
|
101
107
|
```
|
|
102
108
|
|
|
103
109
|
| File or folder | URL |
|
|
@@ -136,14 +142,14 @@ Outside Vite, `flatRoutes(directory)` from `@kasperrt/smolter/react/vite` return
|
|
|
136
142
|
### Manual routes
|
|
137
143
|
|
|
138
144
|
```tsx
|
|
139
|
-
import {
|
|
145
|
+
import { createDataRouter, defineRoute } from "@kasperrt/smolter/react";
|
|
140
146
|
import * as tasks from "./tasks/route";
|
|
141
147
|
import * as task from "./task/route";
|
|
142
148
|
|
|
143
149
|
export const tasksRoute = defineRoute("/tasks", tasks);
|
|
144
150
|
export const taskRoute = defineRoute("/tasks/:id", task);
|
|
145
151
|
export const routes = [tasksRoute, taskRoute];
|
|
146
|
-
export const router =
|
|
152
|
+
export const router = createDataRouter(routes, { browser: true });
|
|
147
153
|
```
|
|
148
154
|
|
|
149
155
|
Or pass handlers directly to `defineRoute(path, { loader, action, default: View })`.
|
|
@@ -171,11 +177,12 @@ export { default } from "./view";
|
|
|
171
177
|
export { loader } from "./loader";
|
|
172
178
|
export { action } from "./action";
|
|
173
179
|
|
|
174
|
-
export { clientLoader } from "./
|
|
175
|
-
export { clientAction } from "./
|
|
180
|
+
export { clientLoader } from "./clientLoader";
|
|
181
|
+
export { clientAction } from "./clientAction";
|
|
176
182
|
|
|
177
|
-
export { shouldRevalidate } from "./
|
|
178
|
-
export { HydrateFallback
|
|
183
|
+
export { shouldRevalidate } from "./shouldRevalidate";
|
|
184
|
+
export { HydrateFallback } from "./HydrateFallback";
|
|
185
|
+
export { ErrorBoundary } from "./ErrorBoundary";
|
|
179
186
|
```
|
|
180
187
|
|
|
181
188
|
With the optional server integration:
|
|
@@ -187,7 +194,7 @@ With the optional server integration:
|
|
|
187
194
|
- SSR executes the server loader and hydration reuses that data.
|
|
188
195
|
|
|
189
196
|
```ts
|
|
190
|
-
//
|
|
197
|
+
// clientLoader.ts
|
|
191
198
|
import type { ClientLoaderArgs } from "@kasperrt/smolter/react/client";
|
|
192
199
|
import type { loader } from "./loader";
|
|
193
200
|
|
|
@@ -197,7 +204,7 @@ export async function clientLoader({ serverLoader }: ClientLoaderArgs<typeof loa
|
|
|
197
204
|
```
|
|
198
205
|
|
|
199
206
|
```ts
|
|
200
|
-
//
|
|
207
|
+
// clientAction.ts
|
|
201
208
|
import type { ClientActionArgs } from "@kasperrt/smolter/react/client";
|
|
202
209
|
import type { action } from "./action";
|
|
203
210
|
|
|
@@ -258,7 +265,7 @@ routerPlugin({
|
|
|
258
265
|
|
|
259
266
|
Each table exports `routes`. The standalone examples contain both tables.
|
|
260
267
|
|
|
261
|
-
Without the optional integration, `
|
|
268
|
+
Without the optional integration, `createDataRouter` invokes handlers locally:
|
|
262
269
|
client handlers take precedence, with ordinary handlers as fallbacks.
|
|
263
270
|
`loadRouter` selects server handlers only. Core-only usage does not create HTTP
|
|
264
271
|
endpoints or compiler boundaries by itself.
|
|
@@ -319,7 +326,7 @@ Fetcher results belong to `fetcher.data`, not `useActionData()`.
|
|
|
319
326
|
## Decide when to revalidate
|
|
320
327
|
|
|
321
328
|
```ts
|
|
322
|
-
//
|
|
329
|
+
// shouldRevalidate.ts
|
|
323
330
|
import type { RevalidationArgs } from "@kasperrt/smolter/react";
|
|
324
331
|
|
|
325
332
|
export function shouldRevalidate({ actionResult, defaultShouldRevalidate }: RevalidationArgs) {
|
|
@@ -344,7 +351,25 @@ and explicit `fetcher.load()` calls always load their destination.
|
|
|
344
351
|
This controls loader refreshes; it does not rerun actions or reload the document.
|
|
345
352
|
Existing page data stays visible during manual and post-action refreshes.
|
|
346
353
|
|
|
347
|
-
`
|
|
354
|
+
`useRevalidator()` returns an async function. Target a registered route ID or path
|
|
355
|
+
pattern to refresh only its active data, including loaded fetchers:
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
const revalidate = useRevalidator();
|
|
359
|
+
|
|
360
|
+
await revalidate({ route: "/tasks/:id" });
|
|
361
|
+
await revalidate({ route: "root" });
|
|
362
|
+
await revalidate({ route: ["root", "/tasks/:id"] });
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Without a target, `revalidate()` refreshes the active page, loaded fetchers, and
|
|
366
|
+
registered root/layout data. `shouldRevalidate` still applies. Inactive routes
|
|
367
|
+
are not loaded, unknown targets do nothing, and revalidation never navigates.
|
|
368
|
+
|
|
369
|
+
The Preact root integration uses `root` as its default ID. Configure its
|
|
370
|
+
`loaderPath` with a registered data route to enable root refreshes. Custom React
|
|
371
|
+
or Preact layouts can use `router.registerRevalidator(id, callback)`; call its
|
|
372
|
+
returned cleanup function when the layout is removed.
|
|
348
373
|
The task-detail examples return `{ changed: false }` for an unchanged title;
|
|
349
374
|
the corresponding loader is skipped.
|
|
350
375
|
|
|
@@ -381,17 +406,17 @@ const hydrationJSON = serialize(router.getSnapshot());
|
|
|
381
406
|
|
|
382
407
|
Create a fresh router per request. Embed the serialized snapshot in a JSON
|
|
383
408
|
script, restore it with `deserialize` from `@kasperrt/smolter/react/client`, and pass it as
|
|
384
|
-
`hydrationData` to `
|
|
409
|
+
`hydrationData` to `createDataRouter`. This preserves loader errors through hydration
|
|
385
410
|
without exposing server stacks or causes. Error boundaries should display the
|
|
386
411
|
error message, not its stack.
|
|
387
412
|
|
|
388
413
|
```ts
|
|
389
414
|
import { deserialize } from "@kasperrt/smolter/react/client";
|
|
390
|
-
import {
|
|
415
|
+
import { createDataRouter } from "@kasperrt/smolter/react";
|
|
391
416
|
import routes from "virtual:smolter/routes";
|
|
392
417
|
|
|
393
418
|
const data = document.getElementById("smolter-data");
|
|
394
|
-
const router =
|
|
419
|
+
const router = createDataRouter(routes, {
|
|
395
420
|
browser: true,
|
|
396
421
|
hydrationData: data?.textContent ? deserialize(data.textContent) : undefined,
|
|
397
422
|
});
|
|
@@ -406,7 +431,7 @@ The SSR examples use the same server route definitions for HTML and data request
|
|
|
406
431
|
|
|
407
432
|
## Package entries
|
|
408
433
|
|
|
409
|
-
Use `@kasperrt/smolter/react` for React 18+ and `@kasperrt/smolter/preact` for Preact
|
|
434
|
+
Use `@kasperrt/smolter/react` for React 18+ and `@kasperrt/smolter/preact` for Preact 11.0.1+.
|
|
410
435
|
Choose one renderer for your application. Both expose the same routing API:
|
|
411
436
|
|
|
412
437
|
| Purpose | React | Preact |
|
|
@@ -421,39 +446,196 @@ The main renderer entries never import server or build modules. Named imports
|
|
|
421
446
|
are tree-shaken; optional transport is included only when you import `/client`.
|
|
422
447
|
Server entries do not import either UI renderer. Vite entries are build-time only.
|
|
423
448
|
|
|
424
|
-
**Migrating
|
|
449
|
+
**Migrating renderer subpaths:** replace `/core` imports for types, router factories, and
|
|
425
450
|
`defineRoute` with your renderer entry. Use `/utils` for `matchRoutes`,
|
|
426
451
|
`internalURL` and `safeWrapAsync`. Move `/client`, `/server`,
|
|
427
452
|
`/files`, and `/vite` imports to the corresponding renderer-prefixed path above.
|
|
428
453
|
The bare package root and old top-level subpaths are no longer exported.
|
|
429
454
|
|
|
455
|
+
See the [0.3 migration guide](https://kasperrt.github.io/smolter/react/docs/migration-0.3/) and [changelog](./CHANGELOG.md).
|
|
456
|
+
|
|
457
|
+
**Router factory migration:** code that previously used `createRouter` with
|
|
458
|
+
loaders, actions, fetchers or revalidation must now use `createDataRouter`.
|
|
459
|
+
`createRouter` now creates a navigation-only router. SSR and file-router
|
|
460
|
+
application helpers select `createDataRouter` automatically.
|
|
461
|
+
|
|
430
462
|
On JSR, install `@kasperrt/smolter` using the
|
|
431
463
|
[JSR package instructions](https://jsr.io/docs/npm-compatibility) and import
|
|
432
|
-
`@kasperrt/smolter/react` or `@kasperrt/smolter/preact`. Its
|
|
433
|
-
`server`, and `files` subpaths match npm. The Vite plugin is npm-only:
|
|
464
|
+
`@kasperrt/smolter/react` or `@kasperrt/smolter/preact`. Its renderer, client, server and utility subpaths match npm. The Vite plugin is npm-only:
|
|
434
465
|
use `@kasperrt/smolter/react/vite` from the npm package when adding file routing.
|
|
435
466
|
|
|
467
|
+
## Application integration
|
|
468
|
+
|
|
469
|
+
Import routing, forms and data hooks from `@kasperrt/smolter/preact`.
|
|
470
|
+
Document rendering is optional: `@kasperrt/smolter/preact/document` supplies
|
|
471
|
+
`Document`, `Meta`, `Links`, `Scripts` and `ScrollRestoration`.
|
|
472
|
+
`@kasperrt/smolter/preact/client` supplies `mountApplication`,
|
|
473
|
+
`configureApplication` and the server transport helpers. Mounting connects
|
|
474
|
+
hydration, root data and navigation presentation to the same core router.
|
|
475
|
+
These modules remain tree-shakable; basic routing does not import the document
|
|
476
|
+
or mounting code. The size table also measures all three together, because
|
|
477
|
+
moving APIs to subpaths does not make their implementation disappear.
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
// vite.config.ts
|
|
481
|
+
import { defineConfig } from "vite";
|
|
482
|
+
import { applicationPlugins } from "@kasperrt/smolter/preact/vite";
|
|
483
|
+
|
|
484
|
+
export default defineConfig({
|
|
485
|
+
plugins: applicationPlugins({ directory: "src/routes" }),
|
|
486
|
+
});
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
```tsx
|
|
490
|
+
// src/entry.client.tsx
|
|
491
|
+
import routes from "virtual:smolter/routes";
|
|
492
|
+
import { mountApplication } from "@kasperrt/smolter/preact/client";
|
|
493
|
+
import * as root from "./root";
|
|
494
|
+
|
|
495
|
+
mountApplication(routes, root);
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
`root` exports a default component containing `Outlet` and a `Layout` wrapping
|
|
499
|
+
it in `Document`, a head, and a body containing `Scripts`. Its optional loader
|
|
500
|
+
supplies root data. `renderApplication(request, routes, root, entry)` from
|
|
501
|
+
`preact/server` renders the same tree and serializes hydration data;
|
|
502
|
+
`dispatchRoute(request, routes)` handles route data requests. Server rendering
|
|
503
|
+
requires the optional `preact-render-to-string` peer.
|
|
504
|
+
|
|
505
|
+
Deployment-specific URL mapping can be configured once at startup with
|
|
506
|
+
`configureApplication({ resolveURL, isDocumentHandoff, leaveDocument,
|
|
507
|
+
entryScript })`. Do not put user-specific or request-specific state there.
|
|
508
|
+
The library does not define application domains, ports, or branding.
|
|
509
|
+
|
|
510
|
+
`utils/http` contains the Node request/response adapter; `utils/assets` contains
|
|
511
|
+
manifest/preload helpers; `utils/responses` contains response normalization.
|
|
512
|
+
Keep the first two in server/build entrypoints. Browser application imports do
|
|
513
|
+
not include them.
|
|
514
|
+
|
|
515
|
+
### Virtual modules and entrypoints
|
|
516
|
+
|
|
517
|
+
`virtual:smolter/routes` is the default route-array export generated by the Vite
|
|
518
|
+
plugin from `src/routes`. It is not a physical file or a runtime package.
|
|
519
|
+
The server build retains loaders/actions; the browser build strips their
|
|
520
|
+
re-exports and installs server proxies. `entry.client.tsx` and `entry.server.tsx`
|
|
521
|
+
are application-owned files wiring those manifests to hydration and SSR.
|
|
522
|
+
Manual tables can be imported directly instead.
|
|
523
|
+
|
|
524
|
+
See the [virtual routes guide](https://kasperrt.github.io/smolter/react/docs/virtual-routes)
|
|
525
|
+
for the exact contract, TypeScript declaration, and both entrypoint examples.
|
|
526
|
+
|
|
527
|
+
### Express hosting
|
|
528
|
+
|
|
529
|
+
Keep Express in control of metrics, tracing, authentication and static files.
|
|
530
|
+
Mount `createRequestHandler({ build, entry })` from
|
|
531
|
+
`@kasperrt/smolter/utils/http` after those middleware. `build` is the compiled
|
|
532
|
+
server entry and `entry` is the browser entry URL from Vite's manifest. Do not
|
|
533
|
+
consume request bodies with Express body parsers before this handler.
|
|
534
|
+
The adapter has no Express dependency and forwards failures to `next(error)`.
|
|
535
|
+
|
|
536
|
+
See the [Express guide](https://kasperrt.github.io/smolter/react/docs/express)
|
|
537
|
+
for a complete server example and current buffering limitations.
|
|
538
|
+
|
|
539
|
+
## Resource subscriptions
|
|
540
|
+
|
|
541
|
+
`@kasperrt/smolter/react/data` adds subscriptions to named resources when their
|
|
542
|
+
lifetime is independent of URL navigation. The state and cancellation logic
|
|
543
|
+
lives in core; the entry adds React bindings without DOM or HTTP globals.
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
import { createResourceRouter } from "@kasperrt/smolter/react/data";
|
|
547
|
+
|
|
548
|
+
const resources = createResourceRouter(new Map([
|
|
549
|
+
["settings", {
|
|
550
|
+
loader: async ({ signal }) => ({
|
|
551
|
+
data: await readSettings(signal),
|
|
552
|
+
error: "",
|
|
553
|
+
}),
|
|
554
|
+
action: async ({ input, signal }) => ({
|
|
555
|
+
data: await saveSettings(input, signal),
|
|
556
|
+
error: "",
|
|
557
|
+
}),
|
|
558
|
+
}],
|
|
559
|
+
]));
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
`ResourceProvider` owns the store lifetime. `Resource` selects a resource by
|
|
563
|
+
`routeId` and params. `useResourceData` reads it; `useRouteLoaderData` reads a
|
|
564
|
+
named resource. `useResourceFetcher` returns `[state, operations]`, with
|
|
565
|
+
independent `load` and `submit` operations. Successful mutations consult the
|
|
566
|
+
resource's `shouldRevalidate` before reloading its data. Last-subscriber cleanup
|
|
567
|
+
aborts pending work; `persistent` keeps a resource until explicitly disposed.
|
|
568
|
+
This optional entry does not contribute to the base React bundle measurement.
|
|
569
|
+
|
|
436
570
|
## Size and checks
|
|
437
571
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
572
|
+
| Need | Public import |
|
|
573
|
+
| --- | --- |
|
|
574
|
+
| Navigation only | `@kasperrt/smolter/react` or `/preact`, using `createRouter` |
|
|
575
|
+
| Loaders, actions, forms and fetchers | Same entry, using `createDataRouter` |
|
|
576
|
+
| Browser server-request transport | Renderer `/client` subpath |
|
|
577
|
+
| Preact application hydration | `@kasperrt/smolter/preact/client` |
|
|
578
|
+
| Preact document components | `@kasperrt/smolter/preact/document` |
|
|
579
|
+
| Lazy Preact route modules | `@kasperrt/smolter/preact/lazy-route` |
|
|
580
|
+
| Optional React resources | `@kasperrt/smolter/react/data` |
|
|
581
|
+
| Server rendering / Vite integration | Renderer `/server` / `/vite` subpaths; keep out of browser imports |
|
|
582
|
+
|
|
583
|
+
The measurements below describe retained exports, not the cost of an import
|
|
584
|
+
statement. Browser transport rows include the router and transport together;
|
|
585
|
+
application rows include the listed modules together. Server and Vite modules
|
|
586
|
+
have no browser cost when kept in their intended server/build entrypoints.
|
|
587
|
+
|
|
588
|
+
Use `createRouter` for navigation only, or `createDataRouter` for loaders,
|
|
589
|
+
actions, fetchers and revalidation. Both are named exports from
|
|
590
|
+
`@kasperrt/smolter/preact` and `/react`. No extra package or plugin is required.
|
|
591
|
+
The production bundler can remove the data engine when only `createRouter` is used.
|
|
592
|
+
|
|
593
|
+
`pnpm size` regenerates this table and the site's size data using identical
|
|
594
|
+
minifiers. The routing-only gate compares our selected exports with wouter's
|
|
595
|
+
routing subset: `Router`, `Route`, `Switch`, `Link`, `useParams`, and `useLocation`.
|
|
596
|
+
Both complete wouter entries are reported too. The APIs differ; this is not
|
|
597
|
+
a claim of identical features.
|
|
443
598
|
|
|
444
599
|
<!-- size-comparison:start -->
|
|
445
600
|
| Browser exports | Gzip bytes |
|
|
446
601
|
| --- | ---: |
|
|
447
|
-
|
|
|
448
|
-
|
|
|
449
|
-
|
|
|
450
|
-
|
|
|
602
|
+
| `React Router: declarative routing subset` | 12882 |
|
|
603
|
+
| `React Router: data routing subset` | 29780 |
|
|
604
|
+
| `React: routing-only named exports` | 1245 |
|
|
605
|
+
| `Preact: routing-only named exports` | 1405 |
|
|
606
|
+
| `@kasperrt/smolter/react` | 2873 |
|
|
607
|
+
| `@kasperrt/smolter/preact` | 4748 |
|
|
608
|
+
| `@kasperrt/smolter/react + @kasperrt/smolter/react/client` | 3870 |
|
|
609
|
+
| `@kasperrt/smolter/preact + server transport` | 5697 |
|
|
610
|
+
| `@kasperrt/smolter/preact + client + document (application without lazy routes)` | 8033 |
|
|
611
|
+
| `@kasperrt/smolter/preact + client + document + lazy-route (file-routed application)` | 8317 |
|
|
612
|
+
| `@kasperrt/smolter/react/data (optional resource API)` | 2962 |
|
|
613
|
+
| `React: common routing/data exports` | 2683 |
|
|
614
|
+
| `Preact: common routing/data exports` | 2966 |
|
|
615
|
+
| `wouter: routing subset (Router, Route, Switch, Link, useParams, useLocation)` | 2854 |
|
|
616
|
+
| `wouter-preact: same routing subset` | 2211 |
|
|
451
617
|
| `wouter@3.13.0` | 3091 |
|
|
452
618
|
| `wouter-preact@3.13.0` | 2450 |
|
|
453
619
|
| `react-router@8.4.0` | 60994 |
|
|
454
620
|
<!-- size-comparison:end -->
|
|
455
621
|
|
|
456
|
-
The
|
|
622
|
+
The common-export rows measure the same named API set: `createDataRouter`,
|
|
623
|
+
`defineRoute`, `RouterProvider`, `Routes`, `Link`, `Form`, `useRouter`,
|
|
624
|
+
`useLoaderData`, `useActionData`, `useParams`, `useLocation`,
|
|
625
|
+
`useRevalidator` and `useFetcher`. Preact’s link and submission APIs currently
|
|
626
|
+
have additional behavior; equal names do not imply identical implementations.
|
|
627
|
+
React Router's declarative subset retains `BrowserRouter`, `Routes`, `Route`,
|
|
628
|
+
`Link`, `useParams`, and `useLocation`. Its data subset retains
|
|
629
|
+
`createBrowserRouter`, `RouterProvider`, `Link`, `Form`, `useLoaderData`,
|
|
630
|
+
`useActionData`, `useParams`, `useLocation`, `useRevalidator`, and `useFetcher`.
|
|
631
|
+
React Router has no dedicated Preact entry; these are React API measurements,
|
|
632
|
+
not a Preact compatibility build. The selected APIs have different capabilities.
|
|
633
|
+
|
|
634
|
+
The routing-only rows retain `createRouter`, `defineRoute`, `RouterProvider`,
|
|
635
|
+
`Routes`, `Link`, `useParams` and `useLocation`. Full Preact also exposes richer
|
|
636
|
+
application APIs. These tiers overlap: do not add the rows together.
|
|
637
|
+
|
|
638
|
+
The table is generated by `pnpm size` from the named subsets above or all exports of each other listed entry,
|
|
457
639
|
using production ESM bundles, esbuild, Terser, and gzip level 9. React, React DOM,
|
|
458
640
|
and Preact are external in every bundle. Transitive library dependencies are
|
|
459
641
|
included. React Router provides a broader API; this compares complete entry
|
|
@@ -461,6 +643,43 @@ sizes, not equivalent feature sets or the minimum code for one route.
|
|
|
461
643
|
`@kasperrt/smolter/react/client` adds browser transport for server loaders/actions; server-only
|
|
462
644
|
rendering code and the Vite plugin are not shipped to the browser.
|
|
463
645
|
|
|
646
|
+
### Routing without data hooks
|
|
647
|
+
|
|
648
|
+
```tsx
|
|
649
|
+
import { render } from "preact";
|
|
650
|
+
import {
|
|
651
|
+
createRouter,
|
|
652
|
+
defineRoute,
|
|
653
|
+
Link,
|
|
654
|
+
RouterProvider,
|
|
655
|
+
Routes,
|
|
656
|
+
} from "@kasperrt/smolter/preact";
|
|
657
|
+
import Home from "./routes/home";
|
|
658
|
+
import About from "./routes/about";
|
|
659
|
+
|
|
660
|
+
const router = createRouter([
|
|
661
|
+
defineRoute("/", { default: Home }),
|
|
662
|
+
defineRoute("/about", { default: About }),
|
|
663
|
+
], { browser: true });
|
|
664
|
+
|
|
665
|
+
render(
|
|
666
|
+
<RouterProvider router={router}>
|
|
667
|
+
<nav>
|
|
668
|
+
<Link href="/">Home</Link>
|
|
669
|
+
<Link href="/about">About</Link>
|
|
670
|
+
</nav>
|
|
671
|
+
<Routes />
|
|
672
|
+
</RouterProvider>,
|
|
673
|
+
document.getElementById("app")!,
|
|
674
|
+
);
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Add `useParams` for dynamic paths such as `/tasks/:id` and `useLocation` for
|
|
678
|
+
the current URL. Both are included in the routing-only measurement, even though
|
|
679
|
+
this example does not need them. Switch the factory to `createDataRouter` when
|
|
680
|
+
adding loaders, actions or fetchers, importing it from the same entry. SSR hydration and file-routed applications use the
|
|
681
|
+
larger client/document/lazy-route tier; they are not represented by the routing-only number.
|
|
682
|
+
|
|
464
683
|
Exact current measurements are generated in
|
|
465
684
|
[`packages/ui/src/size.json`](./packages/ui/src/size.json).
|
|
466
685
|
|
|
@@ -486,8 +705,31 @@ PRs. [Release workflow](https://github.com/kasperrt/smolter/blob/main/RELEASING.
|
|
|
486
705
|
To test a local package build in another project:
|
|
487
706
|
|
|
488
707
|
```sh
|
|
489
|
-
pnpm
|
|
708
|
+
pnpm pack
|
|
490
709
|
pnpm add /absolute/path/to/kasperrt-smolter-0.0.1.tgz
|
|
491
710
|
```
|
|
492
711
|
|
|
493
712
|
Workspace structure and rendering checks are documented in [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
713
|
+
|
|
714
|
+
## Deployment updates
|
|
715
|
+
|
|
716
|
+
For production tabs that survive a deployment, add `deploymentPlugin()` from
|
|
717
|
+
`@kasperrt/smolter/react/vite` or `/preact/vite`, then connect the router:
|
|
718
|
+
|
|
719
|
+
```ts
|
|
720
|
+
import deployment from "virtual:smolter/deployment";
|
|
721
|
+
import { watchDeployment } from "@kasperrt/smolter/preact/client";
|
|
722
|
+
|
|
723
|
+
watchDeployment(router, deployment);
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
Serve the emitted `smolter-manifest.json` with `Cache-Control: no-store` and HTML
|
|
727
|
+
with `no-cache`. Navigation checks its build version before loading another route;
|
|
728
|
+
a newer version reloads the destination, including its query and hash. Vite chunk
|
|
729
|
+
failures are checked again. Hash-only links skip the check, failed checks continue
|
|
730
|
+
normally, and a session marker prevents reload loops. Development/SSR builds
|
|
731
|
+
disable the helper. This optional client module adds nothing to the routing-only
|
|
732
|
+
entrypoints and never replays form submissions.
|
|
733
|
+
|
|
734
|
+
See [deployment updates](https://kasperrt.github.io/smolter/react/docs/deployments/)
|
|
735
|
+
for Express, CDN paths, TypeScript declarations and unsaved-state handling.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NavigationRouter } from "../core/index.ts";
|
|
2
|
+
/** Build identity and the fresh, non-cacheable manifest URL emitted by deploymentPlugin. */
|
|
3
|
+
export interface DeploymentOptions {
|
|
4
|
+
version: string;
|
|
5
|
+
manifestURL: string;
|
|
6
|
+
/** Maximum time to wait for the update check before continuing navigation. */
|
|
7
|
+
timeout?: number;
|
|
8
|
+
/** Override document navigation, for example to ask about unsaved changes. */
|
|
9
|
+
reload?: (destination: URL) => void;
|
|
10
|
+
}
|
|
11
|
+
/** Check for deployment changes before navigation. Returns a cleanup function. */
|
|
12
|
+
export declare function watchDeployment(router: NavigationRouter, options: DeploymentOptions | null): () => void;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//#region .generated/client/deployment.ts
|
|
2
|
+
/** Check for deployment changes before navigation. Returns a cleanup function. */
|
|
3
|
+
function watchDeployment(router, options) {
|
|
4
|
+
if (!options || typeof window === "undefined") return () => {};
|
|
5
|
+
const { version, manifestURL, timeout = 2e3 } = options;
|
|
6
|
+
const navigate = router.navigate;
|
|
7
|
+
const dispose = router.dispose;
|
|
8
|
+
const manifest = new URL(manifestURL, location.href);
|
|
9
|
+
const key = `smolter:deployment:${manifest.href}:${version}`;
|
|
10
|
+
let sequence = 0;
|
|
11
|
+
let stopped = false;
|
|
12
|
+
let replay = false;
|
|
13
|
+
let destination = new URL(location.href);
|
|
14
|
+
let pending;
|
|
15
|
+
let controller;
|
|
16
|
+
function changed() {
|
|
17
|
+
if (pending) return pending;
|
|
18
|
+
controller = new AbortController();
|
|
19
|
+
const timer = setTimeout(() => controller?.abort(), timeout);
|
|
20
|
+
pending = fetch(manifest, {
|
|
21
|
+
cache: "no-store",
|
|
22
|
+
signal: controller.signal
|
|
23
|
+
}).then(async (response) => {
|
|
24
|
+
if (!response.ok) return false;
|
|
25
|
+
const data = await response.json();
|
|
26
|
+
return typeof data === "object" && data !== null && "version" in data && typeof data.version === "string" && data.version.length > 0 && data.version !== version;
|
|
27
|
+
}).catch(() => false).finally(() => {
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
pending = void 0;
|
|
30
|
+
});
|
|
31
|
+
return pending;
|
|
32
|
+
}
|
|
33
|
+
async function reload(target) {
|
|
34
|
+
const allowed = await Promise.resolve().then(() => {
|
|
35
|
+
if (sessionStorage.getItem(key)) return false;
|
|
36
|
+
sessionStorage.setItem(key, "1");
|
|
37
|
+
return true;
|
|
38
|
+
}).catch(() => false);
|
|
39
|
+
if (allowed && !stopped) {
|
|
40
|
+
if (options?.reload) options.reload(target);
|
|
41
|
+
if (!options?.reload) {
|
|
42
|
+
if (target.href === location.href) location.reload();
|
|
43
|
+
if (target.href !== location.href) location.assign(target.href);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return allowed && !stopped;
|
|
47
|
+
}
|
|
48
|
+
const guarded = async (href, operation) => {
|
|
49
|
+
const target = new URL(href, router.getSnapshot().url);
|
|
50
|
+
const currentURL = new URL(router.getSnapshot().url);
|
|
51
|
+
const current = ++sequence;
|
|
52
|
+
destination = target;
|
|
53
|
+
if (target.origin === location.origin && (target.pathname !== currentURL.pathname || target.search !== currentURL.search) && await changed()) {
|
|
54
|
+
if (current !== sequence || stopped || await reload(target)) return;
|
|
55
|
+
}
|
|
56
|
+
if (current === sequence && !stopped) await navigate(href, operation);
|
|
57
|
+
};
|
|
58
|
+
const pop = (event) => {
|
|
59
|
+
if (replay) return;
|
|
60
|
+
const target = new URL(location.href);
|
|
61
|
+
const previous = new URL(router.getSnapshot().url);
|
|
62
|
+
if (target.pathname === previous.pathname && target.search === previous.search) return;
|
|
63
|
+
event.stopImmediatePropagation();
|
|
64
|
+
const current = ++sequence;
|
|
65
|
+
destination = target;
|
|
66
|
+
changed().then(async (updated) => {
|
|
67
|
+
if (stopped || current !== sequence) return;
|
|
68
|
+
if (updated && await reload(target)) return;
|
|
69
|
+
replay = true;
|
|
70
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: event.state }));
|
|
71
|
+
replay = false;
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const preloadError = () => {
|
|
75
|
+
if (router.getSnapshot().state === "submitting") return;
|
|
76
|
+
const current = sequence;
|
|
77
|
+
const target = destination;
|
|
78
|
+
changed().then((updated) => {
|
|
79
|
+
if (updated && current === sequence && !stopped) return reload(target);
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
function cleanup() {
|
|
83
|
+
stopped = true;
|
|
84
|
+
sequence++;
|
|
85
|
+
controller?.abort();
|
|
86
|
+
window.removeEventListener("popstate", pop, true);
|
|
87
|
+
window.removeEventListener("vite:preloadError", preloadError);
|
|
88
|
+
if (router.navigate === guarded) router.navigate = navigate;
|
|
89
|
+
if (router.dispose === guardedDispose) router.dispose = dispose;
|
|
90
|
+
}
|
|
91
|
+
function guardedDispose() {
|
|
92
|
+
cleanup();
|
|
93
|
+
dispose();
|
|
94
|
+
}
|
|
95
|
+
router.navigate = guarded;
|
|
96
|
+
router.dispose = guardedDispose;
|
|
97
|
+
window.addEventListener("popstate", pop, true);
|
|
98
|
+
window.addEventListener("vite:preloadError", preloadError);
|
|
99
|
+
return cleanup;
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { watchDeployment };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region .generated/client/deserialize.ts
|
|
2
|
+
/** Restore a trusted snapshot produced by @kasperrt/smolter/server serialize, including its route error. */
|
|
3
|
+
function deserialize(json) {
|
|
4
|
+
const snapshot = JSON.parse(json);
|
|
5
|
+
const error = snapshot.error;
|
|
6
|
+
if (error) snapshot.error = Object.assign(new Error(error.message), { name: error.name });
|
|
7
|
+
return snapshot;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { deserialize };
|
|
@@ -7,6 +7,7 @@ export type { ClientActionArgs, ClientLoaderArgs } from "../core/index.ts";
|
|
|
7
7
|
export { deserialize } from "./deserialize.ts";
|
|
8
8
|
/** Browser-safe route exports, excluding server implementation code. */
|
|
9
9
|
export interface ClientRouteModule {
|
|
10
|
+
id?: string;
|
|
10
11
|
default?: unknown;
|
|
11
12
|
HydrateFallback?: unknown;
|
|
12
13
|
ErrorBoundary?: unknown;
|
|
@@ -21,3 +22,4 @@ export interface ServerHandlers {
|
|
|
21
22
|
}
|
|
22
23
|
/** Browser transport for server routes. File-router builds generate this call. */
|
|
23
24
|
export declare function serverRoute<M extends RouteModule = RouteModule>(path: string, module: ClientRouteModule, handlers?: ServerHandlers): Route<M>;
|
|
25
|
+
export { type DeploymentOptions, watchDeployment } from "./deployment.ts";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { deserialize } from "./deserialize.js";
|
|
2
|
+
import { watchDeployment } from "./deployment.js";
|
|
3
|
+
//#region .generated/client/index.ts
|
|
4
|
+
/** Browser transport for server routes. File-router builds generate this call. */
|
|
5
|
+
function serverRoute(path, module, handlers = {
|
|
6
|
+
loader: true,
|
|
7
|
+
action: true
|
|
8
|
+
}) {
|
|
9
|
+
const remote = async ({ request, signal }) => {
|
|
10
|
+
const url = new URL(request.url);
|
|
11
|
+
const response = await fetch(`/_smolter?url=${encodeURIComponent(url.pathname + url.search)}`, {
|
|
12
|
+
method: request.method,
|
|
13
|
+
signal,
|
|
14
|
+
...request.method !== "GET" && { body: await request.formData() }
|
|
15
|
+
});
|
|
16
|
+
const location = response.headers.get("X-Smolter-Redirect");
|
|
17
|
+
if (location) return new Response(null, {
|
|
18
|
+
status: 303,
|
|
19
|
+
headers: { Location: location }
|
|
20
|
+
});
|
|
21
|
+
const data = await response.json();
|
|
22
|
+
if (!response.ok) return new Error(data.error ?? "Route request failed");
|
|
23
|
+
return data;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
...module,
|
|
27
|
+
path,
|
|
28
|
+
...handlers.loader && { loader: remote },
|
|
29
|
+
...handlers.action && { action: remote },
|
|
30
|
+
...module.clientLoader && { clientLoader: (args) => module.clientLoader({
|
|
31
|
+
...args,
|
|
32
|
+
serverLoader: () => remote(args)
|
|
33
|
+
}) },
|
|
34
|
+
...module.clientAction && { clientAction: (args) => {
|
|
35
|
+
const request = args.request.clone();
|
|
36
|
+
return module.clientAction({
|
|
37
|
+
...args,
|
|
38
|
+
serverAction: () => remote({
|
|
39
|
+
...args,
|
|
40
|
+
request: request.clone()
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
} }
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { deserialize, serverRoute, watchDeployment };
|