@kasperrt/smolter 0.4.3 → 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/prepare-routes.ts +50 -0
- package/.generated/core/presentation.ts +1 -1
- package/.generated/preact/client.tsx +1 -26
- package/.generated/preact/deferred-hydration.ts +1 -98
- package/.generated/preact/index.ts +6 -1
- package/.generated/preact/navigation.tsx +13 -5
- package/.generated/preact/root-data.ts +1 -45
- package/.generated/preact/router.tsx +2 -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 +17 -1
- 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/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 -253
- package/.generated/server/dispatch.ts +87 -0
- package/.generated/server/index.ts +2 -0
- 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 +10 -0
- package/README.md +41 -41
- 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/prepare-routes.d.ts +7 -0
- package/dist/core/prepare-routes.js +28 -0
- package/dist/core/presentation.js +1 -1
- package/dist/preact/client.js +5 -26
- package/dist/preact/deferred-hydration.d.ts +1 -10
- package/dist/preact/document.js +2 -2
- package/dist/preact/index.d.ts +1 -1
- package/dist/preact/index.js +1 -1
- package/dist/preact/navigation-state.js +1 -1
- package/dist/preact/navigation.js +7 -5
- package/dist/preact/root-data.d.ts +1 -8
- package/dist/preact/route-hooks.js +1 -1
- package/dist/preact/router.d.ts +2 -2
- package/dist/preact/router.js +2 -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 +7 -1
- package/dist/react/index.js +6 -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/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 -8
- package/dist/server/application.js +9 -156
- package/dist/server/dispatch.d.ts +5 -0
- package/dist/server/dispatch.js +50 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2 -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 +18 -10
- package/.generated/core/resource-types.ts +0 -48
- package/.generated/core/resources.ts +0 -446
- package/.generated/react/data.ts +0 -29
- package/.generated/react/resources/components.tsx +0 -416
- package/dist/core/resource-types.d.ts +0 -41
- package/dist/core/resources.d.ts +0 -21
- package/dist/core/resources.js +0 -347
- package/dist/react/data.d.ts +0 -9
- package/dist/react/data.js +0 -3
- package/dist/react/link.js +0 -27
- package/dist/react/resources/components.d.ts +0 -76
- package/dist/react/resources/components.js +0 -250
package/.generated/vite/react.ts
CHANGED
|
@@ -17,7 +17,18 @@ export function routerPlugin(options: FileRouterOptions = {}): Plugin {
|
|
|
17
17
|
/** File routing is the default; this alias keeps configuration explicit. */
|
|
18
18
|
export const fileRouter = routerPlugin;
|
|
19
19
|
|
|
20
|
+
export type { ApplicationOptions } from "./application/vite.ts";
|
|
20
21
|
export {
|
|
21
22
|
type DeploymentPluginOptions,
|
|
22
23
|
deploymentPlugin,
|
|
23
24
|
} from "./deployment.ts";
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
type ApplicationOptions,
|
|
28
|
+
applicationPlugins as plugins,
|
|
29
|
+
} from "./application/vite.ts";
|
|
30
|
+
|
|
31
|
+
/** Configure React application SSR, scoped data transport and lazy file routing. */
|
|
32
|
+
export function applicationPlugins(options: ApplicationOptions = {}): Plugin[] {
|
|
33
|
+
return plugins({ ...options, framework: "react" });
|
|
34
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.4
|
|
4
|
+
|
|
5
|
+
- Remove the separate `/react/data` resource router. Both main adapters provide route data, forms, submissions and independent fetchers through `createDataRouter`; migrate ResourceProvider/Resource hooks to RouterProvider and the main data hooks. The old pathless resource store and tuple fetcher API are removed, not aliased to incompatible signatures.
|
|
6
|
+
|
|
7
|
+
- Complete React routing-hook parity: navigation/submission state, URL/search/history helpers, route errors, root data and route-ID data. Add React RouterApplication and match Preact loader/fetcher type inference.
|
|
8
|
+
- Support React application SSR and hydration, document helpers, lazy routes and application Vite middleware. React DOM 19+ powers integrated documents; routing hooks and manual loadRouter SSR retain React 18 support. Share transport, root revalidation, hash history, hydration payloads and server orchestration across renderers.
|
|
9
|
+
- Export renderer-independent dispatchRoute from both server entries. Preserve scoped endpoints, private caching, redirects, cancellation and mutation ownership.
|
|
10
|
+
- Add React link intent prefetching, structured/GET fetcher submissions and real NavLink pending state in both adapters. Report pending destinations under plain providers as well as retained application views.
|
|
11
|
+
- Update compatibility badges, guides, package-isolation checks and bundle tables. Verify React production early hydration, persistent input, root refresh, CSP and abandoned deferred boundaries.
|
|
12
|
+
|
|
3
13
|
## 0.4.3
|
|
4
14
|
|
|
5
15
|
- Prefetch individual guide articles on pointer, keyboard and touch intent. Keep the shared header, sidebar and current article visible during cold guide navigation; show a quiet pending status without replacing the layout. Link the header directly to the canonical Getting started guide.
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# smolter _
|
|
2
2
|
|
|
3
3
|
<!-- bundlejs:start -->
|
|
4
|
-
[](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.4.4%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&bundle)
|
|
5
|
+
[](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.4.4%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&bundle)
|
|
6
6
|
<!-- bundlejs:end -->
|
|
7
7
|
|
|
8
8
|
A typed data router for React and Preact. **Zero runtime dependencies.**
|
|
@@ -18,6 +18,12 @@ and [React Router](https://reactrouter.com/).
|
|
|
18
18
|
|
|
19
19
|
Start with [loaders](https://kasperrt.github.io/smolter/docs/loaders/), [actions](https://kasperrt.github.io/smolter/docs/actions/), [forms and validation](https://kasperrt.github.io/smolter/docs/forms/), or [fetcher reads and mutations](https://kasperrt.github.io/smolter/docs/fetchers/). Each guide connects the handler to an actual route URL and its consuming view.
|
|
20
20
|
|
|
21
|
+
## React and Preact application APIs
|
|
22
|
+
|
|
23
|
+
Both adapters expose navigation, submission, error, root-data and route-data hooks. Both server entries expose `dispatchRoute` and `renderApplication`; both client entries provide `mountApplication`, scoped transport and deployment recovery. Import `applicationPlugins` from the matching renderer's `/vite` entry for lazy file routes and development SSR.
|
|
24
|
+
|
|
25
|
+
Integrated React documents use React DOM 19+ (`react/document`, `react/client`, `react/server`). React 18 remains supported for routing hooks and manual `loadRouter` SSR. Use React's `use(promise)` inside `Suspense` for deferred rendering. Preact uses `Await` with an explicit `fallback` for early hydration. These renderer choices share transport and data ownership, without loading the other framework.
|
|
26
|
+
|
|
21
27
|
## Contents
|
|
22
28
|
|
|
23
29
|
- [Deployment updates](#deployment-updates)
|
|
@@ -456,7 +462,7 @@ and `internalURL`. Move `/client`, `/server`,
|
|
|
456
462
|
`/files`, and `/vite` imports to the corresponding renderer-prefixed path above.
|
|
457
463
|
The bare package root and old top-level subpaths are no longer exported.
|
|
458
464
|
|
|
459
|
-
See the [
|
|
465
|
+
See the [changelog](./CHANGELOG.md) for release history.
|
|
460
466
|
|
|
461
467
|
**Router factory migration:** code that previously used `createRouter` with
|
|
462
468
|
loaders, actions, fetchers or revalidation must now use `createDataRouter`.
|
|
@@ -540,36 +546,30 @@ The adapter has no Express dependency and forwards failures to `next(error)`.
|
|
|
540
546
|
See the [Express guide](https://kasperrt.github.io/smolter/docs/express)
|
|
541
547
|
for a complete server example and streaming and hydration behavior.
|
|
542
548
|
|
|
543
|
-
##
|
|
549
|
+
## Independent data requests
|
|
544
550
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
551
|
+
Both adapters include `useFetcher`, `useSubmit`, `Form` and the loader/action hooks
|
|
552
|
+
in their main entrypoint. Use `createDataRouter` for data routes and `useFetcher`
|
|
553
|
+
for reads or mutations that should not navigate the page. There is no separate
|
|
554
|
+
React resource router or `/react/data` entrypoint.
|
|
548
555
|
|
|
549
|
-
```
|
|
550
|
-
import {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
`ResourceProvider` owns the store lifetime. `Resource` selects a resource by
|
|
567
|
-
`routeId` and params. `useResourceData` reads it; `useRouteLoaderData` reads a
|
|
568
|
-
named resource. `useResourceFetcher` returns `[state, operations]`, with
|
|
569
|
-
independent `load` and `submit` operations. Successful mutations consult the
|
|
570
|
-
resource's `shouldRevalidate` before reloading its data. Last-subscriber cleanup
|
|
571
|
-
aborts pending work; `persistent` keeps a resource until explicitly disposed.
|
|
572
|
-
This optional entry does not contribute to the base React bundle measurement.
|
|
556
|
+
```tsx
|
|
557
|
+
import { useFetcher } from "@kasperrt/smolter/react";
|
|
558
|
+
|
|
559
|
+
function Settings() {
|
|
560
|
+
const settings = useFetcher();
|
|
561
|
+
return (
|
|
562
|
+
<settings.Form method="post" action="/settings">
|
|
563
|
+
<input name="name" aria-label="Profile name" />
|
|
564
|
+
<button disabled={settings.state !== "idle"}>Save</button>
|
|
565
|
+
</settings.Form>
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Use the same imports from `/preact` in a Preact application. Independent
|
|
571
|
+
fetchers share the application router's cancellation, revalidation and deployment
|
|
572
|
+
recovery ownership; they do not require another provider or store.
|
|
573
573
|
|
|
574
574
|
## Size and checks
|
|
575
575
|
|
|
@@ -581,7 +581,6 @@ This optional entry does not contribute to the base React bundle measurement.
|
|
|
581
581
|
| Preact application hydration | `@kasperrt/smolter/preact/client` |
|
|
582
582
|
| Preact document components | `@kasperrt/smolter/preact/document` |
|
|
583
583
|
| Lazy Preact route modules | `@kasperrt/smolter/preact/lazy-route` |
|
|
584
|
-
| Optional React resources | `@kasperrt/smolter/react/data` |
|
|
585
584
|
| Server rendering / Vite integration | Renderer `/server` / `/vite` subpaths; keep out of browser imports |
|
|
586
585
|
|
|
587
586
|
The measurements below describe retained exports, not the cost of an import
|
|
@@ -605,16 +604,17 @@ a claim of identical features.
|
|
|
605
604
|
| --- | ---: |
|
|
606
605
|
| `React Router: declarative routing subset` | 12882 |
|
|
607
606
|
| `React Router: data routing subset` | 29780 |
|
|
608
|
-
| `React: routing-only named exports` |
|
|
607
|
+
| `React: routing-only named exports` | 1419 |
|
|
609
608
|
| `Preact: routing-only named exports` | 1405 |
|
|
610
|
-
| `@kasperrt/smolter/react` |
|
|
611
|
-
| `@kasperrt/smolter/preact` |
|
|
612
|
-
| `@kasperrt/smolter/react +
|
|
613
|
-
| `@kasperrt/smolter/preact + server transport` |
|
|
614
|
-
| `@kasperrt/smolter/
|
|
615
|
-
| `@kasperrt/smolter/
|
|
616
|
-
| `@kasperrt/smolter/
|
|
617
|
-
|
|
|
609
|
+
| `@kasperrt/smolter/react` | 4801 |
|
|
610
|
+
| `@kasperrt/smolter/preact` | 5076 |
|
|
611
|
+
| `@kasperrt/smolter/react + server transport (named client exports)` | 6060 |
|
|
612
|
+
| `@kasperrt/smolter/preact + server transport` | 6345 |
|
|
613
|
+
| `@kasperrt/smolter/react + client + document (application)` | 9124 |
|
|
614
|
+
| `@kasperrt/smolter/react + client + document + lazy-route` | 9401 |
|
|
615
|
+
| `@kasperrt/smolter/preact + client + document (application without lazy routes)` | 9435 |
|
|
616
|
+
| `@kasperrt/smolter/preact + client + document + lazy-route (file-routed application)` | 9721 |
|
|
617
|
+
| `React: common routing/data exports` | 3071 |
|
|
618
618
|
| `Preact: common routing/data exports` | 3046 |
|
|
619
619
|
| `wouter: routing subset (Router, Route, Switch, Link, useParams, useLocation)` | 2854 |
|
|
620
620
|
| `wouter-preact: same routing subset` | 2211 |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region .generated/client/hash-history.ts
|
|
2
|
+
function connectHashHistory(router) {
|
|
3
|
+
const read = router.getSnapshot;
|
|
4
|
+
const navigate = router.navigate;
|
|
5
|
+
router.navigate = async (url, options) => {
|
|
6
|
+
const target = new URL(url, router.getSnapshot().url);
|
|
7
|
+
const hash = `#${target.pathname}${target.search}${target.hash}`;
|
|
8
|
+
history[options?.replace ? "replaceState" : "pushState"](null, "", hash);
|
|
9
|
+
await navigate(url, options);
|
|
10
|
+
};
|
|
11
|
+
const unsubscribe = router.subscribe(() => {
|
|
12
|
+
const target = new URL(read().url);
|
|
13
|
+
const hash = `#${target.pathname}${target.search}${target.hash}`;
|
|
14
|
+
if (location.hash !== hash) history.replaceState(history.state, "", hash);
|
|
15
|
+
});
|
|
16
|
+
const onHashChange = () => void navigate(location.hash.slice(1) || "/");
|
|
17
|
+
window.addEventListener("hashchange", onHashChange);
|
|
18
|
+
const dispose = router.dispose;
|
|
19
|
+
router.dispose = () => {
|
|
20
|
+
unsubscribe();
|
|
21
|
+
window.removeEventListener("hashchange", onHashChange);
|
|
22
|
+
dispose();
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { connectHashHistory };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Settlement = [number, unknown, string | null];
|
|
2
|
+
export interface DeferredWindow extends Window {
|
|
3
|
+
__smolterDeferred?: Settlement[];
|
|
4
|
+
}
|
|
5
|
+
/** Restore promise identity before hydrating Suspense boundaries. */
|
|
6
|
+
export declare function readHydration(json: string, timeout?: number): {
|
|
7
|
+
hydration: any;
|
|
8
|
+
dispose: () => void;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region .generated/
|
|
1
|
+
//#region .generated/client/hydration.ts
|
|
2
2
|
/** Restore promise identity before hydrating Suspense boundaries. */
|
|
3
3
|
function readHydration(json, timeout = 3e4) {
|
|
4
4
|
if (!Number.isInteger(timeout) || timeout < 1 || timeout > 2147483647) throw new RangeError("deferredTimeout must be an integer between 1 and 2147483647 ms");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RevalidationArgs, Router } from "../core/index.ts";
|
|
2
|
+
interface RootModule {
|
|
3
|
+
default?: unknown;
|
|
4
|
+
Layout?: unknown;
|
|
5
|
+
id?: string;
|
|
6
|
+
loaderPath?: string;
|
|
7
|
+
shouldRevalidate?: (args: RevalidationArgs) => boolean;
|
|
8
|
+
}
|
|
9
|
+
export { createStore as createRootStore } from "../core/store.ts";
|
|
10
|
+
export declare function attachRootData(router: Router, initial: unknown, rootModule?: RootModule): {
|
|
11
|
+
getSnapshot: () => unknown;
|
|
12
|
+
subscribe(listener: () => void): () => void;
|
|
13
|
+
update(next: unknown): void;
|
|
14
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createStore } from "../core/store.js";
|
|
2
|
-
//#region .generated/
|
|
2
|
+
//#region .generated/client/root-data.ts
|
|
3
3
|
function attachRootData(router, initial, rootModule) {
|
|
4
4
|
const store = createStore(initial);
|
|
5
5
|
const loaderPath = rootModule?.loaderPath;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { normalizeResult } from "./responses.js";
|
|
2
|
+
//#region .generated/core/prepare-routes.ts
|
|
3
|
+
function prepareRoutes(routes, preserveDeferred = false) {
|
|
4
|
+
return routes.map((route) => {
|
|
5
|
+
const wrapped = {
|
|
6
|
+
...route,
|
|
7
|
+
path: route.routePath ?? route.path,
|
|
8
|
+
shouldRevalidate: (args) => {
|
|
9
|
+
const { currentUrl, nextUrl } = args;
|
|
10
|
+
if (args.reason === "navigation" && currentUrl.pathname === nextUrl.pathname && currentUrl.search === nextUrl.search && currentUrl.hash !== nextUrl.hash) return false;
|
|
11
|
+
return route.shouldRevalidate?.(args) ?? args.defaultShouldRevalidate;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
for (const key of [
|
|
15
|
+
"loader",
|
|
16
|
+
"action",
|
|
17
|
+
"clientLoader",
|
|
18
|
+
"clientAction"
|
|
19
|
+
]) {
|
|
20
|
+
const handler = route[key];
|
|
21
|
+
if (!handler) continue;
|
|
22
|
+
wrapped[key] = (args) => Promise.resolve().then(() => handler(args)).then((value) => normalizeResult(value, !preserveDeferred), (error) => normalizeResult(error, !preserveDeferred));
|
|
23
|
+
}
|
|
24
|
+
return wrapped;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { prepareRoutes };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region .generated/core/presentation.ts
|
|
2
2
|
var pending = /* @__PURE__ */ new WeakMap();
|
|
3
3
|
function pendingLocation(router) {
|
|
4
|
-
const snapshot = pending.get(router)?.();
|
|
4
|
+
const snapshot = pending.get(router)?.() ?? router.getSnapshot();
|
|
5
5
|
return snapshot?.state !== "idle" && snapshot?.url ? new URL(snapshot.url) : void 0;
|
|
6
6
|
}
|
|
7
7
|
function retainRoute(router, commitView) {
|
package/dist/preact/client.js
CHANGED
|
@@ -3,12 +3,14 @@ import { createDataRouter } from "../core/router.js";
|
|
|
3
3
|
import { configureApplication } from "../core/environment.js";
|
|
4
4
|
import { retainRoute } from "../core/presentation.js";
|
|
5
5
|
import { connectNavigation } from "../core/navigation.js";
|
|
6
|
-
import { attachRootData } from "
|
|
7
|
-
import {
|
|
6
|
+
import { attachRootData } from "../client/root-data.js";
|
|
7
|
+
import { prepareRoutes } from "../core/prepare-routes.js";
|
|
8
|
+
import { RouterApplication } from "./router.js";
|
|
8
9
|
import { deserialize } from "../client/deserialize.js";
|
|
9
10
|
import { recoverDeployment, watchDeployment } from "../client/deployment.js";
|
|
10
11
|
import { serverRoute } from "../client/index.js";
|
|
11
|
-
import {
|
|
12
|
+
import { connectHashHistory } from "../client/hash-history.js";
|
|
13
|
+
import { readHydration } from "../client/hydration.js";
|
|
12
14
|
import { flushSync } from "preact/compat";
|
|
13
15
|
import { jsx } from "preact/jsx-runtime";
|
|
14
16
|
import { hydrate, render } from "preact";
|
|
@@ -74,28 +76,5 @@ async function mountApplication(routes, rootModule, options = false) {
|
|
|
74
76
|
if (target) render(/* @__PURE__ */ jsx(RouterApplication, { ...props }), target);
|
|
75
77
|
return router;
|
|
76
78
|
}
|
|
77
|
-
function connectHashHistory(router) {
|
|
78
|
-
const read = router.getSnapshot;
|
|
79
|
-
const navigate = router.navigate;
|
|
80
|
-
router.navigate = async (url, options) => {
|
|
81
|
-
const target = new URL(url, router.getSnapshot().url);
|
|
82
|
-
const hash = `#${target.pathname}${target.search}${target.hash}`;
|
|
83
|
-
history[options?.replace ? "replaceState" : "pushState"](null, "", hash);
|
|
84
|
-
await navigate(url, options);
|
|
85
|
-
};
|
|
86
|
-
const unsubscribe = router.subscribe(() => {
|
|
87
|
-
const target = new URL(read().url);
|
|
88
|
-
const hash = `#${target.pathname}${target.search}${target.hash}`;
|
|
89
|
-
if (location.hash !== hash) history.replaceState(history.state, "", hash);
|
|
90
|
-
});
|
|
91
|
-
const onHashChange = () => void navigate(location.hash.slice(1) || "/");
|
|
92
|
-
window.addEventListener("hashchange", onHashChange);
|
|
93
|
-
const dispose = router.dispose;
|
|
94
|
-
router.dispose = () => {
|
|
95
|
-
unsubscribe();
|
|
96
|
-
window.removeEventListener("hashchange", onHashChange);
|
|
97
|
-
dispose();
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
79
|
//#endregion
|
|
101
80
|
export { configureApplication, deserialize, mountApplication, recoverDeployment, serverRoute, watchDeployment };
|
|
@@ -1,10 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export interface DeferredWindow extends Window {
|
|
3
|
-
__smolterDeferred?: Settlement[];
|
|
4
|
-
}
|
|
5
|
-
/** Restore promise identity before hydrating Suspense boundaries. */
|
|
6
|
-
export declare function readHydration(json: string, timeout?: number): {
|
|
7
|
-
hydration: any;
|
|
8
|
-
dispose: () => void;
|
|
9
|
-
};
|
|
10
|
-
export {};
|
|
1
|
+
export { type DeferredWindow, readHydration } from "../client/hydration.ts";
|
package/dist/preact/document.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { serializeData } from "../core/serialize.js";
|
|
2
|
-
import { useApplication } from "./root-context.js";
|
|
3
|
-
import { useSnapshot } from "./context.js";
|
|
4
2
|
import { applicationEnvironment } from "../core/environment.js";
|
|
5
3
|
import { scrollPositions } from "../core/navigation.js";
|
|
4
|
+
import { useApplication } from "./root-context.js";
|
|
5
|
+
import { useSnapshot } from "./context.js";
|
|
6
6
|
import { useNavigationSnapshot } from "./navigation-state.js";
|
|
7
7
|
import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
package/dist/preact/index.d.ts
CHANGED
|
@@ -18,5 +18,5 @@ export type { NavigateOptions } from "./navigation.tsx";
|
|
|
18
18
|
export * from "./navigation.tsx";
|
|
19
19
|
export { useMatches, useNavigation, useOutletContext, useRootData, useRouteError, useRouteLoaderData, useSubmit, } from "./route-hooks.tsx";
|
|
20
20
|
export * from "./route-types.ts";
|
|
21
|
-
export { Outlet, type OutletProps, RouterApplication } from "./router.tsx";
|
|
21
|
+
export { type ApplicationProps, Outlet, type OutletProps, RouterApplication, } from "./router.tsx";
|
|
22
22
|
export * from "./routes.ts";
|
package/dist/preact/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineRoute } from "../core/match.js";
|
|
2
2
|
import { createRouter } from "../core/navigation-router.js";
|
|
3
3
|
import { createDataRouter } from "../core/router.js";
|
|
4
|
-
import { readSubmission } from "../core/submission.js";
|
|
5
4
|
import { data, isRouteErrorResponse, normalizeResult, redirect, resolveData } from "../core/responses.js";
|
|
5
|
+
import { readSubmission } from "../core/submission.js";
|
|
6
6
|
import { Await } from "./await.js";
|
|
7
7
|
import { RouterProvider, useRouter } from "./context.js";
|
|
8
8
|
import { Form } from "./form.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useSyncExternalStore } from "./runtime.js";
|
|
2
1
|
import { navigationStore } from "../core/navigation.js";
|
|
2
|
+
import { useSyncExternalStore } from "./runtime.js";
|
|
3
3
|
//#region .generated/preact/navigation-state.ts
|
|
4
4
|
function useNavigationSnapshot(router) {
|
|
5
5
|
const store = navigationStore(router);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { matchRoutes } from "../core/match.js";
|
|
2
2
|
import { navigationClick } from "../core/url.js";
|
|
3
|
+
import { handoffDocument, resolveApplicationURL } from "../core/environment.js";
|
|
3
4
|
import { createElement } from "./runtime.js";
|
|
4
5
|
import { useRouter, useSnapshot } from "./context.js";
|
|
5
|
-
import {
|
|
6
|
-
import { useNavigate, useNavigationType, useSearchParams, useURLLocation } from "./route-hooks.js";
|
|
6
|
+
import { useNavigate, useNavigation, useNavigationType, useSearchParams, useURLLocation } from "./route-hooks.js";
|
|
7
7
|
//#region .generated/preact/navigation.tsx
|
|
8
8
|
/** Render an anchor with same-origin router navigation and optional intent prefetching. */
|
|
9
9
|
function Link(options) {
|
|
@@ -44,11 +44,13 @@ function Link(options) {
|
|
|
44
44
|
/** Render a Link with active-route state and aria-current for the matching destination. */
|
|
45
45
|
function NavLink({ href, to = href ?? "/", end, className, children, ...props }) {
|
|
46
46
|
const location = useURLLocation();
|
|
47
|
-
const
|
|
48
|
-
const
|
|
47
|
+
const destination = resolveApplicationURL(new URL(to, location.href));
|
|
48
|
+
const target = destination.pathname;
|
|
49
|
+
const isActive = destination.origin === location.origin && (location.pathname === target || !end && target !== "/" && location.pathname.startsWith(`${target}/`));
|
|
50
|
+
const pending = useNavigation().location;
|
|
49
51
|
const state = {
|
|
50
52
|
isActive,
|
|
51
|
-
isPending:
|
|
53
|
+
isPending: !!pending && pending.origin === destination.origin && (pending.pathname === target || !end && target !== "/" && pending.pathname.startsWith(`${target}/`))
|
|
52
54
|
};
|
|
53
55
|
return createElement(Link, {
|
|
54
56
|
...props,
|
|
@@ -1,8 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { RootModule } from "./route-types.ts";
|
|
3
|
-
export { createStore as createRootStore } from "../core/store.ts";
|
|
4
|
-
export declare function attachRootData(router: Router, initial: unknown, rootModule?: RootModule): {
|
|
5
|
-
getSnapshot: () => unknown;
|
|
6
|
-
subscribe(listener: () => void): () => void;
|
|
7
|
-
update(next: unknown): void;
|
|
8
|
-
};
|
|
1
|
+
export { attachRootData, createRootStore } from "../client/root-data.ts";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { submissionBody, submissionSearch } from "../core/submission.js";
|
|
2
|
+
import { pendingLocation } from "../core/presentation.js";
|
|
2
3
|
import { useContext, useMemo } from "./runtime.js";
|
|
3
4
|
import { OutletContext, useApplication } from "./root-context.js";
|
|
4
5
|
import { useRouter, useSnapshot } from "./context.js";
|
|
5
|
-
import { pendingLocation } from "../core/presentation.js";
|
|
6
6
|
import { useNavigationSnapshot } from "./navigation-state.js";
|
|
7
7
|
import { useCallback } from "preact/hooks";
|
|
8
8
|
//#region .generated/preact/route-hooks.tsx
|
package/dist/preact/router.d.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import type { ReactNode } from "preact/compat";
|
|
3
3
|
import { type ApplicationContext } from "./root-context.ts";
|
|
4
4
|
import { createRootStore } from "./root-data.ts";
|
|
5
|
-
import type { AppRoute } from "./route-types.ts";
|
|
6
5
|
/** Optional children and context shared with descendants through useOutletContext. */
|
|
7
6
|
export interface OutletProps {
|
|
8
7
|
context?: unknown;
|
|
@@ -10,10 +9,11 @@ export interface OutletProps {
|
|
|
10
9
|
}
|
|
11
10
|
/** Render supplied children or the active route, and provide outlet context to descendants. */
|
|
12
11
|
export declare function Outlet({ context, children, }: OutletProps): import("preact").JSX.Element;
|
|
12
|
+
/** Root data, document components and request-scoped router for an application. */
|
|
13
13
|
export interface ApplicationProps extends ApplicationContext {
|
|
14
14
|
document?: boolean;
|
|
15
15
|
rootStore?: ReturnType<typeof createRootStore>;
|
|
16
16
|
}
|
|
17
17
|
/** Render a Preact root document and active route with shared root data and application context. */
|
|
18
18
|
export declare function RouterApplication(props: ApplicationProps): import("preact").JSX.Element;
|
|
19
|
-
export
|
|
19
|
+
export { prepareRoutes } from "../core/prepare-routes.ts";
|
package/dist/preact/router.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createStore } from "../core/store.js";
|
|
2
2
|
import { useContext, useMemo, useSyncExternalStore } from "./runtime.js";
|
|
3
3
|
import { Application, OutletContext } from "./root-context.js";
|
|
4
4
|
import { RouterProvider } from "./context.js";
|
|
5
|
-
import { createStore } from "../core/store.js";
|
|
6
5
|
import { Routes } from "./routes.js";
|
|
6
|
+
import { prepareRoutes } from "../core/prepare-routes.js";
|
|
7
7
|
import { jsx } from "preact/jsx-runtime";
|
|
8
8
|
//#region .generated/preact/router.tsx
|
|
9
9
|
/** Render supplied children or the active route, and provide outlet context to descendants. */
|
|
@@ -37,29 +37,5 @@ function RouterApplication(props) {
|
|
|
37
37
|
})
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
function prepareRoutes(routes, preserveDeferred = false) {
|
|
41
|
-
return routes.map((route) => {
|
|
42
|
-
const wrapped = {
|
|
43
|
-
...route,
|
|
44
|
-
path: route.routePath ?? route.path,
|
|
45
|
-
shouldRevalidate: (args) => {
|
|
46
|
-
const { currentUrl, nextUrl } = args;
|
|
47
|
-
if (args.reason === "navigation" && currentUrl.pathname === nextUrl.pathname && currentUrl.search === nextUrl.search && currentUrl.hash !== nextUrl.hash) return false;
|
|
48
|
-
return route.shouldRevalidate?.(args) ?? args.defaultShouldRevalidate;
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
for (const key of [
|
|
52
|
-
"loader",
|
|
53
|
-
"action",
|
|
54
|
-
"clientLoader",
|
|
55
|
-
"clientAction"
|
|
56
|
-
]) {
|
|
57
|
-
const handler = route[key];
|
|
58
|
-
if (!handler) continue;
|
|
59
|
-
wrapped[key] = (args) => Promise.resolve().then(() => handler(args)).then((value) => normalizeResult(value, !preserveDeferred), (error) => normalizeResult(error, !preserveDeferred));
|
|
60
|
-
}
|
|
61
|
-
return wrapped;
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
40
|
//#endregion
|
|
65
41
|
export { Outlet, RouterApplication, prepareRoutes };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React document hydration, browser history and deployment recovery.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** @jsxImportSource react */
|
|
7
|
+
import { type DeploymentOptions } from "../client/index.ts";
|
|
8
|
+
import { type Router } from "../core/index.ts";
|
|
9
|
+
import type { AppRoute, RootModule } from "./route-types.ts";
|
|
10
|
+
export * from "../client/index.ts";
|
|
11
|
+
export { configureApplication } from "../core/environment.ts";
|
|
12
|
+
/** Browser hydration settings for hash history, deployment recovery and deferred-data timeout. */
|
|
13
|
+
export interface MountApplicationOptions {
|
|
14
|
+
hash?: boolean;
|
|
15
|
+
deployment?: DeploymentOptions | null;
|
|
16
|
+
/** Total initial deferred-data budget in integer milliseconds; not an inactivity timeout. Defaults to 30,000. */
|
|
17
|
+
deferredTimeout?: number;
|
|
18
|
+
}
|
|
19
|
+
/** Create the browser application router and hydrate the document, installing recovery before initial lazy loading. */
|
|
20
|
+
export declare function mountApplication(routes: AppRoute[], rootModule?: RootModule, options?: boolean | MountApplicationOptions): Promise<Router>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { matchRoutes } from "../core/match.js";
|
|
2
|
+
import { createDataRouter } from "../core/router.js";
|
|
3
|
+
import { configureApplication } from "../core/environment.js";
|
|
4
|
+
import { retainRoute } from "../core/presentation.js";
|
|
5
|
+
import { connectNavigation } from "../core/navigation.js";
|
|
6
|
+
import { RouterApplication } from "./router.js";
|
|
7
|
+
import { attachRootData } from "../client/root-data.js";
|
|
8
|
+
import { prepareRoutes } from "../core/prepare-routes.js";
|
|
9
|
+
import { deserialize } from "../client/deserialize.js";
|
|
10
|
+
import { recoverDeployment, watchDeployment } from "../client/deployment.js";
|
|
11
|
+
import { serverRoute } from "../client/index.js";
|
|
12
|
+
import { connectHashHistory } from "../client/hash-history.js";
|
|
13
|
+
import { readHydration } from "../client/hydration.js";
|
|
14
|
+
import { flushSync } from "react-dom";
|
|
15
|
+
import { createRoot, hydrateRoot } from "react-dom/client";
|
|
16
|
+
import { jsx } from "react/jsx-runtime";
|
|
17
|
+
//#region .generated/react/client.tsx
|
|
18
|
+
/**
|
|
19
|
+
* React document hydration, browser history and deployment recovery.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
/** @jsxImportSource react */
|
|
24
|
+
/** Create the browser application router and hydrate the document, installing recovery before initial lazy loading. */
|
|
25
|
+
async function mountApplication(routes, rootModule, options = false) {
|
|
26
|
+
const { hash = false, deployment = null, deferredTimeout } = typeof options === "boolean" ? { hash: options } : options;
|
|
27
|
+
const initialURL = new URL(hash ? location.hash.slice(1) || "/" : location.href, location.origin);
|
|
28
|
+
await (matchRoutes(routes, initialURL.pathname)?.route)?.preload?.().catch(async (error) => {
|
|
29
|
+
await recoverDeployment(deployment, initialURL);
|
|
30
|
+
throw error;
|
|
31
|
+
});
|
|
32
|
+
routes = prepareRoutes(routes);
|
|
33
|
+
const element = document.getElementById("smolter-data");
|
|
34
|
+
const initial = element?.textContent ? readHydration(element.textContent, deferredTimeout) : void 0;
|
|
35
|
+
const hydration = initial?.hydration;
|
|
36
|
+
if (hydration?.snapshot.error) hydration.snapshot.error = Object.assign(new Error(hydration.snapshot.error.message), hydration.snapshot.error);
|
|
37
|
+
const router = createDataRouter(routes, {
|
|
38
|
+
browser: !hash,
|
|
39
|
+
url: hash ? new URL(location.hash.slice(1) || "/", location.origin).href : location.href,
|
|
40
|
+
...hydration && { hydrationData: hydration.snapshot }
|
|
41
|
+
});
|
|
42
|
+
const dispose = router.dispose;
|
|
43
|
+
router.dispose = () => {
|
|
44
|
+
initial?.dispose();
|
|
45
|
+
dispose();
|
|
46
|
+
};
|
|
47
|
+
watchDeployment(router, deployment);
|
|
48
|
+
if (hash) connectHashHistory(router);
|
|
49
|
+
retainRoute(router, flushSync);
|
|
50
|
+
connectNavigation(router);
|
|
51
|
+
const rootStore = attachRootData(router, hydration?.root, rootModule);
|
|
52
|
+
const props = {
|
|
53
|
+
routes,
|
|
54
|
+
router,
|
|
55
|
+
root: hydration?.root,
|
|
56
|
+
rootModule,
|
|
57
|
+
rootStore,
|
|
58
|
+
rootError: hydration?.rootError,
|
|
59
|
+
initialData: element?.textContent ?? void 0
|
|
60
|
+
};
|
|
61
|
+
if (hydration) {
|
|
62
|
+
const mounted = hydrateRoot(document, /* @__PURE__ */ jsx(RouterApplication, {
|
|
63
|
+
...props,
|
|
64
|
+
document: true
|
|
65
|
+
}));
|
|
66
|
+
const cleanup = router.dispose;
|
|
67
|
+
router.dispose = () => {
|
|
68
|
+
mounted.unmount();
|
|
69
|
+
cleanup();
|
|
70
|
+
};
|
|
71
|
+
return router;
|
|
72
|
+
}
|
|
73
|
+
const target = document.getElementById("root");
|
|
74
|
+
if (target) {
|
|
75
|
+
const mounted = createRoot(target);
|
|
76
|
+
mounted.render(/* @__PURE__ */ jsx(RouterApplication, { ...props }));
|
|
77
|
+
const cleanup = router.dispose;
|
|
78
|
+
router.dispose = () => {
|
|
79
|
+
mounted.unmount();
|
|
80
|
+
cleanup();
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return router;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { configureApplication, deserialize, mountApplication, recoverDeployment, serverRoute, watchDeployment };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React document metadata, resource links and hydration scripts.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** @jsxImportSource react */
|
|
7
|
+
import type { ComponentProps } from "react";
|
|
8
|
+
/** Render root or active-route metadata in the document head. */
|
|
9
|
+
export declare function Meta(): import("react").ReactElement;
|
|
10
|
+
/** Render stylesheet and other link descriptors from the application root. */
|
|
11
|
+
export declare function Links(): import("react").ReactElement;
|
|
12
|
+
/** Nonce and browser entry options for the document hydration scripts. */
|
|
13
|
+
export interface ScriptProps {
|
|
14
|
+
nonce?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Render the hydration snapshot and browser entry script with an optional CSP nonce. */
|
|
17
|
+
export declare function Scripts({ nonce }: ScriptProps): import("react").ReactElement;
|
|
18
|
+
/** Reset scroll after navigation unless the navigation requests preservation. */
|
|
19
|
+
export declare function ScrollRestoration(_props: ScriptProps): null;
|
|
20
|
+
/** HTML attributes for server markup and client document reconciliation. */
|
|
21
|
+
export interface DocumentProps extends ComponentProps<"html"> {
|
|
22
|
+
"data-debug"?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** Render the server document and preserve client-managed theme classes on hydration. */
|
|
25
|
+
export declare function Document(props: DocumentProps): import("react").ReactElement;
|