From 1a6d49573deaf3916cc5be2d3c0ae4f35cdcf645 Mon Sep 17 00:00:00 2001
From: dpiercey
Date: Mon, 27 Jan 2025 11:04:32 -0700
Subject: [PATCH] fix: avoid document range api for html parsing
---
.changeset/cuddly-spies-cough.md | 5 +
.sizes.json | 38 +++---
.sizes/counter.csr/entry.js | 2 +-
.sizes/counter.ssr/entry.js | 2 +-
.sizes/dom.js | 35 +++---
.sizes/name-cache.json | 2 +-
.../__snapshots__/.name-cache.json | 12 ++
.../__snapshots__/csr-sanitized.expected.md | 63 ++++++++++
.../__snapshots__/csr.expected.md | 83 ++++++++++++
.../dom.expected/template.hydrate.js | 15 +++
.../__snapshots__/dom.expected/template.js | 18 +++
.../__snapshots__/html.expected/template.js | 25 ++++
.../resume-sanitized.expected.md | 63 ++++++++++
.../__snapshots__/resume.expected.md | 119 ++++++++++++++++++
.../__snapshots__/ssr-sanitized.expected.md | 9 ++
.../__snapshots__/ssr.expected.md | 40 ++++++
.../conditional-table-row/template.marko | 10 ++
.../fixtures/conditional-table-row/test.ts | 5 +
packages/runtime-tags/src/dom/event.ts | 6 +-
packages/runtime-tags/src/dom/parse-html.ts | 34 +++--
packages/runtime-tags/src/dom/renderer.ts | 4 +-
packages/runtime-tags/src/dom/template.ts | 9 +-
packages/runtime-tags/src/dom/walker.ts | 2 +-
23 files changed, 539 insertions(+), 62 deletions(-)
create mode 100644 .changeset/cuddly-spies-cough.md
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/.name-cache.json
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr-sanitized.expected.md
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr.expected.md
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.hydrate.js
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.js
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/html.expected/template.js
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume-sanitized.expected.md
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume.expected.md
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr-sanitized.expected.md
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr.expected.md
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/template.marko
create mode 100644 packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/test.ts
diff --git a/.changeset/cuddly-spies-cough.md b/.changeset/cuddly-spies-cough.md
new file mode 100644
index 0000000000..4ab30377f2
--- /dev/null
+++ b/.changeset/cuddly-spies-cough.md
@@ -0,0 +1,5 @@
+---
+"@marko/runtime-tags": patch
+---
+
+Switch back to HTMLTemplateElement for parsing client side html content to avoid automatic tag insertions from the document range api.
diff --git a/.sizes.json b/.sizes.json
index bb733d0ecb..8d2d86fec1 100644
--- a/.sizes.json
+++ b/.sizes.json
@@ -7,38 +7,38 @@
{
"name": "*",
"total": {
- "min": 17895,
- "brotli": 6539
+ "min": 17933,
+ "brotli": 6546
}
},
{
"name": "counter",
"user": {
"min": 189,
- "brotli": 144
+ "brotli": 143
},
"runtime": {
- "min": 4034,
- "brotli": 1746
+ "min": 4072,
+ "brotli": 1748
},
"total": {
- "min": 4223,
- "brotli": 1890
+ "min": 4261,
+ "brotli": 1891
}
},
{
"name": "counter 💧",
"user": {
"min": 111,
- "brotli": 100
+ "brotli": 102
},
"runtime": {
- "min": 3502,
+ "min": 3499,
"brotli": 1586
},
"total": {
- "min": 3613,
- "brotli": 1686
+ "min": 3610,
+ "brotli": 1688
}
},
{
@@ -48,12 +48,12 @@
"brotli": 541
},
"runtime": {
- "min": 7702,
- "brotli": 3172
+ "min": 7740,
+ "brotli": 3191
},
"total": {
- "min": 8842,
- "brotli": 3713
+ "min": 8880,
+ "brotli": 3732
}
},
{
@@ -63,12 +63,12 @@
"brotli": 478
},
"runtime": {
- "min": 8678,
- "brotli": 3538
+ "min": 8722,
+ "brotli": 3552
},
"total": {
- "min": 9626,
- "brotli": 4016
+ "min": 9670,
+ "brotli": 4030
}
}
]
diff --git a/.sizes/counter.csr/entry.js b/.sizes/counter.csr/entry.js
index 746ac775a3..381dbef221 100644
--- a/.sizes/counter.csr/entry.js
+++ b/.sizes/counter.csr/entry.js
@@ -1,4 +1,4 @@
-// size: 189 (min) 144 (brotli)
+// size: 189 (min) 143 (brotli)
const _clickCount_effect = effect("a0", (_scope, { 2: clickCount }) =>
on(_scope[0], "click", function () {
_clickCount(_scope, clickCount + 1);
diff --git a/.sizes/counter.ssr/entry.js b/.sizes/counter.ssr/entry.js
index d33855810d..c484f54e38 100644
--- a/.sizes/counter.ssr/entry.js
+++ b/.sizes/counter.ssr/entry.js
@@ -1,4 +1,4 @@
-// size: 111 (min) 100 (brotli)
+// size: 111 (min) 102 (brotli)
const _clickCount_effect = effect("a0", (_scope, { 2: clickCount }) =>
on(_scope[0], "click", function () {
_clickCount(_scope, clickCount + 1);
diff --git a/.sizes/dom.js b/.sizes/dom.js
index d1c9403632..f637ae017b 100644
--- a/.sizes/dom.js
+++ b/.sizes/dom.js
@@ -1,4 +1,4 @@
-// size: 17895 (min) 6539 (brotli)
+// size: 17933 (min) 6546 (brotli)
var empty = [],
rest = Symbol();
function attrTag(attrs2) {
@@ -147,7 +147,7 @@ function handleDelegated(ev) {
if (target) {
let handlersByElement = elementHandlersByEvent.get(ev.type);
if ((handlersByElement.get(target)?.(ev, target), ev.bubbles))
- for (; (target = target.parentElement) && !ev.cancelBubble; )
+ for (; (target = target.parentNode) && !ev.cancelBubble; )
handlersByElement.get(target)?.(ev, target);
}
}
@@ -575,10 +575,10 @@ function normalizeBoolProp(value2) {
function toValueProp(it) {
return it.value;
}
-var fallback = document.createTextNode(""),
- parser = new Range();
+var fallback = new Text(),
+ parser = document.createElement("template");
function parseHTML(html2) {
- return parser.createContextualFragment(html2);
+ return (parser.innerHTML = html2), parser.content;
}
function attr(element, name, value2) {
setAttribute(element, name, normalizeAttrValue(value2));
@@ -834,8 +834,7 @@ function walkInternal(walkCodes, scope, currentWalkIndex) {
if (38 === value2) return currentWalkIndex;
if (32 === value2) scope[currentScopeIndex++] = walker.currentNode;
else {
- let newNode = (scope[currentScopeIndex++] =
- document.createTextNode("")),
+ let newNode = (scope[currentScopeIndex++] = new Text()),
current = walker.currentNode;
current.parentNode.replaceChild(newNode, current),
(walker.currentNode = newNode);
@@ -933,12 +932,16 @@ function createRenderer(template, walks, setup, getArgs) {
function _clone() {
return (this.G ||= (function (html2) {
let content = parseHTML(html2);
- return content.firstChild
- ? content.firstChild === content.lastChild &&
+ if (content.firstChild) {
+ if (
+ content.firstChild === content.lastChild &&
8 !== content.firstChild.nodeType
- ? content.firstChild
- : content
- : fallback;
+ )
+ return content.firstChild;
+ let fragment = new DocumentFragment();
+ return fragment.appendChild(content), fragment;
+ }
+ return fallback;
})(this.F)).cloneNode(!0);
}
var conditional = function (nodeAccessor, fn, getIntersection) {
@@ -1605,14 +1608,14 @@ function mount(input = {}, reference, position) {
args && args(branch, [input]);
});
switch (position) {
+ case "beforebegin":
+ reference.parentNode.insertBefore(dom, reference);
+ break;
case "afterbegin":
reference.insertBefore(dom, reference.firstChild);
break;
case "afterend":
- reference.parentElement.insertBefore(dom, reference.nextSibling);
- break;
- case "beforebegin":
- reference.parentElement.insertBefore(dom, reference);
+ reference.parentNode.insertBefore(dom, reference.nextSibling);
break;
default:
reference.appendChild(dom);
diff --git a/.sizes/name-cache.json b/.sizes/name-cache.json
index 605061da76..7bb65bcfc4 100644
--- a/.sizes/name-cache.json
+++ b/.sizes/name-cache.json
@@ -1 +1 @@
-{"vars":{"props":{"$empty":"e","$rest":"t","$attrTag":"n","$attrTags":"r","$attrTagIterator":"i","$forIn":"l","$forOf":"o","$forTo":"f","$isScheduled":"u","$port2":"a","$flushAndWaitFrame":"c","$triggerMacroTask":"s","$createScope":"d","$emptyScope":"h","$getEmptyScope":"g","$destroyScope":"p","$_destroyScope":"v","$onDestroy":"b","$removeAndDestroyScope":"m","$insertBefore":"y","$registeredValues":"k","$Render":"w","$isResuming":"C","$register":"A","$registerBoundSignal":"S","$init":"N","$registerSubscriber":"x","$nodeRef":"$","$MARK":"M","$CLEAN":"E","$DIRTY":"I","$state":"T","$value":"_","$accessorId":"O","$intersection":"B","$defaultGetOwnerScope":"V","$closure":"j","$dynamicClosure":"R","$childClosures":"q","$dynamicSubscribers":"D","$setTagVar":"P","$tagVarSignal":"W","$setTagVarChange":"L","$tagVarSignalChange":"z","$renderBodyClosures":"F","$tagIdsByGlobal":"U","$nextTagId":"G","$inChild":"J","$intersections":"X","$effect":"Z","$pendingSignals":"H","$pendingEffects":"K","$rendering":"Q","$queueEffect":"Y","$run":"ee","$prepareEffects":"te","$runEffects":"ne","$runSignals":"re","$resetAbortSignal":"ie","$getAbortSignal":"le","$stringifyClassObject":"oe","$NON_DIMENSIONAL":"fe","$stringifyStyleObject":"ue","$toDelimitedString":"ae","$isEventHandler":"ce","$getEventHandlerName":"se","$normalizeDynamicRenderer":"de","$elementHandlersByEvent":"he","$defaultDelegator":"ge","$on":"pe","$createDelegator":"ve","$handleDelegated":"be","$stripSpacesAndPunctuation":"me","$controllable_input_checked":"ye","$controllable_input_checked_effect":"ke","$controllable_input_checkedValue":"we","$controllable_input_checkedValue_effect":"Ce","$controllable_input_value":"Ae","$controllable_input_value_effect":"Se","$controllable_select_value":"Ne","$controllable_select_value_effect":"xe","$setSelectOptions":"$e","$controllable_detailsOrDialog_open":"Me","$controllable_detailsOrDialog_open_effect":"Ee","$inputType":"Ie","$setValueAndUpdateSelection":"Te","$setCheckboxValue":"_e","$delegateFormControl":"Oe","$formChangeHandlers":"Be","$syncControllable":"Ve","$onFormChange":"je","$onFormReset":"Re","$hasValueChanged":"qe","$hasCheckboxChanged":"De","$hasSelectChanged":"Pe","$hasFormElementChanged":"We","$normalizeStrProp":"Le","$normalizeBoolProp":"ze","$toValueProp":"Fe","$fallback":"Ue","$parser":"Ge","$parseHTML":"Je","$attr":"Xe","$setAttribute":"Ze","$classAttr":"He","$styleAttr":"Ke","$data":"Qe","$attrs":"Ye","$hasAttrAlias":"et","$partialAttrs":"tt","$attrsInternal":"nt","$attrsEvents":"rt","$html":"it","$props":"lt","$normalizeAttrValue":"ot","$lifecycle":"ft","$walker":"ut","$trimWalkString":"at","$walk":"ct","$walkInternal":"st","$createScopeWithRenderer":"dt","$createScopeWithTagNameOrRenderer":"ht","$initRenderer":"gt","$dynamicTagAttrs":"pt","$createRendererWithOwner":"vt","$createRenderer":"bt","$_clone":"mt","$conditional":"yt","$inConditionalScope":"kt","$conditionalOnlyChild":"wt","$setConditionalRendererOnlyChild":"Ct","$emptyMarkerMap":"At","$emptyMarkerArray":"St","$emptyMap":"Nt","$emptyArray":"xt","$loopOf":"$t","$loopIn":"Mt","$loopTo":"Et","$loop":"It","$inLoopScope":"Tt","$bySecondArg":"_t","$byFirstArg":"Ot","$isDifferentRenderer":"Bt","$classIdToScope":"Vt","$compat":"jt","$noop":"Rt","$createTemplate":"qt","$mount":"Dt","$parseHTMLOrSingleNode":"Wt","$marker":"Ft","$_clickCount_effect":"Qt","$_clickCount":"Yt","$_setup_":"zt","$_expr_comment_comments_id$ifBody":"ss","$_id$ifBody":"as","$_comment_comments$ifBody":"ts","$_ifBody":"ns","$_expr_input_path_i$forBody":"os","$_if$forBody":"cs","$_open$forBody_effect":"is","$_open$forBody":"ms","$_id$forBody":"ls","$_i$forBody":"us","$_comment_comments$forBody":"es","$_comment_text$forBody":"ds","$_comment$forBody":"rs","$_params_2$forBody":"bs","$_input_path$forBody":"ps","$_for":"hs","$_input_path_":"vs","$_input_comments_":"fs","$_input_$1":"js","$_input_":"ks","$_params__":"Ss","$noop2":"Pt","$textContent":"Lt","$normalizeString":"Ut","$contentClosures":"Gt","$_expr_comment_comments_id$if_content":"Zs","$_id$if_content":"$s","$_comment_comments$if_content":"_s","$_if_content":"gs","$_expr_input_path_i$for_content":"ws","$_if$for_content":"xs","$_open$for_content_effect":"Bs","$_open$for_content":"Ds","$_id$for_content":"Es","$_i$for_content":"Hs","$_comment_comments$for_content":"Xs","$_comment_text$for_content":"qs","$_comment$for_content":"ys","$_params_2$for_content":"zs","$_input_path$for_content":"As","$pendingSignal":"Ht","$scopeIsConnected":"Jt","$sortScopeByDOMPosition":"Xt","$ownerStartNode":"Zt","$queueSource":"Kt","$pendingScopes":"en","$loopClosure":"tn","$conditionalClosure":"nn","$queueRender":"rn","$abort":"on","$destroyBranch":"ln","$removeAndDestroyBranch":"un","$createBranchScopeWithRenderer":"fn","$createBranchScopeWithTagNameOrRenderer":"an","$createBranch":"cn","$pendingRender":"sn","$runRenders":"dn","$comparePendingRenders":"hn"}}}
\ No newline at end of file
+{"vars":{"props":{"$empty":"e","$rest":"t","$attrTag":"n","$attrTags":"r","$attrTagIterator":"i","$forIn":"l","$forOf":"o","$forTo":"f","$isScheduled":"u","$port2":"a","$flushAndWaitFrame":"c","$triggerMacroTask":"s","$createScope":"d","$emptyScope":"h","$getEmptyScope":"g","$destroyScope":"p","$_destroyScope":"v","$onDestroy":"b","$removeAndDestroyScope":"m","$insertBefore":"y","$registeredValues":"k","$Render":"w","$isResuming":"C","$register":"A","$registerBoundSignal":"S","$init":"N","$registerSubscriber":"x","$nodeRef":"$","$MARK":"M","$CLEAN":"E","$DIRTY":"I","$state":"T","$value":"_","$accessorId":"O","$intersection":"B","$defaultGetOwnerScope":"V","$closure":"j","$dynamicClosure":"R","$childClosures":"q","$dynamicSubscribers":"D","$setTagVar":"P","$tagVarSignal":"W","$setTagVarChange":"L","$tagVarSignalChange":"z","$renderBodyClosures":"F","$tagIdsByGlobal":"U","$nextTagId":"G","$inChild":"J","$intersections":"X","$effect":"Z","$pendingSignals":"H","$pendingEffects":"K","$rendering":"Q","$queueEffect":"Y","$run":"ee","$prepareEffects":"te","$runEffects":"ne","$runSignals":"re","$resetAbortSignal":"ie","$getAbortSignal":"le","$stringifyClassObject":"oe","$NON_DIMENSIONAL":"fe","$stringifyStyleObject":"ue","$toDelimitedString":"ae","$isEventHandler":"ce","$getEventHandlerName":"se","$normalizeDynamicRenderer":"de","$elementHandlersByEvent":"he","$defaultDelegator":"ge","$on":"pe","$createDelegator":"ve","$handleDelegated":"be","$stripSpacesAndPunctuation":"me","$controllable_input_checked":"ye","$controllable_input_checked_effect":"ke","$controllable_input_checkedValue":"we","$controllable_input_checkedValue_effect":"Ce","$controllable_input_value":"Ae","$controllable_input_value_effect":"Se","$controllable_select_value":"Ne","$controllable_select_value_effect":"xe","$setSelectOptions":"$e","$controllable_detailsOrDialog_open":"Me","$controllable_detailsOrDialog_open_effect":"Ee","$inputType":"Ie","$setValueAndUpdateSelection":"Te","$setCheckboxValue":"_e","$delegateFormControl":"Oe","$formChangeHandlers":"Be","$syncControllable":"Ve","$onFormChange":"je","$onFormReset":"Re","$hasValueChanged":"qe","$hasCheckboxChanged":"De","$hasSelectChanged":"Pe","$hasFormElementChanged":"We","$normalizeStrProp":"Le","$normalizeBoolProp":"ze","$toValueProp":"Fe","$fallback":"Ue","$parser":"Ge","$parseHTML":"Je","$attr":"Xe","$setAttribute":"Ze","$classAttr":"He","$styleAttr":"Ke","$data":"Qe","$attrs":"Ye","$hasAttrAlias":"et","$partialAttrs":"tt","$attrsInternal":"nt","$attrsEvents":"rt","$html":"it","$props":"lt","$normalizeAttrValue":"ot","$lifecycle":"ft","$walker":"ut","$trimWalkString":"at","$walk":"ct","$walkInternal":"st","$createScopeWithRenderer":"dt","$createScopeWithTagNameOrRenderer":"ht","$initRenderer":"gt","$dynamicTagAttrs":"pt","$createRendererWithOwner":"vt","$createRenderer":"bt","$_clone":"mt","$conditional":"yt","$inConditionalScope":"kt","$conditionalOnlyChild":"wt","$setConditionalRendererOnlyChild":"Ct","$emptyMarkerMap":"At","$emptyMarkerArray":"St","$emptyMap":"Nt","$emptyArray":"xt","$loopOf":"$t","$loopIn":"Mt","$loopTo":"Et","$loop":"It","$inLoopScope":"Tt","$bySecondArg":"_t","$byFirstArg":"Ot","$isDifferentRenderer":"Bt","$classIdToScope":"Vt","$compat":"jt","$noop":"Rt","$createTemplate":"qt","$mount":"Dt","$parseHTMLOrSingleNode":"Wt","$marker":"Ft","$_clickCount_effect":"Qt","$_clickCount":"Yt","$_setup_":"zt","$_expr_comment_comments_id$ifBody":"ss","$_id$ifBody":"as","$_comment_comments$ifBody":"ts","$_ifBody":"ns","$_expr_input_path_i$forBody":"os","$_if$forBody":"cs","$_open$forBody_effect":"is","$_open$forBody":"ms","$_id$forBody":"ls","$_i$forBody":"us","$_comment_comments$forBody":"es","$_comment_text$forBody":"ds","$_comment$forBody":"rs","$_params_2$forBody":"bs","$_input_path$forBody":"ps","$_for":"hs","$_input_path_":"vs","$_input_comments_":"fs","$_input_$1":"js","$_input_":"ks","$_params__":"Ss","$noop2":"Pt","$textContent":"Lt","$normalizeString":"Ut","$contentClosures":"Gt","$_expr_comment_comments_id$if_content":"Zs","$_id$if_content":"$s","$_comment_comments$if_content":"_s","$_if_content":"gs","$_expr_input_path_i$for_content":"ws","$_if$for_content":"xs","$_open$for_content_effect":"Bs","$_open$for_content":"Ds","$_id$for_content":"Es","$_i$for_content":"Hs","$_comment_comments$for_content":"Xs","$_comment_text$for_content":"qs","$_comment$for_content":"ys","$_params_2$for_content":"zs","$_input_path$for_content":"As","$pendingSignal":"Ht","$scopeIsConnected":"Jt","$sortScopeByDOMPosition":"Xt","$ownerStartNode":"Zt","$queueSource":"Kt","$pendingScopes":"en","$loopClosure":"tn","$conditionalClosure":"nn","$queueRender":"rn","$abort":"on","$destroyBranch":"ln","$removeAndDestroyBranch":"un","$createBranchScopeWithRenderer":"fn","$createBranchScopeWithTagNameOrRenderer":"an","$createBranch":"cn","$pendingRender":"sn","$runRenders":"dn","$comparePendingRenders":"hn","$doc":"gn"}}}
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/.name-cache.json b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/.name-cache.json
new file mode 100644
index 0000000000..617d85d698
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/.name-cache.json
@@ -0,0 +1,12 @@
+{
+ "vars": {
+ "props": {
+ "$_$": "t",
+ "$init": "r",
+ "$_if_content": "o",
+ "$_if": "e",
+ "$_show_effect": "a",
+ "$_show": "i"
+ }
+ }
+}
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr-sanitized.expected.md b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr-sanitized.expected.md
new file mode 100644
index 0000000000..6d8672e9dc
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr-sanitized.expected.md
@@ -0,0 +1,63 @@
+# Render
+```html
+
+
+```
+
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
+
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
+
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr.expected.md b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr.expected.md
new file mode 100644
index 0000000000..be8722890c
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/csr.expected.md
@@ -0,0 +1,83 @@
+# Render
+```html
+
+
+```
+
+# Mutations
+```
+INSERT table, button
+```
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
+
+# Mutations
+```
+INSERT table/tbody/tr
+REMOVE #text after table/tbody/tr
+```
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
+
+# Mutations
+```
+INSERT table/tbody/#text
+REMOVE tr after table/tbody/#text
+```
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
+
+# Mutations
+```
+INSERT table/tbody/tr
+REMOVE #text after table/tbody/tr
+```
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.hydrate.js b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.hydrate.js
new file mode 100644
index 0000000000..26b263386f
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.hydrate.js
@@ -0,0 +1,15 @@
+// size: 208 (min) 145 (brotli)
+const _if_content = _$.register(
+ "a0",
+ _$.createRenderer("Hi |
", ""),
+ ),
+ _if = _$.conditional(0, 0),
+ _show_effect = _$.effect("a1", (_scope, { 2: show }) =>
+ _$.on(_scope[1], "click", function () {
+ _show(_scope, !show);
+ }),
+ ),
+ _show = _$.state(2, (_scope, show) => {
+ _show_effect(_scope), _if(_scope, show ? _if_content : null);
+ });
+init();
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.js b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.js
new file mode 100644
index 0000000000..56c385fa23
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/dom.expected/template.js
@@ -0,0 +1,18 @@
+export const _template_ = "";
+export const _walks_ = /* next(2), replace, out(2), get, over(1) */"E%m b";
+import * as _$ from "@marko/runtime-tags/debug/dom";
+const _if_content = _$.register("__tests__/template.marko_1_renderer", /* @__PURE__ */_$.createRenderer("Hi |
", ""));
+const _if = /* @__PURE__ */_$.conditional("#text/0", 0);
+const _show_effect = _$.effect("__tests__/template.marko_0_show", (_scope, {
+ show
+}) => _$.on(_scope["#button/1"], "click", function () {
+ _show(_scope, !show);
+}));
+const _show = /* @__PURE__ */_$.state("show", (_scope, show) => {
+ _show_effect(_scope);
+ _if(_scope, show ? _if_content : null);
+});
+export function _setup_(_scope) {
+ _show(_scope, false);
+}
+export default /* @__PURE__ */_$.createTemplate("__tests__/template.marko", _template_, _walks_, _setup_);
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/html.expected/template.js b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/html.expected/template.js
new file mode 100644
index 0000000000..4e74e197f2
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/html.expected/template.js
@@ -0,0 +1,25 @@
+import * as _$ from "@marko/runtime-tags/debug/html";
+const _renderer = /* @__PURE__ */_$.createRenderer((input, _tagVar) => {
+ const _scope0_id = _$.nextScopeId();
+ const show = false;
+ _$.write("");
+ let _ifScopeId, _ifRenderer;
+ _$.resumeSingleNodeConditional(() => {
+ if (show) {
+ const _scope1_id = _$.nextScopeId();
+ _$.write("Hi |
");
+ _$.writeScope(_scope1_id, {});
+ _$.register(_ifRenderer = /* @__PURE__ */_$.createRenderer(() => {}), "__tests__/template.marko_1_renderer");
+ _ifScopeId = _scope1_id;
+ }
+ }, _scope0_id, "#text/0");
+ _$.write(`
${_$.markResumeNode(_scope0_id, "#button/1")}`);
+ _$.writeEffect(_scope0_id, "__tests__/template.marko_0_show");
+ _$.writeScope(_scope0_id, {
+ "show": show,
+ "#text/0(": _ifRenderer,
+ "#text/0!": _$.getScopeById(_ifScopeId)
+ });
+ _$.resumeClosestBranch(_scope0_id);
+});
+export default /* @__PURE__ */_$.createTemplate("__tests__/template.marko", _renderer);
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume-sanitized.expected.md b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume-sanitized.expected.md
new file mode 100644
index 0000000000..6d8672e9dc
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume-sanitized.expected.md
@@ -0,0 +1,63 @@
+# Render
+```html
+
+
+```
+
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
+
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
+
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume.expected.md b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume.expected.md
new file mode 100644
index 0000000000..4604011a08
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/resume.expected.md
@@ -0,0 +1,119 @@
+# Render
+```html
+
+
+
+
+
+
+
+
+
+```
+
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/table/tbody/tr
+REMOVE #comment after html/body/table/tbody/tr
+```
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/table/tbody/#comment
+REMOVE tr after html/body/table/tbody/#comment
+```
+
+# Render
+```js
+container.querySelector("button").click();
+```
+```html
+
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/table/tbody/tr
+REMOVE #comment after html/body/table/tbody/tr
+```
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr-sanitized.expected.md b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr-sanitized.expected.md
new file mode 100644
index 0000000000..ddf0ad5324
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr-sanitized.expected.md
@@ -0,0 +1,9 @@
+# Render End
+```html
+
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr.expected.md b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr.expected.md
new file mode 100644
index 0000000000..6596945f10
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/__snapshots__/ssr.expected.md
@@ -0,0 +1,40 @@
+# Write
+```html
+
+```
+
+# Render End
+```html
+
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html
+INSERT html/head
+INSERT html/body
+INSERT html/body/table
+INSERT html/body/table/tbody
+INSERT html/body/table/tbody/#comment
+INSERT html/body/button
+INSERT html/body/button/#text
+INSERT html/body/#comment
+INSERT html/body/script
+INSERT html/body/script/#text
+```
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/template.marko b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/template.marko
new file mode 100644
index 0000000000..08e5dc131e
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/conditional-table-row/template.marko
@@ -0,0 +1,10 @@
+
+
+