void-plugin-hono-jsx 0.1.0-alpha.4 → 0.1.0-rc.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +51 -22
  3. package/dist/browser/form-view.d.ts +4 -3
  4. package/dist/browser/form-view.d.ts.map +1 -1
  5. package/dist/browser/form-view.js +6 -4
  6. package/dist/browser/form-view.js.map +1 -1
  7. package/dist/browser/form.d.ts +2 -2
  8. package/dist/browser/form.d.ts.map +1 -1
  9. package/dist/browser/form.js +13 -5
  10. package/dist/browser/form.js.map +1 -1
  11. package/dist/browser/navigation.d.ts.map +1 -1
  12. package/dist/browser/navigation.js +82 -21
  13. package/dist/browser/navigation.js.map +1 -1
  14. package/dist/plugins/compatibility.d.ts +2 -0
  15. package/dist/plugins/compatibility.d.ts.map +1 -1
  16. package/dist/plugins/compatibility.js +13 -4
  17. package/dist/plugins/compatibility.js.map +1 -1
  18. package/dist/plugins/hono-islands.d.ts.map +1 -1
  19. package/dist/plugins/hono-islands.js +78 -31
  20. package/dist/plugins/hono-islands.js.map +1 -1
  21. package/dist/plugins/hono-jsx.d.ts +11 -3
  22. package/dist/plugins/hono-jsx.d.ts.map +1 -1
  23. package/dist/plugins/hono-jsx.js +183 -18
  24. package/dist/plugins/hono-jsx.js.map +1 -1
  25. package/dist/plugins/hono-markdown.d.ts.map +1 -1
  26. package/dist/plugins/hono-markdown.js +25 -14
  27. package/dist/plugins/hono-markdown.js.map +1 -1
  28. package/dist/plugins/jsx-transform.d.ts +4 -0
  29. package/dist/plugins/jsx-transform.d.ts.map +1 -0
  30. package/dist/plugins/jsx-transform.js +21 -0
  31. package/dist/plugins/jsx-transform.js.map +1 -0
  32. package/dist/plugins/markdown-api.d.ts +2 -1
  33. package/dist/plugins/markdown-api.d.ts.map +1 -1
  34. package/dist/plugins/markdown-api.js.map +1 -1
  35. package/dist/server/form-view.d.ts +4 -3
  36. package/dist/server/form-view.d.ts.map +1 -1
  37. package/dist/server/form-view.js +6 -4
  38. package/dist/server/form-view.js.map +1 -1
  39. package/dist/server/form.d.ts +2 -2
  40. package/dist/server/form.d.ts.map +1 -1
  41. package/dist/server/form.js +13 -5
  42. package/dist/server/form.js.map +1 -1
  43. package/package.json +6 -6
  44. package/src/plugins/compatibility.ts +11 -4
  45. package/src/plugins/hono-islands.ts +59 -27
  46. package/src/plugins/hono-jsx.ts +176 -17
  47. package/src/plugins/hono-markdown.ts +20 -10
  48. package/src/plugins/jsx-transform.ts +20 -0
  49. package/src/plugins/markdown-api.ts +2 -1
  50. package/src/runtime/form-view.tsx +9 -5
  51. package/src/runtime/form.ts +13 -8
  52. package/src/runtime/navigation.ts +64 -21
  53. package/dist/plugins/hono-pages.d.ts +0 -12
  54. package/dist/plugins/hono-pages.d.ts.map +0 -1
  55. package/dist/plugins/hono-pages.js +0 -186
  56. package/dist/plugins/hono-pages.js.map +0 -1
  57. package/src/plugins/hono-pages.ts +0 -179
@@ -1,11 +1,10 @@
1
- import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError } from 'void/pages-client';
1
+ import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError, VoidActionError } from 'void/pages-client';
2
2
  /** DOM updates still complete when a native animation is skipped or replaced. */
3
3
  export async function applyWithTransition(enabled, update) {
4
4
  if (!enabled || !document.startViewTransition)
5
5
  return update();
6
6
  const transition = document.startViewTransition(update);
7
- // ready rejects for expected animation skips; updateCallbackDone separately
8
- // propagates rendering failures. Never patch the browser's native method.
7
+ // Rendering failures propagate through updateCallbackDone; animation skips do not.
9
8
  void transition.ready.catch(() => { });
10
9
  void transition.finished?.catch(() => { });
11
10
  await transition.updateCallbackDone;
@@ -24,40 +23,96 @@ export async function createHonoRouter(config) {
24
23
  let routeUrl = currentUrl();
25
24
  let routeParams = engine.initialPageData.params ?? {};
26
25
  let active = null;
26
+ let resource;
27
+ let multipartController;
28
+ let multipartId = 0;
27
29
  const setRoute = (url, params = routeParams) => { routeUrl = url; routeParams = params; config.onRouteChange?.(url); };
28
- const assertCurrent = (resource) => {
29
- if (active !== resource.id)
30
+ const assertCurrent = (id) => {
31
+ if (active !== id)
30
32
  throw new DOMException('Hono Pages: stale navigation callback discarded.', 'AbortError');
31
33
  };
32
- async function apply(resource) {
33
- active = resource.id;
34
+ async function apply(next, navigationMethod = next.method) {
35
+ resource = next;
36
+ active = next.id;
34
37
  const previousParams = routeParams;
35
- config.onNavigationChange?.({ state: resource.method === 'GET' ? 'loading' : 'submitting', location: toUrl(resource.fetchUrl + resource.hash), method: resource.method.toUpperCase() });
38
+ config.onNavigationChange?.({ state: navigationMethod === 'GET' ? 'loading' : 'submitting', location: toUrl(next.fetchUrl + next.hash), method: navigationMethod.toUpperCase() });
36
39
  try {
37
- const result = await resource.shell;
40
+ const result = await next.shell;
38
41
  if (!result)
39
42
  return;
40
- assertCurrent(resource);
41
- setRoute(result.pageData.url || resource.fetchUrl + resource.hash, result.pageData.params ?? {});
42
- await applyWithTransition(resource.options.viewTransition ?? config.viewTransitions, async () => {
43
- assertCurrent(resource);
44
- await config.adapter.applyUpdate(result.pageData, result.component, result.layouts, { preserveState: resource.options.preserveState ?? resource.method !== 'GET' });
43
+ assertCurrent(next.id);
44
+ setRoute(result.pageData.url || next.fetchUrl + next.hash, result.pageData.params ?? {});
45
+ await applyWithTransition(next.options.viewTransition ?? config.viewTransitions, async () => {
46
+ assertCurrent(next.id);
47
+ await config.adapter.applyUpdate(result.pageData, result.component, result.layouts, { preserveState: next.options.preserveState ?? next.method !== 'GET' });
45
48
  });
46
- assertCurrent(resource);
47
- await resource.commit();
48
- if (active === resource.id)
49
+ assertCurrent(next.id);
50
+ await next.commit();
51
+ if (active === next.id)
49
52
  setRoute(currentUrl(), result.pageData.params ?? {});
50
53
  return result.pageData;
51
54
  }
52
55
  catch (error) {
53
- if (active === resource.id)
56
+ if (active === next.id)
54
57
  routeParams = previousParams;
55
- if (resource.method === 'GET' && !resource.options._resolveOnShell && isAbortError(error))
58
+ if (navigationMethod === 'GET' && !next.options._resolveOnShell && isAbortError(error))
56
59
  return;
57
60
  throw error;
58
61
  }
59
62
  finally {
60
- if (active === resource.id) {
63
+ if (resource === next)
64
+ resource = undefined;
65
+ if (active === next.id) {
66
+ active = null;
67
+ setRoute(currentUrl());
68
+ config.onNavigationChange?.(idleNavigationState());
69
+ }
70
+ }
71
+ }
72
+ async function visitMultipart(url, options) {
73
+ const method = (options.method ?? 'GET').toUpperCase();
74
+ if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(method))
75
+ throw new Error('Hono Pages: FormData requires POST, PUT, PATCH or DELETE.');
76
+ const target = toUrl(url);
77
+ if (target.origin !== window.location.origin || !['http:', 'https:'].includes(target.protocol))
78
+ throw new Error('Hono Pages: FormData actions must target the current origin.');
79
+ // Negative IDs cannot collide with the shared engine's positive generation IDs.
80
+ const id = --multipartId;
81
+ active = id;
82
+ resource?.abort();
83
+ multipartController?.abort();
84
+ const controller = multipartController = new AbortController();
85
+ engine.flushAll();
86
+ config.onNavigationChange?.({ state: 'submitting', location: target, method: method });
87
+ try {
88
+ const response = await fetch(target, { method, body: options.data, headers: { 'X-VoidPages': 'true', Accept: 'application/json' }, signal: controller.signal });
89
+ let body;
90
+ if (response.headers.get('content-type')?.includes('application/json'))
91
+ body = await response.json();
92
+ else if (!response.ok)
93
+ body = await response.text();
94
+ else
95
+ await response.body?.cancel();
96
+ assertCurrent(id);
97
+ if (!response.ok)
98
+ throw new VoidActionError({ body, status: response.status, statusText: response.statusText, url });
99
+ if (typeof body === 'object' && body !== null && 'errors' in body && body.errors && Object.keys(body.errors).length) {
100
+ throw new VoidActionError({ body: { errors: body.errors }, status: 422, statusText: 'Unprocessable Content', url });
101
+ }
102
+ if (!response.headers.get('X-VoidPages')) {
103
+ window.location.assign(response.url || target.href);
104
+ return;
105
+ }
106
+ const destination = response.redirected ? response.url : typeof body === 'object' && body !== null && 'url' in body && typeof body.url === 'string' ? body.url : target.href;
107
+ multipartController = undefined;
108
+ // The public engine cannot consume raw FormData or an externally fetched shell.
109
+ // Refresh through its GET protocol, retaining component state and mutation UI.
110
+ return await apply(engine.prepareVisit(normalize(destination), { ...options, method: 'GET', data: undefined, preserveState: options.preserveState ?? true, preserveScroll: options.preserveScroll ?? true }), method);
111
+ }
112
+ finally {
113
+ if (multipartController === controller)
114
+ multipartController = undefined;
115
+ if (active === id) {
61
116
  active = null;
62
117
  setRoute(currentUrl());
63
118
  config.onNavigationChange?.(idleNavigationState());
@@ -70,7 +125,13 @@ export async function createHonoRouter(config) {
70
125
  get path() { return this.url.pathname; },
71
126
  get query() { return this.url.searchParams; },
72
127
  get params() { return routeParams; },
73
- visit(url, options = {}) { return apply(engine.prepareVisit(normalize(url), options)); },
128
+ visit(url, options = {}) {
129
+ if (options.data instanceof FormData)
130
+ return visitMultipart(normalize(url), options);
131
+ multipartController?.abort();
132
+ multipartController = undefined;
133
+ return apply(engine.prepareVisit(normalize(url), options));
134
+ },
74
135
  refresh(options = {}) { return raw.visit(window.location.pathname + window.location.search, { preserveState: true, preserveScroll: true, ...options }); },
75
136
  prefetch(url, options = {}) { return engine.prefetch(normalize(url), options); },
76
137
  flush(url, method = 'GET') { engine.flush(normalize(url), method); },
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/runtime/navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError, type PageNavigationResource, type VoidRouter, type VoidRouterConfig, type NavigationMethod } from 'void/pages-client';\n\n/** DOM updates still complete when a native animation is skipped or replaced. */\nexport async function applyWithTransition(enabled: boolean, update: () => Promise<void>) {\n if (!enabled || !document.startViewTransition) return update();\n const transition = document.startViewTransition(update);\n // ready rejects for expected animation skips; updateCallbackDone separately\n // propagates rendering failures. Never patch the browser's native method.\n void transition.ready.catch(() => {});\n void transition.finished?.catch(() => {});\n await transition.updateCallbackDone;\n}\n\n/** Hono render integration over Void's public navigation engine and facade. */\nexport async function createHonoRouter(config: VoidRouterConfig) {\n const engine = await createPageNavigationEngine(config);\n const currentUrl = () => window.location.pathname + window.location.search + window.location.hash;\n const toUrl = (value: string | URL) => new URL(value, window.location.href);\n const normalize = (value: string | URL) => {\n const url = toUrl(value);\n // Preserve foreign origins and schemes for the shared engine's guards.\n return url.origin === window.location.origin && ['http:', 'https:'].includes(url.protocol)\n ? url.pathname + url.search + url.hash : url.href;\n };\n let routeUrl = currentUrl();\n let routeParams = engine.initialPageData.params ?? {};\n let active: number | null = null;\n const setRoute = (url: string, params = routeParams) => { routeUrl = url; routeParams = params; config.onRouteChange?.(url); };\n const assertCurrent = (resource: PageNavigationResource) => {\n if (active !== resource.id) throw new DOMException('Hono Pages: stale navigation callback discarded.', 'AbortError');\n };\n async function apply(resource: PageNavigationResource) {\n active = resource.id;\n const previousParams = routeParams;\n config.onNavigationChange?.({ state: resource.method === 'GET' ? 'loading' : 'submitting', location: toUrl(resource.fetchUrl + resource.hash), method: resource.method.toUpperCase() as NavigationMethod });\n try {\n const result = await resource.shell;\n if (!result) return;\n assertCurrent(resource);\n setRoute(result.pageData.url || resource.fetchUrl + resource.hash, result.pageData.params ?? {});\n await applyWithTransition(resource.options.viewTransition ?? config.viewTransitions, async () => {\n assertCurrent(resource);\n await config.adapter.applyUpdate(result.pageData, result.component, result.layouts, { preserveState: resource.options.preserveState ?? resource.method !== 'GET' });\n });\n assertCurrent(resource);\n await resource.commit();\n if (active === resource.id) setRoute(currentUrl(), result.pageData.params ?? {});\n return result.pageData;\n } catch (error) {\n if (active === resource.id) routeParams = previousParams;\n if (resource.method === 'GET' && !resource.options._resolveOnShell && isAbortError(error)) return;\n throw error;\n } finally {\n if (active === resource.id) { active = null; setRoute(currentUrl()); config.onNavigationChange?.(idleNavigationState()); }\n }\n }\n const raw: VoidRouter = {\n _hoverDelay: engine._hoverDelay,\n get url() { return toUrl(routeUrl); },\n get path() { return this.url.pathname; },\n get query() { return this.url.searchParams; },\n get params() { return routeParams; },\n visit(url, options = {}) { return apply(engine.prepareVisit(normalize(url), options)); },\n refresh(options = {}) { return raw.visit(window.location.pathname + window.location.search, { preserveState: true, preserveScroll: true, ...options }); },\n prefetch(url, options = {}) { return engine.prefetch(normalize(url), options); },\n flush(url, method = 'GET') { engine.flush(normalize(url), method); },\n flushAll() { engine.flushAll(); },\n };\n const router = createRouterFacade(raw, () => routeUrl, () => routeParams);\n engine.setPopStateHandler((url, options) => { void router.visit(url, options).catch(error => window.dispatchEvent(new CustomEvent('hono-pages:navigation-error', { detail: error }))); });\n return { router, initialPageData: engine.initialPageData, initialComponent: engine.initialComponent, initialLayouts: engine.initialLayouts, start: engine.start, commitInitialRender: engine.commitInitialRender };\n}\n"]}
1
+ {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/runtime/navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError, VoidActionError, type VisitOptions, type PageNavigationResource, type VoidRouter, type VoidRouterConfig, type NavigationMethod } from 'void/pages-client';\n\n/** DOM updates still complete when a native animation is skipped or replaced. */\nexport async function applyWithTransition(enabled: boolean, update: () => Promise<void>) {\n if (!enabled || !document.startViewTransition) return update();\n const transition = document.startViewTransition(update);\n // Rendering failures propagate through updateCallbackDone; animation skips do not.\n void transition.ready.catch(() => {});\n void transition.finished?.catch(() => {});\n await transition.updateCallbackDone;\n}\n\n/** Hono render integration over Void's public navigation engine and facade. */\nexport async function createHonoRouter(config: VoidRouterConfig) {\n const engine = await createPageNavigationEngine(config);\n const currentUrl = () => window.location.pathname + window.location.search + window.location.hash;\n const toUrl = (value: string | URL) => new URL(value, window.location.href);\n const normalize = (value: string | URL) => {\n const url = toUrl(value);\n // Preserve foreign origins and schemes for the shared engine's guards.\n return url.origin === window.location.origin && ['http:', 'https:'].includes(url.protocol)\n ? url.pathname + url.search + url.hash : url.href;\n };\n let routeUrl = currentUrl();\n let routeParams = engine.initialPageData.params ?? {};\n let active: number | null = null;\n let resource: PageNavigationResource | undefined;\n let multipartController: AbortController | undefined;\n let multipartId = 0;\n const setRoute = (url: string, params = routeParams) => { routeUrl = url; routeParams = params; config.onRouteChange?.(url); };\n const assertCurrent = (id: number) => {\n if (active !== id) throw new DOMException('Hono Pages: stale navigation callback discarded.', 'AbortError');\n };\n async function apply(next: PageNavigationResource, navigationMethod = next.method) {\n resource = next; active = next.id;\n const previousParams = routeParams;\n config.onNavigationChange?.({ state: navigationMethod === 'GET' ? 'loading' : 'submitting', location: toUrl(next.fetchUrl + next.hash), method: navigationMethod.toUpperCase() as NavigationMethod });\n try {\n const result = await next.shell;\n if (!result) return;\n assertCurrent(next.id);\n setRoute(result.pageData.url || next.fetchUrl + next.hash, result.pageData.params ?? {});\n await applyWithTransition(next.options.viewTransition ?? config.viewTransitions, async () => {\n assertCurrent(next.id);\n await config.adapter.applyUpdate(result.pageData, result.component, result.layouts, { preserveState: next.options.preserveState ?? next.method !== 'GET' });\n });\n assertCurrent(next.id);\n await next.commit();\n if (active === next.id) setRoute(currentUrl(), result.pageData.params ?? {});\n return result.pageData;\n } catch (error) {\n if (active === next.id) routeParams = previousParams;\n if (navigationMethod === 'GET' && !next.options._resolveOnShell && isAbortError(error)) return;\n throw error;\n } finally {\n if (resource === next) resource = undefined;\n if (active === next.id) { active = null; setRoute(currentUrl()); config.onNavigationChange?.(idleNavigationState()); }\n }\n }\n\n async function visitMultipart(url: string, options: VisitOptions) {\n const method = (options.method ?? 'GET').toUpperCase();\n if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) throw new Error('Hono Pages: FormData requires POST, PUT, PATCH or DELETE.');\n const target = toUrl(url);\n if (target.origin !== window.location.origin || !['http:', 'https:'].includes(target.protocol)) throw new Error('Hono Pages: FormData actions must target the current origin.');\n // Negative IDs cannot collide with the shared engine's positive generation IDs.\n const id = --multipartId;\n active = id;\n resource?.abort(); multipartController?.abort();\n const controller = multipartController = new AbortController();\n engine.flushAll();\n config.onNavigationChange?.({ state: 'submitting', location: target, method: method as NavigationMethod });\n try {\n const response = await fetch(target, { method, body: options.data as FormData, headers: { 'X-VoidPages': 'true', Accept: 'application/json' }, signal: controller.signal });\n let body: unknown;\n if (response.headers.get('content-type')?.includes('application/json')) body = await response.json();\n else if (!response.ok) body = await response.text();\n else await response.body?.cancel();\n assertCurrent(id);\n if (!response.ok) throw new VoidActionError({ body, status: response.status, statusText: response.statusText, url });\n if (typeof body === 'object' && body !== null && 'errors' in body && body.errors && Object.keys(body.errors).length) {\n throw new VoidActionError({ body: { errors: body.errors }, status: 422, statusText: 'Unprocessable Content', url });\n }\n if (!response.headers.get('X-VoidPages')) { window.location.assign(response.url || target.href); return; }\n const destination = response.redirected ? response.url : typeof body === 'object' && body !== null && 'url' in body && typeof body.url === 'string' ? body.url : target.href;\n multipartController = undefined;\n // The public engine cannot consume raw FormData or an externally fetched shell.\n // Refresh through its GET protocol, retaining component state and mutation UI.\n return await apply(engine.prepareVisit(normalize(destination), { ...options, method: 'GET', data: undefined, preserveState: options.preserveState ?? true, preserveScroll: options.preserveScroll ?? true }), method);\n } finally {\n if (multipartController === controller) multipartController = undefined;\n if (active === id) { active = null; setRoute(currentUrl()); config.onNavigationChange?.(idleNavigationState()); }\n }\n }\n\n const raw: VoidRouter = {\n _hoverDelay: engine._hoverDelay,\n get url() { return toUrl(routeUrl); },\n get path() { return this.url.pathname; },\n get query() { return this.url.searchParams; },\n get params() { return routeParams; },\n visit(url, options = {}) {\n if (options.data instanceof FormData) return visitMultipart(normalize(url), options);\n multipartController?.abort(); multipartController = undefined;\n return apply(engine.prepareVisit(normalize(url), options));\n },\n refresh(options = {}) { return raw.visit(window.location.pathname + window.location.search, { preserveState: true, preserveScroll: true, ...options }); },\n prefetch(url, options = {}) { return engine.prefetch(normalize(url), options); },\n flush(url, method = 'GET') { engine.flush(normalize(url), method); },\n flushAll() { engine.flushAll(); },\n };\n const router = createRouterFacade(raw, () => routeUrl, () => routeParams);\n engine.setPopStateHandler((url, options) => { void router.visit(url, options).catch(error => window.dispatchEvent(new CustomEvent('hono-pages:navigation-error', { detail: error }))); });\n return { router, initialPageData: engine.initialPageData, initialComponent: engine.initialComponent, initialLayouts: engine.initialLayouts, start: engine.start, commitInitialRender: engine.commitInitialRender };\n}\n"]}
@@ -4,6 +4,8 @@ export declare const supportedFrameworkRanges: {
4
4
  vite: string;
5
5
  };
6
6
  export declare const supportedViteRange: string;
7
+ export declare const supportedNodeRange = ">=24.21.0 <25.0.0";
8
+ export declare function supportsNode(version: unknown): boolean;
7
9
  export declare function supportsFramework(name: keyof typeof supportedFrameworkRanges, version: unknown): boolean;
8
10
  export declare const supportsVite: (version: unknown) => boolean;
9
11
  export declare function assertSupportedFrameworks({ root, viteVersion }: {
@@ -1 +1 @@
1
- {"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../../src/plugins/compatibility.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,wBAAwB;IACnC,IAAI;IACJ,IAAI;IACJ,IAAI;CACL,CAAC;AACF,eAAO,MAAM,kBAAkB,QAAgC,CAAC;AAIhE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,OAAO,wBAAwB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CASxG;AACD,eAAO,MAAM,YAAY,YAAa,OAAO,KAAG,OAA6C,CAAC;AAwB9F,wBAAgB,yBAAyB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,QAetG"}
1
+ {"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../../src/plugins/compatibility.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,wBAAwB;IACnC,IAAI;IACJ,IAAI;IACJ,IAAI;CACL,CAAC;AACF,eAAO,MAAM,kBAAkB,QAAgC,CAAC;AAChE,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AACtD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAItD;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,OAAO,wBAAwB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CASxG;AACD,eAAO,MAAM,YAAY,YAAa,OAAO,KAAG,OAA6C,CAAC;AAwB9F,wBAAgB,yBAAyB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,QAgBtG"}
@@ -3,13 +3,20 @@ import { existsSync, readFileSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { version as helperViteVersion } from 'vite';
5
5
  export const supportedFrameworkRanges = {
6
- void: '>=0.26.1 <0.28.0',
6
+ void: '>=1.1.11 <1.2.0',
7
7
  hono: '>=4.13.13 <5.0.0',
8
8
  vite: '>=8.0.16 <9.0.0',
9
9
  };
10
10
  export const supportedViteRange = supportedFrameworkRanges.vite;
11
- // Only stable releases are admitted. Void is pre-1.0, so its next minor
12
- // requires explicit validation; Hono and Vite use their current major lines.
11
+ export const supportedNodeRange = '>=24.21.0 <25.0.0';
12
+ export function supportsNode(version) {
13
+ if (typeof version !== 'string')
14
+ return false;
15
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version);
16
+ return !!match && match[1] === '24' && Number.isSafeInteger(Number(match[2])) && Number.isSafeInteger(Number(match[3])) && Number(match[2]) >= 21;
17
+ }
18
+ // Only stable releases are admitted. Void minors are validated explicitly
19
+ // because the adapter also uses its virtual-module integration contracts.
13
20
  export function supportsFramework(name, version) {
14
21
  if (typeof version !== 'string')
15
22
  return false;
@@ -20,7 +27,7 @@ export function supportsFramework(name, version) {
20
27
  if (![major, minor, patch].every(Number.isSafeInteger))
21
28
  return false;
22
29
  if (name === 'void')
23
- return major === 0 && ((minor === 26 && patch >= 1) || minor === 27);
30
+ return major === 1 && minor === 1 && patch >= 11;
24
31
  if (name === 'hono')
25
32
  return major === 4 && (minor > 13 || (minor === 13 && patch >= 13));
26
33
  return major === 8 && (minor > 0 || patch >= 16);
@@ -54,6 +61,8 @@ function describe(name, require) {
54
61
  }
55
62
  }
56
63
  export function assertSupportedFrameworks({ root, viteVersion }) {
64
+ if (!supportsNode(process.versions.node))
65
+ throw new Error(`Hono Pages: Node ${process.versions.node} is unsupported; expected ${supportedNodeRange}. Use Node 24 LTS from your project root.`);
57
66
  const adapter = createRequire(import.meta.url);
58
67
  const project = createRequire(join(root, 'package.json'));
59
68
  const diagnostic = () => `Running Vite from hook context: ${typeof viteVersion === 'string' ? viteVersion : 'unavailable'}. Adapter helper Vite: ${helperViteVersion}; package.json: ${describe('vite', adapter)}. Project Vite package.json: ${describe('vite', project)}. Package paths are diagnostics, not the running Vite identity.`;
@@ -1 +1 @@
1
- {"version":3,"file":"compatibility.js","sourceRoot":"","sources":["../../src/plugins/compatibility.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from 'node:module';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { version as helperViteVersion } from 'vite';\n\nexport const supportedFrameworkRanges = {\n void: '>=0.26.1 <0.28.0',\n hono: '>=4.13.13 <5.0.0',\n vite: '>=8.0.16 <9.0.0',\n};\nexport const supportedViteRange = supportedFrameworkRanges.vite;\n\n// Only stable releases are admitted. Void is pre-1.0, so its next minor\n// requires explicit validation; Hono and Vite use their current major lines.\nexport function supportsFramework(name: keyof typeof supportedFrameworkRanges, version: unknown): boolean {\n if (typeof version !== 'string') return false;\n const match = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$/.exec(version);\n if (!match) return false;\n const [major, minor, patch] = match.slice(1).map(Number);\n if (![major, minor, patch].every(Number.isSafeInteger)) return false;\n if (name === 'void') return major === 0 && ((minor === 26 && patch >= 1) || minor === 27);\n if (name === 'hono') return major === 4 && (minor > 13 || (minor === 13 && patch >= 13));\n return major === 8 && (minor > 0 || patch >= 16);\n}\nexport const supportsVite = (version: unknown): boolean => supportsFramework('vite', version);\n\ntype PackageInfo = { version: string; path: string };\nfunction packageInfo(name: string, require: NodeRequire): PackageInfo {\n let directory = dirname(require.resolve(name));\n while (true) {\n const path = join(directory, 'package.json');\n if (existsSync(path)) {\n const metadata: unknown = JSON.parse(readFileSync(path, 'utf8'));\n if (typeof metadata === 'object' && metadata !== null && 'name' in metadata && (metadata.name === name || (name === 'vite' && metadata.name === '@voidzero-dev/vite-plus-core'))) {\n if (!('version' in metadata) || typeof metadata.version !== 'string') throw new Error(`Hono Pages: ${name} package version is missing (${path}).`);\n return { version: metadata.version, path };\n }\n }\n const parent = dirname(directory);\n if (parent === directory) throw new Error(`Hono Pages: cannot locate ${name} package metadata from ${require.resolve(name)}.`);\n directory = parent;\n }\n}\nfunction describe(name: string, require: NodeRequire): string {\n try { const info = packageInfo(name, require); return `${info.path} (metadata ${info.version})`; }\n catch { return `not resolved from ${require.resolve.paths(name)?.join(', ') ?? 'this context'}`; }\n}\n\nexport function assertSupportedFrameworks({ root, viteVersion }: { root: string; viteVersion: unknown }) {\n const adapter = createRequire(import.meta.url);\n const project = createRequire(join(root, 'package.json'));\n const diagnostic = () => `Running Vite from hook context: ${typeof viteVersion === 'string' ? viteVersion : 'unavailable'}. Adapter helper Vite: ${helperViteVersion}; package.json: ${describe('vite', adapter)}. Project Vite package.json: ${describe('vite', project)}. Package paths are diagnostics, not the running Vite identity.`;\n if (!supportsVite(viteVersion)) {\n throw new Error(`Hono Pages: running Vite ${typeof viteVersion === 'string' ? viteVersion : 'unavailable'} is unsupported; expected ${supportedViteRange}. ${diagnostic()} Update the CLI/launcher that executes Vite. For bundled launchers such as vp, installing project vite alone does not update the running Vite. For standard Vite, install vite@^8.3.4 and run vite directly.`);\n }\n if (!supportsVite(helperViteVersion)) {\n throw new Error(`Hono Pages: adapter helper Vite ${helperViteVersion} is unsupported; expected ${supportedViteRange}. ${diagnostic()} Inspect the adapter's resolved peer copy; install vite@^8.3.4 and refresh the adapter's peer resolution/lockfile if a stale copy remains.`);\n }\n for (const name of ['void', 'hono'] as const) {\n const expected = supportedFrameworkRanges[name];\n const info = packageInfo(name, adapter);\n if (!supportsFramework(name, info.version)) throw new Error(`Hono Pages: ${name} ${info.version} is unsupported; install ${name}@\"${expected}\". Resolved package.json: ${info.path}. Project package.json: ${describe(name, project)}. ${diagnostic()}`);\n }\n}\n"]}
1
+ {"version":3,"file":"compatibility.js","sourceRoot":"","sources":["../../src/plugins/compatibility.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from 'node:module';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { version as helperViteVersion } from 'vite';\n\nexport const supportedFrameworkRanges = {\n void: '>=1.1.11 <1.2.0',\n hono: '>=4.13.13 <5.0.0',\n vite: '>=8.0.16 <9.0.0',\n};\nexport const supportedViteRange = supportedFrameworkRanges.vite;\nexport const supportedNodeRange = '>=24.21.0 <25.0.0';\nexport function supportsNode(version: unknown): boolean {\n if (typeof version !== 'string') return false;\n const match = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$/.exec(version);\n return !!match && match[1] === '24' && Number.isSafeInteger(Number(match[2])) && Number.isSafeInteger(Number(match[3])) && Number(match[2]) >= 21;\n}\n\n// Only stable releases are admitted. Void minors are validated explicitly\n// because the adapter also uses its virtual-module integration contracts.\nexport function supportsFramework(name: keyof typeof supportedFrameworkRanges, version: unknown): boolean {\n if (typeof version !== 'string') return false;\n const match = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$/.exec(version);\n if (!match) return false;\n const [major, minor, patch] = match.slice(1).map(Number);\n if (![major, minor, patch].every(Number.isSafeInteger)) return false;\n if (name === 'void') return major === 1 && minor === 1 && patch >= 11;\n if (name === 'hono') return major === 4 && (minor > 13 || (minor === 13 && patch >= 13));\n return major === 8 && (minor > 0 || patch >= 16);\n}\nexport const supportsVite = (version: unknown): boolean => supportsFramework('vite', version);\n\ntype PackageInfo = { version: string; path: string };\nfunction packageInfo(name: string, require: NodeRequire): PackageInfo {\n let directory = dirname(require.resolve(name));\n while (true) {\n const path = join(directory, 'package.json');\n if (existsSync(path)) {\n const metadata: unknown = JSON.parse(readFileSync(path, 'utf8'));\n if (typeof metadata === 'object' && metadata !== null && 'name' in metadata && (metadata.name === name || (name === 'vite' && metadata.name === '@voidzero-dev/vite-plus-core'))) {\n if (!('version' in metadata) || typeof metadata.version !== 'string') throw new Error(`Hono Pages: ${name} package version is missing (${path}).`);\n return { version: metadata.version, path };\n }\n }\n const parent = dirname(directory);\n if (parent === directory) throw new Error(`Hono Pages: cannot locate ${name} package metadata from ${require.resolve(name)}.`);\n directory = parent;\n }\n}\nfunction describe(name: string, require: NodeRequire): string {\n try { const info = packageInfo(name, require); return `${info.path} (metadata ${info.version})`; }\n catch { return `not resolved from ${require.resolve.paths(name)?.join(', ') ?? 'this context'}`; }\n}\n\nexport function assertSupportedFrameworks({ root, viteVersion }: { root: string; viteVersion: unknown }) {\n if (!supportsNode(process.versions.node)) throw new Error(`Hono Pages: Node ${process.versions.node} is unsupported; expected ${supportedNodeRange}. Use Node 24 LTS from your project root.`);\n const adapter = createRequire(import.meta.url);\n const project = createRequire(join(root, 'package.json'));\n const diagnostic = () => `Running Vite from hook context: ${typeof viteVersion === 'string' ? viteVersion : 'unavailable'}. Adapter helper Vite: ${helperViteVersion}; package.json: ${describe('vite', adapter)}. Project Vite package.json: ${describe('vite', project)}. Package paths are diagnostics, not the running Vite identity.`;\n if (!supportsVite(viteVersion)) {\n throw new Error(`Hono Pages: running Vite ${typeof viteVersion === 'string' ? viteVersion : 'unavailable'} is unsupported; expected ${supportedViteRange}. ${diagnostic()} Update the CLI/launcher that executes Vite. For bundled launchers such as vp, installing project vite alone does not update the running Vite. For standard Vite, install vite@^8.3.4 and run vite directly.`);\n }\n if (!supportsVite(helperViteVersion)) {\n throw new Error(`Hono Pages: adapter helper Vite ${helperViteVersion} is unsupported; expected ${supportedViteRange}. ${diagnostic()} Inspect the adapter's resolved peer copy; install vite@^8.3.4 and refresh the adapter's peer resolution/lockfile if a stale copy remains.`);\n }\n for (const name of ['void', 'hono'] as const) {\n const expected = supportedFrameworkRanges[name];\n const info = packageInfo(name, adapter);\n if (!supportsFramework(name, info.version)) throw new Error(`Hono Pages: ${name} ${info.version} is unsupported; install ${name}@\"${expected}\". Resolved package.json: ${info.path}. Project package.json: ${describe(name, project)}. ${diagnostic()}`);\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"hono-islands.d.ts","sourceRoot":"","sources":["../../src/plugins/hono-islands.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAQ7D,4DAA4D;AAC5D,wBAAgB,WAAW,IAAI,MAAM,CA+LpC"}
1
+ {"version":3,"file":"hono-islands.d.ts","sourceRoot":"","sources":["../../src/plugins/hono-islands.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAQ7D,4DAA4D;AAC5D,wBAAgB,WAAW,IAAI,MAAM,CA+NpC"}
@@ -1,5 +1,5 @@
1
1
  import { readFileSync, realpathSync } from 'node:fs';
2
- import { join, relative } from 'node:path';
2
+ import { join, relative, resolve, dirname, basename } from 'node:path';
3
3
  import { normalizePath, parseSync } from 'vite';
4
4
  import { scanPages, resolveEffectiveLayoutChain, resolvePagesDir } from 'void/pages';
5
5
  import { findIslandImports, editIslandSource, computePagesNeedingClientJS, ISLANDS_RENDERER_ID, ISLANDS_CLIENT_ENTRY_ID } from 'void/pages-islands-plugin';
@@ -11,21 +11,21 @@ export function honoIslands() {
11
11
  const pagesDir = resolvePagesDir(root);
12
12
  const islands = new Map();
13
13
  const pageFiles = new Set();
14
+ let dependencyFiles = new Set();
14
15
  const graph = new Map();
15
16
  const directlyInteractive = new Set();
16
17
  const interactivePages = new Set();
17
18
  const scriptOnlyPages = new Set();
18
19
  let scan;
19
20
  let markdown;
20
- function islandImports(code, filename) {
21
+ function islandImports(code, filename, body = parseSync(filename, code).program.body) {
21
22
  const found = findIslandImports(code, filename);
22
- const attributed = parseSync(filename, code).program.body.filter((node) => node.type === 'ImportDeclaration' && node.attributes?.some(({ key }) => ('name' in key && key.name === 'island') || ('value' in key && key.value === 'island')));
23
+ const attributed = body.filter((node) => node.type === 'ImportDeclaration' && node.attributes?.some(({ key }) => ('name' in key && key.name === 'island') || ('value' in key && key.value === 'island')));
23
24
  if (attributed.length !== found.length)
24
25
  throw new Error(`Hono islands: use one default import with exactly one island attribute using load, visible, idle or media:query in ${filename}.`);
25
26
  return found;
26
27
  }
27
- function imports(code, filename) {
28
- const found = islandImports(code, filename);
28
+ function imports(code, filename, found = islandImports(code, filename)) {
29
29
  for (const entry of found) {
30
30
  if (!['load', 'visible', 'idle'].includes(entry.hydrate)) {
31
31
  const query = entry.hydrate.startsWith('media:') ? entry.hydrate.slice(6).trim() : '';
@@ -52,7 +52,9 @@ export function honoIslands() {
52
52
  }
53
53
  return found;
54
54
  }
55
+ // Refresh before graph analysis, regardless of Markdown hot-hook order.
55
56
  async function rescan() {
57
+ markdown?.refresh();
56
58
  scan = await scanPages(root, { pagesDir });
57
59
  // Never pass Markdown text to the JavaScript static-graph parser.
58
60
  for (const file of [...scan.pages.map(page => page.componentPath), ...scan.layouts.map(layout => layout.filePath), ...scan.namedLayouts.map(layout => layout.filePath)]) {
@@ -65,6 +67,61 @@ export function honoIslands() {
65
67
  directlyInteractive.clear();
66
68
  interactivePages.clear();
67
69
  scriptOnlyPages.clear();
70
+ const analyses = new Map();
71
+ const watched = new Set();
72
+ const watchedBases = new Set();
73
+ const watch = (file) => { const path = normalizePath(file); watched.add(path); dependencyFiles.add(path); };
74
+ const watchImport = (specifier, filename) => {
75
+ const base = resolve(dirname(filename), specifier);
76
+ if (watchedBases.has(base))
77
+ return;
78
+ watchedBases.add(base);
79
+ const bases = new Set([base]);
80
+ // Keep missing targets observable through symlinked directories too.
81
+ try {
82
+ bases.add(join(realpathSync(dirname(base)), basename(base)));
83
+ }
84
+ catch { }
85
+ try {
86
+ bases.add(realpathSync(base));
87
+ }
88
+ catch { }
89
+ for (const candidate of bases) {
90
+ watch(candidate);
91
+ for (const extension of ['.tsx', '.jsx', '.ts', '.js']) {
92
+ watch(candidate + extension);
93
+ watch(join(candidate, 'index' + extension));
94
+ }
95
+ }
96
+ };
97
+ const analyze = (canonical) => {
98
+ const previous = analyses.get(canonical);
99
+ if (previous)
100
+ return previous;
101
+ // Retain failed dependencies until recovery so an edit can trigger a rescan.
102
+ watch(canonical);
103
+ const code = canonical.endsWith('.md') && markdown ? markdown.getGraphSource(canonical) : readFileSync(canonical, 'utf8');
104
+ const parsed = parseSync(canonical, code);
105
+ if (parsed.errors.length)
106
+ throw new Error(`Hono islands: cannot parse ${canonical}: ${parsed.errors.map(error => error.message).join('; ')}`);
107
+ const body = parsed.program.body;
108
+ const found = islandImports(code, canonical, body);
109
+ const edges = [];
110
+ for (const node of body) {
111
+ const source = node.type === 'ImportDeclaration' ? node.source : node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration' ? node.source : undefined;
112
+ const typeOnly = node.type === 'ImportDeclaration' ? node.importKind === 'type' || (node.specifiers.length > 0 && node.specifiers.every((entry) => entry.type === 'ImportSpecifier' && entry.importKind === 'type')) : (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.exportKind === 'type';
113
+ if (!source || typeOnly || !source.value.startsWith('.'))
114
+ continue;
115
+ const target = resolveComponent(source.value, join(canonical, '..'), ['.tsx', '.jsx', '.ts', '.js']);
116
+ if (!target || /\.[jt]sx?$/.test(target))
117
+ watchImport(source.value, canonical);
118
+ if (target && /\.[jt]sx?$/.test(target))
119
+ edges.push({ specifier: source.value, target: realpathSync(target) });
120
+ }
121
+ const analysis = { code, found, edges };
122
+ analyses.set(canonical, analysis);
123
+ return analysis;
124
+ };
68
125
  const visit = (filename) => {
69
126
  const canonical = realpathSync(filename);
70
127
  if (graph.has(canonical))
@@ -72,44 +129,33 @@ export function honoIslands() {
72
129
  const dependencies = new Set();
73
130
  graph.set(canonical, dependencies);
74
131
  pageFiles.add(canonical);
75
- const code = canonical.endsWith('.md') && markdown ? markdown.getGraphSource(canonical) : readFileSync(canonical, 'utf8');
76
- const found = imports(code, canonical);
132
+ const { code, found, edges } = analyze(canonical);
133
+ imports(code, canonical, found);
77
134
  if (found.length) {
78
135
  if (!/\.[jt]sx$/.test(canonical) && !(canonical.endsWith('.md') && markdown))
79
136
  throw new Error('Hono islands: attributed imports require a static TSX/JSX module.');
80
137
  directlyInteractive.add(canonical);
81
138
  }
82
- for (const node of parseSync(canonical, code).program.body) {
83
- const source = node.type === 'ImportDeclaration' ? node.source : node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration' ? node.source : undefined;
84
- const typeOnly = node.type === 'ImportDeclaration' ? node.importKind === 'type' || (node.specifiers.length > 0 && node.specifiers.every((entry) => entry.type === 'ImportSpecifier' && entry.importKind === 'type')) : (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.exportKind === 'type';
85
- if (!source || typeOnly || !source.value.startsWith('.') || found.some((entry) => entry.specifier === source.value))
139
+ for (const edge of edges) {
140
+ if (found.some(entry => entry.specifier === edge.specifier))
86
141
  continue;
87
- const target = resolveComponent(source.value, join(canonical, '..'), ['.tsx', '.jsx', '.ts', '.js']);
88
- if (!target || !/\.[jt]sx?$/.test(target))
89
- continue;
90
- dependencies.add(realpathSync(target));
91
- visit(target);
142
+ dependencies.add(edge.target);
143
+ visit(edge.target);
92
144
  }
93
145
  };
94
146
  for (const entry of [...scan.pages.filter((page) => page.island).map((page) => page.componentPath), ...scan.layouts.filter((layout) => layout.island).map((layout) => layout.filePath), ...scan.namedLayouts.filter((layout) => layout.island).map((layout) => layout.filePath)])
95
147
  visit(join(pagesDir, entry));
96
- const validateInteractive = (filename, seen = new Set()) => {
148
+ const validated = new Set();
149
+ const validateInteractive = (filename) => {
97
150
  const canonical = realpathSync(filename);
98
- if (seen.has(canonical))
151
+ if (validated.has(canonical))
99
152
  return;
100
- seen.add(canonical);
101
- const code = canonical.endsWith('.md') && markdown ? markdown.getGraphSource(canonical) : readFileSync(canonical, 'utf8');
102
- if (islandImports(code, canonical).length)
153
+ validated.add(canonical);
154
+ const { found, edges } = analyze(canonical);
155
+ if (found.length)
103
156
  throw new Error('Hono islands: attributed imports inside an interactive component are unsupported; use ordinary component imports there.');
104
- for (const node of parseSync(canonical, code).program.body) {
105
- const source = node.type === 'ImportDeclaration' ? node.source : node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration' ? node.source : undefined;
106
- const typeOnly = node.type === 'ImportDeclaration' ? node.importKind === 'type' || (node.specifiers.length > 0 && node.specifiers.every((entry) => entry.type === 'ImportSpecifier' && entry.importKind === 'type')) : (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.exportKind === 'type';
107
- if (!source || typeOnly || !source.value.startsWith('.'))
108
- continue;
109
- const target = resolveComponent(source.value, join(canonical, '..'), ['.tsx', '.jsx', '.ts', '.js']);
110
- if (target && /\.[jt]sx?$/.test(target))
111
- validateInteractive(target, seen);
112
- }
157
+ for (const edge of edges)
158
+ validateInteractive(edge.target);
113
159
  };
114
160
  for (const target of islands.values())
115
161
  validateInteractive(target);
@@ -117,6 +163,7 @@ export function honoIslands() {
117
163
  for (const file of [page.componentPath, ...resolveEffectiveLayoutChain(page, scan.layouts, scan.namedLayouts).map((layout) => layout.filePath)])
118
164
  validateInteractive(join(pagesDir, file));
119
165
  }
166
+ dependencyFiles = watched;
120
167
  const needsJS = new Set(directlyInteractive);
121
168
  let changed = true;
122
169
  while (changed) {
@@ -144,7 +191,7 @@ export function honoIslands() {
144
191
  buildStart: rescan,
145
192
  async hotUpdate({ file, modules, timestamp }) {
146
193
  const inPages = normalizePath(file).startsWith(normalizePath(pagesDir) + '/');
147
- if (!inPages && ![...pageFiles, ...islands.values()].some((path) => normalizePath(path) === normalizePath(file)))
194
+ if (!inPages && !dependencyFiles.has(normalizePath(file)))
148
195
  return;
149
196
  await rescan();
150
197
  const invalidated = new Set();