@nifrajs/web-react 2.1.0 → 2.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/README.md +3 -3
- package/dist/await.d.ts +2 -2
- package/dist/await.js +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +13 -6
- package/dist/client.js.map +1 -1
- package/dist/compose.d.ts +4 -3
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +12 -5
- package/dist/compose.js.map +1 -1
- package/dist/content.d.ts +2 -2
- package/dist/content.js +2 -2
- package/dist/error.d.ts +1 -1
- package/dist/error.js +3 -3
- package/dist/fetcher.d.ts +2 -2
- package/dist/fetcher.js +4 -4
- package/dist/fn.d.ts +29 -0
- package/dist/fn.d.ts.map +1 -0
- package/dist/fn.js +31 -0
- package/dist/fn.js.map +1 -0
- package/dist/i18n.d.ts +1 -1
- package/dist/i18n.js +1 -1
- package/dist/image.d.ts +3 -3
- package/dist/image.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/island.d.ts +3 -3
- package/dist/island.js +2 -2
- package/dist/query.d.ts +8 -8
- package/dist/query.js +5 -5
- package/dist/react-dom-server.d.ts +28 -7
- package/dist/react-dom-server.d.ts.map +1 -1
- package/dist/react-dom-server.js +69 -8
- package/dist/react-dom-server.js.map +1 -1
- package/dist/router.d.ts +66 -18
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +80 -15
- package/dist/router.js.map +1 -1
- package/package.json +11 -6
- package/src/await.ts +2 -2
- package/src/client.ts +13 -7
- package/src/compose.ts +17 -5
- package/src/content.ts +2 -2
- package/src/error.ts +3 -3
- package/src/fetcher.ts +4 -4
- package/src/fn.ts +43 -0
- package/src/i18n.ts +1 -1
- package/src/image.ts +3 -3
- package/src/index.ts +7 -7
- package/src/island.ts +3 -3
- package/src/query.ts +9 -9
- package/src/react-dom-server.ts +83 -10
- package/src/router.ts +114 -24
package/dist/query.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@nifrajs/web-react/query`
|
|
2
|
+
* `@nifrajs/web-react/query` - React bindings for the keyed query-cache + mutations: `useQuery`,
|
|
3
3
|
* `useInfiniteQuery`, `useMutation`, `useQueryClient`, `QueryClientProvider`, and the SSR
|
|
4
4
|
* `HydrationBoundary` (+ `dehydrate` re-exported). A drop-in for the TanStack Query surface,
|
|
5
5
|
* backed by `@nifrajs/web`'s agnostic engine. Imports only `react` + `@nifrajs/web` (never `react-dom/*`),
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Resolution order for the client a hook uses: a `QueryClientProvider` in the tree (required for SSR
|
|
9
9
|
* dehydrate/hydrate and for tests), else a lazily-created **client-side** module singleton (the simple
|
|
10
|
-
* client-only app
|
|
10
|
+
* client-only app - the `typeof window` guard means the server has none, so hooks render idle/pending
|
|
11
11
|
* and the first client render matches for a clean hydration).
|
|
12
12
|
*/
|
|
13
13
|
import { createMutation, createQueryClient, } from "@nifrajs/web";
|
|
@@ -35,7 +35,7 @@ const NOOP_CLIENT = {
|
|
|
35
35
|
hydrate: () => { },
|
|
36
36
|
};
|
|
37
37
|
const QueryClientContext = createContext(undefined);
|
|
38
|
-
/** Provide a {@link QueryClient} to the tree
|
|
38
|
+
/** Provide a {@link QueryClient} to the tree - required for SSR dehydrate/hydrate and for tests; a
|
|
39
39
|
* client-only app can omit it and rely on the built-in client-side singleton. */
|
|
40
40
|
export function QueryClientProvider(props) {
|
|
41
41
|
return createElement(QueryClientContext.Provider, { value: props.client }, props.children);
|
|
@@ -91,7 +91,7 @@ export function useQuery(key, fn, options) {
|
|
|
91
91
|
// A disabled query still binds a handle (so it re-renders when re-enabled), but never fetches.
|
|
92
92
|
const handle = client.query(key, fn, queryOpts);
|
|
93
93
|
// Server snapshot = the handle's own snapshot: without a provider the client is the NOOP one (idle),
|
|
94
|
-
// but WITH a hydrated provider client it returns the server-seeded data
|
|
94
|
+
// but WITH a hydrated provider client it returns the server-seeded data - so a HydrationBoundary-fed
|
|
95
95
|
// query renders its data during SSR and the first client render matches (no loading flash, no drift).
|
|
96
96
|
const state = useSyncExternalStore(handle.subscribe, handle.snapshot, handle.snapshot);
|
|
97
97
|
useEffect(() => {
|
|
@@ -159,7 +159,7 @@ export function useInfiniteQuery(key, fn, options) {
|
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
|
-
* Seed the context's {@link QueryClient} from a server {@link dehydrate} snapshot
|
|
162
|
+
* Seed the context's {@link QueryClient} from a server {@link dehydrate} snapshot - the SSR data bridge.
|
|
163
163
|
* Wrap the app (inside `QueryClientProvider`) so server-prefetched queries are in the cache before the
|
|
164
164
|
* first client render, avoiding a loading flash. Hydration runs during render (idempotent, fresher-wins),
|
|
165
165
|
* so the data is available synchronously to child `useQuery`s.
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Resolve `react-dom/server` so it shares ONE React core with the route components
|
|
2
|
+
* Resolve `react-dom/server` so it shares ONE React core with the route components - the fix for the
|
|
3
3
|
* dual-React SSR crash (`resolveDispatcher().useState` is null / "Invalid hook call: mismatching versions
|
|
4
4
|
* of React and the renderer").
|
|
5
5
|
*
|
|
6
6
|
* WHY this exists: under Bun **runtime** SSR (`nifra dev`, `nifra start`, `nifra_render`, all in-process),
|
|
7
7
|
* a static `import "react-dom/server"` in this adapter is resolved by Bun from THIS package's own
|
|
8
|
-
* (symlinked) node_modules
|
|
8
|
+
* (symlinked) node_modules - which can be a DIFFERENT physical `react` than the one the consumer app's
|
|
9
9
|
* route components import. Two React cores → two hook dispatchers → the renderer's dispatcher is the wrong
|
|
10
10
|
* (or null) one → the crash. Resolving `react-dom/server` from the consumer **app root** instead makes
|
|
11
11
|
* react-dom pull the app's `react` transitively, matching the components' `react`: a single core, a single
|
|
12
|
-
* dispatcher, no crash. (Empirically verified against a two-copy install fixture
|
|
12
|
+
* dispatcher, no crash. (Empirically verified against a two-copy install fixture - see
|
|
13
13
|
* test/dual-react.test.ts.)
|
|
14
14
|
*
|
|
15
|
-
* Guarding precisely so the BUILT path is untouched: a bundle is detected two ways
|
|
15
|
+
* Guarding precisely so the BUILT path is untouched: a bundle is detected two ways - `Bun.resolveSync` is
|
|
16
16
|
* unavailable (Node / Deno / Cloudflare / Vercel), OR `buildServer` tagged the output with
|
|
17
17
|
* `process.env.NIFRA_SSR_BUNDLED` (a `target:"bun"` bundle DOES keep `Bun.resolveSync` under the Bun
|
|
18
18
|
* runtime, so the resolver test alone can't see it). In either case the build already bundled+deduped a
|
|
19
19
|
* single `react-dom` (buildServer's `reactDedupePlugin` pins `react`), so the static `import` is correct.
|
|
20
|
-
* Re-rooting a bundle would instead re-import a SECOND react-dom from disk
|
|
20
|
+
* Re-rooting a bundle would instead re-import a SECOND react-dom from disk - a second React core whose hook
|
|
21
21
|
* dispatcher is null for the bundled components → the `…H.useRef of null` SSR crash. The app-root re-root
|
|
22
22
|
* therefore runs ONLY under an UNBUNDLED Bun runtime (nifra dev/start, nifra_render), where the duplication
|
|
23
23
|
* can occur, `Bun.resolveSync` exists, and no bundle marker is present.
|
|
24
24
|
*/
|
|
25
25
|
import type { ReactNode } from "react";
|
|
26
26
|
/** The slice of `react-dom/server` this adapter uses. Typed locally so the dynamic import (which Bun
|
|
27
|
-
* resolves to an absolute path string) stays strict
|
|
27
|
+
* resolves to an absolute path string) stays strict - no `any` crosses the boundary. */
|
|
28
28
|
export interface ReactDomServer {
|
|
29
29
|
renderToString(node: ReactNode): string;
|
|
30
30
|
renderToReadableStream(node: ReactNode): Promise<ReadableStream<Uint8Array>>;
|
|
@@ -37,6 +37,27 @@ export interface ReactDomServer {
|
|
|
37
37
|
export declare function reactDomServer(): Promise<ReactDomServer>;
|
|
38
38
|
/** A `Bun.resolveSync`-shaped function (specifier, from) → absolute path. */
|
|
39
39
|
type ResolveSync = (specifier: string, from: string) => string;
|
|
40
|
+
/**
|
|
41
|
+
* The dual-React crash, caught on the RESOLVED graph instead of after it detonates.
|
|
42
|
+
*
|
|
43
|
+
* The re-root above makes `react-dom/server` load from the app root, which is right almost always. What it
|
|
44
|
+
* cannot guarantee is that react-dom then pulls in the SAME physical `react` the route components import -
|
|
45
|
+
* a nested `react` under react-dom, or a components tree resolving react elsewhere, still yields two cores.
|
|
46
|
+
* Two cores is two hook dispatchers, and the renderer sees a null (or foreign) one: the SSR throws
|
|
47
|
+
* `resolveDispatcher().useState is null` from deep inside react-dom-server, naming a React internal and
|
|
48
|
+
* nothing about the two directories that actually caused it. That is hours of inference from a message
|
|
49
|
+
* that points nowhere useful.
|
|
50
|
+
*
|
|
51
|
+
* `nifra doctor` checks what is INSTALLED; this checks what SSR actually RESOLVED, which is the only thing
|
|
52
|
+
* that can catch a duplicate the two dev pipelines introduce (Bun for SSR, Vite for the client) rather
|
|
53
|
+
* than the install. It compares the realpath of the `react` react-dom will render with against the
|
|
54
|
+
* `react` the components import, and if they differ throws with BOTH paths - turning a five-hour hunt into
|
|
55
|
+
* a five-second read. Silent when they agree, which is the single-copy common case.
|
|
56
|
+
*
|
|
57
|
+
* Never manufactures a failure: if either side cannot be resolved it returns, because a resolver that
|
|
58
|
+
* cannot answer is not evidence of a duplicate. Exported for direct unit testing.
|
|
59
|
+
*/
|
|
60
|
+
export declare function assertSingleReactCore(reactDomServerPath: string, resolve: ResolveSync, realpath?: (path: string) => string): void;
|
|
40
61
|
/**
|
|
41
62
|
* Load `react-dom/server`, preferring the app-root-resolved copy under the Bun runtime. Exported for unit
|
|
42
63
|
* tests: `resolve` defaults to the ambient `Bun.resolveSync` (undefined on non-Bun hosts), and a test can
|
|
@@ -45,7 +66,7 @@ type ResolveSync = (specifier: string, from: string) => string;
|
|
|
45
66
|
*/
|
|
46
67
|
export declare function loadReactDomServer(resolve?: ResolveSync | undefined): Promise<ReactDomServer>;
|
|
47
68
|
/**
|
|
48
|
-
* The resolver `loadReactDomServer` uses by default, or `undefined` when re-rooting must NOT happen
|
|
69
|
+
* The resolver `loadReactDomServer` uses by default, or `undefined` when re-rooting must NOT happen - a
|
|
49
70
|
* non-Bun host (no `Bun.resolveSync`; the static import is the only path) OR a BUNDLED SSR output.
|
|
50
71
|
* `buildServer` defines `process.env.NIFRA_SSR_BUNDLED` to `"1"` in every bundle, where react-dom is
|
|
51
72
|
* already inlined + deduped to the components' React (reactDedupePlugin); re-rooting there would re-import
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-dom-server.d.ts","sourceRoot":"","sources":["../src/react-dom-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;wFACwF;AACxF,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;IACvC,sBAAsB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;CAC7E;
|
|
1
|
+
{"version":3,"file":"react-dom-server.d.ts","sourceRoot":"","sources":["../src/react-dom-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;wFACwF;AACxF,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;IACvC,sBAAsB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;CAC7E;AA6CD;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,CAIxD;AAED,6EAA6E;AAC7E,KAAK,WAAW,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;AAE9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,WAAW,EACpB,QAAQ,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAwB,GACnD,IAAI,CAsBN;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,WAAW,GAAG,SAA2B,GACjD,OAAO,CAAC,cAAc,CAAC,CAyBzB;AAED;;;;;;;;kEAQkE;AAClE,wBAAgB,aAAa,IAAI,WAAW,GAAG,SAAS,CAKvD"}
|
package/dist/react-dom-server.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Resolve `react-dom/server` so it shares ONE React core with the route components
|
|
2
|
+
* Resolve `react-dom/server` so it shares ONE React core with the route components - the fix for the
|
|
3
3
|
* dual-React SSR crash (`resolveDispatcher().useState` is null / "Invalid hook call: mismatching versions
|
|
4
4
|
* of React and the renderer").
|
|
5
5
|
*
|
|
6
6
|
* WHY this exists: under Bun **runtime** SSR (`nifra dev`, `nifra start`, `nifra_render`, all in-process),
|
|
7
7
|
* a static `import "react-dom/server"` in this adapter is resolved by Bun from THIS package's own
|
|
8
|
-
* (symlinked) node_modules
|
|
8
|
+
* (symlinked) node_modules - which can be a DIFFERENT physical `react` than the one the consumer app's
|
|
9
9
|
* route components import. Two React cores → two hook dispatchers → the renderer's dispatcher is the wrong
|
|
10
10
|
* (or null) one → the crash. Resolving `react-dom/server` from the consumer **app root** instead makes
|
|
11
11
|
* react-dom pull the app's `react` transitively, matching the components' `react`: a single core, a single
|
|
12
|
-
* dispatcher, no crash. (Empirically verified against a two-copy install fixture
|
|
12
|
+
* dispatcher, no crash. (Empirically verified against a two-copy install fixture - see
|
|
13
13
|
* test/dual-react.test.ts.)
|
|
14
14
|
*
|
|
15
|
-
* Guarding precisely so the BUILT path is untouched: a bundle is detected two ways
|
|
15
|
+
* Guarding precisely so the BUILT path is untouched: a bundle is detected two ways - `Bun.resolveSync` is
|
|
16
16
|
* unavailable (Node / Deno / Cloudflare / Vercel), OR `buildServer` tagged the output with
|
|
17
17
|
* `process.env.NIFRA_SSR_BUNDLED` (a `target:"bun"` bundle DOES keep `Bun.resolveSync` under the Bun
|
|
18
18
|
* runtime, so the resolver test alone can't see it). In either case the build already bundled+deduped a
|
|
19
19
|
* single `react-dom` (buildServer's `reactDedupePlugin` pins `react`), so the static `import` is correct.
|
|
20
|
-
* Re-rooting a bundle would instead re-import a SECOND react-dom from disk
|
|
20
|
+
* Re-rooting a bundle would instead re-import a SECOND react-dom from disk - a second React core whose hook
|
|
21
21
|
* dispatcher is null for the bundled components → the `…H.useRef of null` SSR crash. The app-root re-root
|
|
22
22
|
* therefore runs ONLY under an UNBUNDLED Bun runtime (nifra dev/start, nifra_render), where the duplication
|
|
23
23
|
* can occur, `Bun.resolveSync` exists, and no bundle marker is present.
|
|
@@ -30,6 +30,15 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
30
30
|
}
|
|
31
31
|
return path;
|
|
32
32
|
};
|
|
33
|
+
function dirnamePortable(path) {
|
|
34
|
+
const slash = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
35
|
+
return slash <= 0 ? "." : path.slice(0, slash);
|
|
36
|
+
}
|
|
37
|
+
function runtimeRealpath(path) {
|
|
38
|
+
const runtimeProcess = globalThis.process;
|
|
39
|
+
const fs = runtimeProcess?.getBuiltinModule?.("node:fs");
|
|
40
|
+
return fs?.realpathSync?.(path) ?? path;
|
|
41
|
+
}
|
|
33
42
|
const bunResolver = globalThis.Bun;
|
|
34
43
|
const hasBunResolveSync = typeof bunResolver?.resolveSync === "function";
|
|
35
44
|
// Resolve once and cache: the module identity is stable for the process lifetime, and re-resolving per
|
|
@@ -57,6 +66,49 @@ export function reactDomServer() {
|
|
|
57
66
|
cached = loadReactDomServer();
|
|
58
67
|
return cached;
|
|
59
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* The dual-React crash, caught on the RESOLVED graph instead of after it detonates.
|
|
71
|
+
*
|
|
72
|
+
* The re-root above makes `react-dom/server` load from the app root, which is right almost always. What it
|
|
73
|
+
* cannot guarantee is that react-dom then pulls in the SAME physical `react` the route components import -
|
|
74
|
+
* a nested `react` under react-dom, or a components tree resolving react elsewhere, still yields two cores.
|
|
75
|
+
* Two cores is two hook dispatchers, and the renderer sees a null (or foreign) one: the SSR throws
|
|
76
|
+
* `resolveDispatcher().useState is null` from deep inside react-dom-server, naming a React internal and
|
|
77
|
+
* nothing about the two directories that actually caused it. That is hours of inference from a message
|
|
78
|
+
* that points nowhere useful.
|
|
79
|
+
*
|
|
80
|
+
* `nifra doctor` checks what is INSTALLED; this checks what SSR actually RESOLVED, which is the only thing
|
|
81
|
+
* that can catch a duplicate the two dev pipelines introduce (Bun for SSR, Vite for the client) rather
|
|
82
|
+
* than the install. It compares the realpath of the `react` react-dom will render with against the
|
|
83
|
+
* `react` the components import, and if they differ throws with BOTH paths - turning a five-hour hunt into
|
|
84
|
+
* a five-second read. Silent when they agree, which is the single-copy common case.
|
|
85
|
+
*
|
|
86
|
+
* Never manufactures a failure: if either side cannot be resolved it returns, because a resolver that
|
|
87
|
+
* cannot answer is not evidence of a duplicate. Exported for direct unit testing.
|
|
88
|
+
*/
|
|
89
|
+
export function assertSingleReactCore(reactDomServerPath, resolve, realpath = runtimeRealpath) {
|
|
90
|
+
let rendererReact;
|
|
91
|
+
let componentsReact;
|
|
92
|
+
try {
|
|
93
|
+
// The `react` react-dom/server itself resolves - the core whose dispatcher the renderer sets.
|
|
94
|
+
rendererReact = realpath(resolve("react", dirnamePortable(reactDomServerPath)));
|
|
95
|
+
// The `react` the app's route components import - the core they call hooks on.
|
|
96
|
+
componentsReact = realpath(resolve("react", appRoot()));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (rendererReact === componentsReact)
|
|
102
|
+
return;
|
|
103
|
+
throw new Error("[nifra/web-react] two copies of React reached SSR, so hooks render against a null dispatcher " +
|
|
104
|
+
"(the `resolveDispatcher().useState is null` crash). react-dom renders with a DIFFERENT React " +
|
|
105
|
+
"than your components import:\n" +
|
|
106
|
+
` react-dom's react: ${rendererReact}\n` +
|
|
107
|
+
` components' react: ${componentsReact}\n` +
|
|
108
|
+
"Module identity is path-based, so two copies fail even at the same version. Dedupe react to one " +
|
|
109
|
+
"physical copy - usually a single hoisted install at the workspace root (`nifra doctor` locates " +
|
|
110
|
+
"the duplicate). A Vite `resolve.dedupe`/alias fixes only the client bundle, not this SSR path.");
|
|
111
|
+
}
|
|
60
112
|
/**
|
|
61
113
|
* Load `react-dom/server`, preferring the app-root-resolved copy under the Bun runtime. Exported for unit
|
|
62
114
|
* tests: `resolve` defaults to the ambient `Bun.resolveSync` (undefined on non-Bun hosts), and a test can
|
|
@@ -68,12 +120,21 @@ export async function loadReactDomServer(resolve = bunResolverFn()) {
|
|
|
68
120
|
// Bun runtime SSR: re-root to the app's copy so react-dom shares the components' React. `react-dom`
|
|
69
121
|
// is a PEER dependency of this adapter, so a correct install puts it at the app root and this
|
|
70
122
|
// resolves. If it somehow doesn't (an unusual nested layout), fall through to the bundled specifier
|
|
71
|
-
// rather than crashing
|
|
123
|
+
// rather than crashing - degraded (possible duplicate) but never a hard failure.
|
|
72
124
|
try {
|
|
73
125
|
const resolved = resolve("react-dom/server", appRoot());
|
|
126
|
+
// Before returning the module, verify the react-dom we just re-rooted shares the components' React.
|
|
127
|
+
// The re-root fixes the common case; this catches the residual duplicate loudly, with both paths,
|
|
128
|
+
// instead of leaving it to throw a dispatcher-null error on the first hook render. Runs once (this
|
|
129
|
+
// whole function is cached), dev/unbundled Bun only (the branch `resolve` gates).
|
|
130
|
+
assertSingleReactCore(resolved, resolve);
|
|
74
131
|
return (await import(__rewriteRelativeImportExtension(resolved)));
|
|
75
132
|
}
|
|
76
|
-
catch {
|
|
133
|
+
catch (err) {
|
|
134
|
+
// A genuine duplicate is surfaced (re-thrown with both paths); any other failure (react-dom not at
|
|
135
|
+
// the app root, an odd nested layout) falls through to the bundled specifier rather than crashing.
|
|
136
|
+
if (err instanceof Error && err.message.includes("two copies of React"))
|
|
137
|
+
throw err;
|
|
77
138
|
// App-root resolution failed; use the specifier resolved from this module's own location below.
|
|
78
139
|
}
|
|
79
140
|
}
|
|
@@ -83,7 +144,7 @@ export async function loadReactDomServer(resolve = bunResolverFn()) {
|
|
|
83
144
|
return (await import("react-dom/server"));
|
|
84
145
|
}
|
|
85
146
|
/**
|
|
86
|
-
* The resolver `loadReactDomServer` uses by default, or `undefined` when re-rooting must NOT happen
|
|
147
|
+
* The resolver `loadReactDomServer` uses by default, or `undefined` when re-rooting must NOT happen - a
|
|
87
148
|
* non-Bun host (no `Bun.resolveSync`; the static import is the only path) OR a BUNDLED SSR output.
|
|
88
149
|
* `buildServer` defines `process.env.NIFRA_SSR_BUNDLED` to `"1"` in every bundle, where react-dom is
|
|
89
150
|
* already inlined + deduped to the components' React (reactDedupePlugin); re-rooting there would re-import
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-dom-server.js","sourceRoot":"","sources":["../src/react-dom-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"react-dom-server.js","sourceRoot":"","sources":["../src/react-dom-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;;;;;;;AAeH,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;IACrE,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;AAChD,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,cAAc,GAAI,UAA2C,CAAC,OAAO,CAAA;IAC3E,MAAM,EAAE,GAAG,cAAc,EAAE,gBAAgB,EAAE,CAAC,SAAS,CAE1C,CAAA;IACb,OAAO,EAAE,EAAE,YAAY,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AACzC,CAAC;AASD,MAAM,WAAW,GAAI,UAA6C,CAAC,GAAG,CAAA;AACtE,MAAM,iBAAiB,GAAG,OAAO,WAAW,EAAE,WAAW,KAAK,UAAU,CAAA;AAExE,uGAAuG;AACvG,qGAAqG;AACrG,8EAA8E;AAC9E,IAAI,MAA2C,CAAA;AAE/C;;;;;;GAMG;AACH,SAAS,OAAO;IACd,OAAO,OAAO,CAAC,GAAG,EAAE,CAAA;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IACvC,MAAM,GAAG,kBAAkB,EAAE,CAAA;IAC7B,OAAO,MAAM,CAAA;AACf,CAAC;AAKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,qBAAqB,CACnC,kBAA0B,EAC1B,OAAoB,EACpB,WAAqC,eAAe;IAEpD,IAAI,aAAqB,CAAA;IACzB,IAAI,eAAuB,CAAA;IAC3B,IAAI,CAAC;QACH,8FAA8F;QAC9F,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAA;QAC/E,+EAA+E;QAC/E,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAM;IACR,CAAC;IACD,IAAI,aAAa,KAAK,eAAe;QAAE,OAAM;IAC7C,MAAM,IAAI,KAAK,CACb,+FAA+F;QAC7F,+FAA+F;QAC/F,gCAAgC;QAChC,yBAAyB,aAAa,IAAI;QAC1C,yBAAyB,eAAe,IAAI;QAC5C,kGAAkG;QAClG,iGAAiG;QACjG,gGAAgG,CACnG,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAAmC,aAAa,EAAE;IAElD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,oGAAoG;QACpG,8FAA8F;QAC9F,oGAAoG;QACpG,iFAAiF;QACjF,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,OAAO,CAAC,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAA;YACvD,oGAAoG;YACpG,kGAAkG;YAClG,mGAAmG;YACnG,kFAAkF;YAClF,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YACxC,OAAO,CAAC,MAAM,MAAM,kCAAC,QAAQ,EAAC,CAAmB,CAAA;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mGAAmG;YACnG,mGAAmG;YACnG,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAAE,MAAM,GAAG,CAAA;YAClF,gGAAgG;QAClG,CAAC;IACH,CAAC;IACD,8FAA8F;IAC9F,oGAAoG;IACpG,kGAAkG;IAClG,OAAO,CAAC,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAmB,CAAA;AAC7D,CAAC;AAED;;;;;;;;kEAQkE;AAClE,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,GAAG;QAAE,OAAO,SAAS,CAAA;IAC3D,OAAO,iBAAiB,IAAI,WAAW,EAAE,WAAW,KAAK,SAAS;QAChE,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC;QAC3C,CAAC,CAAC,SAAS,CAAA;AACf,CAAC"}
|
package/dist/router.d.ts
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@nifrajs/web-react/router`
|
|
2
|
+
* `@nifrajs/web-react/router` - React routing bindings over the agnostic `@nifrajs/web` router:
|
|
3
3
|
* `<Link>`/`<NavLink>`, `useNavigate`, `useParams`, `useLocation`, `useSearchParams`, and `<Navigate>`.
|
|
4
4
|
*
|
|
5
5
|
* These read the current route from a {@link RouterContext} that `compose` provides on BOTH the SSR
|
|
6
6
|
* render and the client mount (seeded from the request match / router state respectively), so the read
|
|
7
7
|
* hooks (`useParams`/`useLocation`/`useSearchParams`) are SSR-correct and hydrate with no mismatch.
|
|
8
8
|
* Navigation goes through `@nifrajs/web`'s DOM-free navigate bridge (`getBrowserNavigate`, populated by
|
|
9
|
-
* `installHistory`)
|
|
9
|
+
* `installHistory`) - so this module imports only `react` (never `react-dom/*`), and a route component
|
|
10
10
|
* can use these on the server and the client without dragging a DOM build into the wrong bundle. No JSX
|
|
11
11
|
* (the package builds with plain `tsc`), so everything is `createElement`.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import type { InferOutput, StandardSchemaV1 } from "@nifrajs/core/server";
|
|
14
|
+
import { type Blocker, type BlockerFunction, type NavigateFunction, type NavigateOptions } from "@nifrajs/web";
|
|
14
15
|
import { type AnchorHTMLAttributes, type CSSProperties, type ReactNode } from "react";
|
|
16
|
+
export type { Blocker, BlockerFunction, BlockerState, NavigateFunction } from "@nifrajs/web";
|
|
15
17
|
/** The current route the routing hooks read. Provided by `compose` on SSR + client mount alike. */
|
|
16
18
|
export interface RouterContextValue {
|
|
17
19
|
/** The matched route's decoded path params (`/users/:id` → `{ id: "7" }`). */
|
|
18
20
|
readonly params: Readonly<Record<string, string>>;
|
|
19
|
-
/** The current URL's `pathname + search` (no hash
|
|
21
|
+
/** The current URL's `pathname + search` (no hash - the router never carries one). */
|
|
20
22
|
readonly path: string;
|
|
21
|
-
/**
|
|
23
|
+
/** The route's typed, validated search params (the loader's `ctx.search`), derived from the URL via
|
|
24
|
+
* the shared `searchOf` on SSR + client alike, read by {@link useSearch}. `{}` when the route
|
|
25
|
+
* declares no `searchSchema` (then it is the raw parsed query) or outside a nifra route tree. */
|
|
26
|
+
readonly search: Record<string, unknown>;
|
|
27
|
+
/** True while a client navigation (or revalidation) is in flight - the current route stays mounted
|
|
22
28
|
* until the new one is ready. Always `false` on SSR (loaders block before render). Drives loading UI
|
|
23
29
|
* via {@link useNavigation}. */
|
|
24
30
|
readonly pending: boolean;
|
|
@@ -26,16 +32,33 @@ export interface RouterContextValue {
|
|
|
26
32
|
* during a same-route revalidation). Powers {@link NavLink}'s per-link `isPending`. */
|
|
27
33
|
readonly pendingPath?: string | undefined;
|
|
28
34
|
}
|
|
29
|
-
/** Router context. The default ({} params, "" path) is what a component sees when rendered
|
|
30
|
-
* nifra route tree
|
|
35
|
+
/** Router context. The default ({} params, "" path, {} search) is what a component sees when rendered
|
|
36
|
+
* outside a nifra route tree (the hooks stay defined, no throw, so a stray `useParams` degrades
|
|
37
|
+
* gracefully). */
|
|
31
38
|
export declare const RouterContext: import("react").Context<RouterContextValue>;
|
|
32
39
|
/**
|
|
33
|
-
* The matched route's decoded path params
|
|
40
|
+
* The matched route's decoded path params - `/users/:id` on `/users/7` → `{ id: "7" }`. SSR-correct:
|
|
34
41
|
* `compose` provides the same value server-side (from the request match) and client-side (from router
|
|
35
42
|
* state), so a param rendered into markup doesn't flash on hydration.
|
|
36
43
|
*/
|
|
37
44
|
export declare function useParams<T extends Record<string, string | undefined> = Record<string, string>>(): Readonly<T>;
|
|
38
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* The route's typed, validated search params: the SAME value the loader received as `ctx.search`.
|
|
47
|
+
* SSR-correct: `compose` provides it from `searchOf(searchSchema, url.search)` server-side and from the
|
|
48
|
+
* identical derivation on the client mount, so a value rendered from it doesn't flash on hydration.
|
|
49
|
+
* Hostile input already failed closed to the schema's defaults at match time, so a component never
|
|
50
|
+
* parses `window.location.search` or guards against a bad query.
|
|
51
|
+
*
|
|
52
|
+
* Pass the route's `searchSchema` as the type argument to get its output type; bare, it's the raw parsed
|
|
53
|
+
* query (`Record<string, unknown>`), which is also what a route without a `searchSchema` yields.
|
|
54
|
+
*
|
|
55
|
+
* ```tsx
|
|
56
|
+
* export const searchSchema = v.object({ page: v.optional(v.fallback(v.number(), 1), 1) })
|
|
57
|
+
* const { page } = useSearch<typeof searchSchema>() // page: number
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function useSearch<Schema extends StandardSchemaV1 | undefined = undefined>(): Schema extends StandardSchemaV1 ? InferOutput<Schema> : Record<string, unknown>;
|
|
61
|
+
/** The parsed current location. `hash` is always `""` - the fragment is client-only and never reaches
|
|
39
62
|
* the router state / server, so exposing a live hash would hydration-mismatch; read `window.location.hash`
|
|
40
63
|
* directly (in an effect) if you truly need it. */
|
|
41
64
|
export interface Location {
|
|
@@ -71,13 +94,38 @@ export interface Navigation {
|
|
|
71
94
|
export declare function useNavigation(): Navigation;
|
|
72
95
|
/** Convenience boolean form of {@link useNavigation}: `true` while a client navigation is in flight. */
|
|
73
96
|
export declare function usePending(): boolean;
|
|
74
|
-
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
/** Get the {@link NavigateFunction}. Stable across renders; resolves the browser navigate at call time
|
|
79
|
-
* (so it works as soon as `installHistory` has run, and no-ops before then / on the server). */
|
|
97
|
+
/** Get the {@link NavigateFunction} (a string path, a history delta, or a typed `{ to, search }` object;
|
|
98
|
+
* a render-time navigate isn't valid - use {@link Navigate}, which navigates in an effect). Stable across
|
|
99
|
+
* renders; resolves the browser navigate at call time (so it works as soon as `installHistory` has run,
|
|
100
|
+
* and no-ops before then / on the server). */
|
|
80
101
|
export declare function useNavigate(): NavigateFunction;
|
|
102
|
+
/**
|
|
103
|
+
* Guard navigation away from a page with unsaved work, confirming with your OWN async UI. Mirrors
|
|
104
|
+
* react-router's `useBlocker`: pass a boolean (`useBlocker(isDirty)`) or a predicate
|
|
105
|
+
* `({ currentLocation, nextLocation }) => boolean`, and get back a {@link Blocker}. When a navigation
|
|
106
|
+
* (a `<Link>`/anchor click, `useNavigate`, or a browser back/forward) is intercepted, `blocker.state`
|
|
107
|
+
* becomes `"blocked"` and `proceed`/`reset` go live - render a dialog and call `proceed()` to continue
|
|
108
|
+
* or `reset()` to stay put. It also arms the browser's native "Leave site?" prompt on tab close / reload.
|
|
109
|
+
* Idle (never blocks, `proceed`/`reset` are `undefined`) on the server and before hydration, so it's
|
|
110
|
+
* SSR-safe and hydration-stable.
|
|
111
|
+
*
|
|
112
|
+
* ```tsx
|
|
113
|
+
* const blocker = useBlocker(form.isDirty)
|
|
114
|
+
* return (
|
|
115
|
+
* <>
|
|
116
|
+
* <MyForm />
|
|
117
|
+
* {blocker.state === "blocked" && (
|
|
118
|
+
* <ConfirmDialog
|
|
119
|
+
* onCancel={blocker.reset}
|
|
120
|
+
* onConfirm={blocker.proceed}
|
|
121
|
+
* message="Discard unsaved changes?"
|
|
122
|
+
* />
|
|
123
|
+
* )}
|
|
124
|
+
* </>
|
|
125
|
+
* )
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function useBlocker(shouldBlock: boolean | BlockerFunction): Blocker;
|
|
81
129
|
/** The value forms `setSearchParams` accepts. */
|
|
82
130
|
export type SearchParamsInit = URLSearchParams | Record<string, string | readonly string[]> | string;
|
|
83
131
|
/** Set the query string. Accepts a `URLSearchParams`, a record, a raw string, or an updater of the
|
|
@@ -99,7 +147,7 @@ export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
|
99
147
|
* A client-navigating anchor. Renders a real `<a href={to}>` (so it's a working link before hydration
|
|
100
148
|
* and for right-click / open-in-new-tab), and on a plain left-click navigates through the router
|
|
101
149
|
* instead of a full reload. Calling `navigate` + `preventDefault` here means `installHistory`'s
|
|
102
|
-
* document-level click handler sees `defaultPrevented` and stands down
|
|
150
|
+
* document-level click handler sees `defaultPrevented` and stands down - exactly one navigation.
|
|
103
151
|
*/
|
|
104
152
|
export declare const Link: import("react").ForwardRefExoticComponent<LinkProps & import("react").RefAttributes<HTMLAnchorElement>>;
|
|
105
153
|
/** The state a {@link NavLink}'s function-form `className`/`style`/`children` receive. */
|
|
@@ -110,7 +158,7 @@ export interface NavLinkRenderProps {
|
|
|
110
158
|
* `false` on SSR and when idle. Use it to show a per-link spinner during the transition. */
|
|
111
159
|
readonly isPending: boolean;
|
|
112
160
|
}
|
|
113
|
-
/** {@link NavLink} props
|
|
161
|
+
/** {@link NavLink} props - like {@link LinkProps}, but `className`/`style`/`children` may be functions
|
|
114
162
|
* of the active state, and `end`/`caseSensitive` tune matching. */
|
|
115
163
|
export interface NavLinkProps extends Omit<LinkProps, "className" | "style" | "children"> {
|
|
116
164
|
/** Match the full path exactly instead of as a prefix (use for `to="/"` so it isn't always active). */
|
|
@@ -134,7 +182,7 @@ export interface NavigateProps {
|
|
|
134
182
|
readonly replace?: boolean;
|
|
135
183
|
}
|
|
136
184
|
/**
|
|
137
|
-
* Declaratively navigate on mount
|
|
185
|
+
* Declaratively navigate on mount - the component analogue of `useNavigate` (e.g. a guard that renders
|
|
138
186
|
* `<Navigate to="/login" replace />`). Navigates in an effect, so it's a safe no-op during SSR (renders
|
|
139
187
|
* `null`); the redirect happens once on the client after hydration.
|
|
140
188
|
*/
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACzE,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EAGpB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAIrB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAKlB,KAAK,SAAS,EAOf,MAAM,OAAO,CAAA;AAEd,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE5F,mGAAmG;AACnG,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjD,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;qGAEiG;IACjG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC;;oCAEgC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB;2FACuF;IACvF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1C;AAMD;;kBAEkB;AAClB,eAAO,MAAM,aAAa,6CAKxB,CAAA;AAWF;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAClE,QAAQ,CAAC,CAAC,CAAC,CAEf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CACvB,MAAM,SAAS,gBAAgB,GAAG,SAAS,GAAG,SAAS,KACpD,MAAM,SAAS,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAInF;AAED;;mDAEmD;AACnD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,kGAAkG;AAClG,wBAAgB,WAAW,IAAI,QAAQ,CAMtC;AAED,iGAAiG;AACjG,MAAM,WAAW,UAAU;IACzB;2FACuF;IACvF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC;8EAC0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAU1C;AAED,wGAAwG;AACxG,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED;;;8CAG8C;AAC9C,wBAAgB,WAAW,IAAI,gBAAgB,CAO9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,OAAO,GAAG,eAAe,GAAG,OAAO,CAiB1E;AAED,iDAAiD;AACjD,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,GAAG,MAAM,CAAA;AAapG;uGACuG;AACvG,MAAM,MAAM,eAAe,GAAG,CAC5B,IAAI,EAAE,gBAAgB,GAAG,CAAC,CAAC,IAAI,EAAE,eAAe,KAAK,gBAAgB,CAAC,EACtE,OAAO,CAAC,EAAE,eAAe,KACtB,IAAI,CAAA;AAET;;;GAGG;AACH,wBAAgB,eAAe,IAAI,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC,CAc7E;AAUD,sGAAsG;AACtG,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACtF,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,IAAI,yGAcf,CAAA;AAEF,0FAA0F;AAC1F,MAAM,WAAW,kBAAkB;IACjC,mGAAmG;IACnG,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B;gGAC4F;IAC5F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;mEACmE;AACnE,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,CAAC;IACvF,uGAAuG;IACvG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;IACtB,wEAAwE;IACxE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,kBAAkB,KAAK,MAAM,GAAG,SAAS,CAAC,CAAA;IACjF,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,CAAC,CAAC,KAAK,EAAE,kBAAkB,KAAK,aAAa,GAAG,SAAS,CAAC,CAAA;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC,CAAA;CAC3E;AASD;;;;;GAKG;AACH,eAAO,MAAM,OAAO,4GAoClB,CAAA;AASF,+FAA+F;AAC/F,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,GAAG,IAAI,CAM7D"}
|
package/dist/router.js
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@nifrajs/web-react/router`
|
|
2
|
+
* `@nifrajs/web-react/router` - React routing bindings over the agnostic `@nifrajs/web` router:
|
|
3
3
|
* `<Link>`/`<NavLink>`, `useNavigate`, `useParams`, `useLocation`, `useSearchParams`, and `<Navigate>`.
|
|
4
4
|
*
|
|
5
5
|
* These read the current route from a {@link RouterContext} that `compose` provides on BOTH the SSR
|
|
6
6
|
* render and the client mount (seeded from the request match / router state respectively), so the read
|
|
7
7
|
* hooks (`useParams`/`useLocation`/`useSearchParams`) are SSR-correct and hydrate with no mismatch.
|
|
8
8
|
* Navigation goes through `@nifrajs/web`'s DOM-free navigate bridge (`getBrowserNavigate`, populated by
|
|
9
|
-
* `installHistory`)
|
|
9
|
+
* `installHistory`) - so this module imports only `react` (never `react-dom/*`), and a route component
|
|
10
10
|
* can use these on the server and the client without dragging a DOM build into the wrong bundle. No JSX
|
|
11
11
|
* (the package builds with plain `tsc`), so everything is `createElement`.
|
|
12
12
|
*/
|
|
13
|
-
import { getBrowserNavigate } from "@nifrajs/web";
|
|
14
|
-
import { createContext, createElement, forwardRef, useCallback, useContext, useEffect, useMemo, } from "react";
|
|
15
|
-
// Frozen empty params so the default context value has a stable reference (no needless re-renders).
|
|
13
|
+
import { getBrowserNavigate, IDLE_BLOCKER, registerBlocker, resolveNavigate, } from "@nifrajs/web";
|
|
14
|
+
import { createContext, createElement, forwardRef, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
15
|
+
// Frozen empty params/search so the default context value has a stable reference (no needless re-renders).
|
|
16
16
|
const EMPTY_PARAMS = Object.freeze({});
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const EMPTY_SEARCH = Object.freeze({});
|
|
18
|
+
/** Router context. The default ({} params, "" path, {} search) is what a component sees when rendered
|
|
19
|
+
* outside a nifra route tree (the hooks stay defined, no throw, so a stray `useParams` degrades
|
|
20
|
+
* gracefully). */
|
|
19
21
|
export const RouterContext = createContext({
|
|
20
22
|
params: EMPTY_PARAMS,
|
|
21
23
|
path: "",
|
|
24
|
+
search: EMPTY_SEARCH,
|
|
22
25
|
pending: false,
|
|
23
26
|
});
|
|
24
27
|
/** Split a `pathname + search` into its parts. `search` keeps its leading `?` (like `location.search`);
|
|
@@ -30,13 +33,31 @@ function splitPath(path) {
|
|
|
30
33
|
: { pathname: path.slice(0, q), search: path.slice(q) };
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
|
-
* The matched route's decoded path params
|
|
36
|
+
* The matched route's decoded path params - `/users/:id` on `/users/7` → `{ id: "7" }`. SSR-correct:
|
|
34
37
|
* `compose` provides the same value server-side (from the request match) and client-side (from router
|
|
35
38
|
* state), so a param rendered into markup doesn't flash on hydration.
|
|
36
39
|
*/
|
|
37
40
|
export function useParams() {
|
|
38
41
|
return useContext(RouterContext).params;
|
|
39
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* The route's typed, validated search params: the SAME value the loader received as `ctx.search`.
|
|
45
|
+
* SSR-correct: `compose` provides it from `searchOf(searchSchema, url.search)` server-side and from the
|
|
46
|
+
* identical derivation on the client mount, so a value rendered from it doesn't flash on hydration.
|
|
47
|
+
* Hostile input already failed closed to the schema's defaults at match time, so a component never
|
|
48
|
+
* parses `window.location.search` or guards against a bad query.
|
|
49
|
+
*
|
|
50
|
+
* Pass the route's `searchSchema` as the type argument to get its output type; bare, it's the raw parsed
|
|
51
|
+
* query (`Record<string, unknown>`), which is also what a route without a `searchSchema` yields.
|
|
52
|
+
*
|
|
53
|
+
* ```tsx
|
|
54
|
+
* export const searchSchema = v.object({ page: v.optional(v.fallback(v.number(), 1), 1) })
|
|
55
|
+
* const { page } = useSearch<typeof searchSchema>() // page: number
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function useSearch() {
|
|
59
|
+
return useContext(RouterContext).search;
|
|
60
|
+
}
|
|
40
61
|
/** The current {@link Location} (`pathname`/`search`/`hash`), derived from the router context. */
|
|
41
62
|
export function useLocation() {
|
|
42
63
|
const { path } = useContext(RouterContext);
|
|
@@ -69,15 +90,59 @@ export function useNavigation() {
|
|
|
69
90
|
export function usePending() {
|
|
70
91
|
return useContext(RouterContext).pending;
|
|
71
92
|
}
|
|
72
|
-
/** Get the {@link NavigateFunction}
|
|
73
|
-
*
|
|
93
|
+
/** Get the {@link NavigateFunction} (a string path, a history delta, or a typed `{ to, search }` object;
|
|
94
|
+
* a render-time navigate isn't valid - use {@link Navigate}, which navigates in an effect). Stable across
|
|
95
|
+
* renders; resolves the browser navigate at call time (so it works as soon as `installHistory` has run,
|
|
96
|
+
* and no-ops before then / on the server). */
|
|
74
97
|
export function useNavigate() {
|
|
75
98
|
return useCallback((to, options) => {
|
|
76
99
|
const navigate = getBrowserNavigate();
|
|
77
|
-
if (navigate
|
|
78
|
-
|
|
100
|
+
if (navigate === undefined)
|
|
101
|
+
return;
|
|
102
|
+
const resolved = resolveNavigate(to, options);
|
|
103
|
+
navigate(resolved.to, resolved.options);
|
|
79
104
|
}, []);
|
|
80
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Guard navigation away from a page with unsaved work, confirming with your OWN async UI. Mirrors
|
|
108
|
+
* react-router's `useBlocker`: pass a boolean (`useBlocker(isDirty)`) or a predicate
|
|
109
|
+
* `({ currentLocation, nextLocation }) => boolean`, and get back a {@link Blocker}. When a navigation
|
|
110
|
+
* (a `<Link>`/anchor click, `useNavigate`, or a browser back/forward) is intercepted, `blocker.state`
|
|
111
|
+
* becomes `"blocked"` and `proceed`/`reset` go live - render a dialog and call `proceed()` to continue
|
|
112
|
+
* or `reset()` to stay put. It also arms the browser's native "Leave site?" prompt on tab close / reload.
|
|
113
|
+
* Idle (never blocks, `proceed`/`reset` are `undefined`) on the server and before hydration, so it's
|
|
114
|
+
* SSR-safe and hydration-stable.
|
|
115
|
+
*
|
|
116
|
+
* ```tsx
|
|
117
|
+
* const blocker = useBlocker(form.isDirty)
|
|
118
|
+
* return (
|
|
119
|
+
* <>
|
|
120
|
+
* <MyForm />
|
|
121
|
+
* {blocker.state === "blocked" && (
|
|
122
|
+
* <ConfirmDialog
|
|
123
|
+
* onCancel={blocker.reset}
|
|
124
|
+
* onConfirm={blocker.proceed}
|
|
125
|
+
* message="Discard unsaved changes?"
|
|
126
|
+
* />
|
|
127
|
+
* )}
|
|
128
|
+
* </>
|
|
129
|
+
* )
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function useBlocker(shouldBlock) {
|
|
133
|
+
const [blocker, setBlocker] = useState(IDLE_BLOCKER);
|
|
134
|
+
// Track the latest predicate in a ref (updated every render) so the single registration always sees
|
|
135
|
+
// current component state (the dirty flag) WITHOUT re-registering - re-registering mid-prompt drops it.
|
|
136
|
+
const shouldBlockRef = useRef(shouldBlock);
|
|
137
|
+
shouldBlockRef.current = shouldBlock;
|
|
138
|
+
// Register once on mount; the returned unregister is the effect cleanup. A boolean reads as itself, a
|
|
139
|
+
// function is called. A no-op before `installHistory` has run (blocker stays idle → native navigation).
|
|
140
|
+
useEffect(() => registerBlocker((args) => {
|
|
141
|
+
const current = shouldBlockRef.current;
|
|
142
|
+
return typeof current === "function" ? current(args) : current;
|
|
143
|
+
}, setBlocker), []);
|
|
144
|
+
return blocker;
|
|
145
|
+
}
|
|
81
146
|
function toSearchParams(init) {
|
|
82
147
|
if (init instanceof URLSearchParams)
|
|
83
148
|
return init;
|
|
@@ -109,7 +174,7 @@ export function useSearchParams() {
|
|
|
109
174
|
}, [navigate, pathname, search]);
|
|
110
175
|
return [searchParams, setSearchParams];
|
|
111
176
|
}
|
|
112
|
-
/** A left-click with no modifier and no new-tab target
|
|
177
|
+
/** A left-click with no modifier and no new-tab target - the only click a client router should
|
|
113
178
|
* intercept (Cmd/Ctrl/Shift/Alt or a `_blank` target means the user wants native behavior). */
|
|
114
179
|
function isPlainLeftClick(event, target) {
|
|
115
180
|
if (event.button !== 0)
|
|
@@ -122,7 +187,7 @@ function isPlainLeftClick(event, target) {
|
|
|
122
187
|
* A client-navigating anchor. Renders a real `<a href={to}>` (so it's a working link before hydration
|
|
123
188
|
* and for right-click / open-in-new-tab), and on a plain left-click navigates through the router
|
|
124
189
|
* instead of a full reload. Calling `navigate` + `preventDefault` here means `installHistory`'s
|
|
125
|
-
* document-level click handler sees `defaultPrevented` and stands down
|
|
190
|
+
* document-level click handler sees `defaultPrevented` and stands down - exactly one navigation.
|
|
126
191
|
*/
|
|
127
192
|
export const Link = forwardRef(function Link({ to, replace, onClick, target, ...rest }, ref) {
|
|
128
193
|
const handleClick = (event) => {
|
|
@@ -187,7 +252,7 @@ function matchesPrefix(current, target) {
|
|
|
187
252
|
return current.startsWith(target === "/" ? "/" : `${target}/`);
|
|
188
253
|
}
|
|
189
254
|
/**
|
|
190
|
-
* Declaratively navigate on mount
|
|
255
|
+
* Declaratively navigate on mount - the component analogue of `useNavigate` (e.g. a guard that renders
|
|
191
256
|
* `<Navigate to="/login" replace />`). Navigates in an effect, so it's a safe no-op during SSR (renders
|
|
192
257
|
* `null`); the redirect happens once on the client after hydration.
|
|
193
258
|
*/
|
package/dist/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|