@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.
- package/.generated/client/hash-history.ts +27 -0
- package/.generated/client/hydration.ts +98 -0
- package/.generated/client/root-data.ts +52 -0
- package/.generated/core/environment.ts +1 -0
- package/.generated/core/navigation.ts +1 -0
- package/.generated/core/prepare-routes.ts +50 -0
- package/.generated/core/presentation.ts +1 -1
- package/.generated/core/responses.ts +12 -0
- package/.generated/core/utils.ts +5 -2
- package/.generated/preact/await.tsx +1 -0
- package/.generated/preact/client.tsx +8 -27
- package/.generated/preact/deferred-hydration.ts +1 -98
- package/.generated/preact/document.tsx +10 -0
- package/.generated/preact/index.ts +11 -2
- package/.generated/preact/lazy-route.ts +7 -0
- package/.generated/preact/navigation.tsx +19 -5
- package/.generated/preact/root-data.ts +1 -45
- package/.generated/preact/route-hooks.tsx +12 -2
- package/.generated/preact/route-types.ts +14 -0
- package/.generated/preact/router.tsx +5 -48
- package/.generated/react/client.tsx +130 -0
- package/.generated/react/document.tsx +235 -0
- package/.generated/react/fetcher.ts +29 -7
- package/.generated/react/hooks.ts +20 -18
- package/.generated/react/index.ts +28 -2
- package/.generated/react/lazy-route.ts +116 -0
- package/.generated/react/link.ts +1 -38
- package/.generated/react/navigation-state.ts +13 -0
- package/.generated/react/navigation.ts +145 -0
- package/.generated/react/outlet.ts +48 -0
- package/.generated/react/root-context.ts +22 -0
- package/.generated/react/route-hooks.ts +159 -0
- package/.generated/react/route-types.ts +91 -0
- package/.generated/react/router.ts +50 -0
- package/.generated/react/runtime.ts +1 -0
- package/.generated/server/application.tsx +18 -250
- package/.generated/server/assets.ts +7 -0
- package/.generated/server/dispatch.ts +87 -0
- package/.generated/server/handler.ts +3 -0
- package/.generated/server/http.ts +2 -0
- package/.generated/server/index.ts +2 -0
- package/.generated/server/node.ts +5 -2
- package/.generated/server/preact.ts +5 -2
- package/.generated/server/react.ts +34 -0
- package/.generated/server/render-application.ts +203 -0
- package/.generated/vite/application/lazy-files.ts +2 -1
- package/.generated/vite/application/vite.ts +12 -4
- package/.generated/vite/react.ts +11 -0
- package/CHANGELOG.md +20 -0
- package/README.md +48 -48
- package/dist/client/hash-history.d.ts +2 -0
- package/dist/client/hash-history.js +26 -0
- package/dist/client/hydration.d.ts +10 -0
- package/dist/{preact/deferred-hydration.js → client/hydration.js} +1 -1
- package/dist/client/root-data.d.ts +14 -0
- package/dist/{preact → client}/root-data.js +1 -1
- package/dist/core/environment.d.ts +1 -0
- package/dist/core/navigation.d.ts +1 -0
- package/dist/core/prepare-routes.d.ts +7 -0
- package/dist/core/prepare-routes.js +28 -0
- package/dist/core/presentation.js +1 -1
- package/dist/core/responses.d.ts +12 -0
- package/dist/core/responses.js +5 -0
- package/dist/core/utils.d.ts +5 -2
- package/dist/core/utils.js +1 -2
- package/dist/preact/await.d.ts +1 -0
- package/dist/preact/await.js +1 -0
- package/dist/preact/client.d.ts +7 -1
- package/dist/preact/client.js +11 -27
- package/dist/preact/deferred-hydration.d.ts +1 -10
- package/dist/preact/document.d.ts +10 -0
- package/dist/preact/document.js +11 -2
- package/dist/preact/index.d.ts +6 -2
- package/dist/preact/index.js +1 -1
- package/dist/preact/lazy-route.d.ts +2 -0
- package/dist/preact/lazy-route.js +6 -0
- package/dist/preact/navigation-state.js +1 -1
- package/dist/preact/navigation.d.ts +6 -0
- package/dist/preact/navigation.js +9 -5
- package/dist/preact/root-data.d.ts +1 -8
- package/dist/preact/route-hooks.d.ts +10 -0
- package/dist/preact/route-hooks.js +13 -3
- package/dist/preact/route-types.d.ts +14 -0
- package/dist/preact/router.d.ts +5 -2
- package/dist/preact/router.js +4 -26
- package/dist/react/client.d.ts +20 -0
- package/dist/react/client.js +86 -0
- package/dist/react/document.d.ts +25 -0
- package/dist/react/document.js +169 -0
- package/dist/react/fetcher.d.ts +8 -4
- package/dist/react/fetcher.js +12 -1
- package/dist/react/form.js +1 -1
- package/dist/react/hooks.d.ts +7 -6
- package/dist/react/index.d.ts +13 -2
- package/dist/react/index.js +7 -2
- package/dist/react/lazy-route.d.ts +22 -0
- package/dist/react/lazy-route.js +57 -0
- package/dist/react/link.d.ts +1 -12
- package/dist/react/navigation-state.d.ts +2 -0
- package/dist/react/navigation-state.js +9 -0
- package/dist/react/navigation.d.ts +33 -0
- package/dist/react/navigation.js +64 -0
- package/dist/react/outlet.d.ts +16 -0
- package/dist/react/outlet.js +26 -0
- package/dist/react/root-context.d.ts +14 -0
- package/dist/react/root-context.js +10 -0
- package/dist/react/route-hooks.d.ts +34 -0
- package/dist/react/route-hooks.js +89 -0
- package/dist/react/route-types.d.ts +69 -0
- package/dist/react/router.d.ts +10 -0
- package/dist/react/router.js +22 -0
- package/dist/react/runtime.d.ts +1 -1
- package/dist/react/runtime.js +1 -1
- package/dist/server/application.d.ts +4 -5
- package/dist/server/application.js +9 -154
- package/dist/server/assets.d.ts +2 -0
- package/dist/server/assets.js +6 -0
- package/dist/server/dispatch.d.ts +5 -0
- package/dist/server/dispatch.js +50 -0
- package/dist/server/handler.d.ts +3 -0
- package/dist/server/http.d.ts +2 -0
- package/dist/server/http.js +2 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2 -1
- package/dist/server/node.d.ts +5 -1
- package/dist/server/preact.d.ts +5 -1
- package/dist/server/preact.js +2 -1
- package/dist/server/react.d.ts +10 -0
- package/dist/server/react.js +22 -0
- package/dist/server/render-application.d.ts +31 -0
- package/dist/server/render-application.js +118 -0
- package/dist/vite/application/lazy-files.d.ts +1 -1
- package/dist/vite/application/lazy-files.js +2 -2
- package/dist/vite/application/vite.d.ts +2 -1
- package/dist/vite/application/vite.js +12 -5
- package/dist/vite/react.d.ts +4 -0
- package/dist/vite/react.js +9 -1
- package/jsr.json +10 -6
- package/package.json +21 -12
- package/.generated/core/resource-types.ts +0 -44
- package/.generated/core/resources.ts +0 -444
- package/.generated/react/data.ts +0 -25
- package/.generated/react/resources/components.tsx +0 -404
- package/dist/core/resource-types.d.ts +0 -37
- package/dist/core/resources.d.ts +0 -19
- package/dist/core/resources.js +0 -345
- package/dist/react/data.d.ts +0 -5
- package/dist/react/data.js +0 -3
- package/dist/react/link.js +0 -27
- package/dist/react/resources/components.d.ts +0 -64
- 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 };
|
package/dist/server/handler.d.ts
CHANGED
|
@@ -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>;
|
package/dist/server/http.d.ts
CHANGED
|
@@ -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 {};
|
package/dist/server/http.js
CHANGED
|
@@ -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) => {
|
package/dist/server/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/server/index.js
CHANGED
|
@@ -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 };
|
package/dist/server/node.d.ts
CHANGED
package/dist/server/preact.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
/**
|
|
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";
|
package/dist/server/preact.js
CHANGED
|
@@ -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 {
|
|
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 [
|
|
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(
|
|
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
|
|
105
|
+
framework,
|
|
99
106
|
dataEndpoint
|
|
100
107
|
})
|
|
101
108
|
];
|
package/dist/vite/react.d.ts
CHANGED
|
@@ -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[];
|
package/dist/vite/react.js
CHANGED
|
@@ -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.
|
|
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/
|
|
7
|
+
"./react/server": "./.generated/server/react.ts",
|
|
8
8
|
"./preact/server": "./.generated/server/preact.ts",
|
|
9
|
-
"./react/client": "./.generated/client
|
|
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": "
|
|
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.
|
|
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/
|
|
78
|
-
"import": "./dist/server/
|
|
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
|
|
86
|
-
"import": "./dist/client
|
|
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
|
-
"
|
|
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
|
-
}
|