@cmj/juice 0.4.0 → 0.6.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/dist/client/Link.d.ts +74 -0
- package/dist/client/Link.d.ts.map +1 -0
- package/dist/client/Link.js +132 -0
- package/dist/client/Link.js.map +1 -0
- package/dist/client/client.d.ts +1 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +6 -34
- package/dist/client/client.js.map +1 -1
- package/dist/client/hooks.d.ts +85 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +82 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/nav.d.ts +40 -0
- package/dist/client/nav.d.ts.map +1 -0
- package/dist/client/nav.js +85 -0
- package/dist/client/nav.js.map +1 -0
- package/dist/compiler/dts.d.ts +33 -0
- package/dist/compiler/dts.d.ts.map +1 -0
- package/dist/compiler/dts.js +102 -0
- package/dist/compiler/dts.js.map +1 -0
- package/dist/compiler/plugin.d.ts.map +1 -1
- package/dist/compiler/plugin.js +42 -1
- package/dist/compiler/plugin.js.map +1 -1
- package/dist/runtime/actions.d.ts +6 -5
- package/dist/runtime/actions.d.ts.map +1 -1
- package/dist/runtime/actions.js +17 -7
- package/dist/runtime/actions.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/matcher.d.ts.map +1 -1
- package/dist/runtime/matcher.js +39 -13
- package/dist/runtime/matcher.js.map +1 -1
- package/dist/runtime/router.js +1 -1
- package/dist/runtime/router.js.map +1 -1
- package/dist/runtime/types.d.ts +48 -11
- package/dist/runtime/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type AnchorHTMLAttributes, type ReactNode, type CSSProperties, type Ref } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Prefetch strategy for the `<Link>` component.
|
|
4
|
+
*
|
|
5
|
+
* - `'hover'` — Prefetch when the user hovers or focuses the link. *(default)*
|
|
6
|
+
* - `'viewport'` — Prefetch when the link scrolls into the viewport.
|
|
7
|
+
* - `'none'` — No automatic prefetching.
|
|
8
|
+
*/
|
|
9
|
+
export type PrefetchStrategy = 'hover' | 'viewport' | 'none';
|
|
10
|
+
/**
|
|
11
|
+
* Props for the `<Link>` component.
|
|
12
|
+
*
|
|
13
|
+
* Extends all standard `<a>` HTML attributes so you can pass
|
|
14
|
+
* `target`, `rel`, `aria-*`, `data-*`, etc. directly.
|
|
15
|
+
*/
|
|
16
|
+
export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
17
|
+
/** The URL to navigate to. Required. */
|
|
18
|
+
href: string;
|
|
19
|
+
/**
|
|
20
|
+
* Prefetch strategy.
|
|
21
|
+
* @default 'hover'
|
|
22
|
+
*/
|
|
23
|
+
prefetch?: PrefetchStrategy;
|
|
24
|
+
/**
|
|
25
|
+
* Replace the current history entry instead of pushing a new one.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
replace?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Scroll to the top after navigation.
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
scroll?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* CSS class name applied when the link's `href` matches the
|
|
36
|
+
* current pathname.
|
|
37
|
+
*/
|
|
38
|
+
activeClassName?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Inline styles applied when the link's `href` matches the
|
|
41
|
+
* current pathname.
|
|
42
|
+
*/
|
|
43
|
+
activeStyle?: CSSProperties;
|
|
44
|
+
/** React ref forwarded to the underlying `<a>` element. */
|
|
45
|
+
ref?: Ref<HTMLAnchorElement>;
|
|
46
|
+
children?: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Client-side navigation link for Juice SPA apps.
|
|
50
|
+
*
|
|
51
|
+
* Renders a standard `<a>` tag that works with or without JavaScript.
|
|
52
|
+
* When the Navigation API is available and `initNavigation()` has been
|
|
53
|
+
* called, clicking this link fetches an RSC payload instead of causing
|
|
54
|
+
* a full page reload.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* import { Link } from '@cmj/juice/client';
|
|
59
|
+
*
|
|
60
|
+
* // Basic usage
|
|
61
|
+
* <Link href="/about">About</Link>
|
|
62
|
+
*
|
|
63
|
+
* // Viewport prefetching
|
|
64
|
+
* <Link href="/products" prefetch="viewport">Products</Link>
|
|
65
|
+
*
|
|
66
|
+
* // Active link styling
|
|
67
|
+
* <Link href="/blog" activeClassName="nav-active">Blog</Link>
|
|
68
|
+
*
|
|
69
|
+
* // Replace history entry
|
|
70
|
+
* <Link href="/login" replace>Log in</Link>
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function Link(props: LinkProps): ReactNode;
|
|
74
|
+
//# sourceMappingURL=Link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../src/client/Link.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,GAAG,EACT,MAAM,OAAO,CAAC;AAYf;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACtF,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAE5B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;IAE5B,2DAA2D;IAC3D,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAE7B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAmHhD"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// ── juice/client — <Link> component ───────────────────────────────
|
|
2
|
+
// Declarative SPA navigation for Juice. Renders a standard `<a>` tag
|
|
3
|
+
// with built-in RSC prefetching and active-state detection.
|
|
4
|
+
//
|
|
5
|
+
// Progressive enhancement: works as a normal link without JS.
|
|
6
|
+
// ──────────────────────────────────────────────────────────────────
|
|
7
|
+
import { createElement, useRef, useEffect, useSyncExternalStore, } from 'react';
|
|
8
|
+
import { prefetchRSC, isSameOrigin, _subscribeToPathname, _getPathnameSnapshot, _getServerPathnameSnapshot, } from './nav.js';
|
|
9
|
+
// ── Helpers ───────────────────────────────────────────────────────
|
|
10
|
+
/**
|
|
11
|
+
* Check if a pathname matches the link's href.
|
|
12
|
+
* Exact match — `/about` matches `/about`, not `/about/team`.
|
|
13
|
+
*/
|
|
14
|
+
function isActive(href, pathname) {
|
|
15
|
+
try {
|
|
16
|
+
const url = new URL(href, 'http://n');
|
|
17
|
+
return url.pathname === pathname;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return href === pathname;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// ── Component ─────────────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* Client-side navigation link for Juice SPA apps.
|
|
26
|
+
*
|
|
27
|
+
* Renders a standard `<a>` tag that works with or without JavaScript.
|
|
28
|
+
* When the Navigation API is available and `initNavigation()` has been
|
|
29
|
+
* called, clicking this link fetches an RSC payload instead of causing
|
|
30
|
+
* a full page reload.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* import { Link } from '@cmj/juice/client';
|
|
35
|
+
*
|
|
36
|
+
* // Basic usage
|
|
37
|
+
* <Link href="/about">About</Link>
|
|
38
|
+
*
|
|
39
|
+
* // Viewport prefetching
|
|
40
|
+
* <Link href="/products" prefetch="viewport">Products</Link>
|
|
41
|
+
*
|
|
42
|
+
* // Active link styling
|
|
43
|
+
* <Link href="/blog" activeClassName="nav-active">Blog</Link>
|
|
44
|
+
*
|
|
45
|
+
* // Replace history entry
|
|
46
|
+
* <Link href="/login" replace>Log in</Link>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function Link(props) {
|
|
50
|
+
const { href, prefetch = 'hover', replace = false, scroll, activeClassName, activeStyle, className, style, children, onPointerEnter, onFocus, ref: externalRef, ...rest } = props;
|
|
51
|
+
const internalRef = useRef(null);
|
|
52
|
+
// Merge refs
|
|
53
|
+
const setRef = (el) => {
|
|
54
|
+
internalRef.current = el;
|
|
55
|
+
if (typeof externalRef === 'function') {
|
|
56
|
+
externalRef(el);
|
|
57
|
+
}
|
|
58
|
+
else if (externalRef && typeof externalRef === 'object') {
|
|
59
|
+
externalRef.current = el;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
// ── Active state ──────────────────────────────────────────────
|
|
63
|
+
const pathname = useSyncExternalStore(_subscribeToPathname, _getPathnameSnapshot, _getServerPathnameSnapshot);
|
|
64
|
+
const active = isActive(href, pathname);
|
|
65
|
+
// ── Viewport prefetching ──────────────────────────────────────
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (prefetch !== 'viewport')
|
|
68
|
+
return;
|
|
69
|
+
const el = internalRef.current;
|
|
70
|
+
if (!el)
|
|
71
|
+
return;
|
|
72
|
+
if (!('IntersectionObserver' in globalThis))
|
|
73
|
+
return;
|
|
74
|
+
const observer = new IntersectionObserver((entries) => {
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (entry.isIntersecting) {
|
|
77
|
+
const resolvedUrl = new URL(href, location.href).href;
|
|
78
|
+
prefetchRSC(resolvedUrl);
|
|
79
|
+
observer.unobserve(entry.target);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}, { rootMargin: '200px' });
|
|
83
|
+
observer.observe(el);
|
|
84
|
+
return () => observer.disconnect();
|
|
85
|
+
}, [href, prefetch]);
|
|
86
|
+
// ── Hover/focus prefetching ───────────────────────────────────
|
|
87
|
+
const handlePointerEnter = (e) => {
|
|
88
|
+
if (prefetch === 'hover' && isSameOrigin(href)) {
|
|
89
|
+
prefetchRSC(new URL(href, location.href).href);
|
|
90
|
+
}
|
|
91
|
+
onPointerEnter?.(e);
|
|
92
|
+
};
|
|
93
|
+
const handleFocus = (e) => {
|
|
94
|
+
if (prefetch === 'hover' && isSameOrigin(href)) {
|
|
95
|
+
prefetchRSC(new URL(href, location.href).href);
|
|
96
|
+
}
|
|
97
|
+
onFocus?.(e);
|
|
98
|
+
};
|
|
99
|
+
// ── Merge class names ─────────────────────────────────────────
|
|
100
|
+
const resolvedClassName = active && activeClassName
|
|
101
|
+
? className
|
|
102
|
+
? `${className} ${activeClassName}`
|
|
103
|
+
: activeClassName
|
|
104
|
+
: className;
|
|
105
|
+
// ── Merge styles ──────────────────────────────────────────────
|
|
106
|
+
const resolvedStyle = active && activeStyle
|
|
107
|
+
? style
|
|
108
|
+
? { ...style, ...activeStyle }
|
|
109
|
+
: activeStyle
|
|
110
|
+
: style;
|
|
111
|
+
// ── Build anchor props ────────────────────────────────────────
|
|
112
|
+
const anchorProps = {
|
|
113
|
+
...rest,
|
|
114
|
+
href,
|
|
115
|
+
ref: setRef,
|
|
116
|
+
className: resolvedClassName,
|
|
117
|
+
style: resolvedStyle,
|
|
118
|
+
onPointerEnter: handlePointerEnter,
|
|
119
|
+
onFocus: handleFocus,
|
|
120
|
+
'data-active': active || undefined,
|
|
121
|
+
};
|
|
122
|
+
// Signal prefetch strategy to the DOM for IntersectionObserver
|
|
123
|
+
if (prefetch === 'viewport') {
|
|
124
|
+
anchorProps['data-prefetch'] = '';
|
|
125
|
+
}
|
|
126
|
+
// Signal replace intent
|
|
127
|
+
if (replace) {
|
|
128
|
+
anchorProps['data-replace'] = '';
|
|
129
|
+
}
|
|
130
|
+
return createElement('a', anchorProps, children);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=Link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/client/Link.ts"],"names":[],"mappings":"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"}
|
package/dist/client/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
+
export { RSC_CONTENT_TYPE, prefetchRSC, isSameOrigin, _subscribeToPathname, _getPathnameSnapshot, _getServerPathnameSnapshot, } from './nav.js';
|
|
2
3
|
/**
|
|
3
4
|
* Callback to update the React root with a new RSC payload.
|
|
4
5
|
* Set by `initNavigation()`.
|
|
@@ -53,5 +54,4 @@ export interface NavigationOptions {
|
|
|
53
54
|
* ```
|
|
54
55
|
*/
|
|
55
56
|
export declare function initNavigation(setPage: PageSetter, options?: NavigationOptions): () => void;
|
|
56
|
-
export {};
|
|
57
57
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AASA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AASA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAWxD,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAElB;;;GAGG;AACH,KAAK,UAAU,GAAG,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE;AAuED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,iBAAsB,GAC9B,MAAM,IAAI,CA+EZ"}
|
package/dist/client/client.js
CHANGED
|
@@ -8,24 +8,9 @@
|
|
|
8
8
|
// ────────────────────────────────────────────────────────────────────
|
|
9
9
|
import { startTransition } from 'react';
|
|
10
10
|
import { createFromFetch } from 'react-server-dom-webpack/client.browser';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*/
|
|
15
|
-
const RSC_CONTENT_TYPE = 'text/x-component';
|
|
16
|
-
// ── Prefetch cache ────────────────────────────────────────────────
|
|
17
|
-
const prefetchCache = new Map();
|
|
18
|
-
function prefetchRSC(url) {
|
|
19
|
-
if (prefetchCache.has(url))
|
|
20
|
-
return;
|
|
21
|
-
const promise = fetch(url, {
|
|
22
|
-
headers: { 'Accept': RSC_CONTENT_TYPE },
|
|
23
|
-
priority: 'low',
|
|
24
|
-
});
|
|
25
|
-
prefetchCache.set(url, promise);
|
|
26
|
-
// Expire after 30 seconds to avoid stale data
|
|
27
|
-
setTimeout(() => prefetchCache.delete(url), 30_000);
|
|
28
|
-
}
|
|
11
|
+
import { RSC_CONTENT_TYPE, prefetchRSC, _consumePrefetch, isSameOrigin, } from './nav.js';
|
|
12
|
+
// Re-export shared utilities for backward compatibility
|
|
13
|
+
export { RSC_CONTENT_TYPE, prefetchRSC, isSameOrigin, _subscribeToPathname, _getPathnameSnapshot, _getServerPathnameSnapshot, } from './nav.js';
|
|
29
14
|
// ── Prefetch observer ─────────────────────────────────────────────
|
|
30
15
|
function setupPrefetching() {
|
|
31
16
|
// Prefetch on hover (desktop) and focus (keyboard nav)
|
|
@@ -80,16 +65,6 @@ function setupPrefetching() {
|
|
|
80
65
|
}).observe(document.body, { childList: true, subtree: true });
|
|
81
66
|
}
|
|
82
67
|
}
|
|
83
|
-
// ── Helpers ───────────────────────────────────────────────────────
|
|
84
|
-
function isSameOrigin(href) {
|
|
85
|
-
try {
|
|
86
|
-
const url = new URL(href, location.href);
|
|
87
|
-
return url.origin === location.origin;
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
68
|
// ── View Transitions wrapper ──────────────────────────────────────
|
|
94
69
|
function withViewTransition(enabled, fn) {
|
|
95
70
|
if (enabled && 'startViewTransition' in document) {
|
|
@@ -166,15 +141,12 @@ export function initNavigation(setPage, options = {}) {
|
|
|
166
141
|
scroll: 'after-transition',
|
|
167
142
|
async handler() {
|
|
168
143
|
const url = event.destination.url;
|
|
169
|
-
// Use prefetched response if available
|
|
170
|
-
const fetchPromise =
|
|
171
|
-
|
|
172
|
-
: fetch(url, {
|
|
144
|
+
// Use prefetched response if available, otherwise fetch
|
|
145
|
+
const fetchPromise = _consumePrefetch(url)
|
|
146
|
+
?? fetch(url, {
|
|
173
147
|
headers: { 'Accept': RSC_CONTENT_TYPE },
|
|
174
148
|
signal: event.signal,
|
|
175
149
|
});
|
|
176
|
-
// Clear the prefetch cache entry
|
|
177
|
-
prefetchCache.delete(url);
|
|
178
150
|
// Decode the RSC payload
|
|
179
151
|
const rscPayload = createFromFetch(fetchPromise, {
|
|
180
152
|
callServer,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,oEAAoE;AACpE,6DAA6D;AAC7D,EAAE;AACF,oDAAoD;AACpD,+DAA+D;AAC/D,8CAA8C;AAC9C,uEAAuE;AAEvE,OAAO,EAAE,eAAe,EAAkB,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE1E
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return type of `useRouter()`.
|
|
3
|
+
*/
|
|
4
|
+
export interface Router {
|
|
5
|
+
/** The current pathname (reactive — re-renders on navigation). */
|
|
6
|
+
pathname: string;
|
|
7
|
+
/**
|
|
8
|
+
* Navigate to a URL, pushing a new history entry.
|
|
9
|
+
*
|
|
10
|
+
* @param url - The URL to navigate to.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const router = useRouter();
|
|
15
|
+
* router.push('/product/42');
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
push: (url: string) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Navigate to a URL, replacing the current history entry.
|
|
21
|
+
*
|
|
22
|
+
* @param url - The URL to navigate to.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const router = useRouter();
|
|
27
|
+
* router.replace('/login');
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
replace: (url: string) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Prefetch an RSC payload for a URL. Idempotent — duplicate
|
|
33
|
+
* calls for the same URL are no-ops until the cache expires.
|
|
34
|
+
*
|
|
35
|
+
* @param url - The URL to prefetch.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const router = useRouter();
|
|
40
|
+
* router.prefetch('/dashboard');
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
prefetch: (url: string) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Go back in history. Equivalent to `history.back()`.
|
|
46
|
+
*/
|
|
47
|
+
back: () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Go forward in history. Equivalent to `history.forward()`.
|
|
50
|
+
*/
|
|
51
|
+
forward: () => void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Programmatic navigation hook for Juice SPA apps.
|
|
55
|
+
*
|
|
56
|
+
* Returns a stable `Router` object with `push`, `replace`, `prefetch`,
|
|
57
|
+
* `back`, `forward`, and a reactive `pathname`.
|
|
58
|
+
*
|
|
59
|
+
* Uses the Navigation API when available. Falls back to `location.assign`
|
|
60
|
+
* when the Navigation API is absent (MPA mode — full page reload).
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* 'use client';
|
|
65
|
+
* import { useRouter } from '@cmj/juice/client';
|
|
66
|
+
*
|
|
67
|
+
* function SearchBar() {
|
|
68
|
+
* const router = useRouter();
|
|
69
|
+
*
|
|
70
|
+
* function handleSubmit(e: FormEvent) {
|
|
71
|
+
* e.preventDefault();
|
|
72
|
+
* const q = new FormData(e.currentTarget).get('q');
|
|
73
|
+
* router.push(`/search?q=${encodeURIComponent(q)}`);
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* return (
|
|
77
|
+
* <form onSubmit={handleSubmit}>
|
|
78
|
+
* <input name="q" />
|
|
79
|
+
* </form>
|
|
80
|
+
* );
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function useRouter(): Router;
|
|
85
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/client/hooks.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;;;;;;;OAUG;IACH,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5B;;;;;;;;;;OAUG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAEhC;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAgDlC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// ── juice/client — useRouter hook ─────────────────────────────────
|
|
2
|
+
// Programmatic navigation for Juice SPA apps. Uses the Navigation API
|
|
3
|
+
// under the hood — the existing `initNavigation()` interceptor handles
|
|
4
|
+
// RSC payload fetching automatically.
|
|
5
|
+
//
|
|
6
|
+
// ~500 bytes minified. Zero dependencies beyond React 19.
|
|
7
|
+
// ──────────────────────────────────────────────────────────────────
|
|
8
|
+
import { useSyncExternalStore, useCallback, useMemo } from 'react';
|
|
9
|
+
import { prefetchRSC, _subscribeToPathname, _getPathnameSnapshot, _getServerPathnameSnapshot, } from './nav.js';
|
|
10
|
+
// ── Hook ──────────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Programmatic navigation hook for Juice SPA apps.
|
|
13
|
+
*
|
|
14
|
+
* Returns a stable `Router` object with `push`, `replace`, `prefetch`,
|
|
15
|
+
* `back`, `forward`, and a reactive `pathname`.
|
|
16
|
+
*
|
|
17
|
+
* Uses the Navigation API when available. Falls back to `location.assign`
|
|
18
|
+
* when the Navigation API is absent (MPA mode — full page reload).
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* 'use client';
|
|
23
|
+
* import { useRouter } from '@cmj/juice/client';
|
|
24
|
+
*
|
|
25
|
+
* function SearchBar() {
|
|
26
|
+
* const router = useRouter();
|
|
27
|
+
*
|
|
28
|
+
* function handleSubmit(e: FormEvent) {
|
|
29
|
+
* e.preventDefault();
|
|
30
|
+
* const q = new FormData(e.currentTarget).get('q');
|
|
31
|
+
* router.push(`/search?q=${encodeURIComponent(q)}`);
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* return (
|
|
35
|
+
* <form onSubmit={handleSubmit}>
|
|
36
|
+
* <input name="q" />
|
|
37
|
+
* </form>
|
|
38
|
+
* );
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function useRouter() {
|
|
43
|
+
const pathname = useSyncExternalStore(_subscribeToPathname, _getPathnameSnapshot, _getServerPathnameSnapshot);
|
|
44
|
+
const push = useCallback((url) => {
|
|
45
|
+
if ('navigation' in globalThis) {
|
|
46
|
+
globalThis.navigation.navigate(url, { history: 'push' });
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
globalThis.location.assign(url);
|
|
50
|
+
}
|
|
51
|
+
}, []);
|
|
52
|
+
const replace = useCallback((url) => {
|
|
53
|
+
if ('navigation' in globalThis) {
|
|
54
|
+
globalThis.navigation.navigate(url, { history: 'replace' });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
globalThis.location.replace(url);
|
|
58
|
+
}
|
|
59
|
+
}, []);
|
|
60
|
+
const prefetch = useCallback((url) => {
|
|
61
|
+
const resolved = new URL(url, globalThis.location?.href ?? 'http://localhost').href;
|
|
62
|
+
prefetchRSC(resolved);
|
|
63
|
+
}, []);
|
|
64
|
+
const back = useCallback(() => {
|
|
65
|
+
if ('navigation' in globalThis) {
|
|
66
|
+
globalThis.navigation.back();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
globalThis.history?.back();
|
|
70
|
+
}
|
|
71
|
+
}, []);
|
|
72
|
+
const forward = useCallback(() => {
|
|
73
|
+
if ('navigation' in globalThis) {
|
|
74
|
+
globalThis.navigation.forward();
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
globalThis.history?.forward();
|
|
78
|
+
}
|
|
79
|
+
}, []);
|
|
80
|
+
return useMemo(() => ({ pathname, push, replace, prefetch, back, forward }), [pathname, push, replace, prefetch, back, forward]);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/client/hooks.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sEAAsE;AACtE,uEAAuE;AACvE,sCAAsC;AACtC,EAAE;AACF,0DAA0D;AAC1D,qEAAqE;AAErE,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEnE,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AA8DlB,qEAAqE;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,QAAQ,GAAG,oBAAoB,CACnC,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,CAC3B,CAAC;IAEF,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,GAAW,EAAE,EAAE;QACvC,IAAI,YAAY,IAAI,UAAU,EAAE,CAAC;YAC9B,UAAkB,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,GAAW,EAAE,EAAE;QAC1C,IAAI,YAAY,IAAI,UAAU,EAAE,CAAC;YAC9B,UAAkB,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,GAAW,EAAE,EAAE;QAC3C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,EAAE,IAAI,IAAI,kBAAkB,CAAC,CAAC,IAAI,CAAC;QACpF,WAAW,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,IAAI,YAAY,IAAI,UAAU,EAAE,CAAC;YAC9B,UAAkB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/B,IAAI,YAAY,IAAI,UAAU,EAAE,CAAC;YAC9B,UAAkB,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;QAChC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAC5D,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CACnD,CAAC;AACJ,CAAC"}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export { initNavigation, type NavigationOptions } from './client.js';
|
|
1
|
+
export { initNavigation, prefetchRSC, type NavigationOptions } from './client.js';
|
|
2
|
+
export { Link, type LinkProps, type PrefetchStrategy } from './Link.js';
|
|
3
|
+
export { useRouter, type Router } from './hooks.js';
|
|
2
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/client/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
// ── juice/client barrel export ────────────────────────────────────
|
|
2
|
-
export { initNavigation } from './client.js';
|
|
2
|
+
export { initNavigation, prefetchRSC } from './client.js';
|
|
3
|
+
export { Link } from './Link.js';
|
|
4
|
+
export { useRouter } from './hooks.js';
|
|
3
5
|
//# sourceMappingURL=index.js.map
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAA0B,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAAE,WAAW,EAA0B,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,IAAI,EAAyC,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RSC content type used for payload negotiation.
|
|
3
|
+
* Must match the server's `RSC_CONTENT_TYPE` in `rsc.ts`.
|
|
4
|
+
*/
|
|
5
|
+
export declare const RSC_CONTENT_TYPE = "text/x-component";
|
|
6
|
+
/**
|
|
7
|
+
* Prefetch an RSC payload for a given URL. Idempotent — duplicate
|
|
8
|
+
* calls for the same URL are no-ops until the cache entry expires.
|
|
9
|
+
*
|
|
10
|
+
* Used internally by `initNavigation()` and exported for use by
|
|
11
|
+
* `<Link prefetch="hover">` and `useRouter().prefetch()`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function prefetchRSC(url: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Get a prefetched response from the cache, if available.
|
|
16
|
+
* Removes the entry from cache (one-time read).
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export declare function _consumePrefetch(url: string): Promise<Response> | undefined;
|
|
20
|
+
/** @internal */
|
|
21
|
+
export declare function isSameOrigin(href: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Subscribe to pathname changes via the Navigation API.
|
|
24
|
+
* Returns an unsubscribe function.
|
|
25
|
+
*
|
|
26
|
+
* For use with React's `useSyncExternalStore`.
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export declare function _subscribeToPathname(callback: () => void): () => void;
|
|
30
|
+
/**
|
|
31
|
+
* Get the current pathname snapshot for `useSyncExternalStore`.
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export declare function _getPathnameSnapshot(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Server snapshot for SSR — always returns '/'.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
export declare function _getServerPathnameSnapshot(): string;
|
|
40
|
+
//# sourceMappingURL=nav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../../src/client/nav.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAMnD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAY7C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,SAAS,CAM3E;AAID,gBAAgB;AAChB,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOlD;AAID;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAUrE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD"}
|