From fd06125cfce50e5ea9f1e27bde8484b8767cee0d Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Thu, 21 May 2026 10:39:24 +1000 Subject: [PATCH 1/2] fix: register content renderer when downstream component has own state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The static-content optimisation in getSectionRegisterReasons incorrectly skipped registering the section renderer when all of: downstreamReasons was specific (not true), the content section had no own serialize reasons, and neither did its parent section. This was too broad — it also fired when the downstream component had its own reactive state that caused it to unconditionally write input.content into its serialized scope, making the renderer unresolvable on the client. The fix tightens the guard to only skip when downstreamReasons is param/input-driven (isReasonDynamic), which covers the purely iterative case from known-rest-attr-tag but not the case where the consumer's own state (e.g. ) forces the content reference into its scope regardless of what the parent passes. Adds a fixture static-content-conditional-consumer to cover the broken scenario: a consumer with that conditionally renders <${input.content}/> where the passed content is entirely static. Co-Authored-By: Claude Sonnet 4.6 --- .../__snapshots__/dom.bundle.debug.js | 30 +++++++++++++ .../__snapshots__/dom.bundle.js | 15 +++++++ .../__snapshots__/html.bundle.debug.js | 35 +++++++++++++++ .../__snapshots__/html.bundle.js | 24 +++++++++++ .../__snapshots__/render.debug.md | 43 +++++++++++++++++++ .../__snapshots__/render.md | 43 +++++++++++++++++++ .../__snapshots__/writes.debug.html | 14 ++++++ .../__snapshots__/writes.html | 10 +++++ .../tags/consumer.marko | 5 +++ .../template.marko | 3 ++ .../test.ts | 9 ++++ .../src/translator/util/sections.ts | 2 +- 12 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.debug.js create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.js create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.debug.js create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.js create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.debug.md create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.md create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.debug.html create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.html create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/tags/consumer.marko create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/template.marko create mode 100644 packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/test.ts diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.debug.js b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.debug.js new file mode 100644 index 0000000000..ce53006e27 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.debug.js @@ -0,0 +1,30 @@ +// tags/consumer.marko +const $template$1 = ""; +const $walks$1 = " b%c"; +const $if_content__dynamicTag = /* @__PURE__ */ _dynamic_tag("#text/0"); +const $if_content__input_content = /* @__PURE__ */ _if_closure("#text/1", 0, ($scope) => $if_content__dynamicTag($scope, $scope._.input_content)); +const $if_content__setup = $if_content__input_content; +const $if = /* @__PURE__ */ _if("#text/1", "", "b%c", $if_content__setup); +const $show__script = _script("__tests__/tags/consumer.marko_0_show", ($scope) => _on($scope["#button/0"], "click", function() { + $show($scope, !$scope.show); +})); +const $show = /* @__PURE__ */ _let("show/5", ($scope) => { + $if($scope, $scope.show ? 0 : 1); + $show__script($scope); +}); +function $setup$1($scope) { + $show($scope, false); +} +const $input = ($scope, input) => $input_content($scope, input.content); +const $input_content = /* @__PURE__ */ _const("input_content", $if_content__input_content); +var consumer_default = /* @__PURE__ */ _template("__tests__/tags/consumer.marko", $template$1, $walks$1, $setup$1, $input); + +// template.marko +const $template = /* @__PURE__ */ ((_w0) => `${_w0}`)($template$1); +const $walks = /* @__PURE__ */ ((_w0) => `/${_w0}&b`)($walks$1); +const $consumer_content = _content_resume("__tests__/template.marko_1_content", "
static content
", "b"); +function $setup($scope) { + $setup$1($scope["#childScope/0"]); + $input_content($scope["#childScope/0"], $consumer_content($scope)); +} +var template_default = /* @__PURE__ */ _template("__tests__/template.marko", $template, $walks, $setup); diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.js b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.js new file mode 100644 index 0000000000..6acffd620b --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/dom.bundle.js @@ -0,0 +1,15 @@ +// total: 13125 (min) 5073 (brotli) +// tags/consumer.marko: 175 (min) 131 (brotli) +const $if_content__dynamicTag = /* @__PURE__ */ _dynamic_tag(0); +const $if_content__input_content = /* @__PURE__ */ _if_closure(1, 0, ($scope) => $if_content__dynamicTag($scope, $scope._.e)); +const $if = /* @__PURE__ */ _if(1, "", "b%c", $if_content__input_content); +const $show__script = _script("b0", ($scope) => _on($scope.a, "click", function() { + $show($scope, !$scope.f); +})); +const $show = /* @__PURE__ */ _let(5, ($scope) => { + $if($scope, $scope.f ? 0 : 1); + $show__script($scope); +}); + +// template.marko: 54 (min) 58 (brotli) +const $consumer_content = _content_resume("a0", "
static content
", "b"); diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.debug.js b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.debug.js new file mode 100644 index 0000000000..15f8512921 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.debug.js @@ -0,0 +1,35 @@ +// tags/consumer.marko +var consumer_default = _template("__tests__/tags/consumer.marko", (input) => { + const $scope0_reason = _scope_reason(); + const $scope0_id = _scope_id(); + let show = false; + _html(`${_el_resume($scope0_id, "#button/0")}`); + _if(() => { + if (show) { + const $scope1_id = _scope_id(); + _dynamic_tag($scope1_id, "#text/0", input.content, {}, 0, 0, _serialize_guard($scope0_reason, 0)); + writeScope($scope1_id, { _: _serialize_if($scope0_reason, 0) && _scope_with_id($scope0_id) }, "__tests__/tags/consumer.marko", "3:2"); + return 0; + } + }, $scope0_id, "#text/1"); + _script($scope0_id, "__tests__/tags/consumer.marko_0_show"); + writeScope($scope0_id, { + input_content: input.content, + show + }, "__tests__/tags/consumer.marko", 0, { + input_content: ["input.content"], + show: "1:6" + }); + _resume_branch($scope0_id); +}); + +// template.marko +var template_default = _template("__tests__/template.marko", (input) => { + _scope_reason(); + const $scope0_id = _scope_id(); + consumer_default({ content: _content_resume("__tests__/template.marko_1_content", () => { + _scope_reason(); + const $scope1_id = _scope_id(); + _html("
static content
"); + }, $scope0_id) }); +}, 1); diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.js b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.js new file mode 100644 index 0000000000..9702495967 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/html.bundle.js @@ -0,0 +1,24 @@ +// tags/consumer.marko +var consumer_default = _template("b", (input) => { + _scope_reason(); + const $scope0_id = _scope_id(); + let show = false; + _html(`${_el_resume($scope0_id, "a")}`); + _if(() => {}, $scope0_id, "b"); + _script($scope0_id, "b0"); + writeScope($scope0_id, { + e: input.content, + f: show + }); + _resume_branch($scope0_id); +}); + +// template.marko +var template_default = _template("a", (input) => { + _scope_reason(); + consumer_default({ content: _content_resume("a0", () => { + _scope_reason(); + _scope_id(); + _html("
static content
"); + }, _scope_id()) }); +}, 1); diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.debug.md b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.debug.md new file mode 100644 index 0000000000..705235b9f5 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.debug.md @@ -0,0 +1,43 @@ +# Render +```html + +``` + +# Update +```js +container.querySelector("button#toggle").click(); +``` +```html + +
+ static content +
+``` +## Change +``` +INSERT: #toggle + div +``` + +# Update +```js +container.querySelector("button#toggle").click(); +``` +```html + +``` +## Change +``` +REMOVE: #toggle + div +``` diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.md b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.md new file mode 100644 index 0000000000..705235b9f5 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/render.md @@ -0,0 +1,43 @@ +# Render +```html + +``` + +# Update +```js +container.querySelector("button#toggle").click(); +``` +```html + +
+ static content +
+``` +## Change +``` +INSERT: #toggle + div +``` + +# Update +```js +container.querySelector("button#toggle").click(); +``` +```html + +``` +## Change +``` +REMOVE: #toggle + div +``` diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.debug.html b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.debug.html new file mode 100644 index 0000000000..acf7e97eb8 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.debug.html @@ -0,0 +1,14 @@ + + diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.html b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.html new file mode 100644 index 0000000000..b4e0ac3c00 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/__snapshots__/writes.html @@ -0,0 +1,10 @@ + + + diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/tags/consumer.marko b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/tags/consumer.marko new file mode 100644 index 0000000000..d3f9eb9ff1 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/tags/consumer.marko @@ -0,0 +1,5 @@ + +toggle + + <${input.content}/> + diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/template.marko b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/template.marko new file mode 100644 index 0000000000..1aa9c2ec05 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/template.marko @@ -0,0 +1,3 @@ + +
static content
+
diff --git a/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/test.ts b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/test.ts new file mode 100644 index 0000000000..792545e3d8 --- /dev/null +++ b/packages/runtime-tags/src/__tests__/fixtures/static-content-conditional-consumer/test.ts @@ -0,0 +1,9 @@ +import type { TestConfig } from "../../main.test"; + +function toggle(container: Element) { + container.querySelector("button#toggle")!.click(); +} + +export const config: TestConfig = { + steps: [{}, toggle, toggle], +}; diff --git a/packages/runtime-tags/src/translator/util/sections.ts b/packages/runtime-tags/src/translator/util/sections.ts index 607dbbafdc..1ff98b0157 100644 --- a/packages/runtime-tags/src/translator/util/sections.ts +++ b/packages/runtime-tags/src/translator/util/sections.ts @@ -323,7 +323,7 @@ export function getSectionRegisterReasons(section: Section) { ); if (!downstreamReasons) return false; if ( - downstreamReasons !== true && + isReasonDynamic(downstreamReasons) && !section.serializeReason && !section.serializeReasons.size && !section.parent?.serializeReason && From 27005826d2e2b25e7994155af49d78717f553a82 Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Thu, 21 May 2026 10:41:35 +1000 Subject: [PATCH 2/2] chore: update changeset --- .changeset/cozy-drinks-roll.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/cozy-drinks-roll.md diff --git a/.changeset/cozy-drinks-roll.md b/.changeset/cozy-drinks-roll.md new file mode 100644 index 0000000000..dd98854508 --- /dev/null +++ b/.changeset/cozy-drinks-roll.md @@ -0,0 +1,6 @@ +--- +"@marko/runtime-tags": patch +"marko": patch +--- + +fix: register content renderer when downstream component has own state