@withl5e/l5e 0.3.3-alpha.4 → 1.0.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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/action.js +10 -9
  3. package/dist/action.js.map +1 -1
  4. package/dist/defineMiddleware-D017PWDc.js +8 -0
  5. package/dist/defineMiddleware-D017PWDc.js.map +1 -0
  6. package/dist/entry-server.js +161 -176
  7. package/dist/entry-server.js.map +1 -1
  8. package/dist/exceptions-D4OdiQjY.js +30 -0
  9. package/dist/exceptions-D4OdiQjY.js.map +1 -0
  10. package/dist/generateMetadata-DcdY-XC1.js +303 -0
  11. package/dist/generateMetadata-DcdY-XC1.js.map +1 -0
  12. package/dist/global-style-BhiGjxVU.js +21 -0
  13. package/dist/global-style-BhiGjxVU.js.map +1 -0
  14. package/dist/i18n.js +8 -11
  15. package/dist/i18n.js.map +1 -1
  16. package/dist/index.js +9 -55
  17. package/dist/island/client.js +2 -5
  18. package/dist/island/runtime.js +90 -107
  19. package/dist/island/runtime.js.map +1 -1
  20. package/dist/island.js +38 -56
  21. package/dist/island.js.map +1 -1
  22. package/dist/jsx-runtime-CI0Jmr-C.js +275 -0
  23. package/dist/jsx-runtime-CI0Jmr-C.js.map +1 -0
  24. package/dist/jsx-runtime.js +2 -31
  25. package/dist/middleware-CnDqZobD.js +217 -0
  26. package/dist/middleware-CnDqZobD.js.map +1 -0
  27. package/dist/middleware.js +3 -8
  28. package/dist/render-hUQUreah.js +152 -0
  29. package/dist/render-hUQUreah.js.map +1 -0
  30. package/dist/rolldown-runtime-B0aSnxlc.js +20 -0
  31. package/dist/router.js +72 -68
  32. package/dist/router.js.map +1 -1
  33. package/dist/seo.js +3 -7
  34. package/dist/server-5BEwGAL4.js +461 -0
  35. package/dist/server-5BEwGAL4.js.map +1 -0
  36. package/dist/server.js +2 -515
  37. package/dist/strategy-registry-wmHnsHQb.js +9 -0
  38. package/dist/strategy-registry-wmHnsHQb.js.map +1 -0
  39. package/dist/swap/server.js +11 -14
  40. package/dist/swap/server.js.map +1 -1
  41. package/dist/swap.js +109 -116
  42. package/dist/swap.js.map +1 -1
  43. package/dist/tooltip.js +145 -141
  44. package/dist/tooltip.js.map +1 -1
  45. package/dist/vite-plugin.js +315 -372
  46. package/dist/vite-plugin.js.map +1 -1
  47. package/package.json +8 -8
  48. package/src/core/bundler.ts +41 -25
  49. package/src/core/vite-plugin.ts +14 -15
  50. package/dist/client-D67hK4Yy.js +0 -9
  51. package/dist/client-D67hK4Yy.js.map +0 -1
  52. package/dist/defineMiddleware-VdHQuiUc.js +0 -7
  53. package/dist/defineMiddleware-VdHQuiUc.js.map +0 -1
  54. package/dist/exceptions-CHPz01DJ.js +0 -39
  55. package/dist/exceptions-CHPz01DJ.js.map +0 -1
  56. package/dist/generateMetadata-Cn8BIxq6.js +0 -144
  57. package/dist/generateMetadata-Cn8BIxq6.js.map +0 -1
  58. package/dist/global-style-DFvaBcuR.js +0 -27
  59. package/dist/global-style-DFvaBcuR.js.map +0 -1
  60. package/dist/index-CybB8Tdk.js +0 -315
  61. package/dist/index-CybB8Tdk.js.map +0 -1
  62. package/dist/index.js.map +0 -1
  63. package/dist/island/client.js.map +0 -1
  64. package/dist/jsx-runtime-DqAp8xlt.js +0 -296
  65. package/dist/jsx-runtime-DqAp8xlt.js.map +0 -1
  66. package/dist/jsx-runtime.js.map +0 -1
  67. package/dist/middleware.js.map +0 -1
  68. package/dist/render-CRag-5V7.js +0 -202
  69. package/dist/render-CRag-5V7.js.map +0 -1
  70. package/dist/seo.js.map +0 -1
  71. package/dist/server.js.map +0 -1
@@ -1,116 +1,99 @@
1
- import { r as i, s as u } from "../client-D67hK4Yy.js";
1
+ import { n as e, t } from "../strategy-registry-wmHnsHQb.js";
2
2
  import "virtual:l5e-island-strategies";
3
- i("load", (e) => {
4
- e();
5
- });
6
- i("idle", (e) => {
7
- "requestIdleCallback" in window ? requestIdleCallback(() => e()) : setTimeout(() => e(), 200);
8
- });
9
- i("visible", (e, t, r) => {
10
- const o = t || "200px", n = new IntersectionObserver(
11
- (d) => {
12
- d.forEach((s) => {
13
- s.isIntersecting && (n.disconnect(), e());
14
- });
15
- },
16
- { rootMargin: o }
17
- );
18
- return n.observe(r), () => n.disconnect();
19
- });
20
- i("media", (e, t) => {
21
- if (!t) {
22
- console.error('[l5e-island] Strategy "media" requires mountOpts (media query)');
23
- return;
24
- }
25
- const r = window.matchMedia(t);
26
- if (r.matches)
27
- e();
28
- else {
29
- const o = (n) => {
30
- n.matches && e();
31
- };
32
- return r.addEventListener("change", o, { once: !0 }), () => r.removeEventListener("change", o);
33
- }
34
- });
35
- i("none", () => {
36
- });
37
- const c = window.__L5E_ISLANDS__ || {};
38
- let a = null;
39
- function m() {
40
- if (a) return a;
41
- const e = document.getElementById("_l5e_data_");
42
- if (!e || !e.textContent) return a = [];
43
- try {
44
- const t = JSON.parse(e.textContent);
45
- a = Array.isArray(t) ? t : [];
46
- } catch (t) {
47
- console.error("[l5e-island] Failed to parse _l5e_data_ props store:", t), a = [];
48
- }
49
- return a;
3
+ t("load", (e) => {
4
+ e();
5
+ }), t("idle", (e) => {
6
+ "requestIdleCallback" in window ? requestIdleCallback(() => e()) : setTimeout(() => e(), 200);
7
+ }), t("visible", (e, t, n) => {
8
+ let r = new IntersectionObserver((t) => {
9
+ t.forEach((t) => {
10
+ t.isIntersecting && (r.disconnect(), e());
11
+ });
12
+ }, { rootMargin: t || "200px" });
13
+ return r.observe(n), () => r.disconnect();
14
+ }), t("media", (e, t) => {
15
+ if (!t) {
16
+ console.error("[l5e-island] Strategy \"media\" requires mountOpts (media query)");
17
+ return;
18
+ }
19
+ let n = window.matchMedia(t);
20
+ if (n.matches) e();
21
+ else {
22
+ let t = (t) => {
23
+ t.matches && e();
24
+ };
25
+ return n.addEventListener("change", t, { once: !0 }), () => n.removeEventListener("change", t);
26
+ }
27
+ }), t("none", () => {});
28
+ var n = window.__L5E_ISLANDS__ || {}, r = null;
29
+ function i() {
30
+ if (r) return r;
31
+ let e = document.getElementById("_l5e_data_");
32
+ if (!e || !e.textContent) return r = [];
33
+ try {
34
+ let t = JSON.parse(e.textContent);
35
+ r = Array.isArray(t) ? t : [];
36
+ } catch (e) {
37
+ console.error("[l5e-island] Failed to parse _l5e_data_ props store:", e), r = [];
38
+ }
39
+ return r;
50
40
  }
51
- function p(e) {
52
- const t = e.getAttribute("data-island-idx");
53
- return t !== null ? m()[Number(t)] || {} : JSON.parse(e.getAttribute("data-island-props") || "{}");
41
+ function a(e) {
42
+ let t = e.getAttribute("data-island-idx");
43
+ return t === null ? JSON.parse(e.getAttribute("data-island-props") || "{}") : i()[Number(t)] || {};
54
44
  }
55
- function f() {
56
- return Array.from(document.querySelectorAll("[data-island]")).map((e) => ({
57
- element: e,
58
- registryKey: e.getAttribute("data-island"),
59
- exportName: e.getAttribute("data-island-name"),
60
- props: p(e),
61
- mount: e.getAttribute("data-island-mount") || "load",
62
- mountOpts: e.getAttribute("data-island-opts") || void 0,
63
- ssr: e.hasAttribute("data-island-ssr")
64
- }));
45
+ function o() {
46
+ return Array.from(document.querySelectorAll("[data-island]")).map((e) => ({
47
+ element: e,
48
+ registryKey: e.getAttribute("data-island"),
49
+ exportName: e.getAttribute("data-island-name"),
50
+ props: a(e),
51
+ mount: e.getAttribute("data-island-mount") || "load",
52
+ mountOpts: e.getAttribute("data-island-opts") || void 0,
53
+ ssr: e.hasAttribute("data-island-ssr")
54
+ }));
65
55
  }
66
- function g(e) {
67
- let t = !1;
68
- return async () => {
69
- if (t) return;
70
- t = !0;
71
- const r = c[e.registryKey];
72
- if (!r) {
73
- console.error(
74
- `[l5e-island] Component "${e.registryKey}" not found in page registry.`,
75
- `Available: ${Object.keys(c).join(", ")}`
76
- );
77
- return;
78
- }
79
- try {
80
- const [o, { createElement: n }, d] = await Promise.all([
81
- import("react-dom/client"),
82
- import("react"),
83
- import(
84
- /* @vite-ignore */
85
- r
86
- )
87
- ]), s = d.default || d[e.exportName];
88
- if (!s) {
89
- console.error(`[l5e-island] No export "default" or "${e.exportName}" in module`);
90
- return;
91
- }
92
- e.ssr ? o.hydrateRoot(e.element, n(s, e.props)) : o.createRoot(e.element).render(n(s, e.props));
93
- } catch (o) {
94
- console.error(`[l5e-island] Failed to mount "${e.registryKey}":`, o);
95
- }
96
- };
56
+ function s(e) {
57
+ let t = !1;
58
+ return async () => {
59
+ if (t) return;
60
+ t = !0;
61
+ let r = n[e.registryKey];
62
+ if (!r) {
63
+ console.error(`[l5e-island] Component "${e.registryKey}" not found in page registry.`, `Available: ${Object.keys(n).join(", ")}`);
64
+ return;
65
+ }
66
+ try {
67
+ let [t, { createElement: n }, i] = await Promise.all([
68
+ import("react-dom/client"),
69
+ import("react"),
70
+ import(
71
+ /* @vite-ignore */
72
+ r
73
+ )
74
+ ]), a = i.default || i[e.exportName];
75
+ if (!a) {
76
+ console.error(`[l5e-island] No export "default" or "${e.exportName}" in module`);
77
+ return;
78
+ }
79
+ e.ssr ? t.hydrateRoot(e.element, n(a, e.props)) : t.createRoot(e.element).render(n(a, e.props));
80
+ } catch (t) {
81
+ console.error(`[l5e-island] Failed to mount "${e.registryKey}":`, t);
82
+ }
83
+ };
97
84
  }
98
- function y(e) {
99
- const t = u.get(e.mount);
100
- if (!t) {
101
- console.error(
102
- `[l5e-island] Strategy "${e.mount}" not found.`,
103
- `Available: ${[...u.keys()].join(", ")}`,
104
- `
105
- Did you forget to registerMountStrategy("${e.mount}", ...)?`
106
- );
107
- return;
108
- }
109
- const r = g(e);
110
- t(r, e.mountOpts, e.element);
85
+ function c(t) {
86
+ let n = e.get(t.mount);
87
+ if (!n) {
88
+ console.error(`[l5e-island] Strategy "${t.mount}" not found.`, `Available: ${[...e.keys()].join(", ")}`, `\nDid you forget to registerMountStrategy("${t.mount}", ...)?`);
89
+ return;
90
+ }
91
+ n(s(t), t.mountOpts, t.element);
111
92
  }
112
93
  function l() {
113
- f().forEach(y);
94
+ o().forEach(c);
114
95
  }
115
96
  document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", l) : l();
116
- //# sourceMappingURL=runtime.js.map
97
+ //#endregion
98
+
99
+ //# sourceMappingURL=runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sources":["../../src/island/runtime.ts"],"sourcesContent":["import type { IslandMeta } from './types';\nimport { strategies, registerMountStrategy } from './strategy-registry';\n\n// ============================================================\n// PART 1: Built-in strategies\n// ============================================================\n\nregisterMountStrategy('load', (mount) => {\n mount();\n});\n\nregisterMountStrategy('idle', (mount) => {\n if ('requestIdleCallback' in window) {\n requestIdleCallback(() => mount());\n } else {\n setTimeout(() => mount(), 200);\n }\n});\n\nregisterMountStrategy('visible', (mount, opts, el) => {\n const rootMargin = opts || '200px';\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n observer.disconnect();\n mount();\n }\n });\n },\n { rootMargin },\n );\n observer.observe(el);\n return () => observer.disconnect();\n});\n\nregisterMountStrategy('media', (mount, opts) => {\n if (!opts) {\n console.error('[l5e-island] Strategy \"media\" requires mountOpts (media query)');\n return;\n }\n const mql = window.matchMedia(opts);\n if (mql.matches) {\n mount();\n } else {\n const handler = (e: MediaQueryListEvent) => {\n if (e.matches) mount();\n };\n mql.addEventListener('change', handler, { once: true });\n return () => mql.removeEventListener('change', handler);\n }\n});\n\nregisterMountStrategy('none', () => {\n // Don't mount - keep placeholder as-is\n});\n\n// ============================================================\n// PART 2: Import custom strategies (if any)\n// ============================================================\n\nimport 'virtual:l5e-island-strategies';\n\n// ============================================================\n// PART 3: Island discovery + mount logic\n// ============================================================\n\n// Per-page island registry injected by server.ts as inline script\n// Format: { \"Counter_a3f2\": \"/assets/Counter-Abc123.js\" }\nconst islandRegistry: Record<string, string> = (window as any).__L5E_ISLANDS__ || {};\n\n// Props for islands rendered in externalized mode live in a single\n// `<script type=\"application/json\" id=\"_l5e_data_\">` at the end of the document\n// (keeps the SSR body lean for crawlers). Parsed once, lazily.\nlet islandDataCache: unknown[] | null = null;\nfunction getIslandData(): unknown[] {\n if (islandDataCache) return islandDataCache;\n const el = document.getElementById('_l5e_data_');\n if (!el || !el.textContent) return (islandDataCache = []);\n try {\n const parsed = JSON.parse(el.textContent);\n islandDataCache = Array.isArray(parsed) ? parsed : [];\n } catch (err) {\n console.error('[l5e-island] Failed to parse _l5e_data_ props store:', err);\n islandDataCache = [];\n }\n return islandDataCache;\n}\n\nfunction readProps(el: Element): Record<string, unknown> {\n const idx = el.getAttribute('data-island-idx');\n if (idx !== null) {\n return (getIslandData()[Number(idx)] as Record<string, unknown>) || {};\n }\n return JSON.parse(el.getAttribute('data-island-props') || '{}');\n}\n\nfunction discoverIslands(): IslandMeta[] {\n return Array.from(document.querySelectorAll('[data-island]')).map((el) => ({\n element: el as HTMLElement,\n registryKey: el.getAttribute('data-island')!,\n exportName: el.getAttribute('data-island-name')!,\n props: readProps(el),\n mount: el.getAttribute('data-island-mount') || 'load',\n mountOpts: el.getAttribute('data-island-opts') || undefined,\n ssr: el.hasAttribute('data-island-ssr'),\n }));\n}\n\nfunction createMountFn(island: IslandMeta): () => Promise<void> {\n let mounted = false;\n return async () => {\n if (mounted) return;\n mounted = true;\n\n // Look up component URL from per-page registry\n const url = islandRegistry[island.registryKey];\n if (!url) {\n console.error(\n `[l5e-island] Component \"${island.registryKey}\" not found in page registry.`,\n `Available: ${Object.keys(islandRegistry).join(', ')}`,\n );\n return;\n }\n\n try {\n const [reactDomClient, { createElement }, mod] = await Promise.all([\n import('react-dom/client'),\n import('react'),\n import(/* @vite-ignore */ url),\n ]);\n\n const Component = mod.default || mod[island.exportName];\n if (!Component) {\n console.error(`[l5e-island] No export \"default\" or \"${island.exportName}\" in module`);\n return;\n }\n\n if (island.ssr) {\n // Server already rendered this component into the element → hydrate it.\n reactDomClient.hydrateRoot(island.element, createElement(Component, island.props));\n } else {\n // Client-only mount (default): fresh render into an empty placeholder.\n const root = reactDomClient.createRoot(island.element);\n root.render(createElement(Component, island.props));\n }\n } catch (error) {\n console.error(`[l5e-island] Failed to mount \"${island.registryKey}\":`, error);\n }\n };\n}\n\nfunction scheduleMount(island: IslandMeta) {\n const strategy = strategies.get(island.mount);\n\n if (!strategy) {\n console.error(\n `[l5e-island] Strategy \"${island.mount}\" not found.`,\n `Available: ${[...strategies.keys()].join(', ')}`,\n `\\nDid you forget to registerMountStrategy(\"${island.mount}\", ...)?`,\n );\n return;\n }\n\n const mountFn = createMountFn(island);\n strategy(mountFn, island.mountOpts, island.element);\n}\n\n// ============================================================\n// PART 4: Boot\n// ============================================================\n\nfunction boot() {\n const islands = discoverIslands();\n islands.forEach(scheduleMount);\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', boot);\n} else {\n boot();\n}\n"],"names":["registerMountStrategy","mount","opts","el","rootMargin","observer","entries","entry","mql","handler","e","islandRegistry","islandDataCache","getIslandData","parsed","err","readProps","idx","discoverIslands","createMountFn","island","mounted","url","reactDomClient","createElement","mod","Component","error","scheduleMount","strategy","strategies","mountFn","boot"],"mappings":"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"}
1
+ {"version":3,"file":"runtime.js","names":[],"sources":["../../src/island/runtime.ts"],"sourcesContent":["import type { IslandMeta } from './types';\nimport { strategies, registerMountStrategy } from './strategy-registry';\n\n// ============================================================\n// PART 1: Built-in strategies\n// ============================================================\n\nregisterMountStrategy('load', (mount) => {\n mount();\n});\n\nregisterMountStrategy('idle', (mount) => {\n if ('requestIdleCallback' in window) {\n requestIdleCallback(() => mount());\n } else {\n setTimeout(() => mount(), 200);\n }\n});\n\nregisterMountStrategy('visible', (mount, opts, el) => {\n const rootMargin = opts || '200px';\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n observer.disconnect();\n mount();\n }\n });\n },\n { rootMargin },\n );\n observer.observe(el);\n return () => observer.disconnect();\n});\n\nregisterMountStrategy('media', (mount, opts) => {\n if (!opts) {\n console.error('[l5e-island] Strategy \"media\" requires mountOpts (media query)');\n return;\n }\n const mql = window.matchMedia(opts);\n if (mql.matches) {\n mount();\n } else {\n const handler = (e: MediaQueryListEvent) => {\n if (e.matches) mount();\n };\n mql.addEventListener('change', handler, { once: true });\n return () => mql.removeEventListener('change', handler);\n }\n});\n\nregisterMountStrategy('none', () => {\n // Don't mount - keep placeholder as-is\n});\n\n// ============================================================\n// PART 2: Import custom strategies (if any)\n// ============================================================\n\nimport 'virtual:l5e-island-strategies';\n\n// ============================================================\n// PART 3: Island discovery + mount logic\n// ============================================================\n\n// Per-page island registry injected by server.ts as inline script\n// Format: { \"Counter_a3f2\": \"/assets/Counter-Abc123.js\" }\nconst islandRegistry: Record<string, string> = (window as any).__L5E_ISLANDS__ || {};\n\n// Props for islands rendered in externalized mode live in a single\n// `<script type=\"application/json\" id=\"_l5e_data_\">` at the end of the document\n// (keeps the SSR body lean for crawlers). Parsed once, lazily.\nlet islandDataCache: unknown[] | null = null;\nfunction getIslandData(): unknown[] {\n if (islandDataCache) return islandDataCache;\n const el = document.getElementById('_l5e_data_');\n if (!el || !el.textContent) return (islandDataCache = []);\n try {\n const parsed = JSON.parse(el.textContent);\n islandDataCache = Array.isArray(parsed) ? parsed : [];\n } catch (err) {\n console.error('[l5e-island] Failed to parse _l5e_data_ props store:', err);\n islandDataCache = [];\n }\n return islandDataCache;\n}\n\nfunction readProps(el: Element): Record<string, unknown> {\n const idx = el.getAttribute('data-island-idx');\n if (idx !== null) {\n return (getIslandData()[Number(idx)] as Record<string, unknown>) || {};\n }\n return JSON.parse(el.getAttribute('data-island-props') || '{}');\n}\n\nfunction discoverIslands(): IslandMeta[] {\n return Array.from(document.querySelectorAll('[data-island]')).map((el) => ({\n element: el as HTMLElement,\n registryKey: el.getAttribute('data-island')!,\n exportName: el.getAttribute('data-island-name')!,\n props: readProps(el),\n mount: el.getAttribute('data-island-mount') || 'load',\n mountOpts: el.getAttribute('data-island-opts') || undefined,\n ssr: el.hasAttribute('data-island-ssr'),\n }));\n}\n\nfunction createMountFn(island: IslandMeta): () => Promise<void> {\n let mounted = false;\n return async () => {\n if (mounted) return;\n mounted = true;\n\n // Look up component URL from per-page registry\n const url = islandRegistry[island.registryKey];\n if (!url) {\n console.error(\n `[l5e-island] Component \"${island.registryKey}\" not found in page registry.`,\n `Available: ${Object.keys(islandRegistry).join(', ')}`,\n );\n return;\n }\n\n try {\n const [reactDomClient, { createElement }, mod] = await Promise.all([\n import('react-dom/client'),\n import('react'),\n import(/* @vite-ignore */ url),\n ]);\n\n const Component = mod.default || mod[island.exportName];\n if (!Component) {\n console.error(`[l5e-island] No export \"default\" or \"${island.exportName}\" in module`);\n return;\n }\n\n if (island.ssr) {\n // Server already rendered this component into the element → hydrate it.\n reactDomClient.hydrateRoot(island.element, createElement(Component, island.props));\n } else {\n // Client-only mount (default): fresh render into an empty placeholder.\n const root = reactDomClient.createRoot(island.element);\n root.render(createElement(Component, island.props));\n }\n } catch (error) {\n console.error(`[l5e-island] Failed to mount \"${island.registryKey}\":`, error);\n }\n };\n}\n\nfunction scheduleMount(island: IslandMeta) {\n const strategy = strategies.get(island.mount);\n\n if (!strategy) {\n console.error(\n `[l5e-island] Strategy \"${island.mount}\" not found.`,\n `Available: ${[...strategies.keys()].join(', ')}`,\n `\\nDid you forget to registerMountStrategy(\"${island.mount}\", ...)?`,\n );\n return;\n }\n\n const mountFn = createMountFn(island);\n strategy(mountFn, island.mountOpts, island.element);\n}\n\n// ============================================================\n// PART 4: Boot\n// ============================================================\n\nfunction boot() {\n const islands = discoverIslands();\n islands.forEach(scheduleMount);\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', boot);\n} else {\n boot();\n}\n"],"mappings":";;AAOA,EAAsB,SAAS,MAAU;CACvC,EAAM;AACR,CAAC,GAED,EAAsB,SAAS,MAAU;CACvC,AAAI,yBAAyB,SAC3B,0BAA0B,EAAM,CAAC,IAEjC,iBAAiB,EAAM,GAAG,GAAG;AAEjC,CAAC,GAED,EAAsB,YAAY,GAAO,GAAM,MAAO;CAEpD,IAAM,IAAW,IAAI,sBAClB,MAAY;EACX,EAAQ,SAAS,MAAU;GACzB,AAAI,EAAM,mBACR,EAAS,WAAW,GACpB,EAAM;EAEV,CAAC;CACH,GACA,EAAE,YAVe,KAAQ,QAUZ,CACf;CAEA,OADA,EAAS,QAAQ,CAAE,SACN,EAAS,WAAW;AACnC,CAAC,GAED,EAAsB,UAAU,GAAO,MAAS;CAC9C,IAAI,CAAC,GAAM;EACT,QAAQ,MAAM,kEAAgE;EAC9E;CACF;CACA,IAAM,IAAM,OAAO,WAAW,CAAI;CAClC,IAAI,EAAI,SACN,EAAM;MACD;EACL,IAAM,KAAW,MAA2B;GAC1C,AAAI,EAAE,WAAS,EAAM;EACvB;EAEA,OADA,EAAI,iBAAiB,UAAU,GAAS,EAAE,MAAM,GAAK,CAAC,SACzC,EAAI,oBAAoB,UAAU,CAAO;CACxD;AACF,CAAC,GAED,EAAsB,cAAc,CAEpC,CAAC;AAcD,IAAM,IAA0C,OAAe,mBAAmB,CAAC,GAK/E,IAAoC;AACxC,SAAS,IAA2B;CAClC,IAAI,GAAiB,OAAO;CAC5B,IAAM,IAAK,SAAS,eAAe,YAAY;CAC/C,IAAI,CAAC,KAAM,CAAC,EAAG,aAAa,OAAQ,IAAkB,CAAC;CACvD,IAAI;EACF,IAAM,IAAS,KAAK,MAAM,EAAG,WAAW;EACxC,IAAkB,MAAM,QAAQ,CAAM,IAAI,IAAS,CAAC;CACtD,SAAS,GAAK;EAEZ,AADA,QAAQ,MAAM,wDAAwD,CAAG,GACzE,IAAkB,CAAC;CACrB;CACA,OAAO;AACT;AAEA,SAAS,EAAU,GAAsC;CACvD,IAAM,IAAM,EAAG,aAAa,iBAAiB;CAI7C,OAHI,MAAQ,OAGL,KAAK,MAAM,EAAG,aAAa,mBAAmB,KAAK,IAAI,IAFpD,EAAc,CAAC,CAAC,OAAO,CAAG,MAAkC,CAAC;AAGzE;AAEA,SAAS,IAAgC;CACvC,OAAO,MAAM,KAAK,SAAS,iBAAiB,eAAe,CAAC,CAAC,CAAC,KAAK,OAAQ;EACzE,SAAS;EACT,aAAa,EAAG,aAAa,aAAa;EAC1C,YAAY,EAAG,aAAa,kBAAkB;EAC9C,OAAO,EAAU,CAAE;EACnB,OAAO,EAAG,aAAa,mBAAmB,KAAK;EAC/C,WAAW,EAAG,aAAa,kBAAkB,KAAK,KAAA;EAClD,KAAK,EAAG,aAAa,iBAAiB;CACxC,EAAE;AACJ;AAEA,SAAS,EAAc,GAAyC;CAC9D,IAAI,IAAU;CACd,OAAO,YAAY;EACjB,IAAI,GAAS;EACb,IAAU;EAGV,IAAM,IAAM,EAAe,EAAO;EAClC,IAAI,CAAC,GAAK;GACR,QAAQ,MACN,2BAA2B,EAAO,YAAY,gCAC9C,cAAc,OAAO,KAAK,CAAc,CAAC,CAAC,KAAK,IAAI,GACrD;GACA;EACF;EAEA,IAAI;GACF,IAAM,CAAC,GAAgB,EAAE,oBAAiB,KAAO,MAAM,QAAQ,IAAI;IACjE,OAAO;IACP,OAAO;IACP;;KAA0B;;GAC5B,CAAC,GAEK,IAAY,EAAI,WAAW,EAAI,EAAO;GAC5C,IAAI,CAAC,GAAW;IACd,QAAQ,MAAM,wCAAwC,EAAO,WAAW,YAAY;IACpF;GACF;GAEA,AAAI,EAAO,MAET,EAAe,YAAY,EAAO,SAAS,EAAc,GAAW,EAAO,KAAK,CAAC,IAIjF,EAD4B,WAAW,EAAO,OAC3C,CAAC,CAAC,OAAO,EAAc,GAAW,EAAO,KAAK,CAAC;EAEtD,SAAS,GAAO;GACd,QAAQ,MAAM,iCAAiC,EAAO,YAAY,KAAK,CAAK;EAC9E;CACF;AACF;AAEA,SAAS,EAAc,GAAoB;CACzC,IAAM,IAAW,EAAW,IAAI,EAAO,KAAK;CAE5C,IAAI,CAAC,GAAU;EACb,QAAQ,MACN,0BAA0B,EAAO,MAAM,eACvC,cAAc,CAAC,GAAG,EAAW,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,KAC9C,8CAA8C,EAAO,MAAM,SAC7D;EACA;CACF;CAGA,EADgB,EAAc,CACf,GAAG,EAAO,WAAW,EAAO,OAAO;AACpD;AAMA,SAAS,IAAO;CAEd,EAAM,CAAC,CAAC,QAAQ,CAAa;AAC/B;AAEI,SAAS,eAAe,YAC1B,SAAS,iBAAiB,oBAAoB,CAAI,IAElD,EAAK"}
package/dist/island.js CHANGED
@@ -1,58 +1,40 @@
1
- import { s as g, l as x, p as I, t as N, n as m } from "./jsx-runtime-DqAp8xlt.js";
2
- function v(d) {
3
- const t = d.split("/");
4
- let s = t[t.length - 1];
5
- return s = s.replace(/\.(tsx?|jsx?)$/, ""), s;
1
+ import { b as e, g as t, h as n, p as r, y as i } from "./jsx-runtime-CI0Jmr-C.js";
2
+ //#region src/island/ClientIsland.ts
3
+ function a(e) {
4
+ let t = e.split("/"), n = t[t.length - 1];
5
+ return n = n.replace(/\.(tsx?|jsx?)$/, ""), n;
6
6
  }
7
- function S(d) {
8
- const {
9
- from: t,
10
- props: s = {},
11
- mount: p = "load",
12
- mountOpts: c,
13
- class: e,
14
- id: i,
15
- children: f,
16
- ssr: u,
17
- __key: r,
18
- __src: l
19
- } = d, o = v(t);
20
- r && l && g(r, l, o);
21
- const a = {
22
- "data-island": r || "unresolved",
23
- "data-island-name": o,
24
- "data-island-mount": p
25
- };
26
- if (x()) {
27
- if (p !== "none") {
28
- const n = I(s);
29
- n >= 0 ? a["data-island-idx"] = String(n) : a["data-island-props"] = JSON.stringify(s);
30
- }
31
- } else
32
- a["data-island-props"] = JSON.stringify(s);
33
- if (c && (a["data-island-opts"] = c), u && l) {
34
- const n = N(l, o, s);
35
- if (n)
36
- return m("div", {
37
- ...a,
38
- "data-island-ssr": n,
39
- ...i ? { id: i } : {},
40
- class: e ? `l5e-island ${e}` : "l5e-island",
41
- // Raw token comment as body; replaced with SSR HTML in entry-server.
42
- setHtml: `<!--${n}-->`
43
- });
44
- }
45
- return m(
46
- "div",
47
- {
48
- ...a,
49
- ...i ? { id: i } : {},
50
- class: e ? `l5e-island ${e}` : "l5e-island"
51
- },
52
- f
53
- );
7
+ function o(o) {
8
+ let { from: s, props: c = {}, mount: l = "load", mountOpts: u, class: d, id: f, children: p, ssr: m, __key: h, __src: g } = o, _ = a(s);
9
+ h && g && i(h, g, _);
10
+ let v = {
11
+ "data-island": h || "unresolved",
12
+ "data-island-name": _,
13
+ "data-island-mount": l
14
+ };
15
+ if (r()) {
16
+ if (l !== "none") {
17
+ let e = t(c);
18
+ e >= 0 ? v["data-island-idx"] = String(e) : v["data-island-props"] = JSON.stringify(c);
19
+ }
20
+ } else v["data-island-props"] = JSON.stringify(c);
21
+ if (u && (v["data-island-opts"] = u), m && g) {
22
+ let t = e(g, _, c);
23
+ if (t) return n("div", {
24
+ ...v,
25
+ "data-island-ssr": t,
26
+ ...f ? { id: f } : {},
27
+ class: d ? `l5e-island ${d}` : "l5e-island",
28
+ setHtml: `<!--${t}-->`
29
+ });
30
+ }
31
+ return n("div", {
32
+ ...v,
33
+ ...f ? { id: f } : {},
34
+ class: d ? `l5e-island ${d}` : "l5e-island"
35
+ }, p);
54
36
  }
55
- export {
56
- S as ClientIsland
57
- };
58
- //# sourceMappingURL=island.js.map
37
+ //#endregion
38
+ export { o as ClientIsland };
39
+
40
+ //# sourceMappingURL=island.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"island.js","sources":["../src/island/ClientIsland.ts"],"sourcesContent":["import {\n jsxFactory,\n isIslandPropsExternalized,\n pushIslandProps,\n registerIsland,\n registerSsrIsland,\n type JSXChild,\n type JSXNode,\n} from '../core/jsx-runtime';\n\n/**\n * Derive component name from `from` path.\n * \"./react/Counter\" → \"Counter\"\n * \"~/features/newsfeed/react/NewsfeedApp\" → \"NewsfeedApp\"\n */\nfunction deriveComponentName(from: string): string {\n const segments = from.split('/');\n let filename = segments[segments.length - 1];\n filename = filename.replace(/\\.(tsx?|jsx?)$/, '');\n return filename;\n}\nexport type MountStrategy = 'load' | 'idle' | 'visible' | 'media' | 'none';\nexport interface ClientIslandProps {\n from: string;\n props?: Record<string, any>;\n mount?: MountStrategy | string;\n mountOpts?: string;\n class?: string;\n id?: string;\n children?: JSXChild | JSXChild[];\n\n /**\n * Opt-in: server-render the component into the placeholder and hydrate on the\n * client (instead of the default client-only mount). Component must be\n * SSR-safe (no top-level browser access) and props must be JSON-serializable.\n */\n ssr?: boolean;\n\n /** INTERNAL — injected by vite-plugin. Format: \"[name]_[hash]\" */\n __key?: string;\n /** INTERNAL — injected by vite-plugin. Manifest-compatible source path with extension */\n __src?: string;\n}\n\nexport function ClientIsland(attrs: ClientIslandProps): JSXNode {\n const {\n from,\n props = {},\n mount = 'load',\n mountOpts,\n class: className,\n id,\n children,\n ssr,\n __key,\n __src,\n } = attrs;\n\n const componentName = deriveComponentName(from);\n\n // Register island in render context (like useClientJs)\n // server.ts will use this to generate per-page window.__L5E_ISLANDS__\n // (needed for BOTH client-only mount AND ssr hydration — the client still\n // loads the component chunk via this registry).\n if (__key && __src) {\n registerIsland(__key, __src, componentName);\n }\n\n const dataAttrs: Record<string, string> = {\n 'data-island': __key || 'unresolved',\n 'data-island-name': componentName,\n 'data-island-mount': mount,\n };\n\n // Props delivery. `mount=\"none\"` never mounts/hydrates on the client, so its\n // props are dead weight → omit them entirely in the externalized mode.\n //\n // Externalized (default): props go into the per-request store and the element\n // just carries a `data-island-idx` pointer; a single `_l5e_data_` script at the\n // end of the document holds every island's props, keeping the SSR body lean.\n // Falls back to inlining when there's no render context (idx === -1).\n //\n // Legacy inline mode is kept byte-for-byte (props inlined even for mount=\"none\").\n if (isIslandPropsExternalized()) {\n if (mount !== 'none') {\n const idx = pushIslandProps(props);\n if (idx >= 0) {\n dataAttrs['data-island-idx'] = String(idx);\n } else {\n dataAttrs['data-island-props'] = JSON.stringify(props);\n }\n }\n } else {\n dataAttrs['data-island-props'] = JSON.stringify(props);\n }\n\n if (mountOpts) {\n dataAttrs['data-island-opts'] = mountOpts;\n }\n\n // SSR opt-in: register a pending server render and embed a unique token as the\n // placeholder body. entry-server replaces the token with renderToString() output\n // after the synchronous render pass, then normalizes data-island-ssr to \"1\".\n // The attribute value holds the token so the post-pass can target this exact\n // island (and strip the attribute on render failure → clean client-only fallback).\n if (ssr && __src) {\n const token = registerSsrIsland(__src, componentName, props);\n if (token) {\n return jsxFactory('div', {\n ...dataAttrs,\n 'data-island-ssr': token,\n ...(id ? { id } : {}),\n class: className ? `l5e-island ${className}` : 'l5e-island',\n // Raw token comment as body; replaced with SSR HTML in entry-server.\n setHtml: `<!--${token}-->`,\n });\n }\n }\n\n return jsxFactory(\n 'div',\n {\n ...dataAttrs,\n ...(id ? { id } : {}),\n class: className ? `l5e-island ${className}` : 'l5e-island',\n },\n children,\n );\n}\n"],"names":["deriveComponentName","from","segments","filename","ClientIsland","attrs","props","mount","mountOpts","className","id","children","ssr","__key","__src","componentName","registerIsland","dataAttrs","isIslandPropsExternalized","idx","pushIslandProps","token","registerSsrIsland","jsxFactory"],"mappings":";AAeA,SAASA,EAAoBC,GAAsB;AACjD,QAAMC,IAAWD,EAAK,MAAM,GAAG;AAC/B,MAAIE,IAAWD,EAASA,EAAS,SAAS,CAAC;AAC3C,SAAAC,IAAWA,EAAS,QAAQ,kBAAkB,EAAE,GACzCA;AACT;AAwBO,SAASC,EAAaC,GAAmC;AAC9D,QAAM;AAAA,IACJ,MAAAJ;AAAA,IACA,OAAAK,IAAQ,CAAA;AAAA,IACR,OAAAC,IAAQ;AAAA,IACR,WAAAC;AAAA,IACA,OAAOC;AAAA,IACP,IAAAC;AAAA,IACA,UAAAC;AAAA,IACA,KAAAC;AAAA,IACA,OAAAC;AAAA,IACA,OAAAC;AAAA,EAAA,IACET,GAEEU,IAAgBf,EAAoBC,CAAI;AAM9C,EAAIY,KAASC,KACXE,EAAeH,GAAOC,GAAOC,CAAa;AAG5C,QAAME,IAAoC;AAAA,IACxC,eAAeJ,KAAS;AAAA,IACxB,oBAAoBE;AAAA,IACpB,qBAAqBR;AAAA,EAAA;AAYvB,MAAIW;AACF,QAAIX,MAAU,QAAQ;AACpB,YAAMY,IAAMC,EAAgBd,CAAK;AACjC,MAAIa,KAAO,IACTF,EAAU,iBAAiB,IAAI,OAAOE,CAAG,IAEzCF,EAAU,mBAAmB,IAAI,KAAK,UAAUX,CAAK;AAAA,IAEzD;AAAA;AAEA,IAAAW,EAAU,mBAAmB,IAAI,KAAK,UAAUX,CAAK;AAYvD,MATIE,MACFS,EAAU,kBAAkB,IAAIT,IAQ9BI,KAAOE,GAAO;AAChB,UAAMO,IAAQC,EAAkBR,GAAOC,GAAeT,CAAK;AAC3D,QAAIe;AACF,aAAOE,EAAW,OAAO;AAAA,QACvB,GAAGN;AAAA,QACH,mBAAmBI;AAAA,QACnB,GAAIX,IAAK,EAAE,IAAAA,EAAA,IAAO,CAAA;AAAA,QAClB,OAAOD,IAAY,cAAcA,CAAS,KAAK;AAAA;AAAA,QAE/C,SAAS,OAAOY,CAAK;AAAA,MAAA,CACtB;AAAA,EAEL;AAEA,SAAOE;AAAA,IACL;AAAA,IACA;AAAA,MACE,GAAGN;AAAA,MACH,GAAIP,IAAK,EAAE,IAAAA,EAAA,IAAO,CAAA;AAAA,MAClB,OAAOD,IAAY,cAAcA,CAAS,KAAK;AAAA,IAAA;AAAA,IAEjDE;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"island.js","names":[],"sources":["../src/island/ClientIsland.ts"],"sourcesContent":["import {\n jsxFactory,\n isIslandPropsExternalized,\n pushIslandProps,\n registerIsland,\n registerSsrIsland,\n type JSXChild,\n type JSXNode,\n} from '../core/jsx-runtime';\n\n/**\n * Derive component name from `from` path.\n * \"./react/Counter\" → \"Counter\"\n * \"~/features/newsfeed/react/NewsfeedApp\" → \"NewsfeedApp\"\n */\nfunction deriveComponentName(from: string): string {\n const segments = from.split('/');\n let filename = segments[segments.length - 1];\n filename = filename.replace(/\\.(tsx?|jsx?)$/, '');\n return filename;\n}\nexport type MountStrategy = 'load' | 'idle' | 'visible' | 'media' | 'none';\nexport interface ClientIslandProps {\n from: string;\n props?: Record<string, any>;\n mount?: MountStrategy | string;\n mountOpts?: string;\n class?: string;\n id?: string;\n children?: JSXChild | JSXChild[];\n\n /**\n * Opt-in: server-render the component into the placeholder and hydrate on the\n * client (instead of the default client-only mount). Component must be\n * SSR-safe (no top-level browser access) and props must be JSON-serializable.\n */\n ssr?: boolean;\n\n /** INTERNAL — injected by vite-plugin. Format: \"[name]_[hash]\" */\n __key?: string;\n /** INTERNAL — injected by vite-plugin. Manifest-compatible source path with extension */\n __src?: string;\n}\n\nexport function ClientIsland(attrs: ClientIslandProps): JSXNode {\n const {\n from,\n props = {},\n mount = 'load',\n mountOpts,\n class: className,\n id,\n children,\n ssr,\n __key,\n __src,\n } = attrs;\n\n const componentName = deriveComponentName(from);\n\n // Register island in render context (like useClientJs)\n // server.ts will use this to generate per-page window.__L5E_ISLANDS__\n // (needed for BOTH client-only mount AND ssr hydration — the client still\n // loads the component chunk via this registry).\n if (__key && __src) {\n registerIsland(__key, __src, componentName);\n }\n\n const dataAttrs: Record<string, string> = {\n 'data-island': __key || 'unresolved',\n 'data-island-name': componentName,\n 'data-island-mount': mount,\n };\n\n // Props delivery. `mount=\"none\"` never mounts/hydrates on the client, so its\n // props are dead weight → omit them entirely in the externalized mode.\n //\n // Externalized (default): props go into the per-request store and the element\n // just carries a `data-island-idx` pointer; a single `_l5e_data_` script at the\n // end of the document holds every island's props, keeping the SSR body lean.\n // Falls back to inlining when there's no render context (idx === -1).\n //\n // Legacy inline mode is kept byte-for-byte (props inlined even for mount=\"none\").\n if (isIslandPropsExternalized()) {\n if (mount !== 'none') {\n const idx = pushIslandProps(props);\n if (idx >= 0) {\n dataAttrs['data-island-idx'] = String(idx);\n } else {\n dataAttrs['data-island-props'] = JSON.stringify(props);\n }\n }\n } else {\n dataAttrs['data-island-props'] = JSON.stringify(props);\n }\n\n if (mountOpts) {\n dataAttrs['data-island-opts'] = mountOpts;\n }\n\n // SSR opt-in: register a pending server render and embed a unique token as the\n // placeholder body. entry-server replaces the token with renderToString() output\n // after the synchronous render pass, then normalizes data-island-ssr to \"1\".\n // The attribute value holds the token so the post-pass can target this exact\n // island (and strip the attribute on render failure → clean client-only fallback).\n if (ssr && __src) {\n const token = registerSsrIsland(__src, componentName, props);\n if (token) {\n return jsxFactory('div', {\n ...dataAttrs,\n 'data-island-ssr': token,\n ...(id ? { id } : {}),\n class: className ? `l5e-island ${className}` : 'l5e-island',\n // Raw token comment as body; replaced with SSR HTML in entry-server.\n setHtml: `<!--${token}-->`,\n });\n }\n }\n\n return jsxFactory(\n 'div',\n {\n ...dataAttrs,\n ...(id ? { id } : {}),\n class: className ? `l5e-island ${className}` : 'l5e-island',\n },\n children,\n );\n}\n"],"mappings":";;AAeA,SAAS,EAAoB,GAAsB;CACjD,IAAM,IAAW,EAAK,MAAM,GAAG,GAC3B,IAAW,EAAS,EAAS,SAAS;CAE1C,OADA,IAAW,EAAS,QAAQ,kBAAkB,EAAE,GACzC;AACT;AAwBA,SAAgB,EAAa,GAAmC;CAC9D,IAAM,EACJ,SACA,WAAQ,CAAC,GACT,WAAQ,QACR,cACA,OAAO,GACP,OACA,aACA,QACA,UACA,aACE,GAEE,IAAgB,EAAoB,CAAI;CAM9C,AAAI,KAAS,KACX,EAAe,GAAO,GAAO,CAAa;CAG5C,IAAM,IAAoC;EACxC,eAAe,KAAS;EACxB,oBAAoB;EACpB,qBAAqB;CACvB;CAWA,IAAI,EAA0B,GACxB;MAAA,MAAU,QAAQ;GACpB,IAAM,IAAM,EAAgB,CAAK;GACjC,AAAI,KAAO,IACT,EAAU,qBAAqB,OAAO,CAAG,IAEzC,EAAU,uBAAuB,KAAK,UAAU,CAAK;EAEzD;QAEA,EAAU,uBAAuB,KAAK,UAAU,CAAK;CAYvD,IATI,MACF,EAAU,sBAAsB,IAQ9B,KAAO,GAAO;EAChB,IAAM,IAAQ,EAAkB,GAAO,GAAe,CAAK;EAC3D,IAAI,GACF,OAAO,EAAW,OAAO;GACvB,GAAG;GACH,mBAAmB;GACnB,GAAI,IAAK,EAAE,MAAG,IAAI,CAAC;GACnB,OAAO,IAAY,cAAc,MAAc;GAE/C,SAAS,OAAO,EAAM;EACxB,CAAC;CAEL;CAEA,OAAO,EACL,OACA;EACE,GAAG;EACH,GAAI,IAAK,EAAE,MAAG,IAAI,CAAC;EACnB,OAAO,IAAY,cAAc,MAAc;CACjD,GACA,CACF;AACF"}