@kasperrt/smolter 0.4.2 → 0.4.4

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 (151) hide show
  1. package/.generated/client/hash-history.ts +27 -0
  2. package/.generated/client/hydration.ts +98 -0
  3. package/.generated/client/root-data.ts +52 -0
  4. package/.generated/core/environment.ts +1 -0
  5. package/.generated/core/navigation.ts +1 -0
  6. package/.generated/core/prepare-routes.ts +50 -0
  7. package/.generated/core/presentation.ts +1 -1
  8. package/.generated/core/responses.ts +12 -0
  9. package/.generated/core/utils.ts +5 -2
  10. package/.generated/preact/await.tsx +1 -0
  11. package/.generated/preact/client.tsx +8 -27
  12. package/.generated/preact/deferred-hydration.ts +1 -98
  13. package/.generated/preact/document.tsx +10 -0
  14. package/.generated/preact/index.ts +11 -2
  15. package/.generated/preact/lazy-route.ts +7 -0
  16. package/.generated/preact/navigation.tsx +19 -5
  17. package/.generated/preact/root-data.ts +1 -45
  18. package/.generated/preact/route-hooks.tsx +12 -2
  19. package/.generated/preact/route-types.ts +14 -0
  20. package/.generated/preact/router.tsx +5 -48
  21. package/.generated/react/client.tsx +130 -0
  22. package/.generated/react/document.tsx +235 -0
  23. package/.generated/react/fetcher.ts +29 -7
  24. package/.generated/react/hooks.ts +20 -18
  25. package/.generated/react/index.ts +28 -2
  26. package/.generated/react/lazy-route.ts +116 -0
  27. package/.generated/react/link.ts +1 -38
  28. package/.generated/react/navigation-state.ts +13 -0
  29. package/.generated/react/navigation.ts +145 -0
  30. package/.generated/react/outlet.ts +48 -0
  31. package/.generated/react/root-context.ts +22 -0
  32. package/.generated/react/route-hooks.ts +159 -0
  33. package/.generated/react/route-types.ts +91 -0
  34. package/.generated/react/router.ts +50 -0
  35. package/.generated/react/runtime.ts +1 -0
  36. package/.generated/server/application.tsx +18 -250
  37. package/.generated/server/assets.ts +7 -0
  38. package/.generated/server/dispatch.ts +87 -0
  39. package/.generated/server/handler.ts +3 -0
  40. package/.generated/server/http.ts +2 -0
  41. package/.generated/server/index.ts +2 -0
  42. package/.generated/server/node.ts +5 -2
  43. package/.generated/server/preact.ts +5 -2
  44. package/.generated/server/react.ts +34 -0
  45. package/.generated/server/render-application.ts +203 -0
  46. package/.generated/vite/application/lazy-files.ts +2 -1
  47. package/.generated/vite/application/vite.ts +12 -4
  48. package/.generated/vite/react.ts +11 -0
  49. package/CHANGELOG.md +20 -0
  50. package/README.md +48 -48
  51. package/dist/client/hash-history.d.ts +2 -0
  52. package/dist/client/hash-history.js +26 -0
  53. package/dist/client/hydration.d.ts +10 -0
  54. package/dist/{preact/deferred-hydration.js → client/hydration.js} +1 -1
  55. package/dist/client/root-data.d.ts +14 -0
  56. package/dist/{preact → client}/root-data.js +1 -1
  57. package/dist/core/environment.d.ts +1 -0
  58. package/dist/core/navigation.d.ts +1 -0
  59. package/dist/core/prepare-routes.d.ts +7 -0
  60. package/dist/core/prepare-routes.js +28 -0
  61. package/dist/core/presentation.js +1 -1
  62. package/dist/core/responses.d.ts +12 -0
  63. package/dist/core/responses.js +5 -0
  64. package/dist/core/utils.d.ts +5 -2
  65. package/dist/core/utils.js +1 -2
  66. package/dist/preact/await.d.ts +1 -0
  67. package/dist/preact/await.js +1 -0
  68. package/dist/preact/client.d.ts +7 -1
  69. package/dist/preact/client.js +11 -27
  70. package/dist/preact/deferred-hydration.d.ts +1 -10
  71. package/dist/preact/document.d.ts +10 -0
  72. package/dist/preact/document.js +11 -2
  73. package/dist/preact/index.d.ts +6 -2
  74. package/dist/preact/index.js +1 -1
  75. package/dist/preact/lazy-route.d.ts +2 -0
  76. package/dist/preact/lazy-route.js +6 -0
  77. package/dist/preact/navigation-state.js +1 -1
  78. package/dist/preact/navigation.d.ts +6 -0
  79. package/dist/preact/navigation.js +9 -5
  80. package/dist/preact/root-data.d.ts +1 -8
  81. package/dist/preact/route-hooks.d.ts +10 -0
  82. package/dist/preact/route-hooks.js +13 -3
  83. package/dist/preact/route-types.d.ts +14 -0
  84. package/dist/preact/router.d.ts +5 -2
  85. package/dist/preact/router.js +4 -26
  86. package/dist/react/client.d.ts +20 -0
  87. package/dist/react/client.js +86 -0
  88. package/dist/react/document.d.ts +25 -0
  89. package/dist/react/document.js +169 -0
  90. package/dist/react/fetcher.d.ts +8 -4
  91. package/dist/react/fetcher.js +12 -1
  92. package/dist/react/form.js +1 -1
  93. package/dist/react/hooks.d.ts +7 -6
  94. package/dist/react/index.d.ts +13 -2
  95. package/dist/react/index.js +7 -2
  96. package/dist/react/lazy-route.d.ts +22 -0
  97. package/dist/react/lazy-route.js +57 -0
  98. package/dist/react/link.d.ts +1 -12
  99. package/dist/react/navigation-state.d.ts +2 -0
  100. package/dist/react/navigation-state.js +9 -0
  101. package/dist/react/navigation.d.ts +33 -0
  102. package/dist/react/navigation.js +64 -0
  103. package/dist/react/outlet.d.ts +16 -0
  104. package/dist/react/outlet.js +26 -0
  105. package/dist/react/root-context.d.ts +14 -0
  106. package/dist/react/root-context.js +10 -0
  107. package/dist/react/route-hooks.d.ts +34 -0
  108. package/dist/react/route-hooks.js +89 -0
  109. package/dist/react/route-types.d.ts +69 -0
  110. package/dist/react/router.d.ts +10 -0
  111. package/dist/react/router.js +22 -0
  112. package/dist/react/runtime.d.ts +1 -1
  113. package/dist/react/runtime.js +1 -1
  114. package/dist/server/application.d.ts +4 -5
  115. package/dist/server/application.js +9 -154
  116. package/dist/server/assets.d.ts +2 -0
  117. package/dist/server/assets.js +6 -0
  118. package/dist/server/dispatch.d.ts +5 -0
  119. package/dist/server/dispatch.js +50 -0
  120. package/dist/server/handler.d.ts +3 -0
  121. package/dist/server/http.d.ts +2 -0
  122. package/dist/server/http.js +2 -0
  123. package/dist/server/index.d.ts +1 -0
  124. package/dist/server/index.js +2 -1
  125. package/dist/server/node.d.ts +5 -1
  126. package/dist/server/preact.d.ts +5 -1
  127. package/dist/server/preact.js +2 -1
  128. package/dist/server/react.d.ts +10 -0
  129. package/dist/server/react.js +22 -0
  130. package/dist/server/render-application.d.ts +31 -0
  131. package/dist/server/render-application.js +118 -0
  132. package/dist/vite/application/lazy-files.d.ts +1 -1
  133. package/dist/vite/application/lazy-files.js +2 -2
  134. package/dist/vite/application/vite.d.ts +2 -1
  135. package/dist/vite/application/vite.js +12 -5
  136. package/dist/vite/react.d.ts +4 -0
  137. package/dist/vite/react.js +9 -1
  138. package/jsr.json +10 -6
  139. package/package.json +21 -12
  140. package/.generated/core/resource-types.ts +0 -44
  141. package/.generated/core/resources.ts +0 -444
  142. package/.generated/react/data.ts +0 -25
  143. package/.generated/react/resources/components.tsx +0 -404
  144. package/dist/core/resource-types.d.ts +0 -37
  145. package/dist/core/resources.d.ts +0 -19
  146. package/dist/core/resources.js +0 -345
  147. package/dist/react/data.d.ts +0 -5
  148. package/dist/react/data.js +0 -3
  149. package/dist/react/link.js +0 -27
  150. package/dist/react/resources/components.d.ts +0 -64
  151. package/dist/react/resources/components.js +0 -239
@@ -0,0 +1,50 @@
1
+ import { matchRoutes } from "../core/match.js";
2
+ import { normalizeResult, resolveData } from "../core/responses.js";
3
+ import { privateResponse } from "./private-response.js";
4
+ //#region .generated/server/dispatch.ts
5
+ async function dispatch(request, routes, document = false) {
6
+ const match = matchRoutes(routes.map((route) => ({
7
+ ...route,
8
+ path: route.routePath ?? route.path
9
+ })), new URL(request.url).pathname);
10
+ if (!match) return Response.json({ error: "No route" }, { status: 404 });
11
+ const handler = request.method === "GET" || request.method === "HEAD" ? match.route.loader : match.route.action;
12
+ if (!handler) return Response.json({ error: "No handler" }, { status: 405 });
13
+ const value = await Promise.resolve().then(() => handler({
14
+ request,
15
+ params: match.params,
16
+ signal: request.signal
17
+ })).then((result) => result, (error) => error);
18
+ if (value instanceof Response) {
19
+ const location = value.headers.get("Location");
20
+ if (document) return value;
21
+ if (!location) {
22
+ if (value.ok) return value;
23
+ return Response.json({ __routeError: {
24
+ message: await value.text(),
25
+ status: value.status,
26
+ statusText: value.statusText
27
+ } });
28
+ }
29
+ const headers = new Headers(value.headers);
30
+ headers.delete("Location");
31
+ headers.set("X-Smolter-Redirect", location);
32
+ return new Response(null, { headers });
33
+ }
34
+ if (value instanceof Error) return Response.json({ __routeError: {
35
+ ...value,
36
+ message: value.message
37
+ } });
38
+ if (value && typeof value === "object" && "__routeData" in value) {
39
+ const result = value;
40
+ return Response.json(await resolveData(result.value), document ? result.init : { headers: result.init.headers });
41
+ }
42
+ return Response.json(await normalizeResult(value) ?? null);
43
+ }
44
+ /** Dispatch a normalized application URL to its route loader or action and return an HTTP response. */
45
+ async function dispatchRoute(request, routes, document = false) {
46
+ const response = await dispatch(request, routes, document);
47
+ return document ? response : privateResponse(response);
48
+ }
49
+ //#endregion
50
+ export { dispatchRoute };
@@ -4,6 +4,7 @@ export interface ServerBuild {
4
4
  default: (request: Request, entry: string) => Response | Promise<Response>;
5
5
  handleDataRequest: (request: Request) => Response | Promise<Response>;
6
6
  }
7
+ /** SSR build, browser entry URL and application-scoped data endpoint for Node or Express. */
7
8
  export interface RequestHandlerOptions {
8
9
  build: ServerBuild;
9
10
  /** Browser entry URL resolved from Vite's production manifest. */
@@ -11,11 +12,13 @@ export interface RequestHandlerOptions {
11
12
  /** Must match the browser and Vite transport endpoint. */
12
13
  dataEndpoint?: string;
13
14
  }
15
+ /** Node request with optional Express protocol and original URL information. */
14
16
  export interface ServerIncomingMessage extends IncomingMessage {
15
17
  /** Express supplies this using the application's trust proxy policy. */
16
18
  protocol?: string;
17
19
  originalUrl?: string;
18
20
  }
21
+ /** Express-compatible callback for forwarding middleware errors. */
19
22
  export type NextHandler = (error?: unknown) => void;
20
23
  /** Node/Express middleware; mount after metrics, tracing and static assets. */
21
24
  export declare function createRequestHandler({ build, entry, dataEndpoint, }: RequestHandlerOptions): (incoming: ServerIncomingMessage, outgoing: ServerResponse, next: NextHandler) => Promise<void>;
@@ -1,8 +1,10 @@
1
1
  import type { ServerResponse } from "node:http";
2
2
  import type { ServerIncomingMessage } from "./handler.ts";
3
+ /** Convert a Node request to a Web Request with cancellation and an optional normalized route target. */
3
4
  export declare function webRequest(incoming: ServerIncomingMessage, outgoing: ServerResponse, target?: string): Promise<Request>;
4
5
  interface StreamingResponse extends ServerResponse {
5
6
  flush?: () => void;
6
7
  }
8
+ /** Write a Web Response to Node with streaming backpressure and disconnect cancellation. */
7
9
  export declare function send(response: Response, outgoing: StreamingResponse): Promise<void>;
8
10
  export {};
@@ -1,6 +1,7 @@
1
1
  import { Readable, Transform } from "node:stream";
2
2
  import { pipeline } from "node:stream/promises";
3
3
  //#region .generated/server/http.ts
4
+ /** Convert a Node request to a Web Request with cancellation and an optional normalized route target. */
4
5
  async function webRequest(incoming, outgoing, target) {
5
6
  const origin = `${incoming.protocol ?? "http"}://${incoming.headers.host}`;
6
7
  const headers = new Headers();
@@ -16,6 +17,7 @@ async function webRequest(incoming, outgoing, target) {
16
17
  ...incoming.method !== "GET" && incoming.method !== "HEAD" && { body: new Uint8Array(Buffer.concat(chunks)) }
17
18
  });
18
19
  }
20
+ /** Write a Web Response to Node with streaming backpressure and disconnect cancellation. */
19
21
  async function send(response, outgoing) {
20
22
  outgoing.statusCode = response.status;
21
23
  response.headers.forEach((value, name) => {
@@ -8,3 +8,4 @@ export declare function loadRouter(routes: readonly Route[], input: string | Req
8
8
  export { serialize } from "../core/index.ts";
9
9
  /** Dispatch a server data request with an explicit private cache policy. */
10
10
  export declare function handleRequest(routes: readonly Route[], request: Request): Promise<Response>;
11
+ export { dispatchRoute } from "./dispatch.ts";
@@ -3,6 +3,7 @@ import { matchRoutes } from "../core/match.js";
3
3
  import { createDataRouter } from "../core/router.js";
4
4
  import { serialize } from "../core/serialize.js";
5
5
  import { privateResponse } from "./private-response.js";
6
+ import { dispatchRoute } from "./dispatch.js";
6
7
  //#region .generated/server/index.ts
7
8
  /**
8
9
  * Request-scoped server rendering and HTTP data dispatch.
@@ -83,4 +84,4 @@ async function handleRequest(routes, request) {
83
84
  return privateResponse(await dispatchRequest(routes, request));
84
85
  }
85
86
  //#endregion
86
- export { handleRequest, loadRouter, serialize };
87
+ export { dispatchRoute, handleRequest, loadRouter, serialize };
@@ -1,3 +1,7 @@
1
- /** Optional application integration. @module */
1
+ /**
2
+ * Streaming Node and Express adapters for Web requests and responses.
3
+ *
4
+ * @module
5
+ */
2
6
  export * from "./handler.ts";
3
7
  export * from "./http.ts";
@@ -1,3 +1,7 @@
1
- /** Preact server rendering and HTTP data dispatch. @module */
1
+ /**
2
+ * Preact SSR rendering and route data dispatch.
3
+ *
4
+ * @module
5
+ */
2
6
  export { dispatchRoute, type RenderApplicationOptions, renderApplication, } from "./application.tsx";
3
7
  export * from "./index.ts";
@@ -1,4 +1,5 @@
1
1
  import { serialize } from "../core/serialize.js";
2
+ import { dispatchRoute } from "./dispatch.js";
2
3
  import { handleRequest, loadRouter } from "./index.js";
3
- import { dispatchRoute, renderApplication } from "./application.js";
4
+ import { renderApplication } from "./application.js";
4
5
  export { dispatchRoute, handleRequest, loadRouter, renderApplication, serialize };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * React SSR rendering and route data dispatch.
3
+ * @module
4
+ */
5
+ import type { AppRoute, RootModule } from "../react/route-types.ts";
6
+ import { type RenderApplicationOptions } from "./render-application.ts";
7
+ export * from "./index.ts";
8
+ export type { RenderApplicationOptions } from "./render-application.ts";
9
+ /** Render a React document with root and route data using React's streaming renderer. */
10
+ export declare function renderApplication(request: Request, routes: AppRoute[], rootModule: RootModule, entry: string, options?: RenderApplicationOptions): Promise<Response>;
@@ -0,0 +1,22 @@
1
+ import { serialize } from "../core/serialize.js";
2
+ import { RouterApplication } from "../react/router.js";
3
+ import { dispatchRoute } from "./dispatch.js";
4
+ import { handleRequest, loadRouter } from "./index.js";
5
+ import { renderDocument } from "./render-application.js";
6
+ import { createElement } from "react";
7
+ import * as ReactDOMServer from "react-dom/server";
8
+ //#region .generated/server/react.ts
9
+ /** Render a React document with root and route data using React's streaming renderer. */
10
+ async function renderApplication(request, routes, rootModule, entry, options = {}) {
11
+ const { renderToReadableStream } = ReactDOMServer;
12
+ if (!renderToReadableStream) throw new Error("renderApplication requires React DOM 19 or newer.");
13
+ return renderDocument(request, routes, rootModule, entry, options, (props) => renderToReadableStream(createElement(RouterApplication, {
14
+ ...props,
15
+ rootModule
16
+ }), {
17
+ nonce: options.nonce,
18
+ signal: request.signal
19
+ }));
20
+ }
21
+ //#endregion
22
+ export { dispatchRoute, handleRequest, loadRouter, renderApplication, serialize };
@@ -0,0 +1,31 @@
1
+ import type { DataArgs, Route, Router } from "../core/index.ts";
2
+ interface ServerRoot {
3
+ loader?: (args: DataArgs) => unknown;
4
+ ErrorBoundary?: unknown;
5
+ }
6
+ export interface RenderProps {
7
+ router: Router;
8
+ routes: (Route & {
9
+ routePath?: string;
10
+ })[];
11
+ root: unknown;
12
+ entry: string;
13
+ rootError: boolean;
14
+ streaming: boolean;
15
+ initialData?: string;
16
+ document: boolean;
17
+ }
18
+ /** SSR settings for CSP nonce and early deferred-data hydration. */
19
+ export interface RenderApplicationOptions {
20
+ nonce?: string;
21
+ earlyHydration?: boolean;
22
+ }
23
+ /** Render a Preact application response with root and route data, streaming deferred content when enabled. */
24
+ export declare function renderDocument(request: Request, routes: (Route & {
25
+ routePath?: string;
26
+ })[], rootModule: ServerRoot, entry: string, options: RenderApplicationOptions, render: (props: RenderProps) => (ReadableStream<Uint8Array> & {
27
+ allReady: Promise<unknown>;
28
+ }) | Promise<ReadableStream<Uint8Array> & {
29
+ allReady: Promise<unknown>;
30
+ }>): Promise<Response>;
31
+ export {};
@@ -0,0 +1,118 @@
1
+ import { safeWrapAsync } from "../core/errors.js";
2
+ import { matchRoutes } from "../core/match.js";
3
+ import { createDataRouter } from "../core/router.js";
4
+ import { normalizeResult } from "../core/responses.js";
5
+ import { prepareRoutes } from "../core/prepare-routes.js";
6
+ import { dispatchRoute } from "./dispatch.js";
7
+ import { loadRouter } from "./index.js";
8
+ import { deferredHydration } from "./deferred-hydration.js";
9
+ import { progressiveDocument } from "./progressive-document.js";
10
+ import { resolveHydration, streamDocument } from "./stream-document.js";
11
+ //#region .generated/server/render-application.ts
12
+ /** Render a Preact application response with root and route data, streaming deferred content when enabled. */
13
+ async function renderDocument(request, routes, rootModule, entry, options, render) {
14
+ routes = routes.map((route) => ({
15
+ ...route,
16
+ path: route.routePath ?? route.path
17
+ }));
18
+ const match = matchRoutes(routes, new URL(request.url).pathname);
19
+ if (match && !match.route.default) return dispatchRoute(request, routes, true);
20
+ let actionResponse;
21
+ let actionData;
22
+ if (request.method !== "GET" && request.method !== "HEAD") {
23
+ actionResponse = await dispatchRoute(request, routes, true);
24
+ if (actionResponse.headers.has("Location") || !actionResponse.headers.get("Content-Type")?.includes("application/json")) return actionResponse;
25
+ actionData = await normalizeResult(await actionResponse.json());
26
+ }
27
+ const rootResult = safeWrapAsync(() => Promise.resolve().then(() => rootModule.loader?.({
28
+ request,
29
+ params: {},
30
+ signal: request.signal
31
+ })).then((value) => normalizeResult(value, false), (error) => error instanceof Response ? normalizeResult(error, false) : error instanceof Error ? error : new Error(String(error), { cause: error })));
32
+ let redirectResponse;
33
+ const prepared = prepareRoutes(routes.map((route) => ({
34
+ ...route,
35
+ ErrorBoundary: route.ErrorBoundary ?? rootModule.ErrorBoundary
36
+ })), true).map((route) => {
37
+ if (!route.loader) return route;
38
+ const loader = route.loader;
39
+ return {
40
+ ...route,
41
+ loader: async (args) => {
42
+ const value = await loader(args);
43
+ if (value instanceof Response && value.headers.has("Location")) redirectResponse = value;
44
+ return value;
45
+ }
46
+ };
47
+ });
48
+ const [rootState, loadedRouter] = await Promise.all([rootResult, loadRouter(prepared, request)]);
49
+ let router = loadedRouter;
50
+ const rootError = rootState.error ?? (rootState.data instanceof Error ? rootState.data : void 0);
51
+ const root = rootError ? void 0 : rootState.data;
52
+ if (root instanceof Response && root.headers.has("Location")) {
53
+ router.dispose();
54
+ return root;
55
+ }
56
+ if (redirectResponse) {
57
+ router.dispose();
58
+ return redirectResponse;
59
+ }
60
+ if (actionResponse || rootError) {
61
+ const hydrationData = {
62
+ ...router.getSnapshot(),
63
+ actionData: actionData instanceof Error ? void 0 : actionData,
64
+ ...actionData instanceof Error && { error: actionData },
65
+ ...rootError && { error: rootError }
66
+ };
67
+ router.dispose();
68
+ router = createDataRouter(router.routes, { hydrationData });
69
+ }
70
+ const snapshot = router.getSnapshot();
71
+ const hydrationData = {
72
+ root,
73
+ snapshot: {
74
+ ...snapshot,
75
+ error: snapshot.error && {
76
+ ...snapshot.error,
77
+ name: snapshot.error.name,
78
+ message: snapshot.error.message
79
+ }
80
+ },
81
+ ...rootError && { rootError: true }
82
+ };
83
+ const deferred = options.earlyHydration ? deferredHydration(hydrationData, options.nonce) : void 0;
84
+ const stream = await Promise.resolve().then(() => render({
85
+ router,
86
+ routes,
87
+ root,
88
+ entry,
89
+ rootError: Boolean(rootError),
90
+ streaming: true,
91
+ initialData: deferred?.initial,
92
+ document: true
93
+ })).catch(async (error) => {
94
+ router.dispose();
95
+ await deferred?.stream.cancel();
96
+ throw error;
97
+ });
98
+ stream.allReady.catch(() => {});
99
+ const headers = new Headers(actionResponse?.headers);
100
+ headers.delete("Content-Length");
101
+ headers.delete("Content-Encoding");
102
+ headers.set("Content-Type", "text/html; charset=utf-8");
103
+ headers.set("Cache-Control", "no-store");
104
+ return new Response(deferred ? progressiveDocument(stream, deferred.stream, {
105
+ signal: request.signal,
106
+ dispose: () => router.dispose()
107
+ }) : streamDocument(stream, {
108
+ hydration: resolveHydration(hydrationData),
109
+ signal: request.signal,
110
+ nonce: options.nonce,
111
+ dispose: () => router.dispose()
112
+ }), {
113
+ status: snapshot.error ? Number("status" in snapshot.error ? snapshot.error.status : 500) : actionResponse?.status ?? 200,
114
+ headers
115
+ });
116
+ }
117
+ //#endregion
118
+ export { renderDocument };
@@ -1,2 +1,2 @@
1
1
  import { type Plugin } from "vite";
2
- export declare function lazyFiles(directory: string, dataEndpoint?: string): Plugin;
2
+ export declare function lazyFiles(directory: string, dataEndpoint?: string, framework?: "react" | "preact"): Plugin;
@@ -3,7 +3,7 @@ import { readFile } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
4
  import { transformWithOxc } from "vite";
5
5
  //#region .generated/vite/application/lazy-files.ts
6
- function lazyFiles(directory, dataEndpoint = "/_smolter") {
6
+ function lazyFiles(directory, dataEndpoint = "/_smolter", framework = "preact") {
7
7
  let root;
8
8
  const modules = /* @__PURE__ */ new Map();
9
9
  return {
@@ -81,7 +81,7 @@ function lazyFiles(directory, dataEndpoint = "/_smolter") {
81
81
  if (fields.default) modules.set(file, String(fields.path));
82
82
  definitions.push(`lazyRoute(${JSON.stringify(fields)}, () => import(${JSON.stringify(file)}))`);
83
83
  }
84
- return ["import { lazyRoute } from \"@kasperrt/smolter/preact/lazy-route\";", `export default [${definitions.join(",\n")}];`].join("\n");
84
+ return [`import { lazyRoute } from "@kasperrt/smolter/${framework}/lazy-route";`, `export default [${definitions.join(",\n")}];`].join("\n");
85
85
  }
86
86
  };
87
87
  }
@@ -1,10 +1,11 @@
1
1
  import type { Plugin, UserConfig } from "vite";
2
2
  export interface ApplicationOptions {
3
3
  directory?: string;
4
+ framework?: "react" | "preact";
4
5
  dataEndpoint?: string;
5
6
  ssr?: boolean;
6
7
  splitRoutes?: boolean;
7
8
  resourceRequest?: (url: string) => boolean;
8
9
  }
9
10
  export declare const preactConfig: UserConfig;
10
- export declare function applicationPlugins({ directory, dataEndpoint, ssr, splitRoutes, resourceRequest, }?: ApplicationOptions): Plugin[];
11
+ export declare function applicationPlugins({ directory, framework, dataEndpoint, ssr, splitRoutes, resourceRequest, }?: ApplicationOptions): Plugin[];
@@ -43,12 +43,19 @@ var preactConfig = {
43
43
  "@kasperrt/smolter/preact/lazy-route"
44
44
  ] }
45
45
  };
46
- function applicationPlugins({ directory = "src/routes", dataEndpoint = "/_smolter", ssr = true, splitRoutes = true, resourceRequest } = {}) {
46
+ function applicationPlugins({ directory = "src/routes", framework = "preact", dataEndpoint = "/_smolter", ssr = true, splitRoutes = true, resourceRequest } = {}) {
47
47
  return [
48
48
  {
49
49
  name: "smolter-application",
50
50
  enforce: "pre",
51
- config: () => preactConfig,
51
+ config: () => framework === "preact" ? preactConfig : {
52
+ resolve: { dedupe: [
53
+ "react",
54
+ "react-dom",
55
+ "@kasperrt/smolter"
56
+ ] },
57
+ ssr: { noExternal: ["@kasperrt/smolter"] }
58
+ },
52
59
  transformIndexHtml: {
53
60
  order: "post",
54
61
  handler(html, context) {
@@ -66,7 +73,7 @@ function applicationPlugins({ directory = "src/routes", dataEndpoint = "/_smolte
66
73
  const target = new URL(incoming.url, "http://local").searchParams.get("url");
67
74
  if (!target?.startsWith("/") || target.startsWith("//")) return send(new Response("Invalid route URL", { status: 400 }), outgoing);
68
75
  const request = await webRequest(incoming, outgoing, target);
69
- const [{ default: routes }, { dispatchRoute }] = await Promise.all([server.ssrLoadModule("virtual:smolter/routes"), server.ssrLoadModule("@kasperrt/smolter/preact/server")]);
76
+ const [{ default: routes }, { dispatchRoute }] = await Promise.all([server.ssrLoadModule("virtual:smolter/routes"), server.ssrLoadModule(`@kasperrt/smolter/${framework}/server`)]);
70
77
  await send(await dispatchRoute(request, routes), outgoing);
71
78
  };
72
79
  run().catch(next);
@@ -92,10 +99,10 @@ function applicationPlugins({ directory = "src/routes", dataEndpoint = "/_smolte
92
99
  }
93
100
  },
94
101
  browserBoundary(),
95
- ...splitRoutes ? [lazyFiles(directory, dataEndpoint)] : [],
102
+ ...splitRoutes ? [lazyFiles(directory, dataEndpoint, framework)] : [],
96
103
  routerPlugin({
97
104
  directory,
98
- framework: "preact",
105
+ framework,
99
106
  dataEndpoint
100
107
  })
101
108
  ];
@@ -7,4 +7,8 @@ export type { FileRouterOptions } from "./index.ts";
7
7
  export declare function routerPlugin(options?: FileRouterOptions): Plugin;
8
8
  /** File routing is the default; this alias keeps configuration explicit. */
9
9
  export declare const fileRouter: typeof routerPlugin;
10
+ export type { ApplicationOptions } from "./application/vite.ts";
10
11
  export { type DeploymentPluginOptions, deploymentPlugin, } from "./deployment.ts";
12
+ import { type ApplicationOptions } from "./application/vite.ts";
13
+ /** Configure React application SSR, scoped data transport and lazy file routing. */
14
+ export declare function applicationPlugins(options?: ApplicationOptions): Plugin[];
@@ -1,6 +1,7 @@
1
1
  import { flatRoutes } from "../files/index.js";
2
2
  import { routerPlugin as routerPlugin$1 } from "./index.js";
3
3
  import { deploymentPlugin } from "./deployment.js";
4
+ import { applicationPlugins as applicationPlugins$1 } from "./application/vite.js";
4
5
  //#region .generated/vite/react.ts
5
6
  /** Configure flat routes and server transport for React. */
6
7
  function routerPlugin(options = {}) {
@@ -11,5 +12,12 @@ function routerPlugin(options = {}) {
11
12
  }
12
13
  /** File routing is the default; this alias keeps configuration explicit. */
13
14
  var fileRouter = routerPlugin;
15
+ /** Configure React application SSR, scoped data transport and lazy file routing. */
16
+ function applicationPlugins(options = {}) {
17
+ return applicationPlugins$1({
18
+ ...options,
19
+ framework: "react"
20
+ });
21
+ }
14
22
  //#endregion
15
- export { deploymentPlugin, fileRouter, flatRoutes, routerPlugin };
23
+ export { applicationPlugins, deploymentPlugin, fileRouter, flatRoutes, routerPlugin };
package/jsr.json CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "name": "@kasperrt/smolter",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "exports": {
5
5
  "./react": "./.generated/react/index.ts",
6
6
  "./preact": "./.generated/preact/index.ts",
7
- "./react/server": "./.generated/server/index.ts",
7
+ "./react/server": "./.generated/server/react.ts",
8
8
  "./preact/server": "./.generated/server/preact.ts",
9
- "./react/client": "./.generated/client/index.ts",
9
+ "./react/client": "./.generated/react/client.tsx",
10
10
  "./preact/client": "./.generated/preact/client.tsx",
11
11
  "./utils": "./.generated/core/utils.ts",
12
12
  "./utils/http": "./.generated/server/node.ts",
13
13
  "./utils/assets": "./.generated/server/assets.ts",
14
14
  "./utils/responses": "./.generated/core/responses.ts",
15
- "./react/data": "./.generated/react/data.ts",
16
15
  "./preact/lazy-route": "./.generated/preact/lazy-route.ts",
17
- "./preact/document": "./.generated/preact/document.tsx"
16
+ "./preact/document": "./.generated/preact/document.tsx",
17
+ "./react/document": "./.generated/react/document.tsx",
18
+ "./react/lazy-route": "./.generated/react/lazy-route.ts"
18
19
  },
19
20
  "imports": {
20
21
  "react": "npm:react@19.3.1",
@@ -23,7 +24,10 @@
23
24
  "preact/hooks": "npm:preact@11.0.1/hooks",
24
25
  "preact/jsx-runtime": "npm:preact@11.0.1/jsx-runtime",
25
26
  "preact-render-to-string": "npm:preact-render-to-string@6.8.1",
26
- "react/jsx-runtime": "npm:react@19.3.1/jsx-runtime"
27
+ "react/jsx-runtime": "npm:react@19.3.1/jsx-runtime",
28
+ "react-dom": "npm:react-dom@19.3.1",
29
+ "react-dom/client": "npm:react-dom@19.3.1/client",
30
+ "react-dom/server": "npm:react-dom@19.3.1/server"
27
31
  },
28
32
  "publish": {
29
33
  "include": [
package/package.json CHANGED
@@ -20,7 +20,7 @@
20
20
  "build:examples": "pnpm --filter \"@smolter/example-*\" build",
21
21
  "smoke:production": "node scripts/production-smoke.mjs",
22
22
  "jsr:check": "node scripts/jsr.mjs",
23
- "build:site": "vite build --config apps/site/vite.config.ts && pnpm --filter @smolter/example-react build --mode hosted && pnpm --filter @smolter/example-preact build --mode hosted",
23
+ "build:site": "pnpm --filter @smolter/example-preact build --mode hosted",
24
24
  "smoke:site": "node scripts/site-smoke.mjs",
25
25
  "docs:badges": "node scripts/readme-badges.mjs",
26
26
  "docs:reference": "pnpm build && node scripts/docs-reference.mjs",
@@ -35,6 +35,7 @@
35
35
  "@smolter/preact": "workspace:*",
36
36
  "@smolter/react": "workspace:*",
37
37
  "@smolter/server": "workspace:*",
38
+ "@smolter/ui": "workspace:*",
38
39
  "@smolter/vite": "workspace:*",
39
40
  "@types/node": "24.10.1",
40
41
  "@types/react": "19.3.0",
@@ -56,7 +57,7 @@
56
57
  "wouter": "3.13.0",
57
58
  "wouter-preact": "3.13.0"
58
59
  },
59
- "version": "0.4.2",
60
+ "version": "0.4.4",
60
61
  "description": "A tiny typed data router for React and Preact.",
61
62
  "sideEffects": false,
62
63
  "license": "MIT",
@@ -74,16 +75,16 @@
74
75
  "import": "./dist/preact/index.js"
75
76
  },
76
77
  "./react/server": {
77
- "types": "./dist/server/index.d.ts",
78
- "import": "./dist/server/index.js"
78
+ "types": "./dist/server/react.d.ts",
79
+ "import": "./dist/server/react.js"
79
80
  },
80
81
  "./preact/server": {
81
82
  "types": "./dist/server/preact.d.ts",
82
83
  "import": "./dist/server/preact.js"
83
84
  },
84
85
  "./react/client": {
85
- "types": "./dist/client/index.d.ts",
86
- "import": "./dist/client/index.js"
86
+ "types": "./dist/react/client.d.ts",
87
+ "import": "./dist/react/client.js"
87
88
  },
88
89
  "./preact/client": {
89
90
  "types": "./dist/preact/client.d.ts",
@@ -113,10 +114,6 @@
113
114
  "types": "./dist/core/responses.d.ts",
114
115
  "import": "./dist/core/responses.js"
115
116
  },
116
- "./react/data": {
117
- "types": "./dist/react/data.d.ts",
118
- "import": "./dist/react/data.js"
119
- },
120
117
  "./preact/lazy-route": {
121
118
  "types": "./dist/preact/lazy-route.d.ts",
122
119
  "import": "./dist/preact/lazy-route.js"
@@ -124,6 +121,14 @@
124
121
  "./preact/document": {
125
122
  "types": "./dist/preact/document.d.ts",
126
123
  "import": "./dist/preact/document.js"
124
+ },
125
+ "./react/document": {
126
+ "types": "./dist/react/document.d.ts",
127
+ "import": "./dist/react/document.js"
128
+ },
129
+ "./react/lazy-route": {
130
+ "types": "./dist/react/lazy-route.d.ts",
131
+ "import": "./dist/react/lazy-route.js"
127
132
  }
128
133
  },
129
134
  "files": [
@@ -135,10 +140,11 @@
135
140
  "CHANGELOG.md"
136
141
  ],
137
142
  "peerDependencies": {
138
- "react": ">=18",
139
143
  "preact": ">=11.0.1",
144
+ "preact-render-to-string": ">=6.8.1",
145
+ "react": ">=18",
140
146
  "vite": ">=8",
141
- "preact-render-to-string": ">=6.8.1"
147
+ "react-dom": ">=18"
142
148
  },
143
149
  "peerDependenciesMeta": {
144
150
  "react": {
@@ -152,6 +158,9 @@
152
158
  },
153
159
  "preact-render-to-string": {
154
160
  "optional": true
161
+ },
162
+ "react-dom": {
163
+ "optional": true
155
164
  }
156
165
  },
157
166
  "author": "Kasper Rynning-Tønnesen <kasperrt@users.noreply.github.com> (https://github.com/kasperrt)",
@@ -1,44 +0,0 @@
1
- export type Params = Readonly<Record<string, string>>;
2
-
3
- export interface LoaderFunctionArgs {
4
- params: Params;
5
- signal: AbortSignal;
6
- }
7
-
8
- export interface ActionFunctionArgs<Input = unknown> {
9
- input: Input;
10
- params: Params;
11
- signal: AbortSignal;
12
- }
13
-
14
- export interface DataResult<Data> {
15
- data: Data | null;
16
- error: string;
17
- unexpected?: true;
18
- }
19
-
20
- export interface ResourceModule {
21
- action?: (args: ActionFunctionArgs) => Promise<DataResult<unknown>>;
22
- loader?: (args: LoaderFunctionArgs) => Promise<DataResult<unknown>>;
23
- shouldRevalidate?: (args: ShouldRevalidateFunctionArgs) => boolean;
24
- }
25
-
26
- export interface ShouldRevalidateFunctionArgs {
27
- actionResult: DataResult<unknown>;
28
- defaultShouldRevalidate: boolean;
29
- params: Params;
30
- }
31
-
32
- export type NavigationState = "idle" | "loading" | "submitting";
33
-
34
- export interface RouteState {
35
- actionData: unknown;
36
- actionError: string;
37
- actionState: "idle" | "submitting";
38
- loaderData: unknown;
39
- loaderError: string;
40
- loaderInitialized: boolean;
41
- loaderState: "idle" | "loading";
42
- navigationState: NavigationState;
43
- unexpectedError: boolean;
44
- }