Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .changeset/empty-text-resume.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@marko/runtime-tags": patch
---

Fix resume claiming the wrong DOM node for a placeholder that renders empty on the server (eg `<div/>${value}` where `value` starts empty and is assigned later). Placeholders now serialize a single `_text_resume`/`_html_resume` call which writes an `EmptyText` resume marker when the rendered text is empty, letting resume create the text node instead of guessing a neighbor.

Fix resume of an unescaped placeholder (`$!{value}`) whose markup parses to multiple top-level nodes: the server now brackets the markup with range markers so resume reconstructs the full range (including its last-child accessor), instead of claiming only the final node and orphaning the rest on the first client update.

This also removes the `<!>` separator comments previously written for empty positions and shrinks both the SSR output and the client resume runtime.
20 changes: 10 additions & 10 deletions .sizes.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
{
"name": "*",
"total": {
"min": 26526,
"brotli": 9878
"min": 26586,
"brotli": 9948
}
},
{
Expand All @@ -33,12 +33,12 @@
"brotli": 82
},
"runtime": {
"min": 2650,
"brotli": 1302
"min": 2612,
"brotli": 1282
},
"total": {
"min": 2731,
"brotli": 1384
"min": 2693,
"brotli": 1364
}
},
{
Expand All @@ -63,12 +63,12 @@
"brotli": 101
},
"runtime": {
"min": 2855,
"brotli": 1378
"min": 2817,
"brotli": 1357
},
"total": {
"min": 2970,
"brotli": 1479
"min": 2932,
"brotli": 1458
}
}
]
Expand Down
17 changes: 6 additions & 11 deletions .sizes/comments.ssr/runtime.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// size: 2855 (min) 1378 (brotli)
// size: 2817 (min) 1357 (brotli)
//#region packages/runtime-tags/dist/dom.mjs
let decodeAccessor = (num) => (num + (num < 26 ? 10 : num < 962 ? 334 : 11998)).toString(36),
rendering,
Expand Down Expand Up @@ -206,19 +206,14 @@ function init(runtimeId = "M") {
if ((processResumes(render.r, effects), readyIds));
let retained = 0;
for (visit of (visits = render.v))
if (
((lastTokenIndex = render.i.length),
((lastTokenIndex = render.i.length),
(visitText = visit.data),
(visitType = visitText[lastTokenIndex++]),
(visitScope = getScope(nextToken())),
visitType === "*")
) {
let prev = visit.previousSibling;
visitScope[nextToken()] =
prev && (prev.nodeType < 8 || prev.data)
? prev
: visit.parentNode.insertBefore(new Text(), visit);
}
(visitScope[nextToken()] =
visitType === "$"
? visit.previousSibling
: visit.parentNode.insertBefore(new Text(), visit)));
return ((visits.length = retained), effects);
}),
(render.w = () => {
Expand Down
17 changes: 6 additions & 11 deletions .sizes/counter.ssr/runtime.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// size: 2650 (min) 1302 (brotli)
// size: 2612 (min) 1282 (brotli)
//#region packages/runtime-tags/dist/dom.mjs
let decodeAccessor = (num) => (num + (num < 26 ? 10 : num < 962 ? 334 : 11998)).toString(36),
rendering,
Expand Down Expand Up @@ -203,19 +203,14 @@ function init(runtimeId = "M") {
if ((processResumes(render.r, effects), readyIds));
let retained = 0;
for (visit of (visits = render.v))
if (
((lastTokenIndex = render.i.length),
((lastTokenIndex = render.i.length),
(visitText = visit.data),
(visitType = visitText[lastTokenIndex++]),
(visitScope = getScope(nextToken())),
visitType === "*")
) {
let prev = visit.previousSibling;
visitScope[nextToken()] =
prev && (prev.nodeType < 8 || prev.data)
? prev
: visit.parentNode.insertBefore(new Text(), visit);
}
(visitScope[nextToken()] =
visitType === "$"
? visit.previousSibling
: visit.parentNode.insertBefore(new Text(), visit)));
return ((visits.length = retained), effects);
}),
(render.w = () => {
Expand Down
62 changes: 34 additions & 28 deletions .sizes/dom.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// size: 26526 (min) 9878 (brotli)
// size: 26586 (min) 9948 (brotli)
//#region packages/runtime-tags/dist/dom.mjs
let unsafeStyleAttrReg = /[\\;]/g,
replaceUnsafeStyleAttr = (c) => (c === ";" ? "\\3B " : "\\\\"),
Expand All @@ -19,6 +19,7 @@ let unsafeStyleAttrReg = /[\\;]/g,
},
decodeAccessor = (num) => (num + (num < 26 ? 10 : num < 962 ? 334 : 11998)).toString(36),
branchesEnabled,
dynamicHtmlEnabled,
rendering,
runId = 2,
caughtError = /* @__PURE__ */ new WeakSet(),
Expand Down Expand Up @@ -98,6 +99,19 @@ let unsafeStyleAttrReg = /[\\;]/g,
lazyEnabled,
isResuming,
cloneCache = {},
_html = /*@__PURE__*/ withDynamicHtml(function (scope, value, accessor) {
let firstChild = scope[accessor],
parentNode = firstChild.parentNode,
lastChild = scope["H" + accessor] || firstChild,
newContent = parseHTML(_to_text(value), parentNode.namespaceURI);
(insertChildNodes(
parentNode,
firstChild,
(scope[accessor] = newContent.firstChild || newContent.appendChild(new Text())),
(scope["H" + accessor] = newContent.lastChild),
),
removeChildNodes(firstChild, lastChild));
}),
R = /[\p{L}\p{N}]/gu,
inputType = "",
controllableScripts = {},
Expand Down Expand Up @@ -400,6 +414,9 @@ function normalizeDynamicRenderer(value) {
function withBranches(runtime) {
return ((branchesEnabled = 1), runtime);
}
function withDynamicHtml(runtime) {
return ((dynamicHtmlEnabled = 1), runtime);
}
function _hoist_read_error() {}
function _assert_hoist(value) {}
function forIn(obj, cb) {
Expand Down Expand Up @@ -953,7 +970,7 @@ function init(runtimeId = "M") {
);
((branch._ ??= visitScope),
(branch.K = branch.S = startVisit),
visitType === "'" && (branch.a = startVisit));
visitType === "(" && (branch.a = startVisit));
} else
((curBranchScopes = push(curBranchScopes, branch)),
accessor &&
Expand Down Expand Up @@ -1028,6 +1045,7 @@ function init(runtimeId = "M") {
lastToken,
lastTokenIndex,
visitBranches,
htmlStart,
embedAnchor;
return (
(serializeContext._ = registeredValues),
Expand All @@ -1042,22 +1060,23 @@ function init(runtimeId = "M") {
}
let retained = 0;
for (visit of (visits = render.v))
if (
((lastTokenIndex = render.i.length),
((lastTokenIndex = render.i.length),
(visitText = visit.data),
(visitType = visitText[lastTokenIndex++]),
(visitScope = getScope(nextToken())),
visitType === "*")
) {
let prev = visit.previousSibling;
visitScope[nextToken()] =
prev && (prev.nodeType < 8 || prev.data)
? prev
: visit.parentNode.insertBefore(new Text(), visit);
} else
branchesEnabled
? (visitBranches ||= createVisitBranches())()
: lazyEnabled && render.b && (visits[retained++] = visit);
dynamicHtmlEnabled && visitType > "%" && visitType <= "'"
? visitType === "&"
? (htmlStart = visit)
: ((visitScope[nextToken()] = htmlStart),
(visitScope["H" + lastToken] = visit))
: branchesEnabled && visitType > "'"
? (visitBranches ||= createVisitBranches())()
: lazyEnabled && render.b && visitType > "%"
? (visits[retained++] = visit)
: (visitScope[nextToken()] =
visitType === "$"
? visit.previousSibling
: visit.parentNode.insertBefore(new Text(), visit)));
return (
embedRenders &&
!embedAnchor &&
Expand Down Expand Up @@ -1313,19 +1332,6 @@ function _attrs_script(scope, nodeAccessor) {
controllableScripts[scope["F" + nodeAccessor]]?.(scope, nodeAccessor);
for (let name in events) _on(el, name, events[name]);
}
function _html(scope, value, accessor) {
let firstChild = scope[accessor],
parentNode = firstChild.parentNode,
lastChild = scope["H" + accessor] || firstChild,
newContent = parseHTML(_to_text(value), parentNode.namespaceURI);
(insertChildNodes(
parentNode,
firstChild,
(scope[accessor] = newContent.firstChild || newContent.appendChild(new Text())),
(scope["H" + accessor] = newContent.lastChild),
),
removeChildNodes(firstChild, lastChild));
}
function normalizeClientRender(value) {
let renderer = normalizeDynamicRenderer(value);
if (renderer && renderer.a) return renderer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"dom": {
"template.marko.page.mjs": {
"total": {
"min": 55532,
"brotli": 17663
"min": 55495,
"brotli": 17664
},
"files": {
"components/custom-tag.marko": 626,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"dom": {
"template.marko.page.mjs": {
"total": {
"min": 64372,
"brotli": 20114
"min": 64335,
"brotli": 20078
},
"files": {
"components/tags-child.marko": 499,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var tags_child_default = _template("__tests__/components/tags-child.marko", (inp
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags>${_escape(count)}${_el_resume($scope0_id, "#text/1")}</button>${_el_resume($scope0_id, "#button/0")}`);
_html(`<button id=tags>${_text_resume($scope0_id, "#text/1", count)}</button>${_el_resume($scope0_id, "#button/0")}`);
_script($scope0_id, "__tests__/components/tags-child.marko_0");
writeScope($scope0_id, {
input_data: input.data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var template_default = _template("__tests__/template.marko", (input) => {
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags>${_escape(count)}${_el_resume($scope0_id, "#text/1")}</button>${_el_resume($scope0_id, "#button/0")}`);
_html(`<button id=tags>${_text_resume($scope0_id, "#text/1", count)}</button>${_el_resume($scope0_id, "#button/0")}`);
_dynamic_tag($scope0_id, "#text/2", _marko_template, {
onSelect: _resume(function() {
count++;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<button
id=tags>0<!--M_*1 #text/1--></button><!--M_*1 #button/0--><!--M_[--><!--M#_0-->
id=tags>0<!--M_$1 #text/1--></button><!--M_$1 #button/0--><!--M_[--><!--M#_0-->
<ul id=list>
<li><button>one*</button><!--F#1--><em>first</em><!--F/--></li>
<li><button>two</button><!--F#1--><em>second</em><!--F/--></li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"dom": {
"template.marko.page.mjs": {
"total": {
"min": 66710,
"brotli": 20984
"min": 66673,
"brotli": 20953
},
"files": {
"components/tags-child.marko": 624,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var tags_counter_default = _template("__tests__/components/tags-counter.marko",
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags${_attr("data-parent", input.count)}>${_escape(count)}${_el_resume($scope0_id, "#text/1")}</button>${_el_resume($scope0_id, "#button/0")}`);
_html(`<button id=tags${_attr("data-parent", input.count)}>${_text_resume($scope0_id, "#text/1", count)}</button>${_el_resume($scope0_id, "#button/0")}`);
_script($scope0_id, "__tests__/components/tags-counter.marko_0");
writeScope($scope0_id, { count }, "__tests__/components/tags-counter.marko", 0, { count: "1:6" });
_resume_branch($scope0_id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var tags_counter_default = _template("b", (input) => {
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags${_attr("data-parent", input.count)}>${_escape(count)}${_el_resume($scope0_id, "b")}</button>${_el_resume($scope0_id, "a")}`);
_html(`<button id=tags${_attr("data-parent", input.count)}>${_text_resume($scope0_id, "b", count)}</button>${_el_resume($scope0_id, "a")}`);
_script($scope0_id, "b0");
writeScope($scope0_id, { f: count });
_resume_branch($scope0_id);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--M#s0--><button id=class>0</button><!--F#1--><button id=tags
data-parent=0>0<!--Ms*1 #text/1--></button><!--Ms*1 #button/0--><!--F/--><!--M/-->
data-parent=0>0<!--Ms$1 #text/1--></button><!--Ms$1 #button/0--><!--F/--><!--M/-->
<script>
WALKER_RUNTIME("M")("s");
M.s.r = [_ => [1, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--M#s0--><button id=class>0</button><!--F#1--><button id=tags
data-parent=0>0<!--Ms*1 b--></button><!--Ms*1 a--><!--F/--><!--M/-->
data-parent=0>0<!--Ms$1 b--></button><!--Ms$1 a--><!--F/--><!--M/-->
<script>
WALKER_RUNTIME("M")("s");
M.s.r = [_ => [1, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"dom": {
"template.marko.page.mjs": {
"total": {
"min": 64603,
"brotli": 20165
"min": 64566,
"brotli": 20188
},
"files": {
"components/tags-counter.marko": 682,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var template_default = _template("__tests__/template.marko", (input) => {
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags>${_escape(count)}${_el_resume($scope0_id, "#text/1")}</button>${_el_resume($scope0_id, "#button/0")}`);
_html(`<button id=tags>${_text_resume($scope0_id, "#text/1", count)}</button>${_el_resume($scope0_id, "#button/0")}`);
_dynamic_tag($scope0_id, "#text/2", _marko_template, { count });
_script($scope0_id, "__tests__/template.marko_0");
writeScope($scope0_id, { count }, "__tests__/template.marko", 0, { count: "1:6" });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var template_default = _template("a", (input) => {
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags>${_escape(count)}${_el_resume($scope0_id, "b")}</button>${_el_resume($scope0_id, "a")}`);
_html(`<button id=tags>${_text_resume($scope0_id, "b", count)}</button>${_el_resume($scope0_id, "a")}`);
_dynamic_tag($scope0_id, "c", _marko_template, { count });
_script($scope0_id, "a0");
writeScope($scope0_id, { d: count });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<button
id=tags>0<!--M_*1 #text/1--></button><!--M_*1 #button/0--><!--M_[--><!--M#_0--><button
id=tags>0<!--M_$1 #text/1--></button><!--M_$1 #button/0--><!--M_[--><!--M#_0--><button
id=class data-parent=0>0</button><!--M/--><!--M_]1 #text/2 2-->
<script>
WALKER_RUNTIME("M")("_");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<button id=tags>0<!--M_*1 b--></button><!--M_*1 a--><!--M_[--><!--M#_0--><button
<button id=tags>0<!--M_$1 b--></button><!--M_$1 a--><!--M_[--><!--M#_0--><button
id=class data-parent=0>0</button><!--M/--><!--M_]1 c 2-->
<script>
WALKER_RUNTIME("M")("_");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"dom": {
"template.marko.page.mjs": {
"total": {
"min": 63843,
"brotli": 19913
"min": 63806,
"brotli": 19876
},
"files": {
"components/class-counter.marko": 1028,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var template_default = _template("__tests__/template.marko", (input) => {
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags>${_escape(count)}${_el_resume($scope0_id, "#text/1")}</button>${_el_resume($scope0_id, "#button/0")}`);
_html(`<button id=tags>${_text_resume($scope0_id, "#text/1", count)}</button>${_el_resume($scope0_id, "#button/0")}`);
_dynamic_tag($scope0_id, "#text/2", _marko_template, { value: "Hello World" }, 0, 0, 0);
_script($scope0_id, "__tests__/template.marko_0");
writeScope($scope0_id, { count }, "__tests__/template.marko", 0, { count: "1:6" });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var template_default = _template("a", (input) => {
_scope_reason();
const $scope0_id = _scope_id();
let count = 0;
_html(`<button id=tags>${_escape(count)}${_el_resume($scope0_id, "b")}</button>${_el_resume($scope0_id, "a")}`);
_html(`<button id=tags>${_text_resume($scope0_id, "b", count)}</button>${_el_resume($scope0_id, "a")}`);
_dynamic_tag($scope0_id, "c", _marko_template, { value: "Hello World" }, 0, 0, 0);
_script($scope0_id, "a0");
writeScope($scope0_id, { d: count });
Expand Down
Loading
Loading