Skip to content

chore(ilha): reinforce island nesting#74

Merged
yzuyr merged 2 commits into
mainfrom
chore/ilha-island-nesting-reinforcement
Jul 20, 2026
Merged

chore(ilha): reinforce island nesting#74
yzuyr merged 2 commits into
mainfrom
chore/ilha-island-nesting-reinforcement

Conversation

@yzuyr

@yzuyr yzuyr commented Jul 20, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Bug Fixes
    • Improved nested island behavior across SSR, hydration, and client updates, including compound children and callback interactions.
    • Sanitized slot props stored in HTML attributes to exclude non-serializable values and prevent oversized payloads.
  • Documentation
    • Clarified prop/children runtime handling and nested layout composition behavior.
  • Tests
    • Added stress and layout-nesting test coverage for slot serialization, hydration revival, and rapid prop churn.
  • Chores
    • Updated package versions for the website, router, store, and project templates.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 07268e66-6b5e-4f9b-8163-5f1cd7a29d06

📥 Commits

Reviewing files that changed from the base of the PR and between 011b3bf and 40d79d1.

📒 Files selected for processing (4)
  • apps/website/package.json
  • packages/ilha/src/index.ts
  • packages/ilha/src/nesting-stress.test.ts
  • packages/router/src/layout-nested-children.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/website/package.json
  • packages/ilha/src/index.ts
  • packages/ilha/src/nesting-stress.test.ts

📝 Walkthrough

Walkthrough

Changes

Island Slot Composition

Layer / File(s) Summary
Slot serialization and render context
packages/ilha/src/index.ts, packages/ilha/README.md
Slot markers now serialize JSON-safe props, revive supported special values, and share render context across duplicate Ilha copies.
Hydration and prop reconciliation
packages/ilha/src/index.ts
Mounting revives serialized props, while stable inline slots propagate updated props to mounted children.
Nested island validation
packages/ilha/src/index.test.ts, packages/ilha/src/nesting-stress.test.ts, packages/ilha/src/jsx-runtime.test.tsx
Tests cover serialization, revival, nested rendering, hydration, callback wiring, prop churn, and keyed or positional slots.
Nested layout composition
packages/router/src/layout-nested-children.test.ts, packages/router/README.md
Layout tests cover compound children, nested layouts, live callbacks, updates, hydration, and keyed or positional slots.
Package and template version alignment
apps/website/package.json, packages/*/package.json, templates/*/package.json
Package versions and dependency constraints are updated for the Ilha, router, and store releases.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ParentIsland
  participant emitIslandSlot
  participant SSRMarkup
  participant mountSlots
  participant ChildIsland
  ParentIsland->>emitIslandSlot: render nested child slot
  emitIslandSlot->>SSRMarkup: write sanitized data-ilha-props
  SSRMarkup->>mountSlots: provide slot marker and snapshot
  mountSlots->>ChildIsland: revive props and mount child
  ParentIsland->>ChildIsland: push updated props during rerender
Loading

Possibly related PRs

  • ilhajs/ilha#64: Related slot-prop parsing, serialization, and hydration changes.
  • ilhajs/ilha#72: Related stable inline slot updates during skipped morphs.
  • ilhajs/ilha#23: Related island slot and interpolation handling.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main change: strengthening Ilha island nesting behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/ilha-island-nesting-reinforcement

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
apps/website/package.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
packages/ilha/src/index.ts (2)

3655-3660: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Good fix, but this is now the third copy of the same "push props into mounted slots" loop.

This exact for (const [id, entry] of mountedSlots) { const next = newSlotMap.get(id); if (next) entry.updateProps(next.props); } pattern already exists at the hydration path (around Lines 3626-3629) and the identical-output fast path (Lines 3683-3686). The fact that this PR had to add a missing instance here is itself evidence the duplication is a maintenance risk — a future change to one copy is likely to miss the others.

♻️ Extract a shared helper
+function pushUpdatedProps(
+  mounted: Map<string, { updateProps: (p?: Record<string, unknown>) => void }>,
+  slotMap: IslandRenderCtx["slots"],
+): void {
+  for (const [id, entry] of mounted) {
+    const next = slotMap.get(id);
+    if (next) entry.updateProps(next.props);
+  }
+}

Then replace all three loop sites with pushUpdatedProps(mountedSlots, newSlotMap).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ilha/src/index.ts` around lines 3655 - 3660, Extract the repeated
mounted-slot prop update loop into a shared helper named pushUpdatedProps,
accepting mountedSlots and newSlotMap and applying each matching entry’s props
via updateProps. Replace the existing loops in the hydration path, this
slot-set-stable path, and the identical-output fast path with calls to the
helper, preserving their current behavior.

323-343: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No cycle guard in encodeSlotPropValue — a circular prop causes stack overflow instead of a clean error.

encodeSlotPropValue recurses into arrays/objects with no visited-set tracking. A circular reference in a prop (a real possibility — self-referencing tree nodes, DOM/element refs, etc.) will recurse indefinitely until RangeError: Maximum call stack size exceeded, crashing the SSR render for that page rather than surfacing a predictable error.

♻️ Add a cycle guard
-function encodeSlotPropValue(value: unknown): unknown {
+function encodeSlotPropValue(value: unknown, seen: WeakSet<object> = new WeakSet()): unknown {
   if (value == null) return value;
   if (typeof value === "function" || typeof value === "symbol") return undefined;
   if (typeof value !== "object") return value;
   if (isRawHtmlValue(value)) return { __ilha: "raw", value: value.value };
+  if (seen.has(value)) return undefined;
+  seen.add(value);
   if (Array.isArray(value)) {
-    return value.map((item) => encodeSlotPropValue(item)).filter((item) => item !== undefined);
+    return value.map((item) => encodeSlotPropValue(item, seen)).filter((item) => item !== undefined);
   }
   if (Object.getPrototypeOf(value) !== Object.prototype) return undefined;
   const obj = value as Record<string, unknown>;
   const encoded: Record<string, unknown> = {};
   for (const key of Object.keys(obj)) {
-    const next = encodeSlotPropValue(obj[key]);
+    const next = encodeSlotPropValue(obj[key], seen);
     if (next !== undefined || obj[key] === null) encoded[key] = next as unknown;
   }
   return encoded;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ilha/src/index.ts` around lines 323 - 343, Update
encodeSlotPropValue to track visited arrays and plain objects during recursive
encoding and detect circular references before descending. On a repeated
reference, surface a predictable error instead of recursing until stack
overflow, while preserving existing handling for raw HTML, unsupported values,
and non-cyclic structures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/website/package.json`:
- Around line 14-18: Update the `@ilha/store` dependency in the apps/website
package manifest from ^0.7.6 to ^0.7.7, leaving the surrounding dependencies
unchanged.

In `@packages/ilha/src/index.ts`:
- Around line 3317-3319: Update the parsed persisted-props handling around
reviveSlotProps so non-object values are validated or safely handled before
revival, preventing reviveSlotProps from receiving invalid input while
preserving valid object props behavior.
- Line 4161: Update the auto-discovery path in mountAll so reviveSlotProps
safely handles malformed parsed values that are not objects before assigning to
props, matching the input validation used by reviveSlotProps’s definition while
preserving valid object revival.
- Around line 3071-3073: Update the parsed-props handling in mount() so
reviveSlotProps is called only when parsed is a non-null object; skip or safely
handle null and primitive JSON values without throwing, while preserving revival
for valid object payloads.
- Around line 345-396: Restrict the legacy conversion in reviveLegacyChildren to
objects matching the exact legacy blob shape, rather than any object containing
a string value; preserve objects with sibling fields as ordinary data and only
pass the exact blob to makeRawHtml. Apply the same shape check in both the
single-child and array-child branches.

In `@packages/router/src/layout-nested-children.test.ts`:
- Around line 37-40: Correct the escaping character class in slotPropsFromSsr so
slotId.replace properly escapes all regex metacharacters, including the closing
bracket, without trailing garbage; match the established pattern used by the
nesting-stress helper.

---

Nitpick comments:
In `@packages/ilha/src/index.ts`:
- Around line 3655-3660: Extract the repeated mounted-slot prop update loop into
a shared helper named pushUpdatedProps, accepting mountedSlots and newSlotMap
and applying each matching entry’s props via updateProps. Replace the existing
loops in the hydration path, this slot-set-stable path, and the identical-output
fast path with calls to the helper, preserving their current behavior.
- Around line 323-343: Update encodeSlotPropValue to track visited arrays and
plain objects during recursive encoding and detect circular references before
descending. On a repeated reference, surface a predictable error instead of
recursing until stack overflow, while preserving existing handling for raw HTML,
unsupported values, and non-cyclic structures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 30155b0a-73f8-4e80-9067-f431062ee1c9

📥 Commits

Reviewing files that changed from the base of the PR and between 7ca8450 and 011b3bf.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • apps/website/package.json
  • packages/ilha/README.md
  • packages/ilha/package.json
  • packages/ilha/src/index.test.ts
  • packages/ilha/src/index.ts
  • packages/ilha/src/jsx-runtime.test.tsx
  • packages/ilha/src/nesting-stress.test.ts
  • packages/router/README.md
  • packages/router/package.json
  • packages/router/src/layout-nested-children.test.ts
  • packages/store/package.json
  • templates/elysia/package.json
  • templates/hono/package.json
  • templates/nitro/package.json
  • templates/vite/package.json

Comment thread apps/website/package.json
Comment thread packages/ilha/src/index.ts
Comment thread packages/ilha/src/index.ts
Comment thread packages/ilha/src/index.ts
Comment thread packages/ilha/src/index.ts
Comment thread packages/router/src/layout-nested-children.test.ts
@yzuyr
yzuyr merged commit cb71105 into main Jul 20, 2026
5 checks passed
@yzuyr
yzuyr deleted the chore/ilha-island-nesting-reinforcement branch July 20, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant