@elurjs/kit 2.4.10 → 2.5.1

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 (188) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/README.md +248 -15
  3. package/dist/lib/action/index.cjs +1 -1
  4. package/dist/lib/action/index.js +1 -1
  5. package/dist/lib/{action-DZHihU3Z.cjs → action-BiE4tEty.cjs} +2 -2
  6. package/dist/lib/{action-DZHihU3Z.cjs.map → action-BiE4tEty.cjs.map} +1 -1
  7. package/dist/lib/{action-BB6N5yD-.js → action-CXVk8rWt.js} +3 -3
  8. package/dist/lib/{action-BB6N5yD-.js.map → action-CXVk8rWt.js.map} +1 -1
  9. package/dist/lib/adapter-DNsjXvq5.js +160 -0
  10. package/dist/lib/adapter-DNsjXvq5.js.map +1 -0
  11. package/dist/lib/adapter-Dz2hzR36.cjs +3 -0
  12. package/dist/lib/adapter-Dz2hzR36.cjs.map +1 -0
  13. package/dist/lib/adapters/bun.cjs +2 -2
  14. package/dist/lib/adapters/bun.cjs.map +1 -1
  15. package/dist/lib/adapters/bun.js +17 -9
  16. package/dist/lib/adapters/bun.js.map +1 -1
  17. package/dist/lib/adapters/index.d.cts +34 -0
  18. package/dist/lib/adapters/index.d.ts +34 -0
  19. package/dist/lib/adapters/netlify.cjs +1 -1
  20. package/dist/lib/adapters/netlify.js +7 -7
  21. package/dist/lib/adapters/node.cjs +5 -5
  22. package/dist/lib/adapters/node.cjs.map +1 -1
  23. package/dist/lib/adapters/node.js +20 -12
  24. package/dist/lib/adapters/node.js.map +1 -1
  25. package/dist/lib/adapters/vercel.cjs +1 -1
  26. package/dist/lib/adapters/vercel.js +8 -8
  27. package/dist/lib/build/build.d.cts +40 -0
  28. package/dist/lib/build/build.d.ts +40 -0
  29. package/dist/lib/build/document-shell.d.cts +47 -1
  30. package/dist/lib/build/document-shell.d.ts +47 -1
  31. package/dist/lib/build/vite-build.d.cts +25 -2
  32. package/dist/lib/build/vite-build.d.ts +25 -2
  33. package/dist/lib/cache/adapter.d.cts +41 -0
  34. package/dist/lib/cache/adapter.d.ts +41 -0
  35. package/dist/lib/cache/index.cjs +2 -0
  36. package/dist/lib/cache/index.cjs.map +1 -0
  37. package/dist/lib/cache/index.d.cts +5 -0
  38. package/dist/lib/cache/index.d.ts +5 -0
  39. package/dist/lib/cache/index.js +113 -0
  40. package/dist/lib/cache/index.js.map +1 -0
  41. package/dist/lib/cache/invalidation.d.cts +36 -0
  42. package/dist/lib/cache/invalidation.d.ts +36 -0
  43. package/dist/lib/cache/redis-adapter.d.cts +49 -0
  44. package/dist/lib/cache/redis-adapter.d.ts +49 -0
  45. package/dist/lib/cache-DGhTYyqh.js +50 -0
  46. package/dist/lib/cache-DGhTYyqh.js.map +1 -0
  47. package/dist/lib/cache-X1n4Ossu.cjs +2 -0
  48. package/dist/lib/cache-X1n4Ossu.cjs.map +1 -0
  49. package/dist/lib/cli/output.d.cts +58 -0
  50. package/dist/lib/cli/output.d.ts +58 -0
  51. package/dist/lib/cli/ports.d.cts +20 -0
  52. package/dist/lib/cli/ports.d.ts +20 -0
  53. package/dist/lib/cli.cjs +2559 -1488
  54. package/dist/lib/cli.cjs.map +1 -1
  55. package/dist/lib/cli.d.cts +21 -0
  56. package/dist/lib/cli.d.ts +21 -0
  57. package/dist/lib/cli.js +2558 -1488
  58. package/dist/lib/cli.js.map +1 -1
  59. package/dist/lib/config/index.cjs +1 -1
  60. package/dist/lib/config/index.cjs.map +1 -1
  61. package/dist/lib/config/index.d.cts +79 -0
  62. package/dist/lib/config/index.d.ts +79 -0
  63. package/dist/lib/config/index.js +21 -14
  64. package/dist/lib/config/index.js.map +1 -1
  65. package/dist/lib/{errors-ePYxDlhU.js → errors-j4cg9VN3.js} +24 -30
  66. package/dist/lib/{errors-ePYxDlhU.js.map → errors-j4cg9VN3.js.map} +1 -1
  67. package/dist/lib/errors-kSl9yLkg.cjs +2 -0
  68. package/dist/lib/{errors-0X7pCFNU.cjs.map → errors-kSl9yLkg.cjs.map} +1 -1
  69. package/dist/lib/hydrate-BSLuI0Nh.cjs +2 -0
  70. package/dist/lib/hydrate-BSLuI0Nh.cjs.map +1 -0
  71. package/dist/lib/hydrate-BaS7-d8C.js +133 -0
  72. package/dist/lib/hydrate-BaS7-d8C.js.map +1 -0
  73. package/dist/lib/idiomorph.esm-BOOTOg1r.cjs +2 -0
  74. package/dist/lib/idiomorph.esm-BOOTOg1r.cjs.map +1 -0
  75. package/dist/lib/idiomorph.esm-CXuY-EHB.js +371 -0
  76. package/dist/lib/idiomorph.esm-CXuY-EHB.js.map +1 -0
  77. package/dist/lib/index.cjs +1 -1
  78. package/dist/lib/index.cjs.map +1 -1
  79. package/dist/lib/index.d.cts +8 -6
  80. package/dist/lib/index.d.ts +8 -6
  81. package/dist/lib/index.js +1 -1
  82. package/dist/lib/index.js.map +1 -1
  83. package/dist/lib/interpolation-plugin-C0dTd69H.cjs +58 -0
  84. package/dist/lib/interpolation-plugin-C0dTd69H.cjs.map +1 -0
  85. package/dist/lib/{interpolation-plugin-Wgb1j4pT.js → interpolation-plugin-K2qE69tF.js} +95 -126
  86. package/dist/lib/interpolation-plugin-K2qE69tF.js.map +1 -0
  87. package/dist/lib/invalidation-CFpNQUv6.cjs +2 -0
  88. package/dist/lib/invalidation-CFpNQUv6.cjs.map +1 -0
  89. package/dist/lib/invalidation-DXhdE9wj.js +64 -0
  90. package/dist/lib/invalidation-DXhdE9wj.js.map +1 -0
  91. package/dist/lib/island/generate-entry.d.cts +40 -2
  92. package/dist/lib/island/generate-entry.d.ts +40 -2
  93. package/dist/lib/island/hydrate.d.cts +19 -6
  94. package/dist/lib/island/hydrate.d.ts +19 -6
  95. package/dist/lib/island/index.cjs +1 -1
  96. package/dist/lib/island/index.d.cts +2 -1
  97. package/dist/lib/island/index.d.ts +2 -1
  98. package/dist/lib/island/index.js +1 -1
  99. package/dist/lib/island/island.d.cts +15 -0
  100. package/dist/lib/island/island.d.ts +15 -0
  101. package/dist/lib/island-CTBnsOhI.js +59 -0
  102. package/dist/lib/island-CTBnsOhI.js.map +1 -0
  103. package/dist/lib/island-kfQEKRe_.cjs +2 -0
  104. package/dist/lib/island-kfQEKRe_.cjs.map +1 -0
  105. package/dist/lib/node-http-DVFcYeBS.js +723 -0
  106. package/dist/lib/node-http-DVFcYeBS.js.map +1 -0
  107. package/dist/lib/node-http-Uct7DhmN.cjs +13 -0
  108. package/dist/lib/node-http-Uct7DhmN.cjs.map +1 -0
  109. package/dist/lib/rolldown-runtime-BIInC43l.cjs +1 -0
  110. package/dist/lib/rolldown-runtime-Dy4uBu1J.js +11 -0
  111. package/dist/lib/router/client.cjs +1 -1
  112. package/dist/lib/router/client.cjs.map +1 -1
  113. package/dist/lib/router/client.d.cts +55 -8
  114. package/dist/lib/router/client.d.ts +55 -8
  115. package/dist/lib/router/client.js +298 -97
  116. package/dist/lib/router/client.js.map +1 -1
  117. package/dist/lib/router/redirects.d.cts +34 -0
  118. package/dist/lib/router/redirects.d.ts +34 -0
  119. package/dist/lib/runtime/handler.d.cts +59 -0
  120. package/dist/lib/runtime/handler.d.ts +59 -0
  121. package/dist/lib/runtime/index.cjs +1 -1
  122. package/dist/lib/runtime/index.d.cts +2 -1
  123. package/dist/lib/runtime/index.d.ts +2 -1
  124. package/dist/lib/runtime/index.js +4 -4
  125. package/dist/lib/runtime/logger.d.cts +47 -0
  126. package/dist/lib/runtime/logger.d.ts +47 -0
  127. package/dist/lib/runtime/node-http.d.cts +11 -1
  128. package/dist/lib/runtime/node-http.d.ts +11 -1
  129. package/dist/lib/runtime-ChE4flX3.cjs +2 -0
  130. package/dist/lib/runtime-ChE4flX3.cjs.map +1 -0
  131. package/dist/lib/runtime-OMyz5yE-.js +905 -0
  132. package/dist/lib/runtime-OMyz5yE-.js.map +1 -0
  133. package/dist/lib/seo/index.cjs +1 -10
  134. package/dist/lib/seo/index.d.cts +1 -0
  135. package/dist/lib/seo/index.d.ts +1 -0
  136. package/dist/lib/seo/index.js +2 -41
  137. package/dist/lib/seo/sitemap-from-routes.d.cts +24 -0
  138. package/dist/lib/seo/sitemap-from-routes.d.ts +24 -0
  139. package/dist/lib/{shared-CfqOLsKj.cjs → shared-CP0Dc3cp.cjs} +72 -47
  140. package/dist/lib/shared-CP0Dc3cp.cjs.map +1 -0
  141. package/dist/lib/{shared-DU3iQgr7.js → shared-D8w-neq2.js} +87 -58
  142. package/dist/lib/shared-D8w-neq2.js.map +1 -0
  143. package/dist/lib/sitemap-from-routes-CaBoiGvE.js +77 -0
  144. package/dist/lib/sitemap-from-routes-CaBoiGvE.js.map +1 -0
  145. package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs +13 -0
  146. package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs.map +1 -0
  147. package/dist/lib/ssr/render.d.cts +8 -2
  148. package/dist/lib/ssr/render.d.ts +8 -2
  149. package/dist/lib/ssr/server.d.cts +7 -0
  150. package/dist/lib/ssr/server.d.ts +7 -0
  151. package/dist/lib/ssr/stream-response.d.cts +30 -0
  152. package/dist/lib/ssr/stream-response.d.ts +30 -0
  153. package/dist/lib/ssr/stream.d.cts +16 -1
  154. package/dist/lib/ssr/stream.d.ts +16 -1
  155. package/dist/lib/{stream-DmZjYqHm.cjs → stream-Cg-xYG82.cjs} +3 -3
  156. package/dist/lib/stream-Cg-xYG82.cjs.map +1 -0
  157. package/dist/lib/stream-D-ytogdu.js +106 -0
  158. package/dist/lib/stream-D-ytogdu.js.map +1 -0
  159. package/dist/lib/vite/index.cjs +1 -1
  160. package/dist/lib/vite/index.cjs.map +1 -1
  161. package/dist/lib/vite/index.d.cts +11 -0
  162. package/dist/lib/vite/index.d.ts +11 -0
  163. package/dist/lib/vite/index.js +101 -94
  164. package/dist/lib/vite/index.js.map +1 -1
  165. package/package.json +25 -8
  166. package/dist/lib/errors-0X7pCFNU.cjs +0 -2
  167. package/dist/lib/hydrate-CJbUWdXu.js +0 -126
  168. package/dist/lib/hydrate-CJbUWdXu.js.map +0 -1
  169. package/dist/lib/hydrate-D6MR24mz.cjs +0 -2
  170. package/dist/lib/hydrate-D6MR24mz.cjs.map +0 -1
  171. package/dist/lib/interpolation-plugin-TWDch8nn.cjs +0 -39
  172. package/dist/lib/interpolation-plugin-TWDch8nn.cjs.map +0 -1
  173. package/dist/lib/interpolation-plugin-Wgb1j4pT.js.map +0 -1
  174. package/dist/lib/node-http-DRAUhO0c.js +0 -617
  175. package/dist/lib/node-http-DRAUhO0c.js.map +0 -1
  176. package/dist/lib/node-http-DhxguYyz.cjs +0 -13
  177. package/dist/lib/node-http-DhxguYyz.cjs.map +0 -1
  178. package/dist/lib/runtime-CrH5X-Es.cjs +0 -2
  179. package/dist/lib/runtime-CrH5X-Es.cjs.map +0 -1
  180. package/dist/lib/runtime-Ds2-wH6P.js +0 -520
  181. package/dist/lib/runtime-Ds2-wH6P.js.map +0 -1
  182. package/dist/lib/seo/index.cjs.map +0 -1
  183. package/dist/lib/seo/index.js.map +0 -1
  184. package/dist/lib/shared-CfqOLsKj.cjs.map +0 -1
  185. package/dist/lib/shared-DU3iQgr7.js.map +0 -1
  186. package/dist/lib/stream-CpOG052V.js +0 -104
  187. package/dist/lib/stream-CpOG052V.js.map +0 -1
  188. package/dist/lib/stream-DmZjYqHm.cjs.map +0 -1
@@ -1,126 +0,0 @@
1
- import { hydrate as e } from "@elurjs/core/hydrate";
2
- //#region src/island/hydrate.ts
3
- var t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
4
- function r(e) {
5
- return { load: e };
6
- }
7
- function i(e) {
8
- return typeof e == "object" && !!e && typeof e.load == "function";
9
- }
10
- function a(e) {
11
- return typeof e == "function" ? e.constructor?.name === "AsyncFunction" || e[Symbol.toStringTag] === "AsyncFunction" : !1;
12
- }
13
- function o() {
14
- return Array.from(document.querySelectorAll("[data-elur-island]")).map((e) => {
15
- let t = {
16
- el: e,
17
- name: e.dataset.elurIsland ?? "",
18
- directive: e.dataset.directive ?? "load",
19
- props: null
20
- };
21
- if (e.dataset.props) try {
22
- t.props = JSON.parse(e.dataset.props);
23
- } catch (e) {
24
- t.propsError = e;
25
- }
26
- return t;
27
- });
28
- }
29
- async function s(n, r) {
30
- try {
31
- if (n.propsError) {
32
- c(n, n.propsError);
33
- return;
34
- }
35
- let o = r[n.name];
36
- if (!o) {
37
- console.warn(`[elur-kit] No island registered for "${n.name}"`);
38
- return;
39
- }
40
- let s;
41
- if (i(o)) {
42
- let e = await o.load();
43
- s = typeof e == "function" ? e : e.default;
44
- } else if (typeof o == "function") {
45
- if (a(o)) {
46
- let e = await o();
47
- s = typeof e == "function" ? e : e.default;
48
- } else s = o;
49
- }
50
- if (typeof s != "function") {
51
- console.warn(`[elur-kit] Island "${n.name}" did not resolve to a component function`);
52
- return;
53
- }
54
- let u = s(n.props);
55
- if (u === null || u === !1 || u === void 0) return;
56
- let d = n.el.__elur_js_island_dispose;
57
- typeof d == "function" && d();
58
- let f = n.el.innerHTML.includes("<!--elur-") ? e(u, n.el, { mismatch: "warn-remount" }) : l(u, n.el), p = () => {
59
- f.unmount(), t.delete(p), delete n.el.__elur_js_island_dispose;
60
- };
61
- n.el.__elur_js_island_dispose = p, t.add(p);
62
- } catch (e) {
63
- c(n, e);
64
- }
65
- }
66
- function c(e, t) {
67
- console.error(`[elur-kit] Failed to hydrate island "${e.name}":`, t);
68
- let n = e.el.ownerDocument.defaultView?.CustomEvent;
69
- n && e.el.dispatchEvent(new n("elur:island-error", {
70
- bubbles: !0,
71
- detail: {
72
- name: e.name,
73
- error: t
74
- }
75
- }));
76
- }
77
- function l(e, t) {
78
- return t.replaceChildren(), { unmount: e._render(t, null) };
79
- }
80
- function u() {
81
- for (let e of n) e();
82
- n.clear();
83
- for (let e of t) e();
84
- t.clear();
85
- }
86
- function d(e) {
87
- if (typeof window > "u") return;
88
- let t = o();
89
- for (let r of t) {
90
- if (r.directive === "load" || r.directive === "only") {
91
- s(r, e);
92
- continue;
93
- }
94
- if (r.directive === "idle") {
95
- let t = () => {};
96
- if ("requestIdleCallback" in window) {
97
- let i = window.requestIdleCallback(() => {
98
- n.delete(t), s(r, e);
99
- });
100
- t = () => window.cancelIdleCallback(i);
101
- } else {
102
- let i = globalThis.setTimeout(() => {
103
- n.delete(t), s(r, e);
104
- }, 0);
105
- t = () => globalThis.clearTimeout(i);
106
- }
107
- n.add(t);
108
- continue;
109
- }
110
- if (r.directive === "visible") {
111
- if ("IntersectionObserver" in window) {
112
- let t = () => {}, i = new IntersectionObserver((a) => {
113
- for (let o of a) o.isIntersecting && (n.delete(t), i.disconnect(), s(r, e));
114
- }, {
115
- rootMargin: "0px",
116
- threshold: 0
117
- });
118
- t = () => i.disconnect(), n.add(t), i.observe(r.el);
119
- } else s(r, e);
120
- }
121
- }
122
- }
123
- //#endregion
124
- export { d as n, r, u as t };
125
-
126
- //# sourceMappingURL=hydrate-CJbUWdXu.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hydrate-CJbUWdXu.js","names":[],"sources":["../../src/island/hydrate.ts"],"sourcesContent":["import type { ElurTemplate } from \"@elurjs/core\";\nimport { hydrate as hydrateTemplate } from \"@elurjs/core/hydrate\";\nimport type { IslandDirective } from \"./island.js\";\n\n// --- Client-side island hydration ---\n\n// Keep track of every active island dispose so we can clean them up before a\n// client-side navigation swaps the whole #app content.\nconst _islandDisposes = new Set<() => void>();\nconst _islandSchedules = new Set<() => void>();\n\n// Finds [data-elur-island] markers in the current document and mounts the\n// corresponding interactive components over them. This runs in the browser.\n\nexport type IslandComponent<TProps = unknown> = (props: TProps) => ElurTemplate | null | false | undefined;\n\n// Re-exported from island.ts so the client entry and the server helper share a\n// single source of truth for the directive union (now includes \"only\").\nexport type { IslandDirective } from \"./island.js\";\n\n/** Lazy island loader in a discriminated form (no probe required to detect). */\nexport interface IslandLoader<TProps = unknown> {\n load: () => Promise<IslandComponent<TProps>>;\n}\n\n/**\n * Island registry entry. Two unambiguous shapes:\n * - `IslandComponent` (eager component function).\n * - `IslandLoader` `{ load }` (lazy, code-split loader).\n *\n * Legacy async loader functions are also accepted for backwards compatibility\n * and detected *without invoking them* (via the `AsyncFunction` tag), so no\n * side effects or duplicate signal creation happen during detection.\n */\nexport type IslandRegistryEntry<TProps = unknown> =\n | IslandComponent<TProps>\n | IslandLoader<TProps>\n | (() => Promise<IslandComponent<TProps>>);\n\nexport type IslandRegistry = Record<string, IslandRegistryEntry<any>>;\n\n/**\n * Wraps a lazy island loader in the discriminated `{ load }` form.\n */\nexport function lazyIsland<TProps = unknown>(\n loader: () => Promise<IslandComponent<TProps>>,\n): IslandLoader<TProps> {\n return { load: loader };\n}\n\nfunction isIslandLoader(value: unknown): value is IslandLoader {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { load?: unknown }).load === \"function\"\n );\n}\n\nfunction isAsyncFunction(value: unknown): boolean {\n if (typeof value !== \"function\") return false;\n const ctor = (value as { constructor?: { name?: string } }).constructor;\n return ctor?.name === \"AsyncFunction\" || (value as { [Symbol.toStringTag]?: string })[Symbol.toStringTag] === \"AsyncFunction\";\n}\n\ninterface IslandMarker {\n el: HTMLElement;\n name: string;\n directive: IslandDirective;\n props: unknown;\n propsError?: unknown;\n}\n\nfunction collectMarkers(): IslandMarker[] {\n const elements = Array.from(\n document.querySelectorAll<HTMLElement>(\"[data-elur-island]\"),\n );\n return elements.map((el) => {\n const marker: IslandMarker = {\n el,\n name: el.dataset.elurIsland ?? \"\",\n directive: (el.dataset.directive as IslandDirective) ?? \"load\",\n props: null,\n };\n if (el.dataset.props) {\n try {\n marker.props = JSON.parse(el.dataset.props);\n } catch (error) {\n marker.propsError = error;\n }\n }\n return marker;\n });\n}\n\nasync function hydrate(marker: IslandMarker, registry: IslandRegistry): Promise<void> {\n try {\n if (marker.propsError) {\n reportIslandError(marker, marker.propsError);\n return;\n }\n\n const entry = registry[marker.name];\n if (!entry) {\n console.warn(`[elur-kit] No island registered for \"${marker.name}\"`);\n return;\n }\n\n // Resolve the component without probing: a `{ load }` loader or an async\n // function is lazy (awaited for the module/component); anything else is an\n // eager component called directly with the island props. Detection never\n // invokes the component with undefined props, so no side effects or\n // duplicate signal creation occur during resolution. Eager components\n // hydrate synchronously; lazy loaders hydrate once the module resolves.\n let Component: IslandComponent | undefined;\n if (isIslandLoader(entry)) {\n const mod = (await (entry as IslandLoader).load()) as IslandComponent | { default?: unknown };\n Component = typeof mod === \"function\" ? (mod as IslandComponent) : (mod as { default?: unknown }).default as IslandComponent | undefined;\n } else if (typeof entry === \"function\") {\n if (isAsyncFunction(entry)) {\n const mod = (await (entry as () => Promise<unknown>)()) as IslandComponent | { default?: unknown };\n Component = typeof mod === \"function\" ? (mod as IslandComponent) : (mod as { default?: unknown }).default as IslandComponent | undefined;\n } else {\n Component = entry as IslandComponent;\n }\n }\n\n if (typeof Component !== \"function\") {\n console.warn(`[elur-kit] Island \"${marker.name}\" did not resolve to a component function`);\n return;\n }\n\n const template = Component(marker.props);\n if (template === null || template === false || template === undefined) return;\n const prevDispose = (marker.el as any).__elur_js_island_dispose;\n if (typeof prevDispose === \"function\") prevDispose();\n\n // Islands with directive \"only\" or options.ssr:false have no SSR-rendered\n // DOM inside the marker — only fallback HTML or nothing. hydrateTemplate\n // assumes the SSR DOM is already present and walks it for hydration markers\n // (<!--elur-N-->, data-elur-e-*). When there's nothing to walk, it silently\n // does nothing (no contexts to match → empty loop → no mount). So we detect\n // the absence of hydration markers and do a fresh _render mount instead.\n const hasSSRMarkers = marker.el.innerHTML.includes(\"<!--elur-\");\n const handle = hasSSRMarkers\n ? hydrateTemplate(template, marker.el, { mismatch: \"warn-remount\" })\n : freshMount(template, marker.el);\n\n const wrappedDispose = () => {\n handle.unmount();\n _islandDisposes.delete(wrappedDispose);\n delete (marker.el as any).__elur_js_island_dispose;\n };\n (marker.el as any).__elur_js_island_dispose = wrappedDispose;\n _islandDisposes.add(wrappedDispose);\n } catch (error) {\n reportIslandError(marker, error);\n }\n}\n\nfunction reportIslandError(marker: IslandMarker, error: unknown): void {\n console.error(`[elur-kit] Failed to hydrate island \"${marker.name}\":`, error);\n const EventConstructor = marker.el.ownerDocument.defaultView?.CustomEvent;\n if (EventConstructor) {\n marker.el.dispatchEvent(new EventConstructor(\"elur:island-error\", {\n bubbles: true,\n detail: { name: marker.name, error },\n }));\n }\n}\n\n/**\n * Mounts a template fresh into a container (no hydration).\n * Used for islands with no SSR DOM (directive \"only\" or ssr:false) where\n * hydrateTemplate can't work — there's nothing to hydrate against.\n */\nfunction freshMount(template: ElurTemplate, container: Element): { unmount: () => void } {\n container.replaceChildren();\n const dispose = template._render(container, null);\n return { unmount: dispose };\n}\n\n/**\n * Hydrates all islands on the page using the provided registry.\n *\n * @param registry Map from island name to component factory.\n */\n/**\n * Dispose all currently hydrated islands. Called by the client router before\n * swapping the page body to prevent leaked effects and stale DOM writes.\n */\nexport function cleanupHydratedIslands(): void {\n for (const cancel of _islandSchedules) cancel();\n _islandSchedules.clear();\n for (const dispose of _islandDisposes) dispose();\n _islandDisposes.clear();\n}\n\nexport function hydrateIslands(registry: IslandRegistry): void {\n if (typeof window === \"undefined\") return;\n\n const markers = collectMarkers();\n\n for (const marker of markers) {\n if (marker.directive === \"load\" || marker.directive === \"only\") {\n // \"only\" is client-only (no SSR) but hydrates immediately on the\n // client, just like \"load\" — the difference is purely server-side.\n void hydrate(marker, registry);\n continue;\n }\n\n if (marker.directive === \"idle\") {\n let cancel = () => { };\n if (\"requestIdleCallback\" in window) {\n const id = window.requestIdleCallback(() => {\n _islandSchedules.delete(cancel);\n void hydrate(marker, registry);\n });\n cancel = () => window.cancelIdleCallback(id);\n } else {\n const id = globalThis.setTimeout(() => {\n _islandSchedules.delete(cancel);\n void hydrate(marker, registry);\n }, 0);\n cancel = () => globalThis.clearTimeout(id);\n }\n _islandSchedules.add(cancel);\n continue;\n }\n\n if (marker.directive === \"visible\") {\n if (\"IntersectionObserver\" in window) {\n let cancel = () => { };\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n _islandSchedules.delete(cancel);\n observer.disconnect();\n void hydrate(marker, registry);\n }\n }\n },\n { rootMargin: \"0px\", threshold: 0 },\n );\n cancel = () => observer.disconnect();\n _islandSchedules.add(cancel);\n observer.observe(marker.el);\n } else {\n void hydrate(marker, registry);\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- let e=require("@elurjs/core/hydrate");var t=new Set,n=new Set;function r(e){return{load:e}}function i(e){return typeof e==`object`&&!!e&&typeof e.load==`function`}function a(e){return typeof e==`function`?e.constructor?.name===`AsyncFunction`||e[Symbol.toStringTag]===`AsyncFunction`:!1}function o(){return Array.from(document.querySelectorAll(`[data-elur-island]`)).map(e=>{let t={el:e,name:e.dataset.elurIsland??``,directive:e.dataset.directive??`load`,props:null};if(e.dataset.props)try{t.props=JSON.parse(e.dataset.props)}catch(e){t.propsError=e}return t})}async function s(n,r){try{if(n.propsError){c(n,n.propsError);return}let o=r[n.name];if(!o){console.warn(`[elur-kit] No island registered for "${n.name}"`);return}let s;if(i(o)){let e=await o.load();s=typeof e==`function`?e:e.default}else if(typeof o==`function`){if(a(o)){let e=await o();s=typeof e==`function`?e:e.default}else s=o}if(typeof s!=`function`){console.warn(`[elur-kit] Island "${n.name}" did not resolve to a component function`);return}let u=s(n.props);if(u===null||u===!1||u===void 0)return;let d=n.el.__elur_js_island_dispose;typeof d==`function`&&d();let f=n.el.innerHTML.includes(`<!--elur-`)?(0,e.hydrate)(u,n.el,{mismatch:`warn-remount`}):l(u,n.el),p=()=>{f.unmount(),t.delete(p),delete n.el.__elur_js_island_dispose};n.el.__elur_js_island_dispose=p,t.add(p)}catch(e){c(n,e)}}function c(e,t){console.error(`[elur-kit] Failed to hydrate island "${e.name}":`,t);let n=e.el.ownerDocument.defaultView?.CustomEvent;n&&e.el.dispatchEvent(new n(`elur:island-error`,{bubbles:!0,detail:{name:e.name,error:t}}))}function l(e,t){return t.replaceChildren(),{unmount:e._render(t,null)}}function u(){for(let e of n)e();n.clear();for(let e of t)e();t.clear()}function d(e){if(typeof window>`u`)return;let t=o();for(let r of t){if(r.directive===`load`||r.directive===`only`){s(r,e);continue}if(r.directive===`idle`){let t=()=>{};if(`requestIdleCallback`in window){let i=window.requestIdleCallback(()=>{n.delete(t),s(r,e)});t=()=>window.cancelIdleCallback(i)}else{let i=globalThis.setTimeout(()=>{n.delete(t),s(r,e)},0);t=()=>globalThis.clearTimeout(i)}n.add(t);continue}if(r.directive===`visible`){if(`IntersectionObserver`in window){let t=()=>{},i=new IntersectionObserver(a=>{for(let o of a)o.isIntersecting&&(n.delete(t),i.disconnect(),s(r,e))},{rootMargin:`0px`,threshold:0});t=()=>i.disconnect(),n.add(t),i.observe(r.el)}else s(r,e)}}}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return u}});
2
- //# sourceMappingURL=hydrate-D6MR24mz.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hydrate-D6MR24mz.cjs","names":[],"sources":["../../src/island/hydrate.ts"],"sourcesContent":["import type { ElurTemplate } from \"@elurjs/core\";\nimport { hydrate as hydrateTemplate } from \"@elurjs/core/hydrate\";\nimport type { IslandDirective } from \"./island.js\";\n\n// --- Client-side island hydration ---\n\n// Keep track of every active island dispose so we can clean them up before a\n// client-side navigation swaps the whole #app content.\nconst _islandDisposes = new Set<() => void>();\nconst _islandSchedules = new Set<() => void>();\n\n// Finds [data-elur-island] markers in the current document and mounts the\n// corresponding interactive components over them. This runs in the browser.\n\nexport type IslandComponent<TProps = unknown> = (props: TProps) => ElurTemplate | null | false | undefined;\n\n// Re-exported from island.ts so the client entry and the server helper share a\n// single source of truth for the directive union (now includes \"only\").\nexport type { IslandDirective } from \"./island.js\";\n\n/** Lazy island loader in a discriminated form (no probe required to detect). */\nexport interface IslandLoader<TProps = unknown> {\n load: () => Promise<IslandComponent<TProps>>;\n}\n\n/**\n * Island registry entry. Two unambiguous shapes:\n * - `IslandComponent` (eager component function).\n * - `IslandLoader` `{ load }` (lazy, code-split loader).\n *\n * Legacy async loader functions are also accepted for backwards compatibility\n * and detected *without invoking them* (via the `AsyncFunction` tag), so no\n * side effects or duplicate signal creation happen during detection.\n */\nexport type IslandRegistryEntry<TProps = unknown> =\n | IslandComponent<TProps>\n | IslandLoader<TProps>\n | (() => Promise<IslandComponent<TProps>>);\n\nexport type IslandRegistry = Record<string, IslandRegistryEntry<any>>;\n\n/**\n * Wraps a lazy island loader in the discriminated `{ load }` form.\n */\nexport function lazyIsland<TProps = unknown>(\n loader: () => Promise<IslandComponent<TProps>>,\n): IslandLoader<TProps> {\n return { load: loader };\n}\n\nfunction isIslandLoader(value: unknown): value is IslandLoader {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { load?: unknown }).load === \"function\"\n );\n}\n\nfunction isAsyncFunction(value: unknown): boolean {\n if (typeof value !== \"function\") return false;\n const ctor = (value as { constructor?: { name?: string } }).constructor;\n return ctor?.name === \"AsyncFunction\" || (value as { [Symbol.toStringTag]?: string })[Symbol.toStringTag] === \"AsyncFunction\";\n}\n\ninterface IslandMarker {\n el: HTMLElement;\n name: string;\n directive: IslandDirective;\n props: unknown;\n propsError?: unknown;\n}\n\nfunction collectMarkers(): IslandMarker[] {\n const elements = Array.from(\n document.querySelectorAll<HTMLElement>(\"[data-elur-island]\"),\n );\n return elements.map((el) => {\n const marker: IslandMarker = {\n el,\n name: el.dataset.elurIsland ?? \"\",\n directive: (el.dataset.directive as IslandDirective) ?? \"load\",\n props: null,\n };\n if (el.dataset.props) {\n try {\n marker.props = JSON.parse(el.dataset.props);\n } catch (error) {\n marker.propsError = error;\n }\n }\n return marker;\n });\n}\n\nasync function hydrate(marker: IslandMarker, registry: IslandRegistry): Promise<void> {\n try {\n if (marker.propsError) {\n reportIslandError(marker, marker.propsError);\n return;\n }\n\n const entry = registry[marker.name];\n if (!entry) {\n console.warn(`[elur-kit] No island registered for \"${marker.name}\"`);\n return;\n }\n\n // Resolve the component without probing: a `{ load }` loader or an async\n // function is lazy (awaited for the module/component); anything else is an\n // eager component called directly with the island props. Detection never\n // invokes the component with undefined props, so no side effects or\n // duplicate signal creation occur during resolution. Eager components\n // hydrate synchronously; lazy loaders hydrate once the module resolves.\n let Component: IslandComponent | undefined;\n if (isIslandLoader(entry)) {\n const mod = (await (entry as IslandLoader).load()) as IslandComponent | { default?: unknown };\n Component = typeof mod === \"function\" ? (mod as IslandComponent) : (mod as { default?: unknown }).default as IslandComponent | undefined;\n } else if (typeof entry === \"function\") {\n if (isAsyncFunction(entry)) {\n const mod = (await (entry as () => Promise<unknown>)()) as IslandComponent | { default?: unknown };\n Component = typeof mod === \"function\" ? (mod as IslandComponent) : (mod as { default?: unknown }).default as IslandComponent | undefined;\n } else {\n Component = entry as IslandComponent;\n }\n }\n\n if (typeof Component !== \"function\") {\n console.warn(`[elur-kit] Island \"${marker.name}\" did not resolve to a component function`);\n return;\n }\n\n const template = Component(marker.props);\n if (template === null || template === false || template === undefined) return;\n const prevDispose = (marker.el as any).__elur_js_island_dispose;\n if (typeof prevDispose === \"function\") prevDispose();\n\n // Islands with directive \"only\" or options.ssr:false have no SSR-rendered\n // DOM inside the marker — only fallback HTML or nothing. hydrateTemplate\n // assumes the SSR DOM is already present and walks it for hydration markers\n // (<!--elur-N-->, data-elur-e-*). When there's nothing to walk, it silently\n // does nothing (no contexts to match → empty loop → no mount). So we detect\n // the absence of hydration markers and do a fresh _render mount instead.\n const hasSSRMarkers = marker.el.innerHTML.includes(\"<!--elur-\");\n const handle = hasSSRMarkers\n ? hydrateTemplate(template, marker.el, { mismatch: \"warn-remount\" })\n : freshMount(template, marker.el);\n\n const wrappedDispose = () => {\n handle.unmount();\n _islandDisposes.delete(wrappedDispose);\n delete (marker.el as any).__elur_js_island_dispose;\n };\n (marker.el as any).__elur_js_island_dispose = wrappedDispose;\n _islandDisposes.add(wrappedDispose);\n } catch (error) {\n reportIslandError(marker, error);\n }\n}\n\nfunction reportIslandError(marker: IslandMarker, error: unknown): void {\n console.error(`[elur-kit] Failed to hydrate island \"${marker.name}\":`, error);\n const EventConstructor = marker.el.ownerDocument.defaultView?.CustomEvent;\n if (EventConstructor) {\n marker.el.dispatchEvent(new EventConstructor(\"elur:island-error\", {\n bubbles: true,\n detail: { name: marker.name, error },\n }));\n }\n}\n\n/**\n * Mounts a template fresh into a container (no hydration).\n * Used for islands with no SSR DOM (directive \"only\" or ssr:false) where\n * hydrateTemplate can't work — there's nothing to hydrate against.\n */\nfunction freshMount(template: ElurTemplate, container: Element): { unmount: () => void } {\n container.replaceChildren();\n const dispose = template._render(container, null);\n return { unmount: dispose };\n}\n\n/**\n * Hydrates all islands on the page using the provided registry.\n *\n * @param registry Map from island name to component factory.\n */\n/**\n * Dispose all currently hydrated islands. Called by the client router before\n * swapping the page body to prevent leaked effects and stale DOM writes.\n */\nexport function cleanupHydratedIslands(): void {\n for (const cancel of _islandSchedules) cancel();\n _islandSchedules.clear();\n for (const dispose of _islandDisposes) dispose();\n _islandDisposes.clear();\n}\n\nexport function hydrateIslands(registry: IslandRegistry): void {\n if (typeof window === \"undefined\") return;\n\n const markers = collectMarkers();\n\n for (const marker of markers) {\n if (marker.directive === \"load\" || marker.directive === \"only\") {\n // \"only\" is client-only (no SSR) but hydrates immediately on the\n // client, just like \"load\" — the difference is purely server-side.\n void hydrate(marker, registry);\n continue;\n }\n\n if (marker.directive === \"idle\") {\n let cancel = () => { };\n if (\"requestIdleCallback\" in window) {\n const id = window.requestIdleCallback(() => {\n _islandSchedules.delete(cancel);\n void hydrate(marker, registry);\n });\n cancel = () => window.cancelIdleCallback(id);\n } else {\n const id = globalThis.setTimeout(() => {\n _islandSchedules.delete(cancel);\n void hydrate(marker, registry);\n }, 0);\n cancel = () => globalThis.clearTimeout(id);\n }\n _islandSchedules.add(cancel);\n continue;\n }\n\n if (marker.directive === \"visible\") {\n if (\"IntersectionObserver\" in window) {\n let cancel = () => { };\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n _islandSchedules.delete(cancel);\n observer.disconnect();\n void hydrate(marker, registry);\n }\n }\n },\n { rootMargin: \"0px\", threshold: 0 },\n );\n cancel = () => observer.disconnect();\n _islandSchedules.add(cancel);\n observer.observe(marker.el);\n } else {\n void hydrate(marker, registry);\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,39 +0,0 @@
1
- const e=require("./node-http-DhxguYyz.cjs");let t=require("node:fs/promises"),n=require("node:path"),r=require("node:module");function i(e,t){let n=e.replace(/[^a-zA-Z0-9_$]/g,`_`);return/^[a-zA-Z_$]/.test(n)?`${n}_${t}`:`_${n}_${t}`}function a(e,t,n=`@elurjs/kit/island`,r=`@elurjs/kit/router`){let a=e.map((e,n)=>({ident:i(e.name,n),name:e.name,spec:o(t,e.filePath)})).map(e=>` ${JSON.stringify(e.name)}: { load: () => import(${JSON.stringify(e.spec)}).then(m => m.default) },`).join(`
2
- `),s=a?`const registry = {
3
- ${a}
4
- };
5
- // Defer hydration until the browser is idle so the first paint is not
6
- // blocked. Islands with directive "load" already have SSR-rendered DOM,
7
- // so the user sees content immediately — hydration only adds interactivity.
8
- const hydrate = () => hydrateIslands(registry);
9
- if ("requestIdleCallback" in window) {
10
- requestIdleCallback(hydrate, { timeout: 2000 });
11
- } else {
12
- setTimeout(hydrate, 0);
13
- }
14
- document.addEventListener("elur:rendered", () => {
15
- cleanupHydratedIslands();
16
- hydrateIslands(registry);
17
- });
18
-
19
- // Vite HMR: when an island module (or the entry itself) updates, dispose the
20
- // current islands and re-hydrate from the updated modules — the registry's
21
- // dynamic import() resolves to the fresh modules, so no full page reload is
22
- // needed (progressive enhancement, audit §10.2 / §12.2).
23
- if (import.meta.hot) {
24
- import.meta.hot.accept((newModule) => {
25
- cleanupHydratedIslands();
26
- hydrateIslands(registry);
27
- if (newModule) {
28
- // Re-run the module so its side effects (router, listeners) apply.
29
- }
30
- });
31
- }`:``;return`// AUTO-GENERATED by @elurjs/kit. Do not edit.
32
- import { startClientRouter } from ${JSON.stringify(r)};
33
- import { hydrateIslands, cleanupHydratedIslands } from ${JSON.stringify(n)};
34
-
35
- startClientRouter();
36
- ${s}
37
- `}function o(e,t){let r=(0,n.relative)((0,n.dirname)(e),t).split(n.sep).join(`/`);return r.startsWith(`.`)||(r=`./${r}`),r}async function s(e){let r=a(e.islands,e.outFile,e.hydrateImport,e.routerImport);return await(0,t.mkdir)((0,n.dirname)(e.outFile),{recursive:!0}),await(0,t.writeFile)(e.outFile,r,`utf8`),e.outFile}async function c(e){let t=[`${e}/src/middleware.ts`,`${e}/middleware.ts`];for(let e of t)try{let t=await import(e),n=t.default??t.middleware;if(typeof n!=`function`)continue;return{handler:n,config:t.config??{}}}catch(e){let t=typeof e==`object`&&e&&`message`in e?String(e.message):String(e);if(t.includes(`Cannot find module`)||t.includes(`Cannot find package`)||t.includes(`ENOENT`)||t.includes(`Module not found`))continue;throw Error(`[elur-kit] Error loading middleware: ${t}`,{cause:e})}return null}function l(t,n){if(!n.matcher||n.matcher.length===0)return!0;let r=t.split(`?`)[0];for(let t of n.matcher){if(t===r)return!0;let n=t.match(/^(.*)\/:[\w]+\*$/);if(n&&r===n[1]||e.o(r,[{path:t,pagePath:``,params:[],layouts:[]}]))return!0}return!1}async function u(e,t,n){let r,i,a,o=[],s={next(e){e?.headers&&(r=e.headers),e?.params&&(i=e.params),e?.locals&&(a=e.locals)},params:n,locals:{}};try{let o=await e.handler(t,s);return o instanceof Response?{kind:`response`,response:o}:{kind:`continue`,headers:r,params:i??n,locals:a}}finally{for(let e of o)try{await e()}catch(e){console.error(`[elur-kit] middleware cleanup error:`,e)}}}var d=(0,r.createRequire)({}.url),f=!1;function p(){f||(f=!0,console.warn('[elur-kit] The legacy interpolation transform is deprecated. Install @elurjs/vite-plugin-elur >= 1.1.0 for compile-time partial attribute interpolation. Remove `interpolation: "legacy"` once migration is complete.'))}function m(){try{let[e,t]=(d(`@elurjs/vite-plugin-elur/package.json`).version??`0.0.0`).split(`.`).map(Number);return e>1||e===1&&t>=1}catch{return!1}}function h(){try{return d(`@elurjs/core`)?.templateFeatures?.partialAttributeInterpolation===!0}catch{return!1}}function g(e){return e===`off`?!1:e===`legacy`?(p(),!0):!m()&&!h()}var _=`html`,v="`";function y(e,t){let n=1,r=t+2;for(;r<e.length&&n>0;){let t=e[r];if(t===`\\`){r+=2;continue}if(t===`"`||t===`'`||t==="`"){let n=t;for(r++;r<e.length;){if(e[r]===`\\`){r+=2;continue}if(e[r]===n)break;r++}r++;continue}t===`{`?n++:t===`}`&&n--,r++}return r}function b(e,t,n){let r=t+1,i=``,a=!1;for(;r<e.length;){let t=e[r];if(t===`\\`){i+=t+(e[r+1]??``),r+=2;continue}if(t===n){r++;break}if(t===`$`&&e[r+1]===`{`){let t=y(e,r);i+=e.slice(r,t),r=t,a=!0;continue}i+=t,r++}return{end:r,inside:i,hasInterp:a}}function x(e){let t=[],n=0,r=``,i=()=>{r&&=(t.push(JSON.stringify(S(r))),``)};for(;n<e.length;){if(e[n]===`\\`){r+=e[n]+(e[n+1]??``),n+=2;continue}if(e[n]===`$`&&e[n+1]===`{`){i();let r=y(e,n),a=e.slice(n+2,r-1).trim();a&&t.push(`(${a})`),n=r;continue}r+=e[n],n++}return i(),t.length===0?`""`:t.length===1?t[0]:t.join(` + `)}function S(e){let t={n:`
38
- `,t:` `,r:`\r`},n=``,r=0;for(;r<e.length;){let i=e[r];if(i===`\\`&&r+1<e.length){let i=e[r+1];if(i in t){n+=t[i],r+=2;continue}n+=i,r+=2;continue}n+=i,r++}return n}function C(e){let t=``,n=0,r=e.length;for(;n<r;){let i=e.indexOf(`<`,n);if(i===-1){t+=e.slice(n);break}if(t+=e.slice(n,i),n=i,e.startsWith(`<!--`,n)){let r=e.indexOf(`-->`,n+4);if(r===-1){t+=e.slice(n);break}t+=e.slice(n,r+3),n=r+3;continue}if(e[n+1]===`/`||e[n+1]===`!`||e[n+1]===`?`){let r=e.indexOf(`>`,n+1);if(r===-1){t+=e.slice(n);break}t+=e.slice(n,r+1),n=r+1;continue}let a=n+1;for(;a<r&&/[a-zA-Z0-9-]/.test(e[a]);)a++;for(t+=e.slice(n,a),n=a;n<r;){let i=``;for(;n<r&&/\s/.test(e[n]);)i+=e[n],n++;if(n>=r){t+=i;break}if(e[n]===`>`){t+=i+`>`,n++;break}if(e[n]===`/`&&e[n+1]===`>`){t+=i+`/>`,n+=2;break}if(e[n]===`$`&&e[n+1]===`{`){let r=y(e,n);t+=i+e.slice(n,r),n=r;continue}let a=n;for(;n<r&&!/[\s=/>"'$]/.test(e[n]);)n++;let o=e.slice(a,n);if(!o){t+=i+e[n],n++;continue}let s=``;for(;n<r&&/\s/.test(e[n]);)s+=e[n],n++;if(e[n]!==`=`){t+=i+o+s;continue}n++;let c=``;for(;n<r&&/\s/.test(e[n]);)c+=e[n],n++;let l=e[n];if(l===`"`||l===`'`){let{end:r,inside:a,hasInterp:u}=b(e,n,l);if(u){let l=y(a,0);if(!(a.startsWith("${")&&l===a.length&&!a.slice(2,l-1).includes("${"))){t+=i+o+s+"=${"+x(a)+`}`,n=r;continue}t+=i+o+s+`=`+c+e.slice(n,r)}else t+=i+o+s+`=`+c+e.slice(n,r);n=r;continue}let u=``;for(;n<r&&!/\s/.test(e[n])&&e[n]!==`>`&&(e[n]!==`/`||e[n+1]!==`>`);)u+=e[n],n++;t+=i+o+s+`=`+c+u}}return t}function w(e){let t=``,n=0;for(;n<e.length;){let r=e.indexOf(_,n);if(r===-1){t+=e.slice(n);break}for(t+=e.slice(n,r+4),n=r+4;n<e.length&&/\s/.test(e[n]);)t+=e[n],n++;if(n>=e.length||e[n]!==v)continue;t+=e[n],n++;let i=1,a=``;for(;n<e.length&&i>0;){let t=e[n];if(t===`\\`){a+=t+e[n+1],n+=2;continue}if(t===v&&(i--,i===0)){n++;break}if(t===`$`&&e[n+1]===`{`){let t=y(e,n);a+=e.slice(n,t),n=t;continue}a+=t,n++}let o=C(a);t+=o,t+=v}return t}function T(e={}){let t=e.appDir??`src/app`,n=e.islandsDir??`src/islands`;return{name:`elur-kit-interpolation`,enforce:`pre`,transform(e,r){if(!r.endsWith(`.ts`)&&!r.endsWith(`.js`)||!r.includes(t)&&!r.includes(n)||!e.includes("html`"))return;let i=w(e);if(i!==e)return{code:i,map:null}}}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return h}});
39
- //# sourceMappingURL=interpolation-plugin-TWDch8nn.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"interpolation-plugin-TWDch8nn.cjs","names":[],"sources":["../../src/island/generate-entry.ts","../../src/middleware/index.ts","../../src/vite/interpolation-plugin.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, relative, sep } from \"node:path\";\nimport type { IslandModule } from \"./scan.js\";\n\n// --- Client entry generator ---\n//\n// Turns a list of scanned islands into a client entry module that imports each\n// island and registers it with `hydrateIslands`. This removes the need to hand-\n// maintain `entry-client.ts` as islands are added or removed.\n//\n// The generated file imports island default exports and passes them to\n// `hydrateIslands` keyed by their registry name.\n\n/** Options for generating the client entry module. */\nexport interface GenerateEntryOptions {\n /** Islands to register, from `scanIslands`. */\n islands: IslandModule[];\n /** Absolute path of the entry file to write (e.g. \".elur/entry-client.ts\"). */\n outFile: string;\n /**\n * Import specifier for the kit's client island helpers.\n * Defaults to the published subpath `@elurjs/kit/island`.\n */\n hydrateImport?: string;\n /**\n * Import specifier for the kit's client router.\n * Defaults to the published subpath `@elurjs/kit/router`.\n */\n routerImport?: string;\n}\n\n/** Turns a registry name into a safe JS identifier for the import binding. */\nfunction toIdentifier(name: string, index: number): string {\n const cleaned = name.replace(/[^a-zA-Z0-9_$]/g, \"_\");\n return /^[a-zA-Z_$]/.test(cleaned) ? `${cleaned}_${index}` : `_${cleaned}_${index}`;\n}\n\n/** Builds the source code of the client entry module. */\nexport function buildEntrySource(\n islands: IslandModule[],\n outFile: string,\n hydrateImport = \"@elurjs/kit/island\",\n routerImport = \"@elurjs/kit/router\",\n): string {\n const bindings = islands.map((island, i) => ({\n ident: toIdentifier(island.name, i),\n name: island.name,\n // Relative import specifier from the entry file to the island module.\n spec: toImportSpecifier(outFile, island.filePath),\n }));\n\n // Lazy registry: each island is loaded on-demand via dynamic import().\n // This enables code-splitting — islands not on the current page (or not yet\n // triggered by their directive) stay out of the initial bundle.\n //\n // The registry maps island name → discriminated lazy loader `{ load }`.\n // hydrateIslands() awaits `entry.load()` before hydrating, so the first\n // paint only needs the small entry chunk + the islands on the page. The\n // discriminated form lets the hydrator tell eager components from lazy\n // loaders without executing a probe.\n const registryLines = bindings\n .map((b) => ` ${JSON.stringify(b.name)}: { load: () => import(${JSON.stringify(b.spec)}).then(m => m.default) },`)\n .join(\"\\n\");\n\n const islandHydration = registryLines\n ? `const registry = {\n${registryLines}\n};\n// Defer hydration until the browser is idle so the first paint is not\n// blocked. Islands with directive \"load\" already have SSR-rendered DOM,\n// so the user sees content immediately — hydration only adds interactivity.\nconst hydrate = () => hydrateIslands(registry);\nif (\"requestIdleCallback\" in window) {\n requestIdleCallback(hydrate, { timeout: 2000 });\n} else {\n setTimeout(hydrate, 0);\n}\ndocument.addEventListener(\"elur:rendered\", () => {\n cleanupHydratedIslands();\n hydrateIslands(registry);\n});\n\n// Vite HMR: when an island module (or the entry itself) updates, dispose the\n// current islands and re-hydrate from the updated modules — the registry's\n// dynamic import() resolves to the fresh modules, so no full page reload is\n// needed (progressive enhancement, audit §10.2 / §12.2).\nif (import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n cleanupHydratedIslands();\n hydrateIslands(registry);\n if (newModule) {\n // Re-run the module so its side effects (router, listeners) apply.\n }\n });\n}`\n : \"\";\n\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\nimport { startClientRouter } from ${JSON.stringify(routerImport)};\nimport { hydrateIslands, cleanupHydratedIslands } from ${JSON.stringify(hydrateImport)};\n\nstartClientRouter();\n${islandHydration}\n`;\n}\n\n/** Computes a POSIX-style relative import specifier between two files. */\nfunction toImportSpecifier(fromFile: string, toFile: string): string {\n let spec = relative(dirname(fromFile), toFile).split(sep).join(\"/\");\n if (!spec.startsWith(\".\")) spec = `./${spec}`;\n return spec;\n}\n\n/**\n * Generates and writes the client entry module for the given islands.\n *\n * @param options Generation options.\n * @returns The absolute path of the written entry file.\n */\nexport async function generateClientEntry(\n options: GenerateEntryOptions,\n): Promise<string> {\n const source = buildEntrySource(\n options.islands,\n options.outFile,\n options.hydrateImport,\n options.routerImport,\n );\n await mkdir(dirname(options.outFile), { recursive: true });\n await writeFile(options.outFile, source, \"utf8\");\n return options.outFile;\n}\n","// --- Middleware ---\n//\n// Convention: `src/middleware.ts` in the project root exports a default\n// function and an optional `config` with a `matcher` array.\n//\n// import type { Middleware } from \"@elurjs/kit\";\n//\n// export default function middleware(request: Request) {\n// if (!request.headers.get(\"Cookie\")?.includes(\"session=\")) {\n// return Response.redirect(new URL(\"/login\", request.url), 307);\n// }\n// }\n//\n// export const config = { matcher: [\"/dashboard/:path*\", \"/admin/:path*\"] };\n//\n// The middleware runs before routing. Return a `Response` to short-circuit\n// (redirect, rewrite, 401, etc.). Return `undefined` or nothing to continue.\n// Use `next()` to pass headers to the loader.\n\nimport { matchRoute } from \"../ssr/match.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/** The middleware function signature. */\nexport type Middleware = (request: Request, context: MiddlewareContext) =>\n | Response\n | void\n | Promise<Response | void>;\n\n/** Context passed to the middleware function. */\nexport interface MiddlewareContext {\n /** Helper to continue to the next handler. Can attach headers, params, and locals. */\n next(options?: {\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n }): void;\n /** Matched route params (only available if the path matches a page route). */\n params?: Record<string, string | string[]>;\n /** Per-request locals (populated by middleware, available to loaders/actions). */\n locals?: Record<string, unknown>;\n}\n\n/** Configuration for the middleware module. */\nexport interface MiddlewareConfig {\n /** Path patterns that trigger the middleware. Supports `:param` and `:param*`. */\n matcher?: string[];\n}\n\nexport interface LoadedMiddleware {\n handler: Middleware;\n config: MiddlewareConfig;\n}\n\n/** Result of running middleware: either a response to short-circuit with, or continue. */\nexport type MiddlewareResult =\n | { kind: \"response\"; response: Response }\n | {\n kind: \"continue\";\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n };\n\n/**\n * Loads the user's `src/middleware.ts` module. Returns `null` if no middleware\n * file exists. Distinguishes \"file not found\" from \"file has errors\" (§6):\n * an import error is not silently treated as \"no middleware\".\n */\nexport async function loadMiddleware(root: string): Promise<LoadedMiddleware | null> {\n const candidates = [\n `${root}/src/middleware.ts`,\n `${root}/middleware.ts`,\n ];\n\n for (const path of candidates) {\n try {\n const mod = await import(path);\n const handler = (mod.default ?? mod.middleware) as Middleware | undefined;\n if (typeof handler !== \"function\") continue;\n const config = (mod.config ?? {}) as MiddlewareConfig;\n return { handler, config };\n } catch (err) {\n // Distinguish \"module not found\" from actual errors.\n // If the error is a module resolution error for this specific file,\n // it means the file doesn't exist — try the next candidate.\n // If it's a syntax/runtime error, rethrow so the user sees it.\n // Note: Bun's ResolveMessage is not `instanceof Error`, so match on the\n // message property instead of relying on the class hierarchy.\n const msg =\n typeof err === \"object\" && err !== null && \"message\" in err\n ? String((err as { message: unknown }).message)\n : String(err);\n if (\n msg.includes(\"Cannot find module\") ||\n msg.includes(\"Cannot find package\") ||\n msg.includes(\"ENOENT\") ||\n msg.includes(\"Module not found\")\n ) {\n // File doesn't exist — try next candidate.\n continue;\n }\n // Actual error in the middleware file — rethrow (§6).\n throw new Error(`[elur-kit] Error loading middleware: ${msg}`, { cause: err });\n }\n }\n\n return null;\n}\n\n/**\n * Checks if a pathname matches any of the middleware's matcher patterns.\n * If no matcher is configured, the middleware runs for every request.\n *\n * Catch-all patterns (`:param*`) match both the base path and any sub-paths,\n * e.g. `/dashboard/:path*` matches `/dashboard` and `/dashboard/settings/users`.\n */\nexport function matchesMiddleware(pathname: string, config: MiddlewareConfig): boolean {\n if (!config.matcher || config.matcher.length === 0) return true;\n\n const cleanPath = pathname.split(\"?\")[0];\n\n for (const pattern of config.matcher) {\n // Exact match.\n if (pattern === cleanPath) return true;\n\n // Check for catch-all: `/foo/:bar*` should also match `/foo`.\n const catchAllMatch = pattern.match(/^(.*)\\/:[\\w]+\\*$/);\n if (catchAllMatch) {\n const base = catchAllMatch[1];\n if (cleanPath === base) return true;\n }\n\n // Use matchRoute for param matching.\n const pseudoRoutes: PageRoute[] = [{\n path: pattern,\n pagePath: \"\",\n params: [],\n layouts: [],\n }];\n if (matchRoute(cleanPath, pseudoRoutes)) return true;\n }\n\n return false;\n}\n\n/**\n * Runs the middleware for a request. Returns the result indicating whether to\n * short-circuit with a response or continue with propagated headers/params/locals.\n *\n * Per §6: cleanup runs in `finally`, response short-circuits the pipeline,\n * headers/params/locals are propagated to downstream handlers.\n */\nexport async function runMiddleware(\n middleware: LoadedMiddleware,\n request: Request,\n params?: Record<string, string | string[]>,\n): Promise<MiddlewareResult> {\n let nextHeaders: Record<string, string> | undefined;\n let nextParams: Record<string, string | string[]> | undefined;\n let nextLocals: Record<string, unknown> | undefined;\n const cleanups: Array<() => void | Promise<void>> = [];\n\n const context: MiddlewareContext = {\n next(options) {\n if (options?.headers) nextHeaders = options.headers;\n if (options?.params) nextParams = options.params;\n if (options?.locals) nextLocals = options.locals;\n },\n params,\n locals: {},\n };\n\n try {\n const result = await middleware.handler(request, context);\n\n if (result instanceof Response) {\n return { kind: \"response\", response: result };\n }\n\n return {\n kind: \"continue\",\n headers: nextHeaders,\n params: nextParams ?? params,\n locals: nextLocals,\n };\n } finally {\n // Run any cleanup functions (§6). Errors in cleanup are logged but\n // do not propagate to the caller.\n for (const cleanup of cleanups) {\n try {\n await cleanup();\n } catch (err) {\n console.error(\"[elur-kit] middleware cleanup error:\", err);\n }\n }\n }\n}\n","import { createRequire } from \"node:module\";\nimport type { Plugin } from \"vite\";\n\n/**\n * How the legacy interpolation transform is handled relative to the installed\n * Elur core and Vite plugin:\n *\n * - `\"auto\"` (default): the kit's legacy transform is only applied when the\n * Vite plugin (`@elurjs/vite-plugin-elur` >= 1.1.0) is NOT installed.\n * The plugin has a more powerful state-machine lexer and takes precedence.\n * - `\"legacy\"`: always apply the kit's transform (for migrations), with a\n * one-time deprecation warning.\n * - `\"off\"`: never apply the kit's transform. Recommended when the Vite\n * plugin is installed.\n */\nexport type InterpolationMode = \"auto\" | \"legacy\" | \"off\";\n\nconst require = createRequire(import.meta.url);\n\nlet _warnedLegacy = false;\n\nfunction warnLegacyOnce(): void {\n if (_warnedLegacy) return;\n _warnedLegacy = true;\n console.warn(\n \"[elur-kit] The legacy interpolation transform is deprecated. \" +\n \"Install @elurjs/vite-plugin-elur >= 1.1.0 for compile-time \" +\n \"partial attribute interpolation. Remove `interpolation: \\\"legacy\\\"` \" +\n \"once migration is complete.\",\n );\n}\n\n/**\n * Detects whether the Vite plugin (`@elurjs/vite-plugin-elur`) is\n * installed and provides compile-time partial attribute interpolation.\n */\nexport function pluginSupportsPartialInterpolation(): boolean {\n try {\n const pkg = require(\"@elurjs/vite-plugin-elur/package.json\") as {\n version?: string;\n };\n // >= 1.1.0 has the interpolation lexer\n const [major, minor] = (pkg.version ?? \"0.0.0\").split(\".\").map(Number);\n return major > 1 || (major === 1 && minor >= 1);\n } catch {\n return false;\n }\n}\n\n/**\n * Detects whether the installed Elur core supports partial attribute\n * interpolation natively (via the public `templateFeatures` capability).\n * Note: as of core v3.4.0, this is always false — the lexer moved to the\n * Vite plugin.\n */\nexport function coreSupportsPartialInterpolation(): boolean {\n try {\n const core = require(\"@elurjs/core\") as {\n templateFeatures?: { partialAttributeInterpolation?: boolean };\n };\n return core?.templateFeatures?.partialAttributeInterpolation === true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolves whether the kit's legacy transform should be applied.\n *\n * In `\"auto\"` mode, the kit's transform runs only when neither the Vite\n * plugin nor the core provides partial interpolation. When the Vite plugin\n * is installed (>= 1.1.0), it takes precedence and the kit's transform is\n * skipped to avoid double-processing.\n */\nexport function shouldUseLegacyInterpolation(mode: InterpolationMode): boolean {\n if (mode === \"off\") return false;\n if (mode === \"legacy\") {\n warnLegacyOnce();\n return true;\n }\n // auto: skip if the Vite plugin handles it\n if (pluginSupportsPartialInterpolation()) return false;\n // fallback: use legacy if core doesn't support it natively\n return !coreSupportsPartialInterpolation();\n}\n\n/**\n * Transforms Elur `html\\`\\`` templates so that attributes with partial\n * interpolation become a single interpolation expression.\n *\n * Elur requires every dynamic attribute to be a single interpolation covering\n * the whole value. This plugin rewrites patterns such as:\n *\n * html\\`<a href=\"/blog/${slug}\">...</a>\\`\n *\n * into:\n *\n * html\\`<a href=${\"/blog/\" + slug}>...</a>\\`\n *\n * Only files inside the app and islands directories are processed.\n *\n * @deprecated Elur core supports partial attribute interpolation natively.\n * Keep this transform only for migrations against older cores\n * (`interpolation: \"legacy\"`).\n */\nexport interface InterpolationPluginOptions {\n appDir?: string;\n islandsDir?: string;\n}\n\nconst HTML_TAG = \"html\";\nconst TEMPLATE_START = \"`\";\n\n/**\n * Scans a `${...}` interpolation starting at `start` (where content[start] is\n * `$` and content[start + 1] is `{`), honoring nested braces, strings and\n * escape sequences. Returns the index just past the closing `}`.\n */\nfunction scanInterpolation(content: string, start: number): number {\n let depth = 1;\n let i = start + 2;\n while (i < content.length && depth > 0) {\n const c = content[i];\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n const q = c;\n i++;\n while (i < content.length) {\n if (content[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (content[i] === q) break;\n i++;\n }\n i++;\n continue;\n }\n if (c === \"{\") depth++;\n else if (c === \"}\") depth--;\n i++;\n }\n return i;\n}\n\n/**\n * Scans a quoted attribute value starting at `start` (where content[start] is\n * the quote character). Handles escapes, `${...}` interpolations with nested\n * braces, and nested quotes. Returns the index just past the closing quote,\n * the raw inner text (escapes preserved as in the source) and whether the\n * value contains at least one interpolation.\n */\nfunction scanQuotedValue(\n content: string,\n start: number,\n quote: string,\n): { end: number; inside: string; hasInterp: boolean } {\n let i = start + 1;\n let inside = \"\";\n let hasInterp = false;\n while (i < content.length) {\n const c = content[i];\n if (c === \"\\\\\") {\n inside += c + (content[i + 1] ?? \"\");\n i += 2;\n continue;\n }\n if (c === quote) {\n i++;\n break;\n }\n if (c === \"$\" && content[i + 1] === \"{\") {\n const end = scanInterpolation(content, i);\n inside += content.slice(i, end);\n i = end;\n hasInterp = true;\n continue;\n }\n inside += c;\n i++;\n }\n return { end: i, inside, hasInterp };\n}\n\n/**\n * Converts the inner text of a quoted attribute value (which may contain\n * `${...}` interpolations) into a JS expression. Literal parts are JSON\n * encoded; interpolations keep their raw expression text.\n *\n * Examples:\n * /blog/${slug} -> \"/blog/\" + (slug)\n * ${slug} -> (slug)\n * tag ${cls({a:1})} -> \"tag \" + (cls({a:1}))\n */\nfunction valueToExpression(value: string): string {\n const parts: string[] = [];\n let i = 0;\n let literal = \"\";\n const flush = () => {\n if (literal) {\n parts.push(JSON.stringify(unescapeAttributeLiteral(literal)));\n literal = \"\";\n }\n };\n\n while (i < value.length) {\n if (value[i] === \"\\\\\") {\n literal += value[i] + (value[i + 1] ?? \"\");\n i += 2;\n continue;\n }\n if (value[i] === \"$\" && value[i + 1] === \"{\") {\n flush();\n const end = scanInterpolation(value, i);\n const expr = value.slice(i + 2, end - 1).trim();\n if (expr) parts.push(`(${expr})`);\n i = end;\n continue;\n }\n literal += value[i];\n i++;\n }\n flush();\n\n if (parts.length === 0) return '\"\"';\n if (parts.length === 1) return parts[0] as string;\n return parts.join(\" + \");\n}\n\n/**\n * Unescapes escape sequences that appear inside a JS template literal so the\n * JSON.stringify output matches the runtime string value.\n */\nfunction unescapeAttributeLiteral(literal: string): string {\n const escapes: Record<string, string> = {\n n: \"\\n\",\n t: \"\\t\",\n r: \"\\r\",\n };\n let out = \"\";\n let i = 0;\n while (i < literal.length) {\n const c = literal[i];\n if (c === \"\\\\\" && i + 1 < literal.length) {\n const next = literal[i + 1];\n if (next in escapes) {\n out += escapes[next];\n i += 2;\n continue;\n }\n out += next;\n i += 2;\n continue;\n }\n out += c;\n i++;\n }\n return out;\n}\n\n/**\n * Rewrites quoted attribute values that contain interpolations inside html``\n * templates, leaving everything else untouched.\n */\nfunction transformTemplateContent(content: string): string {\n let out = \"\";\n let i = 0;\n const n = content.length;\n\n while (i < n) {\n const lt = content.indexOf(\"<\", i);\n if (lt === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, lt);\n i = lt;\n\n // HTML comments: copy verbatim.\n if (content.startsWith(\"<!--\", i)) {\n const end = content.indexOf(\"-->\", i + 4);\n if (end === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, end + 3);\n i = end + 3;\n continue;\n }\n\n // Closing tags, doctype, CDATA, processing instructions: copy verbatim.\n if (content[i + 1] === \"/\" || content[i + 1] === \"!\" || content[i + 1] === \"?\") {\n const gt = content.indexOf(\">\", i + 1);\n if (gt === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, gt + 1);\n i = gt + 1;\n continue;\n }\n\n // Opening tag. Copy the tag name, then walk its attributes.\n let j = i + 1;\n while (j < n && /[a-zA-Z0-9-]/.test(content[j])) j++;\n out += content.slice(i, j);\n i = j;\n\n while (i < n) {\n let ws = \"\";\n while (i < n && /\\s/.test(content[i])) {\n ws += content[i];\n i++;\n }\n if (i >= n) {\n out += ws;\n break;\n }\n if (content[i] === \">\") {\n out += ws + \">\";\n i++;\n break;\n }\n if (content[i] === \"/\" && content[i + 1] === \">\") {\n out += ws + \"/>\";\n i += 2;\n break;\n }\n // Interpolation in the tag body (dynamic attrs/spread): copy verbatim.\n if (content[i] === \"$\" && content[i + 1] === \"{\") {\n const end = scanInterpolation(content, i);\n out += ws + content.slice(i, end);\n i = end;\n continue;\n }\n\n // Attribute name.\n let nameStart = i;\n while (i < n && !/[\\s=/>\"'$]/.test(content[i])) i++;\n const name = content.slice(nameStart, i);\n if (!name) {\n out += ws + content[i];\n i++;\n continue;\n }\n\n let eqWs = \"\";\n while (i < n && /\\s/.test(content[i])) {\n eqWs += content[i];\n i++;\n }\n\n if (content[i] !== \"=\") {\n out += ws + name + eqWs;\n continue;\n }\n\n i++; // consume \"=\"\n let valWs = \"\";\n while (i < n && /\\s/.test(content[i])) {\n valWs += content[i];\n i++;\n }\n\n const quote = content[i];\n if (quote === '\"' || quote === \"'\") {\n const { end, inside, hasInterp } = scanQuotedValue(content, i, quote);\n if (hasInterp) {\n // Skip values that are a single full interpolation: Elur handles\n // `attr=\"${expr}\"` natively, so only partial interpolations need the\n // rewrite.\n const first = scanInterpolation(inside, 0);\n const fullValue =\n inside.startsWith(\"${\") &&\n first === inside.length &&\n !inside.slice(2, first - 1).includes(\"${\");\n if (!fullValue) {\n // Elur needs the interpolation to start right after \"=\" (no space),\n // so the whitespace before the original value is dropped.\n out += ws + name + eqWs + \"=\" + \"${\" + valueToExpression(inside) + \"}\";\n i = end;\n continue;\n }\n out += ws + name + eqWs + \"=\" + valWs + content.slice(i, end);\n } else {\n out += ws + name + eqWs + \"=\" + valWs + content.slice(i, end);\n }\n i = end;\n continue;\n }\n\n // Unquoted value: copy up to whitespace, \">\" or \"/>\".\n let v = \"\";\n while (\n i < n &&\n !/\\s/.test(content[i]) &&\n content[i] !== \">\" &&\n !(content[i] === \"/\" && content[i + 1] === \">\")\n ) {\n v += content[i];\n i++;\n }\n out += ws + name + eqWs + \"=\" + valWs + v;\n }\n }\n\n return out;\n}\n\n/**\n * @deprecated Use the native partial attribute interpolation of Elur core\n * (core >= 3.3). Kept for legacy migrations and direct consumers.\n */\nexport function transformPartialInterpolations(source: string): string {\n let result = \"\";\n let i = 0;\n while (i < source.length) {\n // Find the next html` sequence.\n const htmlIndex = source.indexOf(HTML_TAG, i);\n if (htmlIndex === -1) {\n result += source.slice(i);\n break;\n }\n result += source.slice(i, htmlIndex + HTML_TAG.length);\n i = htmlIndex + HTML_TAG.length;\n\n // Skip whitespace before the backtick.\n while (i < source.length && /\\s/.test(source[i])) {\n result += source[i];\n i++;\n }\n if (i >= source.length || source[i] !== TEMPLATE_START) {\n continue;\n }\n result += source[i];\n i++;\n\n // Parse the template literal until the matching backtick.\n let depth = 1;\n let templateContent = \"\";\n while (i < source.length && depth > 0) {\n const char = source[i];\n if (char === \"\\\\\") {\n templateContent += char + source[i + 1];\n i += 2;\n continue;\n }\n if (char === TEMPLATE_START) {\n depth--;\n if (depth === 0) {\n i++;\n break;\n }\n }\n if (char === \"$\") {\n // Look ahead for ${...}\n if (source[i + 1] === \"{\") {\n const end = scanInterpolation(source, i);\n templateContent += source.slice(i, end);\n i = end;\n continue;\n }\n }\n templateContent += char;\n i++;\n }\n\n const transformed = transformTemplateContent(templateContent);\n result += transformed;\n result += TEMPLATE_START;\n }\n return result;\n}\n\nexport function elurJsInterpolationPlugin(options: InterpolationPluginOptions = {}): Plugin {\n const appDir = options.appDir ?? \"src/app\";\n const islandsDir = options.islandsDir ?? \"src/islands\";\n return {\n name: \"elur-kit-interpolation\",\n enforce: \"pre\",\n transform(code, id) {\n if (!id.endsWith(\".ts\") && !id.endsWith(\".js\")) return;\n if (!id.includes(appDir) && !id.includes(islandsDir)) return;\n if (!code.includes(\"html`\")) return;\n const transformed = transformPartialInterpolations(code);\n if (transformed === code) return;\n return { code: transformed, map: null };\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"interpolation-plugin-Wgb1j4pT.js","names":[],"sources":["../../src/island/generate-entry.ts","../../src/middleware/index.ts","../../src/vite/interpolation-plugin.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, relative, sep } from \"node:path\";\nimport type { IslandModule } from \"./scan.js\";\n\n// --- Client entry generator ---\n//\n// Turns a list of scanned islands into a client entry module that imports each\n// island and registers it with `hydrateIslands`. This removes the need to hand-\n// maintain `entry-client.ts` as islands are added or removed.\n//\n// The generated file imports island default exports and passes them to\n// `hydrateIslands` keyed by their registry name.\n\n/** Options for generating the client entry module. */\nexport interface GenerateEntryOptions {\n /** Islands to register, from `scanIslands`. */\n islands: IslandModule[];\n /** Absolute path of the entry file to write (e.g. \".elur/entry-client.ts\"). */\n outFile: string;\n /**\n * Import specifier for the kit's client island helpers.\n * Defaults to the published subpath `@elurjs/kit/island`.\n */\n hydrateImport?: string;\n /**\n * Import specifier for the kit's client router.\n * Defaults to the published subpath `@elurjs/kit/router`.\n */\n routerImport?: string;\n}\n\n/** Turns a registry name into a safe JS identifier for the import binding. */\nfunction toIdentifier(name: string, index: number): string {\n const cleaned = name.replace(/[^a-zA-Z0-9_$]/g, \"_\");\n return /^[a-zA-Z_$]/.test(cleaned) ? `${cleaned}_${index}` : `_${cleaned}_${index}`;\n}\n\n/** Builds the source code of the client entry module. */\nexport function buildEntrySource(\n islands: IslandModule[],\n outFile: string,\n hydrateImport = \"@elurjs/kit/island\",\n routerImport = \"@elurjs/kit/router\",\n): string {\n const bindings = islands.map((island, i) => ({\n ident: toIdentifier(island.name, i),\n name: island.name,\n // Relative import specifier from the entry file to the island module.\n spec: toImportSpecifier(outFile, island.filePath),\n }));\n\n // Lazy registry: each island is loaded on-demand via dynamic import().\n // This enables code-splitting — islands not on the current page (or not yet\n // triggered by their directive) stay out of the initial bundle.\n //\n // The registry maps island name → discriminated lazy loader `{ load }`.\n // hydrateIslands() awaits `entry.load()` before hydrating, so the first\n // paint only needs the small entry chunk + the islands on the page. The\n // discriminated form lets the hydrator tell eager components from lazy\n // loaders without executing a probe.\n const registryLines = bindings\n .map((b) => ` ${JSON.stringify(b.name)}: { load: () => import(${JSON.stringify(b.spec)}).then(m => m.default) },`)\n .join(\"\\n\");\n\n const islandHydration = registryLines\n ? `const registry = {\n${registryLines}\n};\n// Defer hydration until the browser is idle so the first paint is not\n// blocked. Islands with directive \"load\" already have SSR-rendered DOM,\n// so the user sees content immediately — hydration only adds interactivity.\nconst hydrate = () => hydrateIslands(registry);\nif (\"requestIdleCallback\" in window) {\n requestIdleCallback(hydrate, { timeout: 2000 });\n} else {\n setTimeout(hydrate, 0);\n}\ndocument.addEventListener(\"elur:rendered\", () => {\n cleanupHydratedIslands();\n hydrateIslands(registry);\n});\n\n// Vite HMR: when an island module (or the entry itself) updates, dispose the\n// current islands and re-hydrate from the updated modules — the registry's\n// dynamic import() resolves to the fresh modules, so no full page reload is\n// needed (progressive enhancement, audit §10.2 / §12.2).\nif (import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n cleanupHydratedIslands();\n hydrateIslands(registry);\n if (newModule) {\n // Re-run the module so its side effects (router, listeners) apply.\n }\n });\n}`\n : \"\";\n\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\nimport { startClientRouter } from ${JSON.stringify(routerImport)};\nimport { hydrateIslands, cleanupHydratedIslands } from ${JSON.stringify(hydrateImport)};\n\nstartClientRouter();\n${islandHydration}\n`;\n}\n\n/** Computes a POSIX-style relative import specifier between two files. */\nfunction toImportSpecifier(fromFile: string, toFile: string): string {\n let spec = relative(dirname(fromFile), toFile).split(sep).join(\"/\");\n if (!spec.startsWith(\".\")) spec = `./${spec}`;\n return spec;\n}\n\n/**\n * Generates and writes the client entry module for the given islands.\n *\n * @param options Generation options.\n * @returns The absolute path of the written entry file.\n */\nexport async function generateClientEntry(\n options: GenerateEntryOptions,\n): Promise<string> {\n const source = buildEntrySource(\n options.islands,\n options.outFile,\n options.hydrateImport,\n options.routerImport,\n );\n await mkdir(dirname(options.outFile), { recursive: true });\n await writeFile(options.outFile, source, \"utf8\");\n return options.outFile;\n}\n","// --- Middleware ---\n//\n// Convention: `src/middleware.ts` in the project root exports a default\n// function and an optional `config` with a `matcher` array.\n//\n// import type { Middleware } from \"@elurjs/kit\";\n//\n// export default function middleware(request: Request) {\n// if (!request.headers.get(\"Cookie\")?.includes(\"session=\")) {\n// return Response.redirect(new URL(\"/login\", request.url), 307);\n// }\n// }\n//\n// export const config = { matcher: [\"/dashboard/:path*\", \"/admin/:path*\"] };\n//\n// The middleware runs before routing. Return a `Response` to short-circuit\n// (redirect, rewrite, 401, etc.). Return `undefined` or nothing to continue.\n// Use `next()` to pass headers to the loader.\n\nimport { matchRoute } from \"../ssr/match.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/** The middleware function signature. */\nexport type Middleware = (request: Request, context: MiddlewareContext) =>\n | Response\n | void\n | Promise<Response | void>;\n\n/** Context passed to the middleware function. */\nexport interface MiddlewareContext {\n /** Helper to continue to the next handler. Can attach headers, params, and locals. */\n next(options?: {\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n }): void;\n /** Matched route params (only available if the path matches a page route). */\n params?: Record<string, string | string[]>;\n /** Per-request locals (populated by middleware, available to loaders/actions). */\n locals?: Record<string, unknown>;\n}\n\n/** Configuration for the middleware module. */\nexport interface MiddlewareConfig {\n /** Path patterns that trigger the middleware. Supports `:param` and `:param*`. */\n matcher?: string[];\n}\n\nexport interface LoadedMiddleware {\n handler: Middleware;\n config: MiddlewareConfig;\n}\n\n/** Result of running middleware: either a response to short-circuit with, or continue. */\nexport type MiddlewareResult =\n | { kind: \"response\"; response: Response }\n | {\n kind: \"continue\";\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n };\n\n/**\n * Loads the user's `src/middleware.ts` module. Returns `null` if no middleware\n * file exists. Distinguishes \"file not found\" from \"file has errors\" (§6):\n * an import error is not silently treated as \"no middleware\".\n */\nexport async function loadMiddleware(root: string): Promise<LoadedMiddleware | null> {\n const candidates = [\n `${root}/src/middleware.ts`,\n `${root}/middleware.ts`,\n ];\n\n for (const path of candidates) {\n try {\n const mod = await import(path);\n const handler = (mod.default ?? mod.middleware) as Middleware | undefined;\n if (typeof handler !== \"function\") continue;\n const config = (mod.config ?? {}) as MiddlewareConfig;\n return { handler, config };\n } catch (err) {\n // Distinguish \"module not found\" from actual errors.\n // If the error is a module resolution error for this specific file,\n // it means the file doesn't exist — try the next candidate.\n // If it's a syntax/runtime error, rethrow so the user sees it.\n // Note: Bun's ResolveMessage is not `instanceof Error`, so match on the\n // message property instead of relying on the class hierarchy.\n const msg =\n typeof err === \"object\" && err !== null && \"message\" in err\n ? String((err as { message: unknown }).message)\n : String(err);\n if (\n msg.includes(\"Cannot find module\") ||\n msg.includes(\"Cannot find package\") ||\n msg.includes(\"ENOENT\") ||\n msg.includes(\"Module not found\")\n ) {\n // File doesn't exist — try next candidate.\n continue;\n }\n // Actual error in the middleware file — rethrow (§6).\n throw new Error(`[elur-kit] Error loading middleware: ${msg}`, { cause: err });\n }\n }\n\n return null;\n}\n\n/**\n * Checks if a pathname matches any of the middleware's matcher patterns.\n * If no matcher is configured, the middleware runs for every request.\n *\n * Catch-all patterns (`:param*`) match both the base path and any sub-paths,\n * e.g. `/dashboard/:path*` matches `/dashboard` and `/dashboard/settings/users`.\n */\nexport function matchesMiddleware(pathname: string, config: MiddlewareConfig): boolean {\n if (!config.matcher || config.matcher.length === 0) return true;\n\n const cleanPath = pathname.split(\"?\")[0];\n\n for (const pattern of config.matcher) {\n // Exact match.\n if (pattern === cleanPath) return true;\n\n // Check for catch-all: `/foo/:bar*` should also match `/foo`.\n const catchAllMatch = pattern.match(/^(.*)\\/:[\\w]+\\*$/);\n if (catchAllMatch) {\n const base = catchAllMatch[1];\n if (cleanPath === base) return true;\n }\n\n // Use matchRoute for param matching.\n const pseudoRoutes: PageRoute[] = [{\n path: pattern,\n pagePath: \"\",\n params: [],\n layouts: [],\n }];\n if (matchRoute(cleanPath, pseudoRoutes)) return true;\n }\n\n return false;\n}\n\n/**\n * Runs the middleware for a request. Returns the result indicating whether to\n * short-circuit with a response or continue with propagated headers/params/locals.\n *\n * Per §6: cleanup runs in `finally`, response short-circuits the pipeline,\n * headers/params/locals are propagated to downstream handlers.\n */\nexport async function runMiddleware(\n middleware: LoadedMiddleware,\n request: Request,\n params?: Record<string, string | string[]>,\n): Promise<MiddlewareResult> {\n let nextHeaders: Record<string, string> | undefined;\n let nextParams: Record<string, string | string[]> | undefined;\n let nextLocals: Record<string, unknown> | undefined;\n const cleanups: Array<() => void | Promise<void>> = [];\n\n const context: MiddlewareContext = {\n next(options) {\n if (options?.headers) nextHeaders = options.headers;\n if (options?.params) nextParams = options.params;\n if (options?.locals) nextLocals = options.locals;\n },\n params,\n locals: {},\n };\n\n try {\n const result = await middleware.handler(request, context);\n\n if (result instanceof Response) {\n return { kind: \"response\", response: result };\n }\n\n return {\n kind: \"continue\",\n headers: nextHeaders,\n params: nextParams ?? params,\n locals: nextLocals,\n };\n } finally {\n // Run any cleanup functions (§6). Errors in cleanup are logged but\n // do not propagate to the caller.\n for (const cleanup of cleanups) {\n try {\n await cleanup();\n } catch (err) {\n console.error(\"[elur-kit] middleware cleanup error:\", err);\n }\n }\n }\n}\n","import { createRequire } from \"node:module\";\nimport type { Plugin } from \"vite\";\n\n/**\n * How the legacy interpolation transform is handled relative to the installed\n * Elur core and Vite plugin:\n *\n * - `\"auto\"` (default): the kit's legacy transform is only applied when the\n * Vite plugin (`@elurjs/vite-plugin-elur` >= 1.1.0) is NOT installed.\n * The plugin has a more powerful state-machine lexer and takes precedence.\n * - `\"legacy\"`: always apply the kit's transform (for migrations), with a\n * one-time deprecation warning.\n * - `\"off\"`: never apply the kit's transform. Recommended when the Vite\n * plugin is installed.\n */\nexport type InterpolationMode = \"auto\" | \"legacy\" | \"off\";\n\nconst require = createRequire(import.meta.url);\n\nlet _warnedLegacy = false;\n\nfunction warnLegacyOnce(): void {\n if (_warnedLegacy) return;\n _warnedLegacy = true;\n console.warn(\n \"[elur-kit] The legacy interpolation transform is deprecated. \" +\n \"Install @elurjs/vite-plugin-elur >= 1.1.0 for compile-time \" +\n \"partial attribute interpolation. Remove `interpolation: \\\"legacy\\\"` \" +\n \"once migration is complete.\",\n );\n}\n\n/**\n * Detects whether the Vite plugin (`@elurjs/vite-plugin-elur`) is\n * installed and provides compile-time partial attribute interpolation.\n */\nexport function pluginSupportsPartialInterpolation(): boolean {\n try {\n const pkg = require(\"@elurjs/vite-plugin-elur/package.json\") as {\n version?: string;\n };\n // >= 1.1.0 has the interpolation lexer\n const [major, minor] = (pkg.version ?? \"0.0.0\").split(\".\").map(Number);\n return major > 1 || (major === 1 && minor >= 1);\n } catch {\n return false;\n }\n}\n\n/**\n * Detects whether the installed Elur core supports partial attribute\n * interpolation natively (via the public `templateFeatures` capability).\n * Note: as of core v3.4.0, this is always false — the lexer moved to the\n * Vite plugin.\n */\nexport function coreSupportsPartialInterpolation(): boolean {\n try {\n const core = require(\"@elurjs/core\") as {\n templateFeatures?: { partialAttributeInterpolation?: boolean };\n };\n return core?.templateFeatures?.partialAttributeInterpolation === true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolves whether the kit's legacy transform should be applied.\n *\n * In `\"auto\"` mode, the kit's transform runs only when neither the Vite\n * plugin nor the core provides partial interpolation. When the Vite plugin\n * is installed (>= 1.1.0), it takes precedence and the kit's transform is\n * skipped to avoid double-processing.\n */\nexport function shouldUseLegacyInterpolation(mode: InterpolationMode): boolean {\n if (mode === \"off\") return false;\n if (mode === \"legacy\") {\n warnLegacyOnce();\n return true;\n }\n // auto: skip if the Vite plugin handles it\n if (pluginSupportsPartialInterpolation()) return false;\n // fallback: use legacy if core doesn't support it natively\n return !coreSupportsPartialInterpolation();\n}\n\n/**\n * Transforms Elur `html\\`\\`` templates so that attributes with partial\n * interpolation become a single interpolation expression.\n *\n * Elur requires every dynamic attribute to be a single interpolation covering\n * the whole value. This plugin rewrites patterns such as:\n *\n * html\\`<a href=\"/blog/${slug}\">...</a>\\`\n *\n * into:\n *\n * html\\`<a href=${\"/blog/\" + slug}>...</a>\\`\n *\n * Only files inside the app and islands directories are processed.\n *\n * @deprecated Elur core supports partial attribute interpolation natively.\n * Keep this transform only for migrations against older cores\n * (`interpolation: \"legacy\"`).\n */\nexport interface InterpolationPluginOptions {\n appDir?: string;\n islandsDir?: string;\n}\n\nconst HTML_TAG = \"html\";\nconst TEMPLATE_START = \"`\";\n\n/**\n * Scans a `${...}` interpolation starting at `start` (where content[start] is\n * `$` and content[start + 1] is `{`), honoring nested braces, strings and\n * escape sequences. Returns the index just past the closing `}`.\n */\nfunction scanInterpolation(content: string, start: number): number {\n let depth = 1;\n let i = start + 2;\n while (i < content.length && depth > 0) {\n const c = content[i];\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n const q = c;\n i++;\n while (i < content.length) {\n if (content[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (content[i] === q) break;\n i++;\n }\n i++;\n continue;\n }\n if (c === \"{\") depth++;\n else if (c === \"}\") depth--;\n i++;\n }\n return i;\n}\n\n/**\n * Scans a quoted attribute value starting at `start` (where content[start] is\n * the quote character). Handles escapes, `${...}` interpolations with nested\n * braces, and nested quotes. Returns the index just past the closing quote,\n * the raw inner text (escapes preserved as in the source) and whether the\n * value contains at least one interpolation.\n */\nfunction scanQuotedValue(\n content: string,\n start: number,\n quote: string,\n): { end: number; inside: string; hasInterp: boolean } {\n let i = start + 1;\n let inside = \"\";\n let hasInterp = false;\n while (i < content.length) {\n const c = content[i];\n if (c === \"\\\\\") {\n inside += c + (content[i + 1] ?? \"\");\n i += 2;\n continue;\n }\n if (c === quote) {\n i++;\n break;\n }\n if (c === \"$\" && content[i + 1] === \"{\") {\n const end = scanInterpolation(content, i);\n inside += content.slice(i, end);\n i = end;\n hasInterp = true;\n continue;\n }\n inside += c;\n i++;\n }\n return { end: i, inside, hasInterp };\n}\n\n/**\n * Converts the inner text of a quoted attribute value (which may contain\n * `${...}` interpolations) into a JS expression. Literal parts are JSON\n * encoded; interpolations keep their raw expression text.\n *\n * Examples:\n * /blog/${slug} -> \"/blog/\" + (slug)\n * ${slug} -> (slug)\n * tag ${cls({a:1})} -> \"tag \" + (cls({a:1}))\n */\nfunction valueToExpression(value: string): string {\n const parts: string[] = [];\n let i = 0;\n let literal = \"\";\n const flush = () => {\n if (literal) {\n parts.push(JSON.stringify(unescapeAttributeLiteral(literal)));\n literal = \"\";\n }\n };\n\n while (i < value.length) {\n if (value[i] === \"\\\\\") {\n literal += value[i] + (value[i + 1] ?? \"\");\n i += 2;\n continue;\n }\n if (value[i] === \"$\" && value[i + 1] === \"{\") {\n flush();\n const end = scanInterpolation(value, i);\n const expr = value.slice(i + 2, end - 1).trim();\n if (expr) parts.push(`(${expr})`);\n i = end;\n continue;\n }\n literal += value[i];\n i++;\n }\n flush();\n\n if (parts.length === 0) return '\"\"';\n if (parts.length === 1) return parts[0] as string;\n return parts.join(\" + \");\n}\n\n/**\n * Unescapes escape sequences that appear inside a JS template literal so the\n * JSON.stringify output matches the runtime string value.\n */\nfunction unescapeAttributeLiteral(literal: string): string {\n const escapes: Record<string, string> = {\n n: \"\\n\",\n t: \"\\t\",\n r: \"\\r\",\n };\n let out = \"\";\n let i = 0;\n while (i < literal.length) {\n const c = literal[i];\n if (c === \"\\\\\" && i + 1 < literal.length) {\n const next = literal[i + 1];\n if (next in escapes) {\n out += escapes[next];\n i += 2;\n continue;\n }\n out += next;\n i += 2;\n continue;\n }\n out += c;\n i++;\n }\n return out;\n}\n\n/**\n * Rewrites quoted attribute values that contain interpolations inside html``\n * templates, leaving everything else untouched.\n */\nfunction transformTemplateContent(content: string): string {\n let out = \"\";\n let i = 0;\n const n = content.length;\n\n while (i < n) {\n const lt = content.indexOf(\"<\", i);\n if (lt === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, lt);\n i = lt;\n\n // HTML comments: copy verbatim.\n if (content.startsWith(\"<!--\", i)) {\n const end = content.indexOf(\"-->\", i + 4);\n if (end === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, end + 3);\n i = end + 3;\n continue;\n }\n\n // Closing tags, doctype, CDATA, processing instructions: copy verbatim.\n if (content[i + 1] === \"/\" || content[i + 1] === \"!\" || content[i + 1] === \"?\") {\n const gt = content.indexOf(\">\", i + 1);\n if (gt === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, gt + 1);\n i = gt + 1;\n continue;\n }\n\n // Opening tag. Copy the tag name, then walk its attributes.\n let j = i + 1;\n while (j < n && /[a-zA-Z0-9-]/.test(content[j])) j++;\n out += content.slice(i, j);\n i = j;\n\n while (i < n) {\n let ws = \"\";\n while (i < n && /\\s/.test(content[i])) {\n ws += content[i];\n i++;\n }\n if (i >= n) {\n out += ws;\n break;\n }\n if (content[i] === \">\") {\n out += ws + \">\";\n i++;\n break;\n }\n if (content[i] === \"/\" && content[i + 1] === \">\") {\n out += ws + \"/>\";\n i += 2;\n break;\n }\n // Interpolation in the tag body (dynamic attrs/spread): copy verbatim.\n if (content[i] === \"$\" && content[i + 1] === \"{\") {\n const end = scanInterpolation(content, i);\n out += ws + content.slice(i, end);\n i = end;\n continue;\n }\n\n // Attribute name.\n let nameStart = i;\n while (i < n && !/[\\s=/>\"'$]/.test(content[i])) i++;\n const name = content.slice(nameStart, i);\n if (!name) {\n out += ws + content[i];\n i++;\n continue;\n }\n\n let eqWs = \"\";\n while (i < n && /\\s/.test(content[i])) {\n eqWs += content[i];\n i++;\n }\n\n if (content[i] !== \"=\") {\n out += ws + name + eqWs;\n continue;\n }\n\n i++; // consume \"=\"\n let valWs = \"\";\n while (i < n && /\\s/.test(content[i])) {\n valWs += content[i];\n i++;\n }\n\n const quote = content[i];\n if (quote === '\"' || quote === \"'\") {\n const { end, inside, hasInterp } = scanQuotedValue(content, i, quote);\n if (hasInterp) {\n // Skip values that are a single full interpolation: Elur handles\n // `attr=\"${expr}\"` natively, so only partial interpolations need the\n // rewrite.\n const first = scanInterpolation(inside, 0);\n const fullValue =\n inside.startsWith(\"${\") &&\n first === inside.length &&\n !inside.slice(2, first - 1).includes(\"${\");\n if (!fullValue) {\n // Elur needs the interpolation to start right after \"=\" (no space),\n // so the whitespace before the original value is dropped.\n out += ws + name + eqWs + \"=\" + \"${\" + valueToExpression(inside) + \"}\";\n i = end;\n continue;\n }\n out += ws + name + eqWs + \"=\" + valWs + content.slice(i, end);\n } else {\n out += ws + name + eqWs + \"=\" + valWs + content.slice(i, end);\n }\n i = end;\n continue;\n }\n\n // Unquoted value: copy up to whitespace, \">\" or \"/>\".\n let v = \"\";\n while (\n i < n &&\n !/\\s/.test(content[i]) &&\n content[i] !== \">\" &&\n !(content[i] === \"/\" && content[i + 1] === \">\")\n ) {\n v += content[i];\n i++;\n }\n out += ws + name + eqWs + \"=\" + valWs + v;\n }\n }\n\n return out;\n}\n\n/**\n * @deprecated Use the native partial attribute interpolation of Elur core\n * (core >= 3.3). Kept for legacy migrations and direct consumers.\n */\nexport function transformPartialInterpolations(source: string): string {\n let result = \"\";\n let i = 0;\n while (i < source.length) {\n // Find the next html` sequence.\n const htmlIndex = source.indexOf(HTML_TAG, i);\n if (htmlIndex === -1) {\n result += source.slice(i);\n break;\n }\n result += source.slice(i, htmlIndex + HTML_TAG.length);\n i = htmlIndex + HTML_TAG.length;\n\n // Skip whitespace before the backtick.\n while (i < source.length && /\\s/.test(source[i])) {\n result += source[i];\n i++;\n }\n if (i >= source.length || source[i] !== TEMPLATE_START) {\n continue;\n }\n result += source[i];\n i++;\n\n // Parse the template literal until the matching backtick.\n let depth = 1;\n let templateContent = \"\";\n while (i < source.length && depth > 0) {\n const char = source[i];\n if (char === \"\\\\\") {\n templateContent += char + source[i + 1];\n i += 2;\n continue;\n }\n if (char === TEMPLATE_START) {\n depth--;\n if (depth === 0) {\n i++;\n break;\n }\n }\n if (char === \"$\") {\n // Look ahead for ${...}\n if (source[i + 1] === \"{\") {\n const end = scanInterpolation(source, i);\n templateContent += source.slice(i, end);\n i = end;\n continue;\n }\n }\n templateContent += char;\n i++;\n }\n\n const transformed = transformTemplateContent(templateContent);\n result += transformed;\n result += TEMPLATE_START;\n }\n return result;\n}\n\nexport function elurJsInterpolationPlugin(options: InterpolationPluginOptions = {}): Plugin {\n const appDir = options.appDir ?? \"src/app\";\n const islandsDir = options.islandsDir ?? \"src/islands\";\n return {\n name: \"elur-kit-interpolation\",\n enforce: \"pre\",\n transform(code, id) {\n if (!id.endsWith(\".ts\") && !id.endsWith(\".js\")) return;\n if (!id.includes(appDir) && !id.includes(islandsDir)) return;\n if (!code.includes(\"html`\")) return;\n const transformed = transformPartialInterpolations(code);\n if (transformed === code) return;\n return { code: transformed, map: null };\n },\n };\n}\n"],"mappings":"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"}