Skip to content

Commit 599c5ee

Browse files
authored
fix(ilha): fix interactivity with snapshot: true (#24)
* fix(ilha): fix interactivity with snapshot: true * fix(pr): improvements * chore(changelog): update
1 parent 4456bf6 commit 599c5ee

21 files changed

Lines changed: 71 additions & 967 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
3232

3333
# Finder (MacOS) folder config
3434
.DS_Store
35+
36+
templates/*/bun.lock

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## `ilha`
44

5+
### 0.3.1 - 2026-04-24
6+
7+
- Fixes hydratable() snapshot interactivity regression.
8+
59
### 0.3.0 - 2026-04-24
610

711
- **Breaking:** Removes `.slot()` builder method and `slots` render context. Child islands are now interpolated directly inside `html\`\``` templates.
@@ -71,6 +75,10 @@ Initial release of **@ilha/form** — a tiny, typed form binding library for ilh
7175

7276
## `@ilha/router`
7377

78+
### 0.2.3 - 2026-04-24
79+
80+
- Updates Ilha dependency to 0.3.1
81+
7482
### 0.2.2 - 2026-04-24
7583

7684
- Updates Ilha dependency to 0.3.0
@@ -112,6 +120,10 @@ Initial release of **@ilha/router** — a lightweight, isomorphic router for ilh
112120

113121
## `@ilha/store`
114122

123+
### 0.1.4 - 2026-04-24
124+
125+
- Updates Ilha dependency to 0.3.1
126+
115127
### 0.1.3 - 2026-04-24
116128

117129
- Updates Ilha dependency to 0.3.0

apps/website/docs/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ const Libraries = ilha
7676
<div class="flex-1 p-2 overflow-x-auto bg-[#FBFBFB]" data-action="copy">
7777
<div class="tabs w-full">
7878
<nav role="tablist" aria-orientation="horizontal" class="w-full rounded-full">
79-
<button type="button" role="tab" aria-selected="true" tabindex="0" data-library="router" class="rounded-full">@ilha/router</button>
80-
<button type="button" role="tab" aria-selected="false" tabindex="0" data-library="store" class="rounded-full">@ilha/store</button>
81-
<button type="button" role="tab" aria-selected="false" tabindex="0" data-library="form" class="rounded-full">@ilha/form</button>
79+
<button type="button" role="tab" aria-selected="${state.library() === "router"}" tabindex="0" data-library="router" class="rounded-full">@ilha/router</button>
80+
<button type="button" role="tab" aria-selected="${state.library() === "store"}" tabindex="0" data-library="store" class="rounded-full">@ilha/store</button>
81+
<button type="button" role="tab" aria-selected="${state.library() === "form"}" tabindex="0" data-library="form" class="rounded-full">@ilha/form</button>
8282
</nav>
8383
</div>
8484
<div class="text-sm lg:text-[1rem]">

apps/website/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
"@codesandbox/sandpack-react": "^2.20.0",
1313
"basecoat-css": "^0.3.11",
1414
"dedent": "^1.7.2",
15-
"ilha": "^0.3.0",
15+
"ilha": "workspace:*",
1616
"shiki": "^4.0.2"
1717
},
1818
"devDependencies": {
1919
"@fontsource-variable/geist": "^5.2.8",
2020
"@fontsource-variable/geist-mono": "^5.2.7",
21-
"@ilha/router": "^0.2.2",
21+
"@ilha/router": "workspace:*",
2222
"@rspress/core": "^2.0.9",
2323
"@rspress/plugin-llms": "^2.0.9",
2424
"@rspress/plugin-sitemap": "^2.0.9",

packages/ilha/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ilha",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"description": "A tiny, framework-free island architecture library",
55
"license": "MIT",
66
"author": "Ryuz <ryuzer@proton.me>",

packages/ilha/src/index.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2261,6 +2261,31 @@ describe(".derived", () => {
22612261
cleanup(el);
22622262
});
22632263

2264+
it("client sync derived re-runs after hydration with snapshot", async () => {
2265+
let accessor!: (v?: number) => number | void;
2266+
2267+
const Island = ilha
2268+
.state("count", 2)
2269+
.derived("doubled", ({ state }) => state.count() * 2)
2270+
.render(({ state, derived }) => {
2271+
accessor = state.count as typeof accessor;
2272+
return `<p>${derived.doubled.value}</p>`;
2273+
});
2274+
2275+
const ssr = await Island.hydratable({}, { name: "snap", snapshot: true });
2276+
document.body.innerHTML = ssr;
2277+
const wrapper = document.querySelector("[data-ilha='snap']")!;
2278+
2279+
const unmount = Island.mount(wrapper);
2280+
expect(wrapper.querySelector("p")!.textContent).toBe("4");
2281+
2282+
accessor(10);
2283+
expect(wrapper.querySelector("p")!.textContent).toBe("20");
2284+
2285+
unmount();
2286+
document.body.innerHTML = "";
2287+
});
2288+
22642289
it("client async derived re-runs when tracked state changes", async () => {
22652290
let accessor!: (v?: string) => string | void;
22662291
const calls: string[] = [];

packages/ilha/src/index.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -534,16 +534,23 @@ function buildDerivedSignals<
534534
let skipFirst = derivedSnapshot != null && entry.key in derivedSnapshot;
535535

536536
const stopEffect = effect(() => {
537-
if (skipFirst) {
538-
skipFirst = false;
539-
return;
540-
}
541-
542537
ac.abort();
543538
ac = new AbortController();
544539
const currentAc = ac;
545540
const result = entry.fn({ state, input, signal: currentAc.signal });
546541

542+
if (skipFirst) {
543+
skipFirst = false;
544+
if (result instanceof Promise) {
545+
result.catch(() => {
546+
// Suppress unhandled rejection for the skipped initial run.
547+
// The snapshot already provides the correct value; we only ran
548+
// entry.fn to establish reactive subscriptions.
549+
});
550+
}
551+
return;
552+
}
553+
547554
if (!(result instanceof Promise)) {
548555
const prevSub = setActiveSub(undefined);
549556
env({ loading: false, value: result as unknown, error: undefined });

packages/router/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ilha/router",
3-
"version": "0.2.2",
3+
"version": "0.2.3",
44
"description": "A tiny SPA router for Ilha",
55
"license": "MIT",
66
"author": "Ryuz <ryuzer@proton.me>",
@@ -27,7 +27,7 @@
2727
"test": "bun test"
2828
},
2929
"dependencies": {
30-
"ilha": "0.3.0",
30+
"ilha": "0.3.1",
3131
"rou3": "0.8.1"
3232
},
3333
"devDependencies": {

packages/store/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ilha/store",
3-
"version": "0.1.3",
3+
"version": "0.1.4",
44
"description": "Typed store.",
55
"license": "MIT",
66
"author": "Ryuz <ryuzer@proton.me>",
@@ -25,7 +25,7 @@
2525
},
2626
"dependencies": {
2727
"alien-signals": "3.1.2",
28-
"ilha": "0.3.0"
28+
"ilha": "0.3.1"
2929
},
3030
"devDependencies": {
3131
"zod": "^4.3.6"

0 commit comments

Comments
 (0)