@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.
Files changed (264) hide show
  1. package/{src/internal → .generated}/client/index.ts +1 -0
  2. package/.generated/core/environment.ts +43 -0
  3. package/{src/internal → .generated}/core/errors.ts +1 -1
  4. package/{src/internal → .generated}/core/index.ts +1 -0
  5. package/.generated/core/navigation-router.ts +107 -0
  6. package/.generated/core/navigation.ts +147 -0
  7. package/.generated/core/presentation.ts +108 -0
  8. package/.generated/core/resource-types.ts +44 -0
  9. package/.generated/core/resources.ts +444 -0
  10. package/.generated/core/responses.ts +86 -0
  11. package/{src/internal → .generated}/core/router.ts +136 -63
  12. package/{src/internal → .generated}/core/serialize.ts +7 -2
  13. package/.generated/core/store.ts +25 -0
  14. package/.generated/core/submission.ts +116 -0
  15. package/{src/internal → .generated}/core/types.ts +18 -1
  16. package/.generated/core/url.ts +29 -0
  17. package/.generated/core/utils.ts +5 -0
  18. package/.generated/preact/await.tsx +41 -0
  19. package/.generated/preact/client.tsx +93 -0
  20. package/.generated/preact/context.ts +59 -0
  21. package/.generated/preact/document.tsx +198 -0
  22. package/{src/internal → .generated}/preact/fetcher.ts +43 -23
  23. package/.generated/preact/form.ts +58 -0
  24. package/{src/internal → .generated}/preact/hooks.ts +25 -28
  25. package/.generated/preact/index.ts +34 -0
  26. package/.generated/preact/lazy-route.ts +100 -0
  27. package/.generated/preact/navigation-state.ts +9 -0
  28. package/.generated/preact/navigation.tsx +147 -0
  29. package/.generated/preact/root-context.ts +21 -0
  30. package/.generated/preact/root-data.ts +45 -0
  31. package/.generated/preact/route-hooks.tsx +170 -0
  32. package/.generated/preact/route-types.ts +76 -0
  33. package/.generated/preact/router.tsx +107 -0
  34. package/{src/internal → .generated}/preact/routes.ts +17 -9
  35. package/.generated/react/context.ts +59 -0
  36. package/.generated/react/data.ts +25 -0
  37. package/{src/internal → .generated}/react/fetcher.ts +13 -11
  38. package/.generated/react/form.ts +58 -0
  39. package/{src/internal → .generated}/react/hooks.ts +9 -14
  40. package/{src/internal/preact → .generated/react}/index.ts +5 -1
  41. package/{src/internal → .generated}/react/link.ts +13 -21
  42. package/.generated/react/resources/components.tsx +404 -0
  43. package/{src/internal → .generated}/react/routes.ts +17 -9
  44. package/.generated/server/application.tsx +185 -0
  45. package/.generated/server/assets.ts +19 -0
  46. package/.generated/server/handler.ts +69 -0
  47. package/.generated/server/http.ts +55 -0
  48. package/{src/internal → .generated}/server/index.ts +3 -3
  49. package/.generated/server/node.ts +4 -0
  50. package/.generated/server/preact.ts +7 -0
  51. package/.generated/vite/application/browser-boundary.ts +42 -0
  52. package/.generated/vite/application/lazy-files.ts +136 -0
  53. package/.generated/vite/application/vite.ts +144 -0
  54. package/{src/preact-vite.ts → .generated/vite/preact.ts} +9 -3
  55. package/{src/react-vite.ts → .generated/vite/react.ts} +3 -3
  56. package/CHANGELOG.md +54 -0
  57. package/README.md +227 -35
  58. package/dist/client/deserialize.js +10 -0
  59. package/dist/{internal/client → client}/index.d.ts +1 -0
  60. package/dist/client/index.js +46 -0
  61. package/dist/core/environment.d.ts +12 -0
  62. package/dist/core/environment.js +23 -0
  63. package/dist/core/errors.js +7 -0
  64. package/dist/{internal/core → core}/index.d.ts +1 -0
  65. package/dist/core/match.js +33 -0
  66. package/dist/core/navigation-router.d.ts +15 -0
  67. package/dist/core/navigation-router.js +64 -0
  68. package/dist/core/navigation.d.ts +15 -0
  69. package/dist/core/navigation.js +92 -0
  70. package/dist/core/presentation.d.ts +5 -0
  71. package/dist/core/presentation.js +69 -0
  72. package/dist/core/resource-types.d.ts +37 -0
  73. package/dist/core/resources.d.ts +19 -0
  74. package/dist/core/resources.js +345 -0
  75. package/dist/core/responses.d.ts +15 -0
  76. package/dist/core/responses.js +48 -0
  77. package/dist/{internal/core → core}/router.d.ts +1 -1
  78. package/dist/core/router.js +204 -0
  79. package/dist/{internal/core → core}/serialize.d.ts +2 -0
  80. package/dist/core/serialize.js +17 -0
  81. package/dist/core/store.d.ts +6 -0
  82. package/dist/core/store.js +21 -0
  83. package/dist/core/submission.d.ts +30 -0
  84. package/dist/core/submission.js +41 -0
  85. package/dist/{internal/core → core}/types.d.ts +16 -1
  86. package/dist/core/url.d.ts +7 -0
  87. package/dist/core/url.js +12 -0
  88. package/dist/core/utils.d.ts +5 -0
  89. package/dist/core/utils.js +5 -0
  90. package/dist/files/index.js +64 -0
  91. package/dist/preact/await.d.ts +7 -0
  92. package/dist/preact/await.js +19 -0
  93. package/dist/preact/client.d.ts +7 -0
  94. package/dist/preact/client.js +67 -0
  95. package/dist/preact/context.d.ts +18 -0
  96. package/dist/preact/context.js +23 -0
  97. package/dist/preact/document.d.ts +15 -0
  98. package/dist/preact/document.js +155 -0
  99. package/dist/{internal/preact → preact}/fetcher.d.ts +9 -5
  100. package/dist/preact/fetcher.js +45 -0
  101. package/dist/{internal/preact → preact}/form.d.ts +5 -6
  102. package/dist/preact/form.js +21 -0
  103. package/dist/{internal/preact → preact}/hooks.d.ts +7 -6
  104. package/dist/preact/hooks.js +25 -0
  105. package/dist/preact/index.d.ts +18 -0
  106. package/dist/preact/index.js +15 -0
  107. package/dist/preact/lazy-route.d.ts +19 -0
  108. package/dist/preact/lazy-route.js +51 -0
  109. package/dist/preact/navigation-state.d.ts +2 -0
  110. package/dist/preact/navigation-state.js +9 -0
  111. package/dist/preact/navigation.d.ts +28 -0
  112. package/dist/preact/navigation.js +60 -0
  113. package/dist/preact/root-context.d.ts +13 -0
  114. package/dist/preact/root-context.js +9 -0
  115. package/dist/preact/root-data.d.ts +8 -0
  116. package/dist/preact/root-data.js +28 -0
  117. package/dist/preact/route-hooks.d.ts +32 -0
  118. package/dist/preact/route-hooks.js +94 -0
  119. package/dist/preact/route-types.d.ts +54 -0
  120. package/dist/preact/router.d.ts +16 -0
  121. package/dist/preact/router.js +63 -0
  122. package/dist/{internal/preact → preact}/routes.d.ts +1 -1
  123. package/dist/preact/routes.js +12 -0
  124. package/dist/{internal/preact → preact}/runtime.js +1 -1
  125. package/dist/react/context.d.ts +18 -0
  126. package/dist/react/context.js +23 -0
  127. package/dist/react/data.d.ts +5 -0
  128. package/dist/react/data.js +3 -0
  129. package/dist/{internal/react → react}/fetcher.d.ts +2 -2
  130. package/dist/react/fetcher.js +34 -0
  131. package/dist/{internal/react → react}/form.d.ts +5 -6
  132. package/dist/react/form.js +21 -0
  133. package/dist/react/hooks.js +25 -0
  134. package/dist/{internal/react → react}/index.d.ts +1 -1
  135. package/dist/react/index.js +10 -0
  136. package/dist/{internal/react → react}/link.d.ts +2 -2
  137. package/dist/react/link.js +27 -0
  138. package/dist/react/resources/components.d.ts +64 -0
  139. package/dist/react/resources/components.js +239 -0
  140. package/dist/{internal/react → react}/routes.d.ts +1 -1
  141. package/dist/react/routes.js +12 -0
  142. package/dist/react/runtime.js +1 -0
  143. package/dist/server/application.d.ts +4 -0
  144. package/dist/server/application.js +119 -0
  145. package/dist/server/assets.d.ts +5 -0
  146. package/dist/server/assets.js +7 -0
  147. package/dist/server/handler.d.ts +19 -0
  148. package/dist/server/handler.js +22 -0
  149. package/dist/server/http.d.ts +4 -0
  150. package/dist/server/http.js +27 -0
  151. package/dist/server/index.js +76 -0
  152. package/dist/server/node.d.ts +3 -0
  153. package/dist/server/node.js +3 -0
  154. package/dist/server/preact.d.ts +3 -0
  155. package/dist/server/preact.js +4 -0
  156. package/dist/vite/application/browser-boundary.d.ts +3 -0
  157. package/dist/vite/application/browser-boundary.js +35 -0
  158. package/dist/vite/application/lazy-files.d.ts +2 -0
  159. package/dist/vite/application/lazy-files.js +87 -0
  160. package/dist/vite/application/vite.d.ts +9 -0
  161. package/dist/vite/application/vite.js +101 -0
  162. package/dist/vite/index.js +104 -0
  163. package/dist/{preact-vite.d.ts → vite/preact.d.ts} +5 -3
  164. package/dist/vite/preact.js +15 -0
  165. package/dist/{react-vite.d.ts → vite/react.d.ts} +3 -3
  166. package/dist/vite/react.js +14 -0
  167. package/dist/vite/transport.js +35 -0
  168. package/jsr.json +28 -16
  169. package/package.json +116 -36
  170. package/dist/client.d.ts +0 -2
  171. package/dist/client.js +0 -2
  172. package/dist/core.d.ts +0 -2
  173. package/dist/core.js +0 -2
  174. package/dist/files.d.ts +0 -2
  175. package/dist/files.js +0 -2
  176. package/dist/internal/client/deserialize.js +0 -11
  177. package/dist/internal/client/index.js +0 -44
  178. package/dist/internal/core/errors.js +0 -6
  179. package/dist/internal/core/index.js +0 -7
  180. package/dist/internal/core/match.js +0 -27
  181. package/dist/internal/core/router.js +0 -189
  182. package/dist/internal/core/serialize.js +0 -13
  183. package/dist/internal/core/types.js +0 -5
  184. package/dist/internal/core/url.d.ts +0 -2
  185. package/dist/internal/core/url.js +0 -6
  186. package/dist/internal/files/index.js +0 -77
  187. package/dist/internal/preact/context.d.ts +0 -18
  188. package/dist/internal/preact/context.js +0 -20
  189. package/dist/internal/preact/fetcher.js +0 -28
  190. package/dist/internal/preact/form.js +0 -41
  191. package/dist/internal/preact/hooks.js +0 -35
  192. package/dist/internal/preact/index.d.ts +0 -9
  193. package/dist/internal/preact/index.js +0 -7
  194. package/dist/internal/preact/link.d.ts +0 -12
  195. package/dist/internal/preact/link.js +0 -28
  196. package/dist/internal/preact/routes.js +0 -22
  197. package/dist/internal/react/context.d.ts +0 -18
  198. package/dist/internal/react/context.js +0 -20
  199. package/dist/internal/react/fetcher.js +0 -28
  200. package/dist/internal/react/form.js +0 -41
  201. package/dist/internal/react/hooks.js +0 -35
  202. package/dist/internal/react/index.js +0 -7
  203. package/dist/internal/react/link.js +0 -28
  204. package/dist/internal/react/routes.js +0 -22
  205. package/dist/internal/react/runtime.js +0 -2
  206. package/dist/internal/server/index.js +0 -69
  207. package/dist/internal/vite/index.js +0 -132
  208. package/dist/internal/vite/transport.js +0 -42
  209. package/dist/preact-client.d.ts +0 -2
  210. package/dist/preact-client.js +0 -2
  211. package/dist/preact-server.d.ts +0 -2
  212. package/dist/preact-server.js +0 -2
  213. package/dist/preact-vite.js +0 -9
  214. package/dist/preact.d.ts +0 -2
  215. package/dist/preact.js +0 -2
  216. package/dist/react-client.d.ts +0 -2
  217. package/dist/react-client.js +0 -2
  218. package/dist/react-server.d.ts +0 -2
  219. package/dist/react-server.js +0 -2
  220. package/dist/react-vite.js +0 -9
  221. package/dist/react.d.ts +0 -2
  222. package/dist/react.js +0 -2
  223. package/dist/server.d.ts +0 -2
  224. package/dist/server.js +0 -2
  225. package/dist/utils.d.ts +0 -5
  226. package/dist/utils.js +0 -5
  227. package/dist/vite.d.ts +0 -2
  228. package/dist/vite.js +0 -2
  229. package/src/client.ts +0 -2
  230. package/src/core.ts +0 -2
  231. package/src/files.ts +0 -2
  232. package/src/internal/core/url.ts +0 -6
  233. package/src/internal/preact/context.ts +0 -46
  234. package/src/internal/preact/form.ts +0 -79
  235. package/src/internal/preact/link.ts +0 -46
  236. package/src/internal/react/context.ts +0 -46
  237. package/src/internal/react/form.ts +0 -79
  238. package/src/internal/react/index.ts +0 -13
  239. package/src/preact-client.ts +0 -2
  240. package/src/preact-server.ts +0 -2
  241. package/src/preact.ts +0 -2
  242. package/src/react-client.ts +0 -2
  243. package/src/react-server.ts +0 -2
  244. package/src/react.ts +0 -2
  245. package/src/server.ts +0 -2
  246. package/src/utils.ts +0 -5
  247. package/src/vite.ts +0 -2
  248. /package/{src/internal → .generated}/client/deserialize.ts +0 -0
  249. /package/{src/internal → .generated}/core/match.ts +0 -0
  250. /package/{src/internal → .generated}/files/index.ts +0 -0
  251. /package/{src/internal → .generated}/preact/runtime.ts +0 -0
  252. /package/{src/internal → .generated}/react/runtime.ts +0 -0
  253. /package/{src/internal → .generated}/vite/index.ts +0 -0
  254. /package/{src/internal → .generated}/vite/transport.ts +0 -0
  255. /package/dist/{internal/client → client}/deserialize.d.ts +0 -0
  256. /package/dist/{internal/core → core}/errors.d.ts +0 -0
  257. /package/dist/{internal/core → core}/match.d.ts +0 -0
  258. /package/dist/{internal/files → files}/index.d.ts +0 -0
  259. /package/dist/{internal/preact → preact}/runtime.d.ts +0 -0
  260. /package/dist/{internal/react → react}/hooks.d.ts +0 -0
  261. /package/dist/{internal/react → react}/runtime.d.ts +0 -0
  262. /package/dist/{internal/server → server}/index.d.ts +0 -0
  263. /package/dist/{internal/vite → vite}/index.d.ts +0 -0
  264. /package/dist/{internal/vite → vite}/transport.d.ts +0 -0
@@ -0,0 +1,155 @@
1
+ import { serializeData } from "../core/serialize.js";
2
+ import { useSnapshot } from "./context.js";
3
+ import { applicationEnvironment } from "../core/environment.js";
4
+ import { scrollPositions } from "../core/navigation.js";
5
+ import { useNavigationSnapshot } from "./navigation-state.js";
6
+ import { useApplication } from "./root-context.js";
7
+ import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
8
+ import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
9
+ import { Fragment as Fragment$1, createElement } from "preact";
10
+ //#region .generated/preact/document.tsx
11
+ /** @jsxImportSource preact */
12
+ function renderMeta(item, index) {
13
+ if (typeof item.title === "string") return /* @__PURE__ */ jsx("title", { children: item.title }, index);
14
+ if ("script:ld+json" in item) return /* @__PURE__ */ jsx("script", {
15
+ type: "application/ld+json",
16
+ dangerouslySetInnerHTML: { __html: serializeData(item["script:ld+json"]) }
17
+ }, index);
18
+ const { tagName, ...props } = item;
19
+ return createElement(tagName === "link" ? "link" : "meta", {
20
+ ...props,
21
+ key: index
22
+ });
23
+ }
24
+ function Meta() {
25
+ const { routes, rootModule } = useApplication();
26
+ const snapshot = useSnapshot();
27
+ const route = routes.find((item) => item.path === snapshot.path);
28
+ const args = {
29
+ loaderData: snapshot.data,
30
+ data: snapshot.data,
31
+ params: snapshot.params,
32
+ error: snapshot.error,
33
+ location: new URL(snapshot.url)
34
+ };
35
+ const values = route?.meta?.(args) ?? rootModule?.meta?.(args) ?? [];
36
+ useEffect(() => {
37
+ const title = values.find((item) => typeof item.title === "string")?.title;
38
+ if (typeof title === "string") document.title = title;
39
+ }, [values]);
40
+ return /* @__PURE__ */ jsx(Fragment$1, { children: values.map(renderMeta) });
41
+ }
42
+ function Links() {
43
+ const { rootModule } = useApplication();
44
+ return /* @__PURE__ */ jsx(Fragment$1, { children: rootModule?.links?.().map((props, index) => /* @__PURE__ */ jsx("link", { ...props }, index)) });
45
+ }
46
+ function Scripts({ nonce }) {
47
+ const { root, router, entry } = useApplication();
48
+ const [json] = useState(() => {
49
+ const current = router.getSnapshot();
50
+ const snapshot = {
51
+ ...current,
52
+ error: current.error && {
53
+ ...current.error,
54
+ name: current.error.name,
55
+ message: current.error.message
56
+ }
57
+ };
58
+ return serializeData({
59
+ root,
60
+ snapshot
61
+ });
62
+ });
63
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
64
+ /* @__PURE__ */ jsx("script", {
65
+ ...nonce && { nonce },
66
+ dangerouslySetInnerHTML: { __html: applicationEnvironment?.entryScript ?? "" }
67
+ }),
68
+ /* @__PURE__ */ jsx("script", {
69
+ id: "smolter-data",
70
+ type: "application/json",
71
+ ...nonce && { nonce },
72
+ dangerouslySetInnerHTML: { __html: json }
73
+ }),
74
+ entry && /* @__PURE__ */ jsx("script", {
75
+ type: "module",
76
+ src: entry,
77
+ ...nonce && { nonce }
78
+ })
79
+ ] });
80
+ }
81
+ function ScrollRestoration(_props) {
82
+ const { router } = useApplication();
83
+ const { url, state } = useSnapshot();
84
+ const navigation = useNavigationSnapshot(router);
85
+ const positions = scrollPositions(router);
86
+ const restoredLocation = useRef("");
87
+ useEffect(() => {
88
+ const previous = history.scrollRestoration;
89
+ history.scrollRestoration = "manual";
90
+ return () => {
91
+ history.scrollRestoration = previous;
92
+ };
93
+ }, []);
94
+ useLayoutEffect(() => {
95
+ if (state !== "idle") return;
96
+ if ((history.state?.key ?? "initial") !== navigation.key) return;
97
+ const locationKey = `${navigation.key}:${url}`;
98
+ if (restoredLocation.current === locationKey) return;
99
+ const frame = requestAnimationFrame(() => {
100
+ restoredLocation.current = locationKey;
101
+ const target = new URL(location.href);
102
+ const saved = navigation.type === "POP" && positions.get(navigation.key);
103
+ if (!saved && target.hash) {
104
+ document.getElementById(target.hash.slice(1))?.scrollIntoView();
105
+ return;
106
+ }
107
+ if (!saved && navigation.preventScrollReset) return;
108
+ const [left, top] = saved || [0, 0];
109
+ window.scrollTo({
110
+ left,
111
+ top,
112
+ behavior: "instant"
113
+ });
114
+ });
115
+ return () => cancelAnimationFrame(frame);
116
+ }, [
117
+ url,
118
+ state,
119
+ positions,
120
+ navigation.key,
121
+ navigation.type,
122
+ navigation.preventScrollReset
123
+ ]);
124
+ return null;
125
+ }
126
+ /** Render the server document and preserve client-managed theme classes on hydration. */
127
+ function Document(props) {
128
+ const { children, ...attributes } = props;
129
+ const className = String(props.className ?? "");
130
+ const previousClassName = useRef(className);
131
+ const lang = String(props.lang ?? "en");
132
+ const debug = String(props["data-debug"] ?? false);
133
+ useEffect(() => {
134
+ if (previousClassName.current !== className) {
135
+ const previous = previousClassName.current.split(/\s+/).filter(Boolean);
136
+ const next = className.split(/\s+/).filter(Boolean);
137
+ document.documentElement.classList.remove(...previous);
138
+ document.documentElement.classList.add(...next);
139
+ previousClassName.current = className;
140
+ }
141
+ document.documentElement.lang = lang;
142
+ document.documentElement.dataset.debug = debug;
143
+ }, [
144
+ className,
145
+ lang,
146
+ debug
147
+ ]);
148
+ if (typeof document === "undefined") return /* @__PURE__ */ jsx("html", {
149
+ ...attributes,
150
+ children
151
+ });
152
+ return /* @__PURE__ */ jsx(Fragment, { children });
153
+ }
154
+ //#endregion
155
+ export { Document, Links, Meta, Scripts, ScrollRestoration };
@@ -2,11 +2,13 @@
2
2
  * Preact components and typed data hooks. Import from @kasperrt/smolter/preact.
3
3
  * @module
4
4
  */
5
- import type { AnyDataFunction, DataFunction, DataOf, FetcherSnapshot, Result, Route as RouteDefinition, RouteModule, SubmitOptions } from "../core/index.ts";
5
+ import { type AnyDataFunction, type DataFunction, type DataOf, type FetcherSnapshot, type Result, type Route as RouteDefinition, type RouteModule } from "../core/index.ts";
6
+ import { type Submission, type SubmissionOptions } from "../core/submission.ts";
6
7
  import { type FormProps } from "./form.ts";
7
- import type { ComponentType } from "./runtime.ts";
8
+ import type { RouteData } from "./route-types.ts";
9
+ import { type ComponentType } from "./runtime.ts";
8
10
  /** Action method and optional explicit route URL for a fetcher submission. */
9
- export interface FetcherSubmitOptions extends SubmitOptions {
11
+ export interface FetcherSubmitOptions extends SubmissionOptions {
10
12
  action?: string;
11
13
  }
12
14
  /** Typed independent request state, methods, and a bound Form component. */
@@ -14,9 +16,11 @@ export interface TypedFetcher<T> extends Omit<FetcherSnapshot, "data"> {
14
16
  data?: T;
15
17
  Form: ComponentType<FormProps>;
16
18
  load: (url?: string) => Promise<Result>;
17
- submit: (body: FormData, options?: FetcherSubmitOptions) => Promise<Result>;
19
+ submit: (body: Submission, options?: FetcherSubmitOptions) => Promise<Result>;
18
20
  }
19
21
  /** Create a fetcher for independent route reads and mutations; its data stays local. */
20
22
  export declare function useFetcher<T extends RouteModule>(route: RouteDefinition<T>): TypedFetcher<DataOf<Extract<T["action"] | T["clientAction"], AnyDataFunction>> | DataOf<Extract<T["loader"] | T["clientLoader"], AnyDataFunction>>>;
21
23
  /** Create a fetcher for independent route reads and mutations; its data stays local. */
22
- export declare function useFetcher<T extends AnyDataFunction = DataFunction>(): TypedFetcher<DataOf<T>>;
24
+ export declare function useFetcher<T = DataFunction>(): TypedFetcher<RouteData<T>>;
25
+ /** A fetcher with a bound form and inferred result data. */
26
+ export type FetcherWithComponents<T> = TypedFetcher<RouteData<T>>;
@@ -0,0 +1,45 @@
1
+ import { submissionBody, submissionSearch } from "../core/submission.js";
2
+ import { createElement, useEffect, useMemo, useSyncExternalStore } from "./runtime.js";
3
+ import { useRouter } from "./context.js";
4
+ import { Form } from "./form.js";
5
+ //#region .generated/preact/fetcher.ts
6
+ /** Create a fetcher for independent route reads and mutations; its data stays local. */
7
+ function useFetcher(route) {
8
+ const router = useRouter();
9
+ const path = route?.path;
10
+ const fetcher = useMemo(() => {
11
+ const store = router.createFetcher();
12
+ const target = () => {
13
+ const snapshot = router.getSnapshot();
14
+ return path === void 0 || path === snapshot.path ? snapshot.url : path;
15
+ };
16
+ return {
17
+ ...store,
18
+ Form: (props) => createElement(Form, {
19
+ action: target(),
20
+ ...props,
21
+ fetcher: store
22
+ }),
23
+ load: (url) => store.load(url ?? target()),
24
+ submit: (body, options = {}) => {
25
+ const action = options.action ?? target();
26
+ const data = submissionBody(body, options.encType);
27
+ const method = options.method?.toUpperCase();
28
+ if (method === "GET") {
29
+ const url = new URL(action, router.getSnapshot().url);
30
+ url.search = submissionSearch(data);
31
+ return store.load(url.href);
32
+ }
33
+ return store.submit(action, data, { method });
34
+ }
35
+ };
36
+ }, [router, path]);
37
+ useEffect(() => fetcher.dispose, [fetcher]);
38
+ const state = useSyncExternalStore(fetcher.subscribe, fetcher.getSnapshot);
39
+ return {
40
+ ...fetcher,
41
+ ...state
42
+ };
43
+ }
44
+ //#endregion
45
+ export { useFetcher };
@@ -2,16 +2,15 @@
2
2
  * Preact components and typed data hooks. Import from @kasperrt/smolter/preact.
3
3
  * @module
4
4
  */
5
- import type { Fetcher } from "../core/index.ts";
6
- import type { FormHTMLAttributes, ReactNode } from "./runtime.ts";
5
+ import { type Fetcher } from "../core/index.ts";
6
+ import { type RouteFormOptions } from "../core/submission.ts";
7
+ import { type FormHTMLAttributes, type ReactNode } from "./runtime.ts";
7
8
  /** Form attributes with GET loader requests or POST, PUT, PATCH, and DELETE actions. */
8
- export interface FormProps extends Omit<FormHTMLAttributes<HTMLFormElement>, "action" | "method"> {
9
- action?: string;
10
- method?: "get" | "post" | "put" | "patch" | "delete";
9
+ export interface FormProps extends Omit<FormHTMLAttributes<HTMLFormElement>, "action" | "method">, RouteFormOptions {
11
10
  }
12
11
  interface InternalFormProps extends FormProps {
13
12
  fetcher?: Fetcher;
14
13
  }
15
14
  /** Submit form data to a registered route without a document reload. */
16
- export declare function Form({ action, method, fetcher, onSubmit, ...props }: InternalFormProps): ReactNode;
15
+ export declare function Form(options: InternalFormProps): ReactNode;
17
16
  export {};
@@ -0,0 +1,21 @@
1
+ import { submitForm } from "../core/submission.js";
2
+ import { createElement } from "./runtime.js";
3
+ import { useRouter } from "./context.js";
4
+ //#region .generated/preact/form.ts
5
+ /** Submit form data to a registered route without a document reload. */
6
+ function Form(options) {
7
+ const { action, method, fetcher, onSubmit, reloadDocument, preventScrollReset, replace, ...props } = options;
8
+ const router = useRouter();
9
+ const submit = (event) => {
10
+ onSubmit?.(event);
11
+ submitForm(router, event, event.submitter, options, fetcher);
12
+ };
13
+ return createElement("form", {
14
+ ...props,
15
+ action,
16
+ method: method === "get" ? "get" : "post",
17
+ onSubmit: submit
18
+ });
19
+ }
20
+ //#endregion
21
+ export { Form };
@@ -2,19 +2,20 @@
2
2
  * Preact components and typed data hooks. Import from @kasperrt/smolter/preact.
3
3
  * @module
4
4
  */
5
- import type { AnyDataFunction, DataOf, RouteModule, Router, Snapshot } from "../core/index.ts";
5
+ import type { AnyDataFunction, RouteModule, Router, Snapshot } from "../core/index.ts";
6
+ import type { RouteData } from "./route-types.ts";
6
7
  /** A route module or loader function used for data inference. */
7
8
  export type LoaderSource = RouteModule | AnyDataFunction;
8
9
  /** Infer server and client loader data from a route module or function. */
9
- export type LoaderData<T> = T extends AnyDataFunction ? DataOf<T> : T extends RouteModule ? DataOf<Extract<T["loader"] | T["clientLoader"], AnyDataFunction>> : unknown;
10
+ export type LoaderData<T> = T extends AnyDataFunction ? RouteData<T> : T extends RouteModule ? RouteData<Extract<T["loader"] | T["clientLoader"], AnyDataFunction>> : RouteData<T>;
10
11
  /** Infer server and client action data from a route module or function. */
11
- export type ActionData<T> = T extends AnyDataFunction ? DataOf<T> : T extends RouteModule ? DataOf<Extract<T["action"] | T["clientAction"], AnyDataFunction>> : unknown;
12
+ export type ActionData<T> = T extends AnyDataFunction ? RouteData<T> : T extends RouteModule ? RouteData<Extract<T["action"] | T["clientAction"], AnyDataFunction>> : RouteData<T>;
12
13
  /** Pass a route for inference, or use useLoaderData<typeof loader>() in its module. */
13
- export declare function useLoaderData<T extends LoaderSource = RouteModule>(route?: T): LoaderData<T> | undefined;
14
+ export declare function useLoaderData<T = RouteModule>(route?: T & LoaderSource): LoaderData<T>;
14
15
  /** Read the last page action result, optionally inferring its type from a route. */
15
- export declare function useActionData<T extends LoaderSource = RouteModule>(route?: T): ActionData<T> | undefined;
16
+ export declare function useActionData<T = RouteModule>(route?: T & LoaderSource): ActionData<T> | undefined;
16
17
  /** Read path parameters for the active route. */
17
- export declare function useParams(): Snapshot["params"];
18
+ export declare function useParams<T extends Snapshot["params"] = Snapshot["params"]>(): T;
18
19
  /** Read the current path, search, and hash together with the navigate function. */
19
20
  export declare function useLocation(): readonly [string, Router["navigate"]];
20
21
  /** Get the function that revalidates the current loader subject to shouldRevalidate. */
@@ -0,0 +1,25 @@
1
+ import { useRouter, useSnapshot } from "./context.js";
2
+ //#region .generated/preact/hooks.ts
3
+ /** Pass a route for inference, or use useLoaderData<typeof loader>() in its module. */
4
+ function useLoaderData(route) {
5
+ return useSnapshot("data", route?.path);
6
+ }
7
+ /** Read the last page action result, optionally inferring its type from a route. */
8
+ function useActionData(route) {
9
+ return useSnapshot("actionData", route?.path);
10
+ }
11
+ /** Read path parameters for the active route. */
12
+ function useParams() {
13
+ return useSnapshot("params");
14
+ }
15
+ /** Read the current path, search, and hash together with the navigate function. */
16
+ function useLocation() {
17
+ const router = useRouter();
18
+ return [useSnapshot("url").replace(/^\w+:\/\/[^/]+/, ""), router.navigate];
19
+ }
20
+ /** Get the function that revalidates the current loader subject to shouldRevalidate. */
21
+ function useRevalidator() {
22
+ return useRouter().revalidate;
23
+ }
24
+ //#endregion
25
+ export { useActionData, useLoaderData, useLocation, useParams, useRevalidator };
@@ -0,0 +1,18 @@
1
+ /** Framework components and typed data hooks. @module */
2
+ export type * from "../core/index.ts";
3
+ export { createDataRouter, createRouter, defineRoute, } from "../core/index.ts";
4
+ export type { ApplicationEnvironment } from "../core/environment.ts";
5
+ export * from "../core/responses.ts";
6
+ export type { Submission, SubmissionOptions } from "../core/submission.ts";
7
+ export { readSubmission } from "../core/submission.ts";
8
+ export { Await } from "./await.tsx";
9
+ export { RouterProvider, type RouterProviderProps, useRouter, } from "./context.ts";
10
+ export * from "./fetcher.ts";
11
+ export * from "./form.ts";
12
+ export * from "./hooks.ts";
13
+ export type { NavigateOptions } from "./navigation.tsx";
14
+ export * from "./navigation.tsx";
15
+ export { useMatches, useNavigation, useOutletContext, useRootData, useRouteError, useRouteLoaderData, useSubmit, } from "./route-hooks.tsx";
16
+ export * from "./route-types.ts";
17
+ export { Outlet, RouterApplication } from "./router.tsx";
18
+ export * from "./routes.ts";
@@ -0,0 +1,15 @@
1
+ import { defineRoute } from "../core/match.js";
2
+ import { createRouter } from "../core/navigation-router.js";
3
+ import { createDataRouter } from "../core/router.js";
4
+ import { readSubmission } from "../core/submission.js";
5
+ import { data, isRouteErrorResponse, normalizeResult, redirect, resolveData } from "../core/responses.js";
6
+ import { Await } from "./await.js";
7
+ import { RouterProvider, useRouter } from "./context.js";
8
+ import { Form } from "./form.js";
9
+ import { useFetcher } from "./fetcher.js";
10
+ import { useActionData, useLoaderData, useLocation, useParams, useRevalidator } from "./hooks.js";
11
+ import { useMatches, useNavigate, useNavigation, useNavigationType, useOutletContext, useRootData, useRouteError, useRouteLoaderData, useSearchParams, useSubmit, useURLLocation } from "./route-hooks.js";
12
+ import { Link, NavLink } from "./navigation.js";
13
+ import { Routes } from "./routes.js";
14
+ import { Outlet, RouterApplication } from "./router.js";
15
+ export { Await, Form, Link, NavLink, Outlet, RouterApplication, RouterProvider, Routes, createDataRouter, createRouter, data, defineRoute, isRouteErrorResponse, normalizeResult, readSubmission, redirect, resolveData, useActionData, useFetcher, useLoaderData, useLocation, useMatches, useNavigate, useNavigation, useNavigationType, useOutletContext, useParams, useRevalidator, useRootData, useRouteError, useRouteLoaderData, useRouter, useSearchParams, useSubmit, useURLLocation };
@@ -0,0 +1,19 @@
1
+ import type { RouteModule } from "../core/index.ts";
2
+ import { type ComponentType } from "preact";
3
+ import type { AppRoute } from "./route-types.ts";
4
+ export interface LazyRouteOptions {
5
+ path: string;
6
+ id?: string;
7
+ loader?: boolean;
8
+ action?: boolean;
9
+ clientAction?: boolean;
10
+ meta?: boolean;
11
+ shouldRevalidate?: boolean;
12
+ ErrorBoundary?: boolean;
13
+ }
14
+ type LoadedRoute = RouteModule & Pick<AppRoute, "meta">;
15
+ interface LazyAppRoute extends AppRoute {
16
+ resolveComponent: () => ComponentType | undefined;
17
+ }
18
+ export declare function lazyRoute(options: LazyRouteOptions, importer: () => Promise<LoadedRoute>): LazyAppRoute;
19
+ export {};
@@ -0,0 +1,51 @@
1
+ import { useSyncExternalStore } from "./runtime.js";
2
+ import { useRouter } from "./context.js";
3
+ import { serverRoute } from "../client/index.js";
4
+ import { createElement } from "preact";
5
+ //#region .generated/preact/lazy-route.ts
6
+ function LazyView() {
7
+ const router = useRouter();
8
+ const { path } = useSyncExternalStore(router.subscribe, router.getSnapshot);
9
+ const Component = router.routes.find((item) => item.path === path)?.resolveComponent();
10
+ return Component && createElement(Component, {});
11
+ }
12
+ function lazyRoute(options, importer) {
13
+ let module;
14
+ let pending;
15
+ function preload() {
16
+ pending ??= importer().then((loaded) => {
17
+ module = serverRoute(options.path, loaded, {
18
+ loader: options.loader,
19
+ action: options.action
20
+ });
21
+ }).catch((error) => {
22
+ pending = void 0;
23
+ throw error;
24
+ });
25
+ return pending;
26
+ }
27
+ async function run(args, action = false) {
28
+ await preload();
29
+ if (args.signal.aborted) return /* @__PURE__ */ new Error("Navigation cancelled");
30
+ return (action ? module.clientAction ?? module.action : module.clientLoader ?? module.loader)?.(args);
31
+ }
32
+ function ErrorBoundary(props) {
33
+ const Component = module?.ErrorBoundary;
34
+ if (!Component) return props.error.message;
35
+ return createElement(Component, props);
36
+ }
37
+ return {
38
+ path: options.path,
39
+ id: options.id,
40
+ preload,
41
+ default: LazyView,
42
+ resolveComponent: () => module?.default,
43
+ clientLoader: (args) => run(args),
44
+ ...(options.action || options.clientAction) && { clientAction: (args) => run(args, true) },
45
+ ...options.meta && { meta: (args) => module?.meta?.(args) ?? [] },
46
+ ...options.shouldRevalidate && { shouldRevalidate: (args) => module?.shouldRevalidate?.(args) ?? true },
47
+ ...options.ErrorBoundary && { ErrorBoundary }
48
+ };
49
+ }
50
+ //#endregion
51
+ export { lazyRoute };
@@ -0,0 +1,2 @@
1
+ import type { Router } from "../core/index.ts";
2
+ export declare function useNavigationSnapshot(router: Router): import("../core/navigation.ts").NavigationSnapshot;
@@ -0,0 +1,9 @@
1
+ import { useSyncExternalStore } from "./runtime.js";
2
+ import { navigationStore } from "../core/navigation.js";
3
+ //#region .generated/preact/navigation-state.ts
4
+ function useNavigationSnapshot(router) {
5
+ const store = navigationStore(router);
6
+ return useSyncExternalStore(store.subscribe, store.getSnapshot);
7
+ }
8
+ //#endregion
9
+ export { useNavigationSnapshot };
@@ -0,0 +1,28 @@
1
+ import { type NavigateOptions } from "../core/index.ts";
2
+ import type { ComponentProps, ReactNode } from "preact/compat";
3
+ export type { NavigateOptions } from "../core/index.ts";
4
+ export type { NavigationType } from "../core/navigation.ts";
5
+ export type NavigateFunction = (to: string | number | Partial<Pick<URL, "pathname" | "search" | "hash">>, options?: NavigateOptions) => void;
6
+ export interface LinkProps extends Omit<ComponentProps<"a">, "href" | "role"> {
7
+ to?: string;
8
+ href?: string;
9
+ replace?: boolean;
10
+ role?: "link" | "button" | "checkbox" | "menuitem" | "menuitemcheckbox" | "menuitemradio" | "option" | "radio" | "switch" | "tab" | "treeitem" | "doc-backlink" | "doc-biblioref" | "doc-glossref" | "doc-noteref";
11
+ prefetch?: string;
12
+ state?: unknown;
13
+ preventScrollReset?: boolean;
14
+ viewTransition?: boolean;
15
+ reloadDocument?: boolean;
16
+ }
17
+ export interface NavLinkRenderProps {
18
+ isActive: boolean;
19
+ isPending: boolean;
20
+ }
21
+ export interface NavLinkProps extends Omit<LinkProps, "className" | "children"> {
22
+ end?: boolean;
23
+ className?: string | ((state: NavLinkRenderProps) => string);
24
+ children?: ReactNode | ((state: NavLinkRenderProps) => ReactNode);
25
+ }
26
+ export declare function Link(options: LinkProps): ReactNode;
27
+ export declare function NavLink({ href, to, end, className, children, ...props }: NavLinkProps): ReactNode;
28
+ export { useNavigate, useNavigationType, useSearchParams, useURLLocation, } from "./route-hooks.tsx";
@@ -0,0 +1,60 @@
1
+ import { matchRoutes } from "../core/match.js";
2
+ import { navigationClick } from "../core/url.js";
3
+ import { createElement } from "./runtime.js";
4
+ import { useRouter, useSnapshot } from "./context.js";
5
+ import { handoffDocument, resolveApplicationURL } from "../core/environment.js";
6
+ import { useNavigate, useNavigationType, useSearchParams, useURLLocation } from "./route-hooks.js";
7
+ //#region .generated/preact/navigation.tsx
8
+ function Link(options) {
9
+ const { href: hrefProp, to = hrefProp ?? "/", replace, preventScrollReset, viewTransition, prefetch, state, reloadDocument, ...props } = options;
10
+ const router = useRouter();
11
+ const current = new URL(useSnapshot("url"));
12
+ const destination = resolveApplicationURL(new URL(to, current));
13
+ const internal = destination.origin === current.origin;
14
+ const preload = () => {
15
+ (matchRoutes(router.routes, destination.pathname)?.route)?.preload?.().catch(() => {});
16
+ };
17
+ if (!reloadDocument && prefetch !== "none" && internal) for (const name of [
18
+ "onPointerEnter",
19
+ "onFocus",
20
+ "onTouchStart"
21
+ ]) {
22
+ const handler = props[name];
23
+ props[name] = (event) => {
24
+ handler?.(event);
25
+ preload();
26
+ };
27
+ }
28
+ return createElement("a", {
29
+ ...props,
30
+ href: destination.href,
31
+ onClick(event) {
32
+ props.onClick?.(event);
33
+ if (reloadDocument || !navigationClick(event, event.currentTarget)) return;
34
+ if (!internal) {
35
+ handoffDocument(event, destination);
36
+ return;
37
+ }
38
+ event.preventDefault();
39
+ router.navigate(destination.href, options);
40
+ }
41
+ });
42
+ }
43
+ function NavLink({ href, to = href ?? "/", end, className, children, ...props }) {
44
+ const location = useURLLocation();
45
+ const target = new URL(to, location.href).pathname;
46
+ const isActive = location.pathname === target || !end && target !== "/" && location.pathname.startsWith(`${target}/`);
47
+ const state = {
48
+ isActive,
49
+ isPending: false
50
+ };
51
+ return createElement(Link, {
52
+ ...props,
53
+ to,
54
+ className: typeof className === "function" ? className(state) : className,
55
+ ...isActive && { "aria-current": "page" },
56
+ children: typeof children === "function" ? children(state) : children
57
+ });
58
+ }
59
+ //#endregion
60
+ export { Link, NavLink, useNavigate, useNavigationType, useSearchParams, useURLLocation };
@@ -0,0 +1,13 @@
1
+ import type { Router } from "../core/index.ts";
2
+ import type { AppRoute, Hydration, RootModule } from "./route-types.ts";
3
+ export interface ApplicationContext {
4
+ router: Router;
5
+ routes: AppRoute[];
6
+ root: unknown;
7
+ rootModule?: RootModule;
8
+ hydration?: Hydration;
9
+ entry?: string;
10
+ }
11
+ export declare const Application: import("preact").Context<ApplicationContext | null>;
12
+ export declare const OutletContext: import("preact").Context<unknown>;
13
+ export declare function useApplication(): ApplicationContext;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "./runtime.js";
2
+ //#region .generated/preact/root-context.ts
3
+ var Application = createContext(null);
4
+ var OutletContext = createContext(void 0);
5
+ function useApplication() {
6
+ return useContext(Application);
7
+ }
8
+ //#endregion
9
+ export { Application, OutletContext, useApplication };
@@ -0,0 +1,8 @@
1
+ import type { Router } from "../core/index.ts";
2
+ import type { RootModule } from "./route-types.ts";
3
+ export { createStore as createRootStore } from "../core/store.ts";
4
+ export declare function attachRootData(router: Router, initial: unknown, rootModule?: RootModule): {
5
+ getSnapshot: () => unknown;
6
+ subscribe(listener: () => void): () => void;
7
+ update(next: unknown): void;
8
+ };
@@ -0,0 +1,28 @@
1
+ import { createStore } from "../core/store.js";
2
+ //#region .generated/preact/root-data.ts
3
+ function attachRootData(router, initial, rootModule) {
4
+ const store = createStore(initial);
5
+ const loaderPath = rootModule?.loaderPath;
6
+ if (!rootModule || !loaderPath) return store;
7
+ let pending;
8
+ const unregister = router.registerRevalidator(rootModule.id ?? "root", async (args) => {
9
+ if (rootModule.shouldRevalidate?.(args) === false) return;
10
+ pending?.dispose();
11
+ const fetcher = router.createFetcher();
12
+ pending = fetcher;
13
+ const result = await fetcher.load(loaderPath);
14
+ if (pending === fetcher && !result.error) store.update(result.data);
15
+ fetcher.dispose();
16
+ if (pending === fetcher) pending = void 0;
17
+ });
18
+ const dispose = router.dispose;
19
+ router.dispose = () => {
20
+ pending?.dispose();
21
+ pending = void 0;
22
+ unregister();
23
+ dispose();
24
+ };
25
+ return store;
26
+ }
27
+ //#endregion
28
+ export { attachRootData, createStore as createRootStore };
@@ -0,0 +1,32 @@
1
+ import type { NavigateOptions } from "../core/index.ts";
2
+ import type { NavigationType } from "../core/navigation.ts";
3
+ import { type Submission, type SubmissionOptions } from "../core/submission.ts";
4
+ import { useSnapshot } from "./context.ts";
5
+ import type { NavigateFunction } from "./navigation.tsx";
6
+ import type { RouteData } from "./route-types.ts";
7
+ export declare function useRootData<T = unknown>(): RouteData<T>;
8
+ /** Read root data or the active route's data by its manifest identifier. */
9
+ export declare function useRouteLoaderData<T = unknown>(id: string): RouteData<T> | undefined;
10
+ export { useSnapshot };
11
+ export declare function useRouteError(): Error | undefined;
12
+ export declare function useNavigation(): {
13
+ state: "idle" | "loading" | "submitting";
14
+ location: URL | undefined;
15
+ };
16
+ export declare function useMatches(): {
17
+ id: string;
18
+ data: unknown;
19
+ params: import("../core/index.ts").Params;
20
+ }[];
21
+ export declare function useOutletContext<T>(): T;
22
+ export declare function useSubmit(): (value: Submission, options?: SubmissionOptions) => Promise<void> | Promise<import("../core/index.ts").Result<unknown>>;
23
+ export declare function useURLLocation(): URL & {
24
+ state: unknown;
25
+ key: string;
26
+ };
27
+ export declare function useNavigate(): NavigateFunction;
28
+ export declare function useSearchParams(): [
29
+ URLSearchParams,
30
+ (value: URLSearchParams | Record<string, string>, options?: NavigateOptions) => void
31
+ ];
32
+ export declare function useNavigationType(): NavigationType;