@kasperrt/smolter 0.2.0 → 0.3.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/{src/internal → .generated}/client/index.ts +1 -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 +136 -0
- package/.generated/vite/application/vite.ts +144 -0
- package/{src/preact-vite.ts → .generated/vite/preact.ts} +9 -3
- package/{src/react-vite.ts → .generated/vite/react.ts} +3 -3
- package/CHANGELOG.md +54 -0
- package/README.md +227 -35
- package/dist/client/deserialize.js +10 -0
- package/dist/{internal/client → client}/index.d.ts +1 -0
- package/dist/client/index.js +46 -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 +67 -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 +87 -0
- package/dist/vite/application/vite.d.ts +9 -0
- package/dist/vite/application/vite.js +101 -0
- package/dist/vite/index.js +104 -0
- package/dist/{preact-vite.d.ts → vite/preact.d.ts} +5 -3
- package/dist/vite/preact.js +15 -0
- package/dist/{react-vite.d.ts → vite/react.d.ts} +3 -3
- package/dist/vite/react.js +14 -0
- package/dist/vite/transport.js +35 -0
- package/jsr.json +28 -16
- package/package.json +116 -36
- 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/index.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
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { safeWrapAsync } from "./errors.js";
|
|
2
|
+
import { matchRoutes } from "./match.js";
|
|
3
|
+
import { internalURL } from "./url.js";
|
|
4
|
+
//#region .generated/core/router.ts
|
|
5
|
+
/** Create an isolated router. Use browser: true for browser history, or loadRouter for SSR. */
|
|
6
|
+
function createDataRouter(routes, options = {}) {
|
|
7
|
+
const browser = options.browser && typeof window !== "undefined";
|
|
8
|
+
const hydrated = options.hydrationData?.state === "idle";
|
|
9
|
+
const initial = {
|
|
10
|
+
loaded: hydrated,
|
|
11
|
+
...options.hydrationData ?? {
|
|
12
|
+
url: new URL(options.url ?? (browser ? location.href : "/"), "http://localhost").href,
|
|
13
|
+
params: {},
|
|
14
|
+
state: "idle"
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const revalidators = /* @__PURE__ */ new Set();
|
|
18
|
+
const channel = (initial, local) => {
|
|
19
|
+
let snapshot = initial;
|
|
20
|
+
let loadedURL;
|
|
21
|
+
let submission;
|
|
22
|
+
let controller;
|
|
23
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
24
|
+
const emit = (state, next) => {
|
|
25
|
+
snapshot = {
|
|
26
|
+
...snapshot,
|
|
27
|
+
...next,
|
|
28
|
+
state
|
|
29
|
+
};
|
|
30
|
+
for (const listener of listeners) listener();
|
|
31
|
+
};
|
|
32
|
+
const run = async (href, body, operation = {}, navigation, redirects = 10) => {
|
|
33
|
+
controller?.abort();
|
|
34
|
+
controller = new AbortController();
|
|
35
|
+
const { signal } = controller;
|
|
36
|
+
const { method = "POST" } = operation;
|
|
37
|
+
revalidators.add(task);
|
|
38
|
+
submission = body;
|
|
39
|
+
const previous = snapshot;
|
|
40
|
+
const url = internalURL(main.getSnapshot().url, href);
|
|
41
|
+
const match = url && matchRoutes(routes, url.pathname);
|
|
42
|
+
const commit = () => {
|
|
43
|
+
if (local || !url) return;
|
|
44
|
+
const changed = snapshot.url !== url.href;
|
|
45
|
+
if (!changed && snapshot.path !== void 0) return;
|
|
46
|
+
if (browser && (navigation || body) && changed) history[operation.replace ? "replaceState" : "pushState"](null, "", url);
|
|
47
|
+
const sameRoute = snapshot.path === match?.route.path;
|
|
48
|
+
snapshot = {
|
|
49
|
+
...snapshot,
|
|
50
|
+
url: url.href,
|
|
51
|
+
path: match?.route.path,
|
|
52
|
+
params: match?.params ?? {},
|
|
53
|
+
actionData: void 0,
|
|
54
|
+
...!sameRoute && {
|
|
55
|
+
data: void 0,
|
|
56
|
+
loaded: false
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
if (!body) commit();
|
|
61
|
+
emit(body ? "submitting" : "loading", { error: void 0 });
|
|
62
|
+
const result = await safeWrapAsync(() => {
|
|
63
|
+
if (!match) return Error(url ? "No route" : "External URL");
|
|
64
|
+
if (navigation !== void 0 && previous.loaded && !previous.error && previous.path === match.route.path && match.route.shouldRevalidate?.({
|
|
65
|
+
url,
|
|
66
|
+
currentUrl: new URL(previous.url),
|
|
67
|
+
nextUrl: url,
|
|
68
|
+
defaultShouldRevalidate: true,
|
|
69
|
+
reason: "navigation"
|
|
70
|
+
}) === false) return previous.data;
|
|
71
|
+
const fn = match.route[body ? "clientAction" : "clientLoader"] || match.route[body ? "action" : "loader"];
|
|
72
|
+
if (!fn) return body && Error("No action");
|
|
73
|
+
return fn({
|
|
74
|
+
request: new Request(url, {
|
|
75
|
+
signal,
|
|
76
|
+
method: body && method,
|
|
77
|
+
body
|
|
78
|
+
}),
|
|
79
|
+
params: match.params,
|
|
80
|
+
signal
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
if (signal.aborted) return result;
|
|
84
|
+
const redirectTo = result.data instanceof Response && result.data.headers.get("Location");
|
|
85
|
+
if (redirectTo && redirects) {
|
|
86
|
+
await main.load(new URL(redirectTo, url).href, void 0, { replace: true }, true, redirects - 1);
|
|
87
|
+
if (!signal.aborted) {
|
|
88
|
+
submission = void 0;
|
|
89
|
+
emit("idle");
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
if (redirectTo) result.error = Error("Redirect loop");
|
|
94
|
+
if (!body && !result.error) loadedURL = url.href;
|
|
95
|
+
if (body && !result.error) commit();
|
|
96
|
+
emit(body && !result.error ? "loading" : "idle", {
|
|
97
|
+
[body && !local ? "actionData" : "data"]: result.data,
|
|
98
|
+
error: result.error,
|
|
99
|
+
loaded: body ? snapshot.loaded : !result.error
|
|
100
|
+
});
|
|
101
|
+
if (body && !result.error) {
|
|
102
|
+
await router.revalidate("action", {
|
|
103
|
+
formMethod: method,
|
|
104
|
+
formAction: url.href,
|
|
105
|
+
formData: body,
|
|
106
|
+
actionResult: result.data
|
|
107
|
+
}).finally(() => {
|
|
108
|
+
if (!signal.aborted) {
|
|
109
|
+
submission = void 0;
|
|
110
|
+
emit("idle");
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
if (!signal.aborted) submission = void 0;
|
|
116
|
+
return result;
|
|
117
|
+
};
|
|
118
|
+
const task = {
|
|
119
|
+
revalidate: async (args, targets) => {
|
|
120
|
+
if (local && (!loadedURL || submission)) return;
|
|
121
|
+
const url = new URL(local ? loadedURL : main.getSnapshot().url);
|
|
122
|
+
const route = matchRoutes(routes, url.pathname)?.route;
|
|
123
|
+
if (!route || targets && !targets.includes(route.id) && !targets.includes(route.path)) return;
|
|
124
|
+
if (route.shouldRevalidate?.({
|
|
125
|
+
...args,
|
|
126
|
+
url,
|
|
127
|
+
currentUrl: url,
|
|
128
|
+
nextUrl: url
|
|
129
|
+
}) === false) return;
|
|
130
|
+
return run(url.href);
|
|
131
|
+
},
|
|
132
|
+
getSnapshot: () => snapshot,
|
|
133
|
+
subscribe(listener) {
|
|
134
|
+
listeners.add(listener);
|
|
135
|
+
return () => {
|
|
136
|
+
listeners.delete(listener);
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
load: run,
|
|
140
|
+
submit: run,
|
|
141
|
+
dispose() {
|
|
142
|
+
controller?.abort();
|
|
143
|
+
listeners.clear();
|
|
144
|
+
revalidators.delete(task);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
revalidators.add(task);
|
|
148
|
+
return task;
|
|
149
|
+
};
|
|
150
|
+
const main = channel(initial);
|
|
151
|
+
let started = 0;
|
|
152
|
+
const pop = () => main.load(location.href, void 0, void 0, false);
|
|
153
|
+
const router = {
|
|
154
|
+
...main,
|
|
155
|
+
routes,
|
|
156
|
+
start() {
|
|
157
|
+
if (!started++ && browser) addEventListener("popstate", pop);
|
|
158
|
+
if (!hydrated && main.getSnapshot() === initial) main.load(initial.url);
|
|
159
|
+
return () => {
|
|
160
|
+
if (!--started && browser) removeEventListener("popstate", pop);
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
async navigate(url, operation) {
|
|
164
|
+
await main.load(url, void 0, operation, true);
|
|
165
|
+
},
|
|
166
|
+
async revalidate(options = {}, context) {
|
|
167
|
+
const { route, reason = "manual", ...details } = typeof options === "string" ? {
|
|
168
|
+
reason: options,
|
|
169
|
+
...context
|
|
170
|
+
} : options;
|
|
171
|
+
const url = new URL(main.getSnapshot().url);
|
|
172
|
+
const args = {
|
|
173
|
+
...details,
|
|
174
|
+
url,
|
|
175
|
+
currentUrl: url,
|
|
176
|
+
nextUrl: url,
|
|
177
|
+
defaultShouldRevalidate: true,
|
|
178
|
+
reason
|
|
179
|
+
};
|
|
180
|
+
const targets = typeof route === "string" ? [route] : route;
|
|
181
|
+
await Promise.all([...revalidators].map((refresh) => refresh.revalidate(args, targets)));
|
|
182
|
+
},
|
|
183
|
+
registerRevalidator(id, refresh) {
|
|
184
|
+
const task = { revalidate: async (args, targets) => {
|
|
185
|
+
if (!targets || targets.includes(id)) return refresh(args);
|
|
186
|
+
} };
|
|
187
|
+
revalidators.add(task);
|
|
188
|
+
return () => {
|
|
189
|
+
revalidators.delete(task);
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
createFetcher() {
|
|
193
|
+
return channel({ state: "idle" }, true);
|
|
194
|
+
},
|
|
195
|
+
dispose() {
|
|
196
|
+
for (const refresh of revalidators) refresh.dispose?.();
|
|
197
|
+
revalidators.clear();
|
|
198
|
+
if (browser) removeEventListener("popstate", pop);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
return router;
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
204
|
+
export { createDataRouter };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { Snapshot } from "./types.ts";
|
|
2
2
|
/** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
|
|
3
3
|
export declare function serialize(snapshot: Snapshot): string;
|
|
4
|
+
/** Encode JSON for embedding inside an HTML script element. */
|
|
5
|
+
export declare function serializeData(value: unknown): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region .generated/core/serialize.ts
|
|
2
|
+
/** Safe inside a script element. Data must be JSON serializable; error stacks and causes are omitted. */
|
|
3
|
+
function serialize(snapshot) {
|
|
4
|
+
return serializeData({
|
|
5
|
+
...snapshot,
|
|
6
|
+
error: snapshot.error && {
|
|
7
|
+
name: snapshot.error.name,
|
|
8
|
+
message: snapshot.error.message
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
/** Encode JSON for embedding inside an HTML script element. */
|
|
13
|
+
function serializeData(value) {
|
|
14
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { serialize, serializeData };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region .generated/core/store.ts
|
|
2
|
+
/** Small observable value shared by layout data and navigation state. */
|
|
3
|
+
function createStore(initial) {
|
|
4
|
+
let value = initial;
|
|
5
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
6
|
+
return {
|
|
7
|
+
getSnapshot: () => value,
|
|
8
|
+
subscribe(listener) {
|
|
9
|
+
listeners.add(listener);
|
|
10
|
+
return () => {
|
|
11
|
+
listeners.delete(listener);
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
update(next) {
|
|
15
|
+
value = next;
|
|
16
|
+
for (const listener of listeners) listener();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { createStore };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Fetcher, NavigateOptions, Router } from "./types.ts";
|
|
2
|
+
/** Values accepted by programmatic route submissions. */
|
|
3
|
+
export type Submission = FormData | HTMLFormElement | Record<string, unknown>;
|
|
4
|
+
/** Destination and encoding for a programmatic route submission. */
|
|
5
|
+
export interface SubmissionOptions {
|
|
6
|
+
action?: string;
|
|
7
|
+
encType?: string;
|
|
8
|
+
method?: string;
|
|
9
|
+
replace?: boolean;
|
|
10
|
+
preventScrollReset?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function formData(value: Submission): FormData;
|
|
13
|
+
export declare function submissionBody(value: Submission, encoding?: string): FormData;
|
|
14
|
+
/** Encode successful form controls for a GET route request. */
|
|
15
|
+
export declare function submissionSearch(body: FormData): string;
|
|
16
|
+
/** Read the JSON payload of a programmatic submission. */
|
|
17
|
+
export declare function readSubmission(request: Request): Promise<unknown>;
|
|
18
|
+
/** Browser form behavior shared by the React and Preact adapters. */
|
|
19
|
+
export interface RouteFormOptions extends NavigateOptions {
|
|
20
|
+
action?: string;
|
|
21
|
+
method?: "get" | "post" | "put" | "patch" | "delete";
|
|
22
|
+
reloadDocument?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface FormSubmissionEvent {
|
|
25
|
+
currentTarget: HTMLFormElement;
|
|
26
|
+
defaultPrevented: boolean;
|
|
27
|
+
preventDefault: () => void;
|
|
28
|
+
}
|
|
29
|
+
/** Submit an internal form while leaving external actions to the browser. */
|
|
30
|
+
export declare function submitForm(router: Router, event: FormSubmissionEvent, submitter: HTMLElement | null, options: RouteFormOptions, fetcher?: Fetcher): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { internalURL } from "./url.js";
|
|
2
|
+
//#region .generated/core/submission.ts
|
|
3
|
+
function formData(value) {
|
|
4
|
+
if (value instanceof FormData) return value;
|
|
5
|
+
if (typeof HTMLFormElement !== "undefined" && value instanceof HTMLFormElement) return new FormData(value);
|
|
6
|
+
const body = new FormData();
|
|
7
|
+
for (const [key, field] of Object.entries(value)) body.append(key, String(field));
|
|
8
|
+
return body;
|
|
9
|
+
}
|
|
10
|
+
function submissionBody(value, encoding) {
|
|
11
|
+
return formData(encoding === "application/json" ? { _payload: JSON.stringify(value) } : value);
|
|
12
|
+
}
|
|
13
|
+
/** Encode successful form controls for a GET route request. */
|
|
14
|
+
function submissionSearch(body) {
|
|
15
|
+
const search = new URLSearchParams();
|
|
16
|
+
for (const [key, value] of body) search.append(key, typeof value === "string" ? value : value.name);
|
|
17
|
+
return String(search);
|
|
18
|
+
}
|
|
19
|
+
/** Read the JSON payload of a programmatic submission. */
|
|
20
|
+
async function readSubmission(request) {
|
|
21
|
+
if (request.headers.get("Content-Type")?.includes("application/json")) return request.json();
|
|
22
|
+
const body = await request.formData();
|
|
23
|
+
return JSON.parse(String(body.get("_payload") ?? "{}"));
|
|
24
|
+
}
|
|
25
|
+
/** Submit an internal form while leaving external actions to the browser. */
|
|
26
|
+
function submitForm(router, event, submitter, options, fetcher) {
|
|
27
|
+
if (event.defaultPrevented || options.reloadDocument) return;
|
|
28
|
+
const target = internalURL(router.getSnapshot().url, submitter?.getAttribute("formaction") ?? options.action);
|
|
29
|
+
if (!target) return;
|
|
30
|
+
const data = new FormData(event.currentTarget, submitter);
|
|
31
|
+
const method = (submitter?.getAttribute("formmethod") ?? options.method)?.toUpperCase();
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
if (method === "GET") {
|
|
34
|
+
target.search = submissionSearch(data);
|
|
35
|
+
fetcher ? fetcher.load(target.href) : router.navigate(target.href, options);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
(fetcher ?? router).submit(target.href, data, { method });
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { formData, readSubmission, submissionBody, submissionSearch, submitForm };
|
|
@@ -41,6 +41,8 @@ export interface RevalidationArgs extends RevalidationContext {
|
|
|
41
41
|
}
|
|
42
42
|
/** A route component and its optional server and client data handlers. */
|
|
43
43
|
export interface RouteModule {
|
|
44
|
+
/** Stable identifier for targeted revalidation; defaults to the path. */
|
|
45
|
+
id?: string;
|
|
44
46
|
default?: unknown;
|
|
45
47
|
HydrateFallback?: unknown;
|
|
46
48
|
ErrorBoundary?: unknown;
|
|
@@ -80,7 +82,11 @@ export interface RouterOptions {
|
|
|
80
82
|
}
|
|
81
83
|
/** Options for replacing rather than appending a browser history entry. */
|
|
82
84
|
export interface NavigateOptions {
|
|
85
|
+
state?: unknown;
|
|
86
|
+
viewTransition?: boolean;
|
|
83
87
|
replace?: boolean;
|
|
88
|
+
/** Preserve scroll in integrations that manage scroll restoration. */
|
|
89
|
+
preventScrollReset?: boolean;
|
|
84
90
|
}
|
|
85
91
|
/** HTTP method for a route action submission; defaults to POST. */
|
|
86
92
|
export interface SubmitOptions {
|
|
@@ -104,6 +110,13 @@ export interface Fetcher {
|
|
|
104
110
|
/** Abort work and release subscriptions when this imperative fetcher is no longer used. */
|
|
105
111
|
dispose: () => void;
|
|
106
112
|
}
|
|
113
|
+
/** Refresh active route data without navigation, optionally selecting route IDs or paths. */
|
|
114
|
+
export interface RevalidateOptions extends RevalidationContext {
|
|
115
|
+
route?: string | readonly string[];
|
|
116
|
+
reason?: RevalidationArgs["reason"];
|
|
117
|
+
}
|
|
118
|
+
/** A registered layout loader's revalidation callback. */
|
|
119
|
+
export type Revalidator = (args: RevalidationArgs) => void | Promise<void>;
|
|
107
120
|
/** Route navigation, subscriptions, fetchers, and loader revalidation. */
|
|
108
121
|
export interface Router {
|
|
109
122
|
routes: readonly Route[];
|
|
@@ -111,7 +124,9 @@ export interface Router {
|
|
|
111
124
|
subscribe: (listener: () => void) => () => void;
|
|
112
125
|
start: () => () => void;
|
|
113
126
|
navigate: (url: string, options?: NavigateOptions) => Promise<void>;
|
|
114
|
-
revalidate: (
|
|
127
|
+
revalidate: (options?: RevalidateOptions | RevalidationArgs["reason"], context?: RevalidationContext) => Promise<void>;
|
|
128
|
+
/** Register layout data under a route ID. The returned function unregisters it. */
|
|
129
|
+
registerRevalidator: (id: string, revalidate: Revalidator) => () => void;
|
|
115
130
|
submit: (url: string, body: FormData, options?: SubmitOptions) => Promise<Result>;
|
|
116
131
|
/** Create an independent fetcher. Framework useFetcher hooks manage disposal automatically. */
|
|
117
132
|
createFetcher: () => Fetcher;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Resolve an internal URL; external links remain the browser's responsibility. */
|
|
2
|
+
export declare function internalURL(base: string, href?: string): URL | undefined;
|
|
3
|
+
/** Browser and framework event fields used by navigation interception. */
|
|
4
|
+
type NavigationClickEvent = Pick<MouseEvent, "defaultPrevented" | "button" | "metaKey" | "ctrlKey" | "altKey" | "shiftKey">;
|
|
5
|
+
/** Whether an anchor click should use router navigation. */
|
|
6
|
+
export declare function navigationClick(event: NavigationClickEvent, anchor: HTMLAnchorElement): boolean;
|
|
7
|
+
export {};
|
package/dist/core/url.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region .generated/core/url.ts
|
|
2
|
+
/** Resolve an internal URL; external links remain the browser's responsibility. */
|
|
3
|
+
function internalURL(base, href = base) {
|
|
4
|
+
const url = new URL(href, base);
|
|
5
|
+
if (url.origin === new URL(base).origin) return url;
|
|
6
|
+
}
|
|
7
|
+
/** Whether an anchor click should use router navigation. */
|
|
8
|
+
function navigationClick(event, anchor) {
|
|
9
|
+
return !(event.defaultPrevented || event.button || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey || anchor.hasAttribute("download") || anchor.target && anchor.target !== "_self");
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { internalURL, navigationClick };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { safeWrapAsync } from "./errors.js";
|
|
2
|
+
import { defineRoute, matchRoutes } from "./match.js";
|
|
3
|
+
import { internalURL, navigationClick } from "./url.js";
|
|
4
|
+
import { serialize, serializeData } from "./serialize.js";
|
|
5
|
+
export { defineRoute, internalURL, matchRoutes, navigationClick, safeWrapAsync, serialize, serializeData };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { safeWrapAsync } from "../core/errors.js";
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
//#region .generated/files/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Build-time discovery of flat file routes.
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
var routeFile = /^route\.[jt]sx?$/;
|
|
10
|
+
var flatFile = /\.[jt]sx?$/;
|
|
11
|
+
/** Read only flat files and immediate route folders. Colocated helpers are ignored. */
|
|
12
|
+
async function flatRoutes(directory) {
|
|
13
|
+
const result = await safeWrapAsync(async () => {
|
|
14
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
15
|
+
const routes = [];
|
|
16
|
+
const patterns = /* @__PURE__ */ new Set();
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
let name = entry.name;
|
|
19
|
+
let file = resolve(directory, name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
const candidates = (await readdir(file)).filter((name) => routeFile.test(name));
|
|
22
|
+
if (candidates.length > 1) return /* @__PURE__ */ new Error(`Multiple route files in ${file}`);
|
|
23
|
+
if (!candidates.length) continue;
|
|
24
|
+
file = resolve(file, candidates[0]);
|
|
25
|
+
} else {
|
|
26
|
+
if (!entry.isFile() || !flatFile.test(name) || name.endsWith(".d.ts")) continue;
|
|
27
|
+
name = name.replace(flatFile, "");
|
|
28
|
+
}
|
|
29
|
+
if (/[()[\]:*?]/.test(name)) return /* @__PURE__ */ new Error(`Unsupported flat route name: ${name}`);
|
|
30
|
+
const segments = name.split(".");
|
|
31
|
+
const parts = [];
|
|
32
|
+
for (const [index, segment] of segments.entries()) {
|
|
33
|
+
if (!segment) return /* @__PURE__ */ new Error(`Empty route segment: ${name}`);
|
|
34
|
+
if (segment === "_index" && index !== segments.length - 1) return /* @__PURE__ */ new Error(`_index must be last: ${name}`);
|
|
35
|
+
if (segment === "$" && index !== segments.length - 1) return /* @__PURE__ */ new Error(`Splat must be last: ${name}`);
|
|
36
|
+
if (segment.startsWith("_")) continue;
|
|
37
|
+
const part = segment.replace(/_$/, "");
|
|
38
|
+
parts.push(part === "$" ? "*" : part.replace(/^\$/, ":"));
|
|
39
|
+
}
|
|
40
|
+
if (segments.at(-1)?.startsWith("_") && segments.at(-1) !== "_index") continue;
|
|
41
|
+
const path = `/${parts.join("/")}`;
|
|
42
|
+
const pattern = path.replace(/:[^/]+/g, ":");
|
|
43
|
+
if (patterns.has(pattern)) return /* @__PURE__ */ new Error(`Conflicting flat route: ${path}`);
|
|
44
|
+
patterns.add(pattern);
|
|
45
|
+
routes.push({
|
|
46
|
+
path,
|
|
47
|
+
file
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return routes.sort((a, b) => {
|
|
51
|
+
const left = a.path.split("/");
|
|
52
|
+
const right = b.path.split("/");
|
|
53
|
+
const rank = (part) => part === "*" ? 0 : part?.startsWith(":") ? 1 : part === void 0 ? 2 : 3;
|
|
54
|
+
for (let index = 0; index < Math.max(left.length, right.length); index++) {
|
|
55
|
+
const difference = rank(right[index]) - rank(left[index]);
|
|
56
|
+
if (difference) return difference;
|
|
57
|
+
}
|
|
58
|
+
return a.path.localeCompare(b.path);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
return result.error ?? result.data;
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { flatRoutes };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ComponentChildren } from "preact";
|
|
2
|
+
export interface AwaitProps<T> {
|
|
3
|
+
resolve: T | Promise<T>;
|
|
4
|
+
children: (value: Awaited<T>) => ComponentChildren;
|
|
5
|
+
errorElement?: ComponentChildren;
|
|
6
|
+
}
|
|
7
|
+
export declare function Await<T>({ resolve, children, errorElement, }: AwaitProps<T>): ComponentChildren;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { use } from "preact/compat";
|
|
2
|
+
//#region .generated/preact/await.tsx
|
|
3
|
+
var resolutions = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
function Await({ resolve, children, errorElement }) {
|
|
5
|
+
if (!(resolve instanceof Promise)) return children(resolve);
|
|
6
|
+
let pending = resolutions.get(resolve);
|
|
7
|
+
if (!pending) {
|
|
8
|
+
pending = resolve.then((value) => ({ value }), (error) => ({ error }));
|
|
9
|
+
resolutions.set(resolve, pending);
|
|
10
|
+
}
|
|
11
|
+
const result = use(pending);
|
|
12
|
+
if ("error" in result) {
|
|
13
|
+
if (errorElement !== void 0) return errorElement;
|
|
14
|
+
throw result.error;
|
|
15
|
+
}
|
|
16
|
+
return children(result.value);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { Await };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Browser mounting and hydration for Preact applications. @module */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
import { type Router } from "../core/index.ts";
|
|
4
|
+
import type { AppRoute, RootModule } from "./route-types.ts";
|
|
5
|
+
export * from "../client/index.ts";
|
|
6
|
+
export { configureApplication } from "../core/environment.ts";
|
|
7
|
+
export declare function mountApplication(routes: AppRoute[], rootModule?: RootModule, hash?: boolean): Promise<Router>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { matchRoutes } from "../core/match.js";
|
|
2
|
+
import { createDataRouter } from "../core/router.js";
|
|
3
|
+
import { configureApplication } from "../core/environment.js";
|
|
4
|
+
import { retainRoute } from "../core/presentation.js";
|
|
5
|
+
import { connectNavigation } from "../core/navigation.js";
|
|
6
|
+
import { attachRootData } from "./root-data.js";
|
|
7
|
+
import { RouterApplication, prepareRoutes } from "./router.js";
|
|
8
|
+
import { deserialize } from "../client/deserialize.js";
|
|
9
|
+
import { serverRoute } from "../client/index.js";
|
|
10
|
+
import { flushSync } from "preact/compat";
|
|
11
|
+
import { jsx } from "preact/jsx-runtime";
|
|
12
|
+
import { hydrate, render } from "preact";
|
|
13
|
+
//#region .generated/preact/client.tsx
|
|
14
|
+
/** Browser mounting and hydration for Preact applications. @module */
|
|
15
|
+
/** @jsxImportSource preact */
|
|
16
|
+
async function mountApplication(routes, rootModule, hash = false) {
|
|
17
|
+
const initialURL = new URL(hash ? location.hash.slice(1) || "/" : location.href, location.origin);
|
|
18
|
+
await (matchRoutes(routes, initialURL.pathname)?.route)?.preload?.();
|
|
19
|
+
routes = prepareRoutes(routes);
|
|
20
|
+
const element = document.getElementById("smolter-data");
|
|
21
|
+
const hydration = element?.textContent ? JSON.parse(element.textContent) : void 0;
|
|
22
|
+
if (hydration?.snapshot.error) hydration.snapshot.error = Object.assign(new Error(hydration.snapshot.error.message), hydration.snapshot.error);
|
|
23
|
+
const router = createDataRouter(routes, {
|
|
24
|
+
browser: !hash,
|
|
25
|
+
url: hash ? new URL(location.hash.slice(1) || "/", location.origin).href : location.href,
|
|
26
|
+
...hydration && { hydrationData: hydration.snapshot }
|
|
27
|
+
});
|
|
28
|
+
retainRoute(router, flushSync);
|
|
29
|
+
if (hash) connectHashHistory(router);
|
|
30
|
+
connectNavigation(router);
|
|
31
|
+
const rootStore = attachRootData(router, hydration?.root, rootModule);
|
|
32
|
+
const props = {
|
|
33
|
+
routes,
|
|
34
|
+
router,
|
|
35
|
+
root: hydration?.root,
|
|
36
|
+
rootModule,
|
|
37
|
+
rootStore
|
|
38
|
+
};
|
|
39
|
+
if (hydration) {
|
|
40
|
+
hydrate(/* @__PURE__ */ jsx(RouterApplication, {
|
|
41
|
+
...props,
|
|
42
|
+
document: true
|
|
43
|
+
}), document);
|
|
44
|
+
return router;
|
|
45
|
+
}
|
|
46
|
+
const target = document.getElementById("root");
|
|
47
|
+
if (target) render(/* @__PURE__ */ jsx(RouterApplication, { ...props }), target);
|
|
48
|
+
return router;
|
|
49
|
+
}
|
|
50
|
+
function connectHashHistory(router) {
|
|
51
|
+
const navigate = router.navigate;
|
|
52
|
+
router.navigate = async (url, options) => {
|
|
53
|
+
const target = new URL(url, router.getSnapshot().url);
|
|
54
|
+
const hash = `#${target.pathname}${target.search}`;
|
|
55
|
+
history[options?.replace ? "replaceState" : "pushState"](null, "", hash);
|
|
56
|
+
await navigate(url, options);
|
|
57
|
+
};
|
|
58
|
+
const onHashChange = () => void navigate(location.hash.slice(1) || "/");
|
|
59
|
+
window.addEventListener("hashchange", onHashChange);
|
|
60
|
+
const dispose = router.dispose;
|
|
61
|
+
router.dispose = () => {
|
|
62
|
+
window.removeEventListener("hashchange", onHashChange);
|
|
63
|
+
dispose();
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { configureApplication, deserialize, mountApplication, serverRoute };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preact components and typed data hooks. Import from @kasperrt/smolter/preact.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import type { NavigationRouter, Snapshot } from "../core/index.ts";
|
|
6
|
+
import { type ReactNode } from "./runtime.ts";
|
|
7
|
+
/** Router instance and children rendered within its context. */
|
|
8
|
+
export interface RouterProviderProps {
|
|
9
|
+
router: NavigationRouter;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/** Provide a router to descendant hooks and connect its browser lifecycle. */
|
|
13
|
+
export declare function RouterProvider({ router, children, }: RouterProviderProps): ReactNode;
|
|
14
|
+
/** Hooks require a RouterProvider. */
|
|
15
|
+
export declare function useRouter<T extends NavigationRouter = NavigationRouter>(): T;
|
|
16
|
+
export declare function useSnapshot(): Snapshot;
|
|
17
|
+
export declare function useSnapshot<K extends keyof Snapshot>(key: K): Snapshot[K];
|
|
18
|
+
export declare function useSnapshot<K extends keyof Snapshot>(key: K, path: string | undefined): Snapshot[K] | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createContext, createElement, useContext, useEffect, useSyncExternalStore } from "./runtime.js";
|
|
2
|
+
//#region .generated/preact/context.ts
|
|
3
|
+
var RouterContext = createContext(void 0);
|
|
4
|
+
/** Provide a router to descendant hooks and connect its browser lifecycle. */
|
|
5
|
+
function RouterProvider({ router, children }) {
|
|
6
|
+
useEffect(router.start, [router]);
|
|
7
|
+
return createElement(RouterContext, { value: router }, children);
|
|
8
|
+
}
|
|
9
|
+
/** Hooks require a RouterProvider. */
|
|
10
|
+
function useRouter() {
|
|
11
|
+
return useContext(RouterContext);
|
|
12
|
+
}
|
|
13
|
+
function useSnapshot(key, path) {
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
const getSnapshot = () => {
|
|
16
|
+
const snapshot = router.getSnapshot();
|
|
17
|
+
if (path !== void 0 && path !== snapshot.path) return void 0;
|
|
18
|
+
return key ? snapshot[key] : snapshot;
|
|
19
|
+
};
|
|
20
|
+
return useSyncExternalStore(router.subscribe, getSnapshot);
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { RouterProvider, useRouter, useSnapshot };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { ComponentProps } from "preact/compat";
|
|
3
|
+
export declare function Meta(): import("preact").JSX.Element;
|
|
4
|
+
export declare function Links(): import("preact").JSX.Element;
|
|
5
|
+
export interface ScriptProps {
|
|
6
|
+
nonce?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function Scripts({ nonce }: ScriptProps): import("preact").JSX.Element;
|
|
9
|
+
export declare function ScrollRestoration(_props: ScriptProps): null;
|
|
10
|
+
/** HTML attributes for server markup and client document reconciliation. */
|
|
11
|
+
export interface DocumentProps extends ComponentProps<"html"> {
|
|
12
|
+
"data-debug"?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Render the server document and preserve client-managed theme classes on hydration. */
|
|
15
|
+
export declare function Document(props: DocumentProps): import("preact").JSX.Element;
|