@kasperrt/smolter 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{src/internal → .generated}/client/index.ts +1 -0
- package/.generated/core/environment.ts +43 -0
- package/{src/internal → .generated}/core/errors.ts +1 -1
- package/{src/internal → .generated}/core/index.ts +1 -0
- package/.generated/core/navigation-router.ts +107 -0
- package/.generated/core/navigation.ts +147 -0
- package/.generated/core/presentation.ts +108 -0
- package/.generated/core/resource-types.ts +44 -0
- package/.generated/core/resources.ts +444 -0
- package/.generated/core/responses.ts +86 -0
- package/{src/internal → .generated}/core/router.ts +136 -63
- package/{src/internal → .generated}/core/serialize.ts +7 -2
- package/.generated/core/store.ts +25 -0
- package/.generated/core/submission.ts +116 -0
- package/{src/internal → .generated}/core/types.ts +18 -1
- package/.generated/core/url.ts +29 -0
- package/.generated/core/utils.ts +5 -0
- package/.generated/preact/await.tsx +41 -0
- package/.generated/preact/client.tsx +93 -0
- package/.generated/preact/context.ts +59 -0
- package/.generated/preact/document.tsx +198 -0
- package/{src/internal → .generated}/preact/fetcher.ts +43 -23
- package/.generated/preact/form.ts +58 -0
- package/{src/internal → .generated}/preact/hooks.ts +25 -28
- package/.generated/preact/index.ts +34 -0
- package/.generated/preact/lazy-route.ts +100 -0
- package/.generated/preact/navigation-state.ts +9 -0
- package/.generated/preact/navigation.tsx +147 -0
- package/.generated/preact/root-context.ts +21 -0
- package/.generated/preact/root-data.ts +45 -0
- package/.generated/preact/route-hooks.tsx +170 -0
- package/.generated/preact/route-types.ts +76 -0
- package/.generated/preact/router.tsx +107 -0
- package/{src/internal → .generated}/preact/routes.ts +17 -9
- package/.generated/react/context.ts +59 -0
- package/.generated/react/data.ts +25 -0
- package/{src/internal → .generated}/react/fetcher.ts +13 -11
- package/.generated/react/form.ts +58 -0
- package/{src/internal → .generated}/react/hooks.ts +9 -14
- package/{src/internal/preact → .generated/react}/index.ts +5 -1
- package/{src/internal → .generated}/react/link.ts +13 -21
- package/.generated/react/resources/components.tsx +404 -0
- package/{src/internal → .generated}/react/routes.ts +17 -9
- package/.generated/server/application.tsx +185 -0
- package/.generated/server/assets.ts +19 -0
- package/.generated/server/handler.ts +69 -0
- package/.generated/server/http.ts +55 -0
- package/{src/internal → .generated}/server/index.ts +3 -3
- package/.generated/server/node.ts +4 -0
- package/.generated/server/preact.ts +7 -0
- package/.generated/vite/application/browser-boundary.ts +42 -0
- package/.generated/vite/application/lazy-files.ts +136 -0
- package/.generated/vite/application/vite.ts +144 -0
- package/{src/preact-vite.ts → .generated/vite/preact.ts} +9 -3
- package/{src/react-vite.ts → .generated/vite/react.ts} +3 -3
- package/CHANGELOG.md +54 -0
- package/README.md +227 -35
- package/dist/client/deserialize.js +10 -0
- package/dist/{internal/client → client}/index.d.ts +1 -0
- package/dist/client/index.js +46 -0
- package/dist/core/environment.d.ts +12 -0
- package/dist/core/environment.js +23 -0
- package/dist/core/errors.js +7 -0
- package/dist/{internal/core → core}/index.d.ts +1 -0
- package/dist/core/match.js +33 -0
- package/dist/core/navigation-router.d.ts +15 -0
- package/dist/core/navigation-router.js +64 -0
- package/dist/core/navigation.d.ts +15 -0
- package/dist/core/navigation.js +92 -0
- package/dist/core/presentation.d.ts +5 -0
- package/dist/core/presentation.js +69 -0
- package/dist/core/resource-types.d.ts +37 -0
- package/dist/core/resources.d.ts +19 -0
- package/dist/core/resources.js +345 -0
- package/dist/core/responses.d.ts +15 -0
- package/dist/core/responses.js +48 -0
- package/dist/{internal/core → core}/router.d.ts +1 -1
- package/dist/core/router.js +204 -0
- package/dist/{internal/core → core}/serialize.d.ts +2 -0
- package/dist/core/serialize.js +17 -0
- package/dist/core/store.d.ts +6 -0
- package/dist/core/store.js +21 -0
- package/dist/core/submission.d.ts +30 -0
- package/dist/core/submission.js +41 -0
- package/dist/{internal/core → core}/types.d.ts +16 -1
- package/dist/core/url.d.ts +7 -0
- package/dist/core/url.js +12 -0
- package/dist/core/utils.d.ts +5 -0
- package/dist/core/utils.js +5 -0
- package/dist/files/index.js +64 -0
- package/dist/preact/await.d.ts +7 -0
- package/dist/preact/await.js +19 -0
- package/dist/preact/client.d.ts +7 -0
- package/dist/preact/client.js +67 -0
- package/dist/preact/context.d.ts +18 -0
- package/dist/preact/context.js +23 -0
- package/dist/preact/document.d.ts +15 -0
- package/dist/preact/document.js +155 -0
- package/dist/{internal/preact → preact}/fetcher.d.ts +9 -5
- package/dist/preact/fetcher.js +45 -0
- package/dist/{internal/preact → preact}/form.d.ts +5 -6
- package/dist/preact/form.js +21 -0
- package/dist/{internal/preact → preact}/hooks.d.ts +7 -6
- package/dist/preact/hooks.js +25 -0
- package/dist/preact/index.d.ts +18 -0
- package/dist/preact/index.js +15 -0
- package/dist/preact/lazy-route.d.ts +19 -0
- package/dist/preact/lazy-route.js +51 -0
- package/dist/preact/navigation-state.d.ts +2 -0
- package/dist/preact/navigation-state.js +9 -0
- package/dist/preact/navigation.d.ts +28 -0
- package/dist/preact/navigation.js +60 -0
- package/dist/preact/root-context.d.ts +13 -0
- package/dist/preact/root-context.js +9 -0
- package/dist/preact/root-data.d.ts +8 -0
- package/dist/preact/root-data.js +28 -0
- package/dist/preact/route-hooks.d.ts +32 -0
- package/dist/preact/route-hooks.js +94 -0
- package/dist/preact/route-types.d.ts +54 -0
- package/dist/preact/router.d.ts +16 -0
- package/dist/preact/router.js +63 -0
- package/dist/{internal/preact → preact}/routes.d.ts +1 -1
- package/dist/preact/routes.js +12 -0
- package/dist/{internal/preact → preact}/runtime.js +1 -1
- package/dist/react/context.d.ts +18 -0
- package/dist/react/context.js +23 -0
- package/dist/react/data.d.ts +5 -0
- package/dist/react/data.js +3 -0
- package/dist/{internal/react → react}/fetcher.d.ts +2 -2
- package/dist/react/fetcher.js +34 -0
- package/dist/{internal/react → react}/form.d.ts +5 -6
- package/dist/react/form.js +21 -0
- package/dist/react/hooks.js +25 -0
- package/dist/{internal/react → react}/index.d.ts +1 -1
- package/dist/react/index.js +10 -0
- package/dist/{internal/react → react}/link.d.ts +2 -2
- package/dist/react/link.js +27 -0
- package/dist/react/resources/components.d.ts +64 -0
- package/dist/react/resources/components.js +239 -0
- package/dist/{internal/react → react}/routes.d.ts +1 -1
- package/dist/react/routes.js +12 -0
- package/dist/react/runtime.js +1 -0
- package/dist/server/application.d.ts +4 -0
- package/dist/server/application.js +119 -0
- package/dist/server/assets.d.ts +5 -0
- package/dist/server/assets.js +7 -0
- package/dist/server/handler.d.ts +19 -0
- package/dist/server/handler.js +22 -0
- package/dist/server/http.d.ts +4 -0
- package/dist/server/http.js +27 -0
- package/dist/server/index.js +76 -0
- package/dist/server/node.d.ts +3 -0
- package/dist/server/node.js +3 -0
- package/dist/server/preact.d.ts +3 -0
- package/dist/server/preact.js +4 -0
- package/dist/vite/application/browser-boundary.d.ts +3 -0
- package/dist/vite/application/browser-boundary.js +35 -0
- package/dist/vite/application/lazy-files.d.ts +2 -0
- package/dist/vite/application/lazy-files.js +87 -0
- package/dist/vite/application/vite.d.ts +9 -0
- package/dist/vite/application/vite.js +101 -0
- package/dist/vite/index.js +104 -0
- package/dist/{preact-vite.d.ts → vite/preact.d.ts} +5 -3
- package/dist/vite/preact.js +15 -0
- package/dist/{react-vite.d.ts → vite/react.d.ts} +3 -3
- package/dist/vite/react.js +14 -0
- package/dist/vite/transport.js +35 -0
- package/jsr.json +28 -16
- package/package.json +116 -36
- package/dist/client.d.ts +0 -2
- package/dist/client.js +0 -2
- package/dist/core.d.ts +0 -2
- package/dist/core.js +0 -2
- package/dist/files.d.ts +0 -2
- package/dist/files.js +0 -2
- package/dist/internal/client/deserialize.js +0 -11
- package/dist/internal/client/index.js +0 -44
- package/dist/internal/core/errors.js +0 -6
- package/dist/internal/core/index.js +0 -7
- package/dist/internal/core/match.js +0 -27
- package/dist/internal/core/router.js +0 -189
- package/dist/internal/core/serialize.js +0 -13
- package/dist/internal/core/types.js +0 -5
- package/dist/internal/core/url.d.ts +0 -2
- package/dist/internal/core/url.js +0 -6
- package/dist/internal/files/index.js +0 -77
- package/dist/internal/preact/context.d.ts +0 -18
- package/dist/internal/preact/context.js +0 -20
- package/dist/internal/preact/fetcher.js +0 -28
- package/dist/internal/preact/form.js +0 -41
- package/dist/internal/preact/hooks.js +0 -35
- package/dist/internal/preact/index.d.ts +0 -9
- package/dist/internal/preact/index.js +0 -7
- package/dist/internal/preact/link.d.ts +0 -12
- package/dist/internal/preact/link.js +0 -28
- package/dist/internal/preact/routes.js +0 -22
- package/dist/internal/react/context.d.ts +0 -18
- package/dist/internal/react/context.js +0 -20
- package/dist/internal/react/fetcher.js +0 -28
- package/dist/internal/react/form.js +0 -41
- package/dist/internal/react/hooks.js +0 -35
- package/dist/internal/react/index.js +0 -7
- package/dist/internal/react/link.js +0 -28
- package/dist/internal/react/routes.js +0 -22
- package/dist/internal/react/runtime.js +0 -2
- package/dist/internal/server/index.js +0 -69
- package/dist/internal/vite/index.js +0 -132
- package/dist/internal/vite/transport.js +0 -42
- package/dist/preact-client.d.ts +0 -2
- package/dist/preact-client.js +0 -2
- package/dist/preact-server.d.ts +0 -2
- package/dist/preact-server.js +0 -2
- package/dist/preact-vite.js +0 -9
- package/dist/preact.d.ts +0 -2
- package/dist/preact.js +0 -2
- package/dist/react-client.d.ts +0 -2
- package/dist/react-client.js +0 -2
- package/dist/react-server.d.ts +0 -2
- package/dist/react-server.js +0 -2
- package/dist/react-vite.js +0 -9
- package/dist/react.d.ts +0 -2
- package/dist/react.js +0 -2
- package/dist/server.d.ts +0 -2
- package/dist/server.js +0 -2
- package/dist/utils.d.ts +0 -5
- package/dist/utils.js +0 -5
- package/dist/vite.d.ts +0 -2
- package/dist/vite.js +0 -2
- package/src/client.ts +0 -2
- package/src/core.ts +0 -2
- package/src/files.ts +0 -2
- package/src/internal/core/url.ts +0 -6
- package/src/internal/preact/context.ts +0 -46
- package/src/internal/preact/form.ts +0 -79
- package/src/internal/preact/link.ts +0 -46
- package/src/internal/react/context.ts +0 -46
- package/src/internal/react/form.ts +0 -79
- package/src/internal/react/index.ts +0 -13
- package/src/preact-client.ts +0 -2
- package/src/preact-server.ts +0 -2
- package/src/preact.ts +0 -2
- package/src/react-client.ts +0 -2
- package/src/react-server.ts +0 -2
- package/src/react.ts +0 -2
- package/src/server.ts +0 -2
- package/src/utils.ts +0 -5
- package/src/vite.ts +0 -2
- /package/{src/internal → .generated}/client/deserialize.ts +0 -0
- /package/{src/internal → .generated}/core/match.ts +0 -0
- /package/{src/internal → .generated}/files/index.ts +0 -0
- /package/{src/internal → .generated}/preact/runtime.ts +0 -0
- /package/{src/internal → .generated}/react/runtime.ts +0 -0
- /package/{src/internal → .generated}/vite/index.ts +0 -0
- /package/{src/internal → .generated}/vite/transport.ts +0 -0
- /package/dist/{internal/client → client}/deserialize.d.ts +0 -0
- /package/dist/{internal/core → core}/errors.d.ts +0 -0
- /package/dist/{internal/core → core}/match.d.ts +0 -0
- /package/dist/{internal/files → files}/index.d.ts +0 -0
- /package/dist/{internal/preact → preact}/runtime.d.ts +0 -0
- /package/dist/{internal/react → react}/hooks.d.ts +0 -0
- /package/dist/{internal/react → react}/runtime.d.ts +0 -0
- /package/dist/{internal/server → server}/index.d.ts +0 -0
- /package/dist/{internal/vite → vite}/index.d.ts +0 -0
- /package/dist/{internal/vite → vite}/transport.d.ts +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Component, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region .generated/react/resources/components.tsx
|
|
4
|
+
var RouterContext = createContext(null);
|
|
5
|
+
var RouteContext = createContext(null);
|
|
6
|
+
var FormContext = createContext(null);
|
|
7
|
+
function RouterProvider({ children, initialMatches = EMPTY_MATCHES, router }) {
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
for (const match of initialMatches) router.load(match.routeId, match.params ?? {});
|
|
10
|
+
return () => router.dispose();
|
|
11
|
+
}, [initialMatches, router]);
|
|
12
|
+
return /* @__PURE__ */ jsx(RouterContext.Provider, {
|
|
13
|
+
value: router,
|
|
14
|
+
children
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function Route({ children, params = {}, persistent = false, routeId }) {
|
|
18
|
+
const router = useRouter();
|
|
19
|
+
const stableParams = useStableParams(params);
|
|
20
|
+
const state = useRouteState(routeId, stableParams, true);
|
|
21
|
+
const route = router.getRoute(routeId);
|
|
22
|
+
const retry = useCallback(() => void router.retry(routeId, stableParams), [
|
|
23
|
+
routeId,
|
|
24
|
+
router,
|
|
25
|
+
stableParams
|
|
26
|
+
]);
|
|
27
|
+
const contextValue = useMemo(() => ({
|
|
28
|
+
params: stableParams,
|
|
29
|
+
routeId
|
|
30
|
+
}), [routeId, stableParams]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (persistent) router.persist(routeId, stableParams);
|
|
33
|
+
}, [
|
|
34
|
+
persistent,
|
|
35
|
+
routeId,
|
|
36
|
+
router,
|
|
37
|
+
stableParams
|
|
38
|
+
]);
|
|
39
|
+
let content = children;
|
|
40
|
+
if (children === void 0 && route?.default) {
|
|
41
|
+
const RouteComponent = route.default;
|
|
42
|
+
content = /* @__PURE__ */ jsx(RouteComponent, {});
|
|
43
|
+
}
|
|
44
|
+
if (state.unexpectedError && route?.ErrorBoundary) {
|
|
45
|
+
const ErrorBoundary = route.ErrorBoundary;
|
|
46
|
+
content = /* @__PURE__ */ jsx(ErrorBoundary, { retry });
|
|
47
|
+
}
|
|
48
|
+
if (!state.unexpectedError && !state.loaderInitialized && route?.HydrateFallback) {
|
|
49
|
+
const HydrateFallback = route.HydrateFallback;
|
|
50
|
+
content = /* @__PURE__ */ jsx(HydrateFallback, {});
|
|
51
|
+
}
|
|
52
|
+
return /* @__PURE__ */ jsx(RouteContext.Provider, {
|
|
53
|
+
value: contextValue,
|
|
54
|
+
children: /* @__PURE__ */ jsx(RenderErrorBoundary, {
|
|
55
|
+
onRetry: retry,
|
|
56
|
+
resetKey: `${routeId}:${JSON.stringify(stableParams)}`,
|
|
57
|
+
...route?.ErrorBoundary ? { fallback: route.ErrorBoundary } : {},
|
|
58
|
+
children: content
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function useLoaderData() {
|
|
63
|
+
const route = useCurrentRoute();
|
|
64
|
+
return useRouteLoaderData(route.routeId, route.params);
|
|
65
|
+
}
|
|
66
|
+
function useRouteLoaderData(routeId, params = {}) {
|
|
67
|
+
return useRouteState(routeId, useStableParams(params), true).loaderData;
|
|
68
|
+
}
|
|
69
|
+
function useActionData() {
|
|
70
|
+
const route = useCurrentRoute();
|
|
71
|
+
return useRouteState(route.routeId, route.params, false).actionData;
|
|
72
|
+
}
|
|
73
|
+
function useNavigation() {
|
|
74
|
+
const route = useCurrentRoute();
|
|
75
|
+
return { state: useRouteState(route.routeId, route.params, false).navigationState };
|
|
76
|
+
}
|
|
77
|
+
function useRevalidator() {
|
|
78
|
+
const route = useCurrentRoute();
|
|
79
|
+
const router = useRouter();
|
|
80
|
+
const state = useRouteState(route.routeId, route.params, false);
|
|
81
|
+
return useMemo(() => ({
|
|
82
|
+
revalidate: () => router.load(route.routeId, route.params, true),
|
|
83
|
+
state: state.navigationState === "loading" ? "loading" : "idle"
|
|
84
|
+
}), [
|
|
85
|
+
route,
|
|
86
|
+
router,
|
|
87
|
+
state.navigationState
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
function Form({ action, children, input, params }) {
|
|
91
|
+
const currentRoute = useContext(RouteContext);
|
|
92
|
+
const router = useRouter();
|
|
93
|
+
const routeId = action ?? currentRoute?.routeId ?? "";
|
|
94
|
+
const routeParams = useStableParams(params ?? currentRoute?.params ?? {});
|
|
95
|
+
const submit = useCallback((override) => router.submit(routeId, override === void 0 ? input : override, routeParams), [
|
|
96
|
+
input,
|
|
97
|
+
routeId,
|
|
98
|
+
routeParams,
|
|
99
|
+
router
|
|
100
|
+
]);
|
|
101
|
+
if (!routeId) throw new Error("Form requires an action or a parent Route.");
|
|
102
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
103
|
+
value: submit,
|
|
104
|
+
children
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function useSubmit() {
|
|
108
|
+
const submit = useContext(FormContext);
|
|
109
|
+
if (!submit) throw new Error("useSubmit must be used inside Form.");
|
|
110
|
+
return submit;
|
|
111
|
+
}
|
|
112
|
+
function useFetcher(options = {}) {
|
|
113
|
+
const currentRoute = useContext(RouteContext);
|
|
114
|
+
const router = useRouter();
|
|
115
|
+
const routeId = options.routeId ?? currentRoute?.routeId ?? "";
|
|
116
|
+
const params = useStableParams(options.params ?? currentRoute?.params ?? {});
|
|
117
|
+
const [data, setData] = useState(null);
|
|
118
|
+
const [error, setError] = useState("");
|
|
119
|
+
const [state, setState] = useState("idle");
|
|
120
|
+
const controller = useRef(null);
|
|
121
|
+
useEffect(() => () => {
|
|
122
|
+
controller.current?.abort();
|
|
123
|
+
}, []);
|
|
124
|
+
const run = useCallback(async (mutating, input, target) => {
|
|
125
|
+
controller.current?.abort();
|
|
126
|
+
const nextController = new AbortController();
|
|
127
|
+
const { signal } = nextController;
|
|
128
|
+
const targetId = target.routeId ?? routeId;
|
|
129
|
+
const targetParams = target.params ?? params;
|
|
130
|
+
controller.current = nextController;
|
|
131
|
+
setError("");
|
|
132
|
+
setState(mutating ? "submitting" : "loading");
|
|
133
|
+
const result = await (mutating ? router.fetchAction(targetId, input, targetParams, signal) : router.fetchLoader(targetId, targetParams, signal));
|
|
134
|
+
if (signal.aborted) return result;
|
|
135
|
+
controller.current = null;
|
|
136
|
+
setData(result.data);
|
|
137
|
+
setError(result.error);
|
|
138
|
+
setState("idle");
|
|
139
|
+
if (result.unexpected && currentRoute) router.reportUnexpected(currentRoute.routeId, currentRoute.params);
|
|
140
|
+
return result;
|
|
141
|
+
}, [
|
|
142
|
+
currentRoute,
|
|
143
|
+
params,
|
|
144
|
+
routeId,
|
|
145
|
+
router
|
|
146
|
+
]);
|
|
147
|
+
const submit = useCallback((input, target = {}) => run(true, input, target), [run]);
|
|
148
|
+
const load = useCallback((target = {}) => run(false, void 0, target), [run]);
|
|
149
|
+
const BoundForm = useCallback(({ children, input }) => /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
150
|
+
value: (override) => submit(override === void 0 ? input : override),
|
|
151
|
+
children
|
|
152
|
+
}), [submit]);
|
|
153
|
+
if (!routeId) throw new Error("useFetcher requires a routeId or a parent Route.");
|
|
154
|
+
return [useMemo(() => ({
|
|
155
|
+
data,
|
|
156
|
+
error,
|
|
157
|
+
state
|
|
158
|
+
}), [
|
|
159
|
+
data,
|
|
160
|
+
error,
|
|
161
|
+
state
|
|
162
|
+
]), useMemo(() => ({
|
|
163
|
+
Form: BoundForm,
|
|
164
|
+
load,
|
|
165
|
+
submit
|
|
166
|
+
}), [
|
|
167
|
+
BoundForm,
|
|
168
|
+
load,
|
|
169
|
+
submit
|
|
170
|
+
])];
|
|
171
|
+
}
|
|
172
|
+
function useRouter() {
|
|
173
|
+
const router = useContext(RouterContext);
|
|
174
|
+
if (!router) throw new Error("RouterProvider is missing.");
|
|
175
|
+
return router;
|
|
176
|
+
}
|
|
177
|
+
function useCurrentRoute() {
|
|
178
|
+
const route = useContext(RouteContext);
|
|
179
|
+
if (!route) throw new Error("This hook requires a parent Route.");
|
|
180
|
+
return route;
|
|
181
|
+
}
|
|
182
|
+
function useRouteState(routeId, params, loadOnMount) {
|
|
183
|
+
const router = useRouter();
|
|
184
|
+
const subscribe = useCallback((listener) => router.subscribe(routeId, params, listener), [
|
|
185
|
+
params,
|
|
186
|
+
routeId,
|
|
187
|
+
router
|
|
188
|
+
]);
|
|
189
|
+
const getSnapshot = useCallback(() => router.getSnapshot(routeId, params), [
|
|
190
|
+
params,
|
|
191
|
+
routeId,
|
|
192
|
+
router
|
|
193
|
+
]);
|
|
194
|
+
const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (loadOnMount) router.load(routeId, params);
|
|
197
|
+
}, [
|
|
198
|
+
loadOnMount,
|
|
199
|
+
params,
|
|
200
|
+
routeId,
|
|
201
|
+
router
|
|
202
|
+
]);
|
|
203
|
+
return state;
|
|
204
|
+
}
|
|
205
|
+
function useStableParams(params) {
|
|
206
|
+
const key = JSON.stringify(Object.entries(params).sort(([left], [right]) => left.localeCompare(right)));
|
|
207
|
+
const ref = useRef({
|
|
208
|
+
key,
|
|
209
|
+
value: params
|
|
210
|
+
});
|
|
211
|
+
if (ref.current.key !== key) ref.current = {
|
|
212
|
+
key,
|
|
213
|
+
value: params
|
|
214
|
+
};
|
|
215
|
+
return ref.current.value;
|
|
216
|
+
}
|
|
217
|
+
var RenderErrorBoundary = class extends Component {
|
|
218
|
+
state = { error: null };
|
|
219
|
+
static getDerivedStateFromError(error) {
|
|
220
|
+
return { error };
|
|
221
|
+
}
|
|
222
|
+
componentDidUpdate(previousProps) {
|
|
223
|
+
if (this.state.error && previousProps.resetKey !== this.props.resetKey) this.setState({ error: null });
|
|
224
|
+
}
|
|
225
|
+
render() {
|
|
226
|
+
if (this.state.error && this.props.fallback) {
|
|
227
|
+
const ErrorBoundary = this.props.fallback;
|
|
228
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, { retry: () => {
|
|
229
|
+
this.setState({ error: null });
|
|
230
|
+
this.props.onRetry();
|
|
231
|
+
} });
|
|
232
|
+
}
|
|
233
|
+
if (this.state.error) throw this.state.error;
|
|
234
|
+
return this.props.children;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var EMPTY_MATCHES = [];
|
|
238
|
+
//#endregion
|
|
239
|
+
export { Form, Route, RouterProvider, useActionData, useFetcher, useLoaderData, useNavigation, useRevalidator, useRouteLoaderData, useRouter, useSubmit };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
5
|
import type { RouteModule } from "../core/index.ts";
|
|
6
|
-
import type
|
|
6
|
+
import { type ComponentType, type ReactNode } from "./runtime.ts";
|
|
7
7
|
/** Renderable route module with optional loading and error components. */
|
|
8
8
|
export interface ViewModule extends RouteModule {
|
|
9
9
|
default?: ComponentType;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createElement, useMemo } from "./runtime.js";
|
|
2
|
+
import { useRouter, useSnapshot } from "./context.js";
|
|
3
|
+
//#region .generated/react/routes.ts
|
|
4
|
+
/** Renders the first matching registered module. */
|
|
5
|
+
function Routes({ fallback }) {
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
const state = useSnapshot();
|
|
8
|
+
const component = router.routes.find((route) => route.path === state.path)?.[state.error ? "ErrorBoundary" : state.state === "loading" && !state.loaded ? "HydrateFallback" : "default"];
|
|
9
|
+
return useMemo(() => component && createElement(component, { error: state.error }), [component, state.error]) ?? state.error?.message ?? fallback;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { Routes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { AppRoute, RootModule } from "../preact/route-types.ts";
|
|
3
|
+
export declare function renderApplication(request: Request, routes: AppRoute[], rootModule: RootModule, entry: string): Promise<Response>;
|
|
4
|
+
export declare function dispatchRoute(request: Request, routes: AppRoute[], document?: boolean): Promise<Response>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { matchRoutes } from "../core/match.js";
|
|
2
|
+
import { createDataRouter } from "../core/router.js";
|
|
3
|
+
import { normalizeResult, resolveData } from "../core/responses.js";
|
|
4
|
+
import { RouterApplication, prepareRoutes } from "../preact/router.js";
|
|
5
|
+
import { loadRouter } from "./index.js";
|
|
6
|
+
import { jsx } from "preact/jsx-runtime";
|
|
7
|
+
import { renderToStringAsync } from "preact-render-to-string";
|
|
8
|
+
//#region .generated/server/application.tsx
|
|
9
|
+
/** @jsxImportSource preact */
|
|
10
|
+
async function renderApplication(request, routes, rootModule, entry) {
|
|
11
|
+
routes = routes.map((route) => ({
|
|
12
|
+
...route,
|
|
13
|
+
path: route.routePath ?? route.path
|
|
14
|
+
}));
|
|
15
|
+
const match = matchRoutes(routes, new URL(request.url).pathname);
|
|
16
|
+
if (match && !match.route.default) return dispatchRoute(request, routes, true);
|
|
17
|
+
let actionResponse;
|
|
18
|
+
let actionData;
|
|
19
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
20
|
+
actionResponse = await dispatchRoute(request, routes, true);
|
|
21
|
+
if (actionResponse.headers.has("Location") || !actionResponse.headers.get("Content-Type")?.includes("application/json")) return actionResponse;
|
|
22
|
+
actionData = await normalizeResult(await actionResponse.json());
|
|
23
|
+
}
|
|
24
|
+
const root = await resolveData(await rootModule.loader?.({
|
|
25
|
+
request,
|
|
26
|
+
params: {},
|
|
27
|
+
signal: request.signal
|
|
28
|
+
}));
|
|
29
|
+
let redirectResponse;
|
|
30
|
+
const prepared = prepareRoutes(routes.map((route) => ({
|
|
31
|
+
...route,
|
|
32
|
+
ErrorBoundary: route.ErrorBoundary ?? rootModule.ErrorBoundary
|
|
33
|
+
}))).map((route) => {
|
|
34
|
+
if (!route.loader) return route;
|
|
35
|
+
const loader = route.loader;
|
|
36
|
+
return {
|
|
37
|
+
...route,
|
|
38
|
+
loader: async (args) => {
|
|
39
|
+
const value = await loader(args);
|
|
40
|
+
if (value instanceof Response && value.headers.has("Location")) redirectResponse = value;
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
let router = await loadRouter(prepared, request);
|
|
46
|
+
if (redirectResponse) {
|
|
47
|
+
router.dispose();
|
|
48
|
+
return redirectResponse;
|
|
49
|
+
}
|
|
50
|
+
if (actionResponse) {
|
|
51
|
+
const hydrationData = {
|
|
52
|
+
...router.getSnapshot(),
|
|
53
|
+
actionData: actionData instanceof Error ? void 0 : actionData,
|
|
54
|
+
...actionData instanceof Error && { error: actionData }
|
|
55
|
+
};
|
|
56
|
+
router.dispose();
|
|
57
|
+
router = createDataRouter(router.routes, { hydrationData });
|
|
58
|
+
}
|
|
59
|
+
const snapshot = router.getSnapshot();
|
|
60
|
+
const html = await renderToStringAsync(/* @__PURE__ */ jsx(RouterApplication, {
|
|
61
|
+
router,
|
|
62
|
+
routes,
|
|
63
|
+
root,
|
|
64
|
+
rootModule,
|
|
65
|
+
entry,
|
|
66
|
+
document: true
|
|
67
|
+
}));
|
|
68
|
+
router.dispose();
|
|
69
|
+
const headers = new Headers(actionResponse?.headers);
|
|
70
|
+
headers.delete("Content-Length");
|
|
71
|
+
headers.delete("Content-Encoding");
|
|
72
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
73
|
+
headers.set("Cache-Control", "no-store");
|
|
74
|
+
return new Response(`<!doctype html>${html}`, {
|
|
75
|
+
status: snapshot.error ? Number("status" in snapshot.error ? snapshot.error.status : 500) : actionResponse?.status ?? 200,
|
|
76
|
+
headers
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async function dispatchRoute(request, routes, document = false) {
|
|
80
|
+
const match = matchRoutes(routes.map((route) => ({
|
|
81
|
+
...route,
|
|
82
|
+
path: route.routePath ?? route.path
|
|
83
|
+
})), new URL(request.url).pathname);
|
|
84
|
+
if (!match) return Response.json({ error: "No route" }, { status: 404 });
|
|
85
|
+
const handler = request.method === "GET" || request.method === "HEAD" ? match.route.loader : match.route.action;
|
|
86
|
+
if (!handler) return Response.json({ error: "No handler" }, { status: 405 });
|
|
87
|
+
const value = await Promise.resolve().then(() => handler({
|
|
88
|
+
request,
|
|
89
|
+
params: match.params,
|
|
90
|
+
signal: request.signal
|
|
91
|
+
})).then((result) => result, (error) => error);
|
|
92
|
+
if (value instanceof Response) {
|
|
93
|
+
const location = value.headers.get("Location");
|
|
94
|
+
if (document) return value;
|
|
95
|
+
if (!location) {
|
|
96
|
+
if (value.ok) return value;
|
|
97
|
+
return Response.json({ __routeError: {
|
|
98
|
+
message: await value.text(),
|
|
99
|
+
status: value.status,
|
|
100
|
+
statusText: value.statusText
|
|
101
|
+
} });
|
|
102
|
+
}
|
|
103
|
+
const headers = new Headers(value.headers);
|
|
104
|
+
headers.delete("Location");
|
|
105
|
+
headers.set("X-Smolter-Redirect", location);
|
|
106
|
+
return new Response(null, { headers });
|
|
107
|
+
}
|
|
108
|
+
if (value instanceof Error) return Response.json({ __routeError: {
|
|
109
|
+
...value,
|
|
110
|
+
message: value.message
|
|
111
|
+
} });
|
|
112
|
+
if (value && typeof value === "object" && "__routeData" in value) {
|
|
113
|
+
const result = value;
|
|
114
|
+
return Response.json(await resolveData(result.value), document ? result.init : { headers: result.init.headers });
|
|
115
|
+
}
|
|
116
|
+
return Response.json(await normalizeResult(value) ?? null);
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { dispatchRoute, renderApplication };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { matchRoutes } from "../core/match.js";
|
|
2
|
+
//#region .generated/server/assets.ts
|
|
3
|
+
function routePreloads(routes, pathname, base) {
|
|
4
|
+
return ((matchRoutes(routes, pathname)?.route)?.files ?? []).map((file) => `<${base.replace(/\/$/, "")}/${file}>; rel=modulepreload`).join(", ");
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
export { routePreloads };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
/** Server module produced by an application's SSR entrypoint. */
|
|
3
|
+
export interface ServerBuild {
|
|
4
|
+
default: (request: Request, entry: string) => Response | Promise<Response>;
|
|
5
|
+
handleDataRequest: (request: Request) => Response | Promise<Response>;
|
|
6
|
+
}
|
|
7
|
+
export interface RequestHandlerOptions {
|
|
8
|
+
build: ServerBuild;
|
|
9
|
+
/** Browser entry URL resolved from Vite's production manifest. */
|
|
10
|
+
entry: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ServerIncomingMessage extends IncomingMessage {
|
|
13
|
+
/** Express supplies this using the application's trust proxy policy. */
|
|
14
|
+
protocol?: string;
|
|
15
|
+
originalUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
export type NextHandler = (error?: unknown) => void;
|
|
18
|
+
/** Node/Express middleware; mount after metrics, tracing and static assets. */
|
|
19
|
+
export declare function createRequestHandler({ build, entry, }: RequestHandlerOptions): (incoming: ServerIncomingMessage, outgoing: ServerResponse, next: NextHandler) => Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { send, webRequest } from "./http.js";
|
|
2
|
+
//#region .generated/server/handler.ts
|
|
3
|
+
/** Node/Express middleware; mount after metrics, tracing and static assets. */
|
|
4
|
+
function createRequestHandler({ build, entry }) {
|
|
5
|
+
return (incoming, outgoing, next) => {
|
|
6
|
+
const handle = async () => {
|
|
7
|
+
const url = new URL(incoming.originalUrl ?? incoming.url ?? "/", "http://local");
|
|
8
|
+
const dataRequest = url.pathname === "/_smolter";
|
|
9
|
+
const target = dataRequest ? url.searchParams.get("url") : void 0;
|
|
10
|
+
if (dataRequest && (!target?.startsWith("/") || new URL(target, "http://local").origin !== "http://local")) {
|
|
11
|
+
await send(new Response("Invalid route URL", { status: 400 }), outgoing);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const request = await webRequest(incoming, outgoing, target ?? void 0);
|
|
15
|
+
const response = dataRequest ? await build.handleDataRequest(request) : await build.default(request, entry);
|
|
16
|
+
await send(response, outgoing);
|
|
17
|
+
};
|
|
18
|
+
return handle().catch(next);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { createRequestHandler };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ServerResponse } from "node:http";
|
|
2
|
+
import type { ServerIncomingMessage } from "./handler.ts";
|
|
3
|
+
export declare function webRequest(incoming: ServerIncomingMessage, outgoing: ServerResponse, target?: string): Promise<Request>;
|
|
4
|
+
export declare function send(response: Response, outgoing: ServerResponse): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region .generated/server/http.ts
|
|
2
|
+
async function webRequest(incoming, outgoing, target) {
|
|
3
|
+
const origin = `${incoming.protocol ?? "http"}://${incoming.headers.host}`;
|
|
4
|
+
const headers = new Headers();
|
|
5
|
+
for (const [name, value] of Object.entries(incoming.headers)) if (value) headers.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
outgoing.once("close", () => controller.abort());
|
|
8
|
+
const chunks = [];
|
|
9
|
+
for await (const chunk of incoming) chunks.push(chunk);
|
|
10
|
+
return new Request(new URL(target ?? incoming.originalUrl ?? incoming.url ?? "/", origin), {
|
|
11
|
+
method: incoming.method,
|
|
12
|
+
headers,
|
|
13
|
+
signal: controller.signal,
|
|
14
|
+
...incoming.method !== "GET" && incoming.method !== "HEAD" && { body: new Uint8Array(Buffer.concat(chunks)) }
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async function send(response, outgoing) {
|
|
18
|
+
outgoing.statusCode = response.status;
|
|
19
|
+
response.headers.forEach((value, name) => {
|
|
20
|
+
if (name !== "set-cookie") outgoing.setHeader(name, value);
|
|
21
|
+
});
|
|
22
|
+
const cookies = response.headers.getSetCookie();
|
|
23
|
+
if (cookies.length) outgoing.setHeader("set-cookie", cookies.map((cookie) => process.env.NODE_ENV === "production" ? cookie : cookie.replace(/; Secure/gi, "")));
|
|
24
|
+
outgoing.end(new Uint8Array(await response.arrayBuffer()));
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { send, webRequest };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { safeWrapAsync } from "../core/errors.js";
|
|
2
|
+
import { matchRoutes } from "../core/match.js";
|
|
3
|
+
import { createDataRouter } from "../core/router.js";
|
|
4
|
+
import { serialize } from "../core/serialize.js";
|
|
5
|
+
//#region .generated/server/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* Request-scoped server rendering and HTTP data dispatch.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
/** Create one router per server request, await this, then render the provider. */
|
|
11
|
+
async function loadRouter(routes, input) {
|
|
12
|
+
const url = typeof input === "string" ? input : input.url;
|
|
13
|
+
const wrap = (handler) => {
|
|
14
|
+
if (!handler || typeof input === "string") return handler;
|
|
15
|
+
return (args) => {
|
|
16
|
+
const signal = AbortSignal.any([input.signal, args.signal]);
|
|
17
|
+
const request = new Request(args.request, { signal });
|
|
18
|
+
input.headers.forEach((value, name) => {
|
|
19
|
+
if (!request.headers.has(name)) request.headers.set(name, value);
|
|
20
|
+
});
|
|
21
|
+
return handler({
|
|
22
|
+
...args,
|
|
23
|
+
request,
|
|
24
|
+
signal
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const serverRoutes = routes.map((route) => ({
|
|
29
|
+
...route,
|
|
30
|
+
clientLoader: void 0,
|
|
31
|
+
clientAction: void 0,
|
|
32
|
+
loader: wrap(route.loader),
|
|
33
|
+
action: wrap(route.action)
|
|
34
|
+
}));
|
|
35
|
+
const router = createDataRouter(serverRoutes, { url });
|
|
36
|
+
await router.navigate(url);
|
|
37
|
+
const snapshot = router.getSnapshot();
|
|
38
|
+
if (!snapshot.error && snapshot.data === void 0 && routes.some((route) => route.path === snapshot.path && route.clientLoader && !route.loader)) return createDataRouter(serverRoutes, {
|
|
39
|
+
url,
|
|
40
|
+
hydrationData: {
|
|
41
|
+
...snapshot,
|
|
42
|
+
loaded: false,
|
|
43
|
+
state: "loading"
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return router;
|
|
47
|
+
}
|
|
48
|
+
/** Dispatch an HTTP data request to a registered server loader or action. */
|
|
49
|
+
async function handleRequest(routes, request) {
|
|
50
|
+
const match = matchRoutes(routes, new URL(request.url).pathname);
|
|
51
|
+
const reading = request.method === "GET";
|
|
52
|
+
if (!match) return Response.json({ error: "No route" }, { status: 404 });
|
|
53
|
+
if (!reading && ![
|
|
54
|
+
"POST",
|
|
55
|
+
"PUT",
|
|
56
|
+
"PATCH",
|
|
57
|
+
"DELETE"
|
|
58
|
+
].includes(request.method)) return Response.json({ error: "Method not allowed" }, { status: 405 });
|
|
59
|
+
const handler = reading ? match.route.loader : match.route.action;
|
|
60
|
+
if (!handler) return Response.json({ error: `No ${reading ? "loader" : "action"}` }, { status: 405 });
|
|
61
|
+
const result = await safeWrapAsync(() => handler({
|
|
62
|
+
request,
|
|
63
|
+
params: match.params,
|
|
64
|
+
signal: request.signal
|
|
65
|
+
}));
|
|
66
|
+
const error = result.error ?? (result.data instanceof Error && result.data);
|
|
67
|
+
if (error) return Response.json({ error: error.message }, { status: 500 });
|
|
68
|
+
if (result.data instanceof Response) {
|
|
69
|
+
const location = result.data.headers.get("Location");
|
|
70
|
+
if (location) return new Response(null, { headers: { "X-Smolter-Redirect": location } });
|
|
71
|
+
return result.data;
|
|
72
|
+
}
|
|
73
|
+
return Response.json(result.data ?? null, { headers: { "Cache-Control": "no-store" } });
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { handleRequest, loadRouter, serialize };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
2
|
+
//#region .generated/vite/application/browser-boundary.ts
|
|
3
|
+
var builtins = new Set(builtinModules.flatMap((id) => [id, `node:${id}`]));
|
|
4
|
+
var forbiddenModules = [
|
|
5
|
+
/(?:vite-browser-external|__vite-browser-external)/,
|
|
6
|
+
/\/node_modules\/(?:react|react-dom|react-router|react-router-dom)\//,
|
|
7
|
+
/\/smolter\/dist\/(?:internal\/)?(?:server|vite|files)\//,
|
|
8
|
+
/\/smolter\/packages\/(?:server|vite)\/src\//,
|
|
9
|
+
/\/vite\/dist\/node\//,
|
|
10
|
+
/\/src\/[^?]*\.server\.[cm]?[jt]sx?(?:\?|$)/
|
|
11
|
+
];
|
|
12
|
+
/** Check emitted modules, after tree shaking, rather than import strings. */
|
|
13
|
+
function browserBoundary() {
|
|
14
|
+
return {
|
|
15
|
+
name: "smolter-browser-boundary",
|
|
16
|
+
apply: "build",
|
|
17
|
+
applyToEnvironment: (environment) => environment.name === "client",
|
|
18
|
+
generateBundle(_options, bundle) {
|
|
19
|
+
for (const output of Object.values(bundle)) {
|
|
20
|
+
if (output.type !== "chunk") continue;
|
|
21
|
+
for (const id of [...output.imports, ...output.dynamicImports]) {
|
|
22
|
+
if (!builtins.has(id)) continue;
|
|
23
|
+
this.error(`Node builtin in browser chunk ${output.fileName}: ${id}`);
|
|
24
|
+
}
|
|
25
|
+
for (const id of Object.keys(output.modules)) {
|
|
26
|
+
const normalized = id.replaceAll("\\", "/");
|
|
27
|
+
if (!forbiddenModules.some((pattern) => pattern.test(normalized))) continue;
|
|
28
|
+
this.error(`Unexpected browser module in ${output.fileName}: ${id}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { browserBoundary };
|