fix(renderer): preserve hydration range topology - #161
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes hydration topology/identity issues in the renderer by (1) correctly adopting keyed For rows even when preceded by static siblings, and (2) keeping component-returned arrays (notably context scopes) structurally transparent across SSR/SSG + hydration and subsequent updates/rollback/cleanup. It also updates the docs and bumps the package version to prepare @askrjs/askr@0.0.78.
Changes:
- Add jsdom regressions covering keyed
Foradoption after static siblings during hydration, and transparent hydration for context-scope/component-returned arrays (including portal host behavior, focus/selection preservation, and rollback). - Update renderer hydration/commit logic to treat arrays and marked “transparent component results” as range-based (no visible wrapper host) and to adopt keyed elements produced by component vnodes during mixed-parent hydration.
- Bump package version to
0.0.78and align rendering/recipe docs with the new hydration/topology guarantees.
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/jsdom/ssr/for-static-sibling-hydration.test.tsx | Regression ensuring keyed For adopts only its server range after a preceding static sibling and preserves identity through updates. |
| tests/jsdom/ssr/component-array-hydration.test.tsx | Regression ensuring context-scope/component-array hydration stays wrapper-free and preserves identity/focus across portal updates. |
| tests/jsdom/dom/component-array-structure.test.tsx | SPA-side structure, focus/selection preservation, and rollback coverage for component-returned arrays. |
| src/runtime/context.ts | Marks defineScope’s internal provider component as “transparent result” to enable wrapper-free hydration/updates. |
| src/common/control.ts | Introduces a durable marker API for “transparent component results” (markTransparentComponentResult / hasTransparentComponentResult). |
| src/renderer/child-shape.ts | Centralizes “transparent component result” and “transparent range result” detection (arrays/fragments/control boundaries/marked components). |
| src/renderer/component-host-results.ts | Avoids materializing wrapper hosts when component results are transparent (including arrays). Adds nested-result resolution for host-based reuse. |
| src/renderer/component-range-commit.ts | Enhances range replacement to resolve and sync nested transparent component results during commit/hydration. |
| src/renderer/component-fragment-range.ts | Extends fragment-range syncing to array results and preserves automatic portal hosts during staging moves. |
| src/renderer/component-host-instances.ts | Improves stable instance resolution for unkeyed multi-node ranges moving across transparent wrapper owners. |
| src/renderer/boundary-range-adoption.ts | Adds canAdoptHydratedElement to allow keyed hydration adoption for component vnodes. |
| src/renderer/boundary-range-sync.ts | Uses canAdoptHydratedElement when adopting keyed rows in mixed-parent hydration. |
| docs/core/rendering.md | Updates documentation to describe “transparent component ranges” for fragments and arrays; documents keyed For adoption behavior. |
| docs/guides/platform-recipes.md | Updates documented version contract to @askrjs/askr@0.0.78. |
| package.json | Bumps package version to 0.0.78. |
| package-lock.json | Aligns lockfile version metadata to 0.0.78. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Resolved follow-up for open review thread: commit f1bf8f9 now includes the hydrated instance in in (it now uses ). This should address unresolved Copilot thread on src/renderer/component-host-results.ts. PR still has other open review items in the current history. |
|
Resolved the open review item on component-host-results.ts. Commit f1bf8f9 updates adoptEmptySSRPortalHydrationHost so __ASKR_INSTANCES now always includes the hydrated owner instance (set of [instance, ...retainedInstances]). This should address the unresolved Copilot thread. |
Summary
Showboundary sequences so following components are adopted once@askrjs/askr@0.0.78and align the rendering/versioned recipe docsRed to green
Forafter a staticLink: hydration previously grew 8 anchors to 15; now stays at 8 while the direct-Forcontrol stays at 7, with server identity preserved through reorder/removalShowboundaries: hydration previously duplicated the following component and lost its server identity; the cursor now advances once per empty boundary and preserves the tail through open/close/reopenShowboundaries: the populated boundary previously materialized before the live cursor and duplicated the following component; hydration now adopts a shape-compatible server element without reusing state across branch replacementsValidation
npm run check: 249 jsdom files / 1,275 tests; 13 browser files / 40 tests; 20 unit files / 151 tests; all formatting, lint, type, build, publint, checks, installed-package, and packed-artifact gatesnpm run bench: tiers 1-4, no budget failures@askrjs/askr@0.0.78tarball SHA-25676aac85462a387885a8504d513fd93305f3928162af39e4363dfb37c340a81b5aria-expandedfalse → true → false, complete Sheet teardown, restored sibling topology, no renderer wrappers, and zero console errorsFixes #159
Fixes #160
Fixes #162
Fixes #163
Fixes #164