From 768c462c85b7dfd8dbddd6218ffb3f3dcb38348c Mon Sep 17 00:00:00 2001 From: Jack Lukic Date: Sat, 2 May 2026 16:02:13 -0400 Subject: [PATCH 01/16] Bug: Test perf characteristics of rming lazy eval --- .../src/engines/native/blocks/each.js | 30 ++++++++-- .../test/browser/ssr-hydration.test.js | 60 +++++++++++++++++-- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/packages/renderer/src/engines/native/blocks/each.js b/packages/renderer/src/engines/native/blocks/each.js index eb3ecce29..d4b065fac 100644 --- a/packages/renderer/src/engines/native/blocks/each.js +++ b/packages/renderer/src/engines/native/blocks/each.js @@ -642,12 +642,30 @@ const eachBlock = defineBlock({ }); }, - hydrate({ node, lookupExpression, self }) { - // Trust server DOM. Register a dep on the collection so the first data - // change invalidates the block — `update` then clears the region and - // rebuilds via reconcile. Per-item reactivity (text/attr Reactions - // inside item content) is established at that rebuild, not on hydrate. - lookupExpression(node.over); + hydrate({ node, data, scope, region, renderAST, lookupExpression, hydrateInnerContent, self, isSVG }) { + // Resolve items now (this also registers the collection dep so future + // signal changes invalidate the block). If the server emitted per-item + // markers, adopt the existing DOM and wire per-item Reactions against + // it — without this, items that never change after hydrate would have + // unwired per-item attribute bindings forever. + const { items, collectionType } = resolveItems(node, lookupExpression); + if (items.length > 0) { + const adopted = adoptServerItems({ + self, + items, + collectionType, + node, + data, + scope, + region, + renderAST, + hydrateInnerContent, + isSVG, + }); + if (adopted) { return; } + } + // No per-item markers (legacy SSR output) or empty list — let `update` + // handle it on the first data change via the standard reconcile path. self.hasHydrated = true; }, diff --git a/packages/renderer/test/browser/ssr-hydration.test.js b/packages/renderer/test/browser/ssr-hydration.test.js index 4981beb38..a96ed8dfc 100644 --- a/packages/renderer/test/browser/ssr-hydration.test.js +++ b/packages/renderer/test/browser/ssr-hydration.test.js @@ -1,6 +1,7 @@ import { defineComponent, renderToString } from '@semantic-ui/component'; import { $ } from '@semantic-ui/query'; import { Reaction } from '@semantic-ui/reactivity'; +import { Template } from '@semantic-ui/templating'; import { beforeEach, describe, expect, it } from 'vitest'; /******************************* @@ -32,11 +33,22 @@ function shadowHTML(el) { async function ssrAndHydrate(opts, attrs = {}) { const tag = uniqueTag(); const Component = defineComponent({ tagName: tag, renderingEngine: 'native', ...opts }); - const html = renderToString(Component, attrs); - - // Inject into page — browser parses DSD + // Toggle Template.isServer so renderToString actually emits SSR HTML in + // the browser test env (it gates server vs client renderer selection). + const wasServer = Template.isServer; + Template.isServer = true; + let html; + try { + html = renderToString(Component, attrs); + } + finally { + Template.isServer = wasServer; + } + + // Use setHTMLUnsafe to actually parse Declarative Shadow DOM. innerHTML + // does NOT process