void-plugin-hono-jsx 0.1.0-alpha.2 → 0.1.0-alpha.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.
Files changed (95) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +73 -20
  3. package/THIRD_PARTY_NOTICES.md +2 -2
  4. package/dist/browser/client.d.ts +2 -1
  5. package/dist/browser/client.d.ts.map +1 -1
  6. package/dist/browser/client.js +76 -22
  7. package/dist/browser/client.js.map +1 -1
  8. package/dist/browser/document-head.d.ts +1 -1
  9. package/dist/browser/document-head.d.ts.map +1 -1
  10. package/dist/browser/document-head.js +22 -1
  11. package/dist/browser/document-head.js.map +1 -1
  12. package/dist/browser/form-view.d.ts +10 -0
  13. package/dist/browser/form-view.d.ts.map +1 -0
  14. package/dist/browser/form-view.js +16 -0
  15. package/dist/browser/form-view.js.map +1 -0
  16. package/dist/browser/form.d.ts +28 -12
  17. package/dist/browser/form.d.ts.map +1 -1
  18. package/dist/browser/form.js +84 -23
  19. package/dist/browser/form.js.map +1 -1
  20. package/dist/browser/markdown.d.ts +15 -0
  21. package/dist/browser/markdown.d.ts.map +1 -0
  22. package/dist/browser/markdown.js +6 -0
  23. package/dist/browser/markdown.js.map +1 -0
  24. package/dist/browser/navigation.d.ts +13 -0
  25. package/dist/browser/navigation.d.ts.map +1 -0
  26. package/dist/browser/navigation.js +83 -0
  27. package/dist/browser/navigation.js.map +1 -0
  28. package/dist/browser/pages.d.ts +33 -3
  29. package/dist/browser/pages.d.ts.map +1 -1
  30. package/dist/browser/pages.js +76 -13
  31. package/dist/browser/pages.js.map +1 -1
  32. package/dist/plugins/compatibility.d.ts +12 -1
  33. package/dist/plugins/compatibility.d.ts.map +1 -1
  34. package/dist/plugins/compatibility.js +65 -22
  35. package/dist/plugins/compatibility.js.map +1 -1
  36. package/dist/plugins/hono-islands.d.ts +1 -1
  37. package/dist/plugins/hono-islands.d.ts.map +1 -1
  38. package/dist/plugins/hono-islands.js +156 -24
  39. package/dist/plugins/hono-islands.js.map +1 -1
  40. package/dist/plugins/hono-markdown.d.ts +4 -0
  41. package/dist/plugins/hono-markdown.d.ts.map +1 -0
  42. package/dist/plugins/hono-markdown.js +91 -0
  43. package/dist/plugins/hono-markdown.js.map +1 -0
  44. package/dist/plugins/hono-pages.d.ts +10 -2
  45. package/dist/plugins/hono-pages.d.ts.map +1 -1
  46. package/dist/plugins/hono-pages.js +55 -18
  47. package/dist/plugins/hono-pages.js.map +1 -1
  48. package/dist/plugins/markdown-api.d.ts +10 -0
  49. package/dist/plugins/markdown-api.d.ts.map +1 -0
  50. package/dist/plugins/markdown-api.js +13 -0
  51. package/dist/plugins/markdown-api.js.map +1 -0
  52. package/dist/plugins/markdown-compiler.d.ts +43 -0
  53. package/dist/plugins/markdown-compiler.d.ts.map +1 -0
  54. package/dist/plugins/markdown-compiler.js +91 -0
  55. package/dist/plugins/markdown-compiler.js.map +1 -0
  56. package/dist/plugins/resolve-component.d.ts +3 -0
  57. package/dist/plugins/resolve-component.d.ts.map +1 -0
  58. package/dist/plugins/resolve-component.js +15 -0
  59. package/dist/plugins/resolve-component.js.map +1 -0
  60. package/dist/plugins/runtime-paths.d.ts +1 -0
  61. package/dist/plugins/runtime-paths.d.ts.map +1 -1
  62. package/dist/plugins/runtime-paths.js +2 -0
  63. package/dist/plugins/runtime-paths.js.map +1 -1
  64. package/dist/server/form-view.d.ts +10 -0
  65. package/dist/server/form-view.d.ts.map +1 -0
  66. package/dist/server/form-view.js +16 -0
  67. package/dist/server/form-view.js.map +1 -0
  68. package/dist/server/form.d.ts +28 -12
  69. package/dist/server/form.d.ts.map +1 -1
  70. package/dist/server/form.js +84 -23
  71. package/dist/server/form.js.map +1 -1
  72. package/dist/server/markdown.d.ts +15 -0
  73. package/dist/server/markdown.d.ts.map +1 -0
  74. package/dist/server/markdown.js +6 -0
  75. package/dist/server/markdown.js.map +1 -0
  76. package/dist/server/pages.d.ts +33 -3
  77. package/dist/server/pages.d.ts.map +1 -1
  78. package/dist/server/pages.js +76 -13
  79. package/dist/server/pages.js.map +1 -1
  80. package/package.json +24 -6
  81. package/src/plugins/compatibility.ts +56 -19
  82. package/src/plugins/hono-islands.ts +121 -23
  83. package/src/plugins/hono-markdown.ts +78 -0
  84. package/src/plugins/hono-pages.ts +54 -17
  85. package/src/plugins/markdown-api.ts +17 -0
  86. package/src/plugins/markdown-compiler.ts +77 -0
  87. package/src/plugins/resolve-component.ts +12 -0
  88. package/src/plugins/runtime-paths.ts +2 -0
  89. package/src/runtime/client.tsx +66 -24
  90. package/src/runtime/document-head.ts +20 -1
  91. package/src/runtime/form-view.tsx +23 -0
  92. package/src/runtime/form.ts +82 -41
  93. package/src/runtime/markdown.ts +7 -0
  94. package/src/runtime/navigation.ts +72 -0
  95. package/src/runtime/pages.tsx +77 -15
@@ -1,9 +1,15 @@
1
- import { createContext, useContext, type JSX } from 'hono/jsx';
2
- import type { VoidRouter } from 'void/pages-client';
1
+ import { createContext, useContext, useEffect, useRef, type JSX } from 'hono/jsx';
2
+ import { createActionClient, mergeDataIntoHref, type NavigationState, type NavigationMethod, type VoidRouter } from 'void/pages-client';
3
+ import type { CacheForInput } from 'void/pages-prefetch';
3
4
  import type { PageObject } from 'void/pages-protocol';
4
5
  export type { DeferredState as Deferred } from 'void';
5
6
 
7
+ const actionClient = createActionClient();
8
+ export const action = actionClient.action;
9
+ export const setActionRouter = actionClient.setActionRouter;
10
+
6
11
  export const RouterContext = createContext<VoidRouter | null>(null);
12
+ export const NavigationContext = createContext<NavigationState | null>(null);
7
13
  export const PageContext = createContext<PageObject | null>(null);
8
14
 
9
15
  function usePage() {
@@ -12,6 +18,12 @@ function usePage() {
12
18
  return page;
13
19
  }
14
20
 
21
+ export function useNavigation(): NavigationState {
22
+ const navigation = useContext(NavigationContext);
23
+ if (!navigation) throw new Error('Hono Pages: useNavigation requires a page provider.');
24
+ return navigation;
25
+ }
26
+
15
27
  export function useShared() { return usePage().shared; }
16
28
  export function useParams() { return usePage().params; }
17
29
  export function useErrors() { return usePage().errors ?? {}; }
@@ -22,24 +34,74 @@ export function useRouter(): VoidRouter {
22
34
  return router;
23
35
  }
24
36
 
25
- type LinkProps = Omit<JSX.IntrinsicElements['a'], 'href' | 'onClick'> & {
37
+ export type PrefetchStrategy = 'hover' | 'click' | 'mount' | 'visible';
38
+ export type LinkProps = Omit<JSX.IntrinsicElements['a'], 'href' | 'onClick'> & {
26
39
  href: string;
40
+ method?: NavigationMethod | Lowercase<NavigationMethod>;
41
+ data?: Record<string, unknown>;
42
+ preserveScroll?: boolean;
43
+ preserveState?: boolean;
27
44
  reloadDocument?: boolean;
28
45
  replace?: boolean;
46
+ viewTransition?: boolean;
47
+ prefetch?: boolean | PrefetchStrategy | ReadonlyArray<PrefetchStrategy>;
48
+ cacheFor?: CacheForInput;
49
+ onNavigate?: (event: { preventDefault(): void }) => void;
29
50
  onClick?: (event: MouseEvent) => void;
30
51
  };
31
52
 
32
- export function Link({ href, reloadDocument, replace, onClick, ...attributes }: LinkProps) {
53
+ export function Link({ href, method = 'GET', data, preserveScroll, preserveState, reloadDocument, replace, viewTransition,
54
+ prefetch = false, cacheFor, onNavigate, onClick, onMouseEnter, onMouseLeave, onFocus, onMouseDown, onTouchStart,
55
+ ...attributes }: LinkProps) {
33
56
  const router = useRouter();
34
- return <a {...attributes} href={href} onClick={(event: MouseEvent) => {
35
- onClick?.(event);
36
- if (event.defaultPrevented || reloadDocument || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
37
- if (attributes.download !== undefined || (attributes.target && attributes.target !== '_self') || attributes.rel?.split(/\s+/).includes('external')) return;
38
- const url = new URL(href, window.location.href);
39
- if (url.origin !== window.location.origin || !['http:', 'https:'].includes(url.protocol)) return;
40
- event.preventDefault();
41
- void router.visit(url, { replace }).catch((error: unknown) => {
42
- window.dispatchEvent(new CustomEvent('hono-pages:navigation-error', { detail: error }));
43
- });
44
- }} />;
57
+ const element = useRef<HTMLAnchorElement | HTMLButtonElement | null>(null);
58
+ const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
59
+ const normalized = method.toUpperCase();
60
+ const get = normalized === 'GET';
61
+ if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(normalized)) throw new Error('Hono Link: unsupported method.');
62
+ if (!get && (prefetch !== false || reloadDocument)) throw new Error('Hono Link: prefetch and reloadDocument only support GET links.');
63
+ const destination = get ? mergeDataIntoHref(href, data) : href;
64
+ const strategies = reloadDocument || prefetch === false ? [] : prefetch === true ? ['hover'] : typeof prefetch === 'string' ? [prefetch] : prefetch;
65
+ if (strategies.some((strategy) => !['hover', 'click', 'mount', 'visible'].includes(strategy))) throw new Error('Hono Link: unsupported prefetch strategy.');
66
+ const clear = () => { clearTimeout(timer.current); timer.current = undefined; };
67
+ const canHandle = () => {
68
+ const url = new URL(destination, window.location.href);
69
+ return !reloadDocument && attributes.download === undefined && (!attributes.target || attributes.target === '_self') &&
70
+ !attributes.rel?.split(/\s+/).includes('external') && url.origin === window.location.origin && ['http:', 'https:'].includes(url.protocol);
71
+ };
72
+ const report = (error: unknown) => window.dispatchEvent(new CustomEvent('hono-pages:navigation-error', { detail: error }));
73
+ const fetchAhead = () => {
74
+ if (canHandle()) void router.prefetch(destination, { cacheFor }).catch(report);
75
+ };
76
+ useEffect(() => {
77
+ if (strategies.includes('mount')) fetchAhead();
78
+ let observer: IntersectionObserver | undefined;
79
+ if (strategies.includes('visible') && element.current) {
80
+ observer = new IntersectionObserver((entries) => {
81
+ if (entries.some((entry) => entry.isIntersecting)) { observer?.disconnect(); fetchAhead(); }
82
+ });
83
+ observer.observe(element.current);
84
+ }
85
+ return () => { clear(); observer?.disconnect(); };
86
+ }, [destination, prefetch, cacheFor, reloadDocument, router]);
87
+ const events = {
88
+ onClick(event: MouseEvent) {
89
+ onClick?.(event);
90
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || !canHandle()) return;
91
+ let cancelled = false;
92
+ onNavigate?.({ preventDefault() { cancelled = true; } });
93
+ event.preventDefault();
94
+ if (!cancelled) void router.visit(new URL(destination, window.location.href), { method: normalized, data: get ? undefined : data, preserveScroll, preserveState, replace, viewTransition }).catch(report);
95
+ },
96
+ onMouseEnter(event: MouseEvent) {
97
+ onMouseEnter?.(event); clear();
98
+ if (!event.defaultPrevented && strategies.includes('hover')) timer.current = setTimeout(fetchAhead, router._hoverDelay);
99
+ },
100
+ onMouseLeave(event: MouseEvent) { onMouseLeave?.(event); clear(); },
101
+ onFocus(event: FocusEvent) { onFocus?.(event); if (!event.defaultPrevented && strategies.includes('hover')) fetchAhead(); },
102
+ onMouseDown(event: MouseEvent) { onMouseDown?.(event); if (!event.defaultPrevented && strategies.includes('click')) fetchAhead(); },
103
+ onTouchStart(event: TouchEvent) { onTouchStart?.(event); if (!event.defaultPrevented && strategies.includes('click')) fetchAhead(); },
104
+ };
105
+ if (get) return <a {...attributes} {...events} ref={element} href={destination} />;
106
+ return <button {...attributes} {...events} ref={element} type="button" />;
45
107
  }