diff --git a/.changeset/funny-eyes-rescue.md b/.changeset/funny-eyes-rescue.md
new file mode 100644
index 0000000000..5fe80d5ade
--- /dev/null
+++ b/.changeset/funny-eyes-rescue.md
@@ -0,0 +1,6 @@
+---
+"@marko/translator-interop-class-tags": patch
+"marko": patch
+---
+
+Update tags api compat layer.
diff --git a/.changeset/thick-parents-vanish.md b/.changeset/thick-parents-vanish.md
new file mode 100644
index 0000000000..1896931cf1
--- /dev/null
+++ b/.changeset/thick-parents-vanish.md
@@ -0,0 +1,5 @@
+---
+"@marko/runtime-tags": patch
+---
+
+Support namespaced tags (svg/math).
diff --git a/.sizes.json b/.sizes.json
index bea2028700..cfb21259b7 100644
--- a/.sizes.json
+++ b/.sizes.json
@@ -7,8 +7,8 @@
{
"name": "*",
"total": {
- "min": 18085,
- "brotli": 6624
+ "min": 18364,
+ "brotli": 6749
}
},
{
@@ -18,12 +18,12 @@
"brotli": 144
},
"runtime": {
- "min": 4178,
- "brotli": 1803
+ "min": 4409,
+ "brotli": 1898
},
"total": {
- "min": 4367,
- "brotli": 1947
+ "min": 4598,
+ "brotli": 2042
}
},
{
@@ -34,11 +34,11 @@
},
"runtime": {
"min": 3688,
- "brotli": 1661
+ "brotli": 1656
},
"total": {
"min": 3799,
- "brotli": 1761
+ "brotli": 1756
}
},
{
@@ -48,12 +48,12 @@
"brotli": 541
},
"runtime": {
- "min": 7879,
- "brotli": 3253
+ "min": 8145,
+ "brotli": 3363
},
"total": {
- "min": 9019,
- "brotli": 3794
+ "min": 9285,
+ "brotli": 3904
}
},
{
@@ -63,12 +63,12 @@
"brotli": 478
},
"runtime": {
- "min": 8972,
- "brotli": 3643
+ "min": 9253,
+ "brotli": 3772
},
"total": {
- "min": 9920,
- "brotli": 4121
+ "min": 10201,
+ "brotli": 4250
}
}
]
diff --git a/.sizes/dom.js b/.sizes/dom.js
index 8ebaa48269..227713563b 100644
--- a/.sizes/dom.js
+++ b/.sizes/dom.js
@@ -1,4 +1,4 @@
-// size: 18085 (min) 6624 (brotli)
+// size: 18364 (min) 6749 (brotli)
var empty = [],
rest = Symbol();
function attrTag(attrs2) {
@@ -128,10 +128,10 @@ var registeredValues = {},
Render = class {
n = [];
o = {};
- z = { _: registeredValues };
+ A = { _: registeredValues };
constructor(renders, runtimeId, renderId) {
- (this.A = renders),
- (this.B = runtimeId),
+ (this.B = renders),
+ (this.C = runtimeId),
(this.p = renderId),
(this.q = renders[renderId]),
this.s();
@@ -141,7 +141,7 @@ var registeredValues = {},
}
s() {
let data2 = this.q,
- serializeContext = this.z,
+ serializeContext = this.A,
scopeLookup = this.o,
visits = data2.v,
branchIds = new Set(),
@@ -226,7 +226,7 @@ var registeredValues = {},
{ $global: $global } = scopeLookup;
$global ||
((scopeLookup.$global = $global = scopes.$ || {}),
- ($global.runtimeId = this.B),
+ ($global.runtimeId = this.C),
($global.renderId = this.p));
for (let scopeId in scopes)
if ("$" !== scopeId) {
@@ -251,7 +251,7 @@ var registeredValues = {},
}
} else
i === len || "string" != typeof resumes[i]
- ? delete this.A[this.p]
+ ? delete this.B[this.p]
: registeredValues[resumes[i++]](
scopeLookup[resumeData],
scopeLookup[resumeData],
@@ -294,7 +294,7 @@ function init(runtimeId = "M") {
});
}
function registerSubscriber(id, signal) {
- return register(id, signal.C), signal;
+ return register(id, signal.D), signal;
}
function nodeRef(id, key) {
return register(id, (scope) => () => scope[key]);
@@ -554,9 +554,11 @@ function normalizeBoolProp(value2) {
function toValueProp(it) {
return it.value;
}
-var parser = document.createElement("template");
-function parseHTML(html2) {
- return (parser.innerHTML = html2), parser.content;
+var parsers = {};
+function parseHTML(html2, ns) {
+ let parser = (parsers[ns] ||= document.createElementNS(ns, "template")),
+ content = ((parser.innerHTML = html2), parser.content || parser);
+ return content.firstChild || content.appendChild(new Text()), content;
}
function attr(element, name, value2) {
setAttribute(element, name, normalizeAttrValue(value2));
@@ -727,11 +729,18 @@ function attrsEvents(scope, nodeAccessor) {
}
function html(scope, value2, accessor) {
let firstChild = scope[accessor],
+ parentNode = firstChild.parentNode,
lastChild = scope[accessor + "-"] || firstChild,
- newContent = parseHTML(value2 || 0 === value2 ? value2 + "" : "");
- (scope[accessor] = newContent.firstChild),
+ newContent = parseHTML(
+ value2 || 0 === value2 ? value2 + "" : "",
+ parentNode.namespaceURI,
+ );
+ insertChildNodes(
+ parentNode,
+ firstChild,
+ (scope[accessor] = newContent.firstChild),
(scope[accessor + "-"] = newContent.lastChild),
- firstChild.parentNode.insertBefore(newContent, firstChild),
+ ),
removeChildNodes(firstChild, lastChild);
}
function props(scope, nodeIndex, index) {
@@ -766,6 +775,20 @@ function removeChildNodes(startNode, endNode) {
current.remove(), (current = next);
}
}
+function insertChildNodes(parentNode, referenceNode, startNode, endNode) {
+ parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
+}
+function toInsertNode(startNode, endNode) {
+ if (startNode === endNode) return startNode;
+ let parent = new DocumentFragment(),
+ stop = endNode.nextSibling,
+ current = startNode;
+ for (; current !== stop; ) {
+ let next = current.nextSibling;
+ parent.appendChild(current), (current = next);
+ }
+ return parent;
+}
var pendingScopes = [];
function createScope($global) {
let scope = { g: 1, $global: $global };
@@ -779,9 +802,9 @@ function destroyBranch(branch) {
branch.t?.k?.delete(branch), destroyNestedBranches(branch);
}
function destroyNestedBranches(branch) {
- (branch.D = 1),
+ (branch.E = 1),
branch.k?.forEach(destroyNestedBranches),
- branch.E?.forEach((scope) => {
+ branch.F?.forEach((scope) => {
for (let id in scope.h) scope.h[id]?.abort();
});
}
@@ -789,12 +812,7 @@ function removeAndDestroyBranch(branch) {
destroyBranch(branch), removeChildNodes(branch.a, branch.b);
}
function insertBranchBefore(branch, parentNode, nextSibling) {
- let current = branch.a,
- stop = branch.b.nextSibling;
- for (; current !== stop; ) {
- let next = current.nextSibling;
- parentNode.insertBefore(current, nextSibling), (current = next);
- }
+ insertChildNodes(parentNode, nextSibling, branch.a, branch.b);
}
var walker = document.createTreeWalker(document);
function trimWalkString(walkString) {
@@ -851,27 +869,41 @@ function walkInternal(walkCodes, scope, currentWalkIndex) {
}
return currentWalkIndex;
}
-function createBranchScopeWithRenderer(renderer, $global, parentScope) {
+function createBranchScopeWithRenderer(
+ renderer,
+ $global,
+ parentScope,
+ parentNode,
+) {
let branch = createBranch($global, renderer.u || parentScope, parentScope);
- return initBranch(renderer, branch), branch;
+ return initBranch(renderer, branch, parentNode), branch;
}
function createBranchScopeWithTagNameOrRenderer(
tagNameOrRenderer,
$global,
parentScope,
+ parentNode,
) {
if ("string" != typeof tagNameOrRenderer)
return createBranchScopeWithRenderer(
tagNameOrRenderer,
$global,
parentScope,
+ parentNode,
);
let branch = createBranch($global, parentScope, parentScope);
return (
(branch[0] =
branch.a =
branch.b =
- document.createElement(tagNameOrRenderer)),
+ document.createElementNS(
+ "svg" === tagNameOrRenderer
+ ? "http://www.w3.org/2000/svg"
+ : "math" === tagNameOrRenderer
+ ? "http://www.w3.org/1998/Math/MathML"
+ : parentNode.namespaceURI,
+ tagNameOrRenderer,
+ )),
branch
);
}
@@ -889,15 +921,13 @@ function createBranch($global, ownerScope, parentScope) {
branch
);
}
-function initBranch(renderer, branch) {
- let dom = renderer.l();
- return (
- walk(11 === dom.nodeType ? dom.firstChild : dom, renderer.F, branch),
- (branch.a = 11 === dom.nodeType ? dom.firstChild : dom),
- (branch.b = 11 === dom.nodeType ? dom.lastChild : dom),
- renderer.x && queueRender(branch, renderer.x),
- dom
- );
+function initBranch(renderer, branch, parentNode) {
+ let clone = renderer.l(parentNode.namespaceURI),
+ cloneParent = clone.parentNode;
+ walk(cloneParent?.firstChild || clone, renderer.G, branch),
+ (branch.a = cloneParent?.firstChild || clone),
+ (branch.b = cloneParent?.lastChild || clone),
+ renderer.x && queueRender(branch, renderer.x);
}
function dynamicTagAttrs(nodeAccessor, getContent, inputIsArgs) {
return (scope, attrsOrOp) => {
@@ -927,12 +957,12 @@ function createRendererWithOwner(template, rawWalks, setup, getArgs) {
walks = rawWalks ? trimWalkString(rawWalks) : " ";
return (owner) => ({
j: id,
- G: template,
- F: walks,
+ y: template,
+ G: walks,
x: setup,
l: _clone,
u: owner,
- H: void 0,
+ J: void 0,
get d() {
return (args ||= getArgs?.());
},
@@ -941,21 +971,19 @@ function createRendererWithOwner(template, rawWalks, setup, getArgs) {
function createRenderer(template, walks, setup, getArgs) {
return createRendererWithOwner(template, walks, setup, getArgs)();
}
-function _clone() {
- return (this.H ||= (function (html2) {
- let content = parseHTML(html2);
- if (content.firstChild) {
- if (
- content.firstChild === content.lastChild &&
- 8 !== content.firstChild.nodeType
- )
- return content.firstChild;
- let fragment = new DocumentFragment();
- return fragment.appendChild(content), fragment;
- }
- return new Text();
- })(this.G)).cloneNode(!0);
+function _clone(ns) {
+ return ((cloneCache[ns] ||= {})[this.y] ||= (function (html2, ns) {
+ let { firstChild: firstChild, lastChild: lastChild } = parseHTML(html2, ns),
+ parent = document.createElementNS(ns, "t");
+ return (
+ insertChildNodes(parent, null, firstChild, lastChild),
+ firstChild === lastChild && firstChild.nodeType < 8
+ ? () => firstChild.cloneNode(!0)
+ : () => parent.cloneNode(!0).firstChild
+ );
+ })(this.y, ns))();
}
+var cloneCache = {};
var conditional = function (nodeAccessor, fn, getIntersection) {
let rendererAccessor = nodeAccessor + "(",
intersection2 =
@@ -978,6 +1006,7 @@ var conditional = function (nodeAccessor, fn, getIntersection) {
newRenderer,
scope.$global,
scope,
+ prevBranch.b.parentNode,
)
: getEmptyBranch(scope[nodeAccessor]);
insertBranchBefore(
@@ -1028,6 +1057,7 @@ function setConditionalRendererOnlyChild(scope, nodeAccessor, newRenderer) {
newRenderer,
scope.$global,
scope,
+ referenceNode,
)
: void 0;
(referenceNode.textContent = ""),
@@ -1074,29 +1104,30 @@ function loop(nodeAccessor, renderer, forEach) {
let newMap,
newArray,
afterReference,
- parentNode,
referenceNode = scope[nodeAccessor],
- referenceIsMarker =
- 8 === referenceNode.nodeType || 3 === referenceNode.nodeType,
+ referenceIsMarker = referenceNode.nodeType > 1,
oldMap =
scope[nodeAccessor + "("] ||
(referenceIsMarker ? emptyMarkerMap : emptyMap),
oldArray = scope[nodeAccessor + "!"] || Array.from(oldMap.values()),
+ parentNode = referenceIsMarker
+ ? referenceNode.parentNode || oldArray[0].a.parentNode
+ : referenceNode,
needsReconciliation = !0;
- if (
- (forEach(valueOrOp, (key, args) => {
- let branch = oldMap.get(key);
- branch ||
- (branch = createBranchScopeWithRenderer(
- renderer,
- scope.$global,
- scope,
- )),
- params && params(branch, args),
- newMap
- ? (newMap.set(key, branch), newArray.push(branch))
- : ((newMap = new Map([[key, branch]])), (newArray = [branch]));
- }),
+ forEach(valueOrOp, (key, args) => {
+ let branch = oldMap.get(key);
+ branch ||
+ (branch = createBranchScopeWithRenderer(
+ renderer,
+ scope.$global,
+ scope,
+ parentNode,
+ )),
+ params && params(branch, args),
+ newMap
+ ? (newMap.set(key, branch), newArray.push(branch))
+ : ((newMap = new Map([[key, branch]])), (newArray = [branch]));
+ }),
newMap ||
(referenceIsMarker
? ((newMap = emptyMarkerMap),
@@ -1107,134 +1138,141 @@ function loop(nodeAccessor, renderer, forEach) {
(newMap = emptyMap),
(newArray = emptyArray),
(needsReconciliation = !1))),
- needsReconciliation)
- ) {
- if (referenceIsMarker) {
- oldMap === emptyMarkerMap && getEmptyBranch(referenceNode);
- let oldLastChild = oldArray[oldArray.length - 1];
- (afterReference = oldLastChild.b.nextSibling),
- (parentNode = oldLastChild.a.parentNode);
- } else (afterReference = null), (parentNode = referenceNode);
- !(function (parent, oldBranches, newBranches, afterReference) {
- let i,
- j,
- k,
- nextSibling,
- oldBranch,
- newBranch,
- oldStart = 0,
- newStart = 0,
- oldEnd = oldBranches.length - 1,
- newEnd = newBranches.length - 1,
- oldStartBranch = oldBranches[oldStart],
- newStartBranch = newBranches[newStart],
- oldEndBranch = oldBranches[oldEnd],
- newEndBranch = newBranches[newEnd];
- outer: {
- for (; oldStartBranch === newStartBranch; ) {
- if (
- (++oldStart, ++newStart, oldStart > oldEnd || newStart > newEnd)
- )
- break outer;
- (oldStartBranch = oldBranches[oldStart]),
- (newStartBranch = newBranches[newStart]);
- }
- for (; oldEndBranch === newEndBranch; ) {
- if ((--oldEnd, --newEnd, oldStart > oldEnd || newStart > newEnd))
- break outer;
- (oldEndBranch = oldBranches[oldEnd]),
- (newEndBranch = newBranches[newEnd]);
+ needsReconciliation &&
+ (referenceIsMarker
+ ? (oldMap === emptyMarkerMap && getEmptyBranch(referenceNode),
+ (afterReference = oldArray[oldArray.length - 1].b.nextSibling))
+ : (afterReference = null),
+ (function (parent, oldBranches, newBranches, afterReference) {
+ let i,
+ j,
+ k,
+ nextSibling,
+ oldBranch,
+ newBranch,
+ oldStart = 0,
+ newStart = 0,
+ oldEnd = oldBranches.length - 1,
+ newEnd = newBranches.length - 1,
+ oldStartBranch = oldBranches[oldStart],
+ newStartBranch = newBranches[newStart],
+ oldEndBranch = oldBranches[oldEnd],
+ newEndBranch = newBranches[newEnd];
+ outer: {
+ for (; oldStartBranch === newStartBranch; ) {
+ if (
+ (++oldStart, ++newStart, oldStart > oldEnd || newStart > newEnd)
+ )
+ break outer;
+ (oldStartBranch = oldBranches[oldStart]),
+ (newStartBranch = newBranches[newStart]);
+ }
+ for (; oldEndBranch === newEndBranch; ) {
+ if ((--oldEnd, --newEnd, oldStart > oldEnd || newStart > newEnd))
+ break outer;
+ (oldEndBranch = oldBranches[oldEnd]),
+ (newEndBranch = newBranches[newEnd]);
+ }
}
- }
- if (oldStart > oldEnd) {
- if (newStart <= newEnd) {
- (k = newEnd + 1),
- (nextSibling =
- k < newBranches.length ? newBranches[k].a : afterReference);
+ if (oldStart > oldEnd) {
+ if (newStart <= newEnd) {
+ (k = newEnd + 1),
+ (nextSibling =
+ k < newBranches.length ? newBranches[k].a : afterReference);
+ do {
+ insertBranchBefore(
+ newBranches[newStart++],
+ parent,
+ nextSibling,
+ );
+ } while (newStart <= newEnd);
+ }
+ } else if (newStart > newEnd)
do {
- insertBranchBefore(newBranches[newStart++], parent, nextSibling);
- } while (newStart <= newEnd);
- }
- } else if (newStart > newEnd)
- do {
- removeAndDestroyBranch(oldBranches[oldStart++]);
- } while (oldStart <= oldEnd);
- else {
- let oldLength = oldEnd - oldStart + 1,
- newLength = newEnd - newStart + 1,
- aNullable = oldBranches,
- sources = new Array(newLength);
- for (i = 0; i < newLength; ++i) sources[i] = -1;
- let pos = 0,
- synced = 0,
- keyIndex = new Map();
- for (j = newStart; j <= newEnd; ++j) keyIndex.set(newBranches[j], j);
- for (i = oldStart; i <= oldEnd && synced < newLength; ++i)
- (oldBranch = oldBranches[i]),
- (j = keyIndex.get(oldBranch)),
- void 0 !== j &&
- ((pos = pos > j ? 2147483647 : j),
- ++synced,
- (newBranch = newBranches[j]),
- (sources[j - newStart] = i),
- (aNullable[i] = null));
- if (oldLength === oldBranches.length && 0 === synced) {
- for (; newStart < newLength; ++newStart)
- insertBranchBefore(newBranches[newStart], parent, afterReference);
- for (; oldStart < oldLength; ++oldStart)
- removeAndDestroyBranch(oldBranches[oldStart]);
- } else {
- for (i = oldLength - synced; i > 0; )
- (oldBranch = aNullable[oldStart++]),
- null !== oldBranch && (removeAndDestroyBranch(oldBranch), i--);
- if (2147483647 === pos) {
- let seq = (function (a) {
- let u,
- v,
- p = a.slice(),
- result = [];
- result.push(0);
- for (let i = 0, il = a.length; i < il; ++i) {
- if (-1 === a[i]) continue;
- let j = result[result.length - 1];
- if (a[j] < a[i]) (p[i] = j), result.push(i);
- else {
- for (u = 0, v = result.length - 1; u < v; ) {
- let c = ((u + v) / 2) | 0;
- a[result[c]] < a[i] ? (u = c + 1) : (v = c);
+ removeAndDestroyBranch(oldBranches[oldStart++]);
+ } while (oldStart <= oldEnd);
+ else {
+ let oldLength = oldEnd - oldStart + 1,
+ newLength = newEnd - newStart + 1,
+ aNullable = oldBranches,
+ sources = new Array(newLength);
+ for (i = 0; i < newLength; ++i) sources[i] = -1;
+ let pos = 0,
+ synced = 0,
+ keyIndex = new Map();
+ for (j = newStart; j <= newEnd; ++j)
+ keyIndex.set(newBranches[j], j);
+ for (i = oldStart; i <= oldEnd && synced < newLength; ++i)
+ (oldBranch = oldBranches[i]),
+ (j = keyIndex.get(oldBranch)),
+ void 0 !== j &&
+ ((pos = pos > j ? 2147483647 : j),
+ ++synced,
+ (newBranch = newBranches[j]),
+ (sources[j - newStart] = i),
+ (aNullable[i] = null));
+ if (oldLength === oldBranches.length && 0 === synced) {
+ for (; newStart < newLength; ++newStart)
+ insertBranchBefore(
+ newBranches[newStart],
+ parent,
+ afterReference,
+ );
+ for (; oldStart < oldLength; ++oldStart)
+ removeAndDestroyBranch(oldBranches[oldStart]);
+ } else {
+ for (i = oldLength - synced; i > 0; )
+ (oldBranch = aNullable[oldStart++]),
+ null !== oldBranch &&
+ (removeAndDestroyBranch(oldBranch), i--);
+ if (2147483647 === pos) {
+ let seq = (function (a) {
+ let u,
+ v,
+ p = a.slice(),
+ result = [];
+ result.push(0);
+ for (let i = 0, il = a.length; i < il; ++i) {
+ if (-1 === a[i]) continue;
+ let j = result[result.length - 1];
+ if (a[j] < a[i]) (p[i] = j), result.push(i);
+ else {
+ for (u = 0, v = result.length - 1; u < v; ) {
+ let c = ((u + v) / 2) | 0;
+ a[result[c]] < a[i] ? (u = c + 1) : (v = c);
+ }
+ a[i] < a[result[u]] &&
+ (u > 0 && (p[i] = result[u - 1]), (result[u] = i));
}
- a[i] < a[result[u]] &&
- (u > 0 && (p[i] = result[u - 1]), (result[u] = i));
}
- }
- for (u = result.length, v = result[u - 1]; u-- > 0; )
- (result[u] = v), (v = p[v]);
- return result;
- })(sources);
- for (
- j = seq.length - 1, k = newBranches.length, i = newLength - 1;
- i >= 0;
- --i
- )
- -1 === sources[i] || j < 0 || i !== seq[j]
- ? ((pos = i + newStart),
+ for (u = result.length, v = result[u - 1]; u-- > 0; )
+ (result[u] = v), (v = p[v]);
+ return result;
+ })(sources);
+ for (
+ j = seq.length - 1, k = newBranches.length, i = newLength - 1;
+ i >= 0;
+ --i
+ )
+ -1 === sources[i] || j < 0 || i !== seq[j]
+ ? ((pos = i + newStart),
+ (newBranch = newBranches[pos++]),
+ (nextSibling =
+ pos < k ? newBranches[pos].a : afterReference),
+ insertBranchBefore(newBranch, parent, nextSibling))
+ : --j;
+ } else if (synced !== newLength)
+ for (k = newBranches.length, i = newLength - 1; i >= 0; --i)
+ -1 === sources[i] &&
+ ((pos = i + newStart),
(newBranch = newBranches[pos++]),
(nextSibling =
pos < k ? newBranches[pos].a : afterReference),
- insertBranchBefore(newBranch, parent, nextSibling))
- : --j;
- } else if (synced !== newLength)
- for (k = newBranches.length, i = newLength - 1; i >= 0; --i)
- -1 === sources[i] &&
- ((pos = i + newStart),
- (newBranch = newBranches[pos++]),
- (nextSibling = pos < k ? newBranches[pos].a : afterReference),
- insertBranchBefore(newBranch, parent, nextSibling));
+ insertBranchBefore(newBranch, parent, nextSibling));
+ }
}
- }
- })(parentNode, oldArray, newArray, afterReference);
- }
- (scope[nodeAccessor + "("] = newMap),
+ })(parentNode, oldArray, newArray, afterReference)),
+ (scope[nodeAccessor + "("] = newMap),
(scope[nodeAccessor + "!"] = newArray);
};
}
@@ -1382,7 +1420,7 @@ function dynamicClosure(
(helperSignal._ = (scope, value2) => {
_signal(scope, value2), subscribe(scope);
}),
- (helperSignal.C = subscribe),
+ (helperSignal.D = subscribe),
helperSignal
);
}
@@ -1436,7 +1474,7 @@ function queueSource(scope, signal, value2) {
}
function queueRender(scope, signal, value2) {
let i = pendingRenders.length,
- render = { m: scope, I: signal, J: value2, y: i };
+ render = { m: scope, H: signal, I: value2, z: i };
for (pendingRenders.push(render); i; ) {
let parentIndex = (i - 1) >> 1,
parent = pendingRenders[parentIndex];
@@ -1502,7 +1540,7 @@ function runRenders() {
}
pendingRenders[i] = item;
}
- render.m.c?.D || render.I(render.m, render.J);
+ render.m.c?.E || render.H(render.m, render.I);
}
!(function () {
for (let scope of pendingScopes) scope.g = 0;
@@ -1510,7 +1548,7 @@ function runRenders() {
})();
}
function comparePendingRenders(a, b) {
- return getBranchDepth(a) - getBranchDepth(b) || a.y - b.y;
+ return getBranchDepth(a) - getBranchDepth(b) || a.z - b.z;
}
function getBranchDepth(render) {
return render.m.c?.f || 0;
@@ -1521,7 +1559,7 @@ function resetAbortSignal(scope, id) {
}
function getAbortSignal(scope, id) {
return (
- scope.c && (scope.c.E ||= new Set()).add(scope),
+ scope.c && (scope.c.F ||= new Set()).add(scope),
((scope.h ||= {})[id] ||= new AbortController()).signal
);
}
@@ -1596,12 +1634,12 @@ var classIdToBranch = new Map(),
? (applyArgs(branch, MARK), (existing = !0))
: ((branch = component.scope = createScope(out.global)),
(branch._ = renderer.u),
- initBranch(renderer, branch)),
+ initBranch(renderer, branch, document.body)),
applyArgs(branch, args);
})),
!existing)
)
- return branch.a === branch.b ? branch.a : branch.a.parentNode;
+ return toInsertNode(branch.a, branch.b);
},
};
function noop() {}
@@ -1615,7 +1653,6 @@ var createTemplate = (templateId, ...rendererArgs) => {
};
function mount(input = {}, reference, position) {
let branch,
- dom,
parentNode = reference,
nextSibling = null,
{ $global: $global } = input;
@@ -1639,11 +1676,11 @@ function mount(input = {}, reference, position) {
let args = this.d,
effects = prepareEffects(() => {
(branch = createScope($global)),
- (dom = initBranch(this, branch)),
+ initBranch(this, branch, parentNode),
args?.(branch, [input]);
});
return (
- parentNode.insertBefore(dom, nextSibling),
+ insertChildNodes(parentNode, nextSibling, branch.a, branch.b),
runEffects(effects),
{
update: (newInput) => {
diff --git a/.sizes/name-cache.json b/.sizes/name-cache.json
index 051e6cddbf..4160627b9f 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","$doc":"gn","$pendingRenders":"pn","$getBranchId":"bn","$getBranchDepth":"vn","$initBranch":"yn","$emptyBranch":"mn","$getEmptyBranch":"kn","$insertBranchBefore":"Cn","$destroyNestedBranches":"wn","$removeChildNodes":"An","$classIdToBranch":"Sn"}}}
\ 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","$pendingRenders":"pn","$getBranchId":"bn","$getBranchDepth":"vn","$initBranch":"yn","$emptyBranch":"mn","$getEmptyBranch":"kn","$insertBranchBefore":"Cn","$destroyNestedBranches":"wn","$removeChildNodes":"An","$classIdToBranch":"Sn","$getParseKind":"Nn","$insertChildNodes":"xn","$toInsertNode":"$n","$parseCache":"Mn","$parseHTMLChild":"In","$parsers":"_n","$cloneCache":"En"}}}
\ No newline at end of file
diff --git a/packages/runtime-class/src/runtime/helpers/tags-compat/runtime-dom.js b/packages/runtime-class/src/runtime/helpers/tags-compat/runtime-dom.js
index cff5cd4f55..ff64397a93 100644
--- a/packages/runtime-class/src/runtime/helpers/tags-compat/runtime-dom.js
+++ b/packages/runtime-class/src/runtime/helpers/tags-compat/runtime-dom.js
@@ -121,20 +121,13 @@ exports.p = function (domCompat) {
};
}
newRenderer = domCompat.createRenderer(
- (scope) => {
- if (skipAttrs) {
- renderAndMorph(scope, rendererFromAnywhere, renderer, {});
- }
- },
- () => {
- const realFragment = document.createDocumentFragment();
- ___createFragmentNode(null, null, realFragment);
- return realFragment;
- },
- (scope, input) => {
- if (domCompat.isOp(input)) return;
- renderAndMorph(scope, rendererFromAnywhere, renderer, input);
- },
+ (scope) =>
+ skipAttrs &&
+ renderAndMorph(scope, rendererFromAnywhere, renderer, {}),
+ () => ___createFragmentNode().startNode,
+ (scope, input) =>
+ domCompat.isOp(input) ||
+ renderAndMorph(scope, rendererFromAnywhere, renderer, input),
);
rendererCache.set(renderer, newRenderer);
}
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/.name-cache.json b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/.name-cache.json
new file mode 100644
index 0000000000..6df0d6a808
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/.name-cache.json
@@ -0,0 +1,25 @@
+{
+ "vars": {
+ "props": {
+ "$_$": "e",
+ "$init": "t",
+ "$_Child_content2": "r",
+ "$_Child_content": "i",
+ "$_input_value$Parent_content": "n",
+ "$_setup$Parent_content": "a",
+ "$_Parent_content": "c",
+ "$_expr_Parent_Child_effect": "o",
+ "$_expr_Parent_Child": "s",
+ "$_Parent_input": "m",
+ "$_dynamicTagName3": "d",
+ "$_Child_input2": "f",
+ "$_dynamicTagName2": "l",
+ "$_Child_input": "g",
+ "$_dynamicTagName": "u",
+ "$_Child_effect": "h",
+ "$_Child": "b",
+ "$_Parent_effect": "y",
+ "$_Parent": "A"
+ }
+ }
+}
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/csr-sanitized.expected.md b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/csr-sanitized.expected.md
new file mode 100644
index 0000000000..52cc3776c7
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/csr-sanitized.expected.md
@@ -0,0 +1,326 @@
+# Render `{"value":" "}`
+
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+ Hi
+
+
+
+ Hi
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+ Hi
+
+
+
+ Hi
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/csr.expected.md b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/csr.expected.md
new file mode 100644
index 0000000000..b7c3c81ba2
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/csr.expected.md
@@ -0,0 +1,390 @@
+# Render `{"value":" "}`
+
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+# Mutations
+```
+INSERT div
+UPDATE div/svg/a0[ns] null => "http://www.w3.org/2000/svg"
+UPDATE div/svg/a1[ns] null => "http://www.w3.org/2000/svg"
+UPDATE div/math/a0[ns] null => "http://www.w3.org/1998/Math/MathML"
+UPDATE div/math/a1[ns] null => "http://www.w3.org/1998/Math/MathML"
+UPDATE div/div/a[ns] null => "http://www.w3.org/1999/xhtml"
+```
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+# Mutations
+```
+INSERT div/svg1
+REMOVE div after div/math
+INSERT #text
+INSERT div/svg1/a
+REMOVE #text after div/svg1/a
+UPDATE div/svg1/a[ns] null => "http://www.w3.org/2000/svg"
+```
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+# Mutations
+```
+INSERT div/div
+REMOVE svg after div/math
+INSERT #text
+INSERT div/div/a
+REMOVE #text after div/div/a
+UPDATE div/div/a[ns] null => "http://www.w3.org/1999/xhtml"
+```
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+# Mutations
+```
+INSERT div/svg1
+REMOVE div after div/math
+INSERT #text
+INSERT div/svg1/a
+REMOVE #text after div/svg1/a
+UPDATE div/svg1/a[ns] null => "http://www.w3.org/2000/svg"
+```
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+ Hi
+
+
+
+ Hi
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+# Mutations
+```
+INSERT div/svg0/#text
+REMOVE a after div/svg0/a
+INSERT div/math/#text
+REMOVE a after div/math/a
+```
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+# Mutations
+```
+INSERT div/svg0/a1
+REMOVE #text after div/svg0/a0
+INSERT div/svg0/a1/#text
+UPDATE div/svg0/a1[href] null => "#bar"
+INSERT div/math/a1
+REMOVE #text after div/math/a0
+INSERT div/math/a1/#text
+UPDATE div/math/a1[href] null => "#bar"
+UPDATE div/svg0/a1[ns] null => "http://www.w3.org/2000/svg"
+UPDATE div/math/a1[ns] null => "http://www.w3.org/1998/Math/MathML"
+```
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+ Hi
+
+
+
+ Hi
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+# Mutations
+```
+INSERT div/svg0/#text
+REMOVE a after div/svg0/a
+INSERT div/math/#text
+REMOVE a after div/math/a
+```
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/dom.expected/template.hydrate.js b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/dom.expected/template.hydrate.js
new file mode 100644
index 0000000000..49ecf55890
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/dom.expected/template.hydrate.js
@@ -0,0 +1,72 @@
+// size: 1030 (min) 429 (brotli)
+const _Child_content2 = _$.register("a0", _$.createRendererWithOwner("Hi", "")),
+ _Child_content = _$.register("a1", _$.createRendererWithOwner("Hi", "")),
+ _input_value$Parent_content = _$.registerSubscriber(
+ "a2",
+ _$.dynamicClosure((_scope, input_value) => _$.html(_scope, input_value, 0)),
+ ),
+ _setup$Parent_content = (_scope) => {
+ _input_value$Parent_content._(_scope, _scope._[10]);
+ },
+ _Parent_content = _$.register(
+ "a3",
+ _$.createRendererWithOwner(" ", " ", _setup$Parent_content),
+ ),
+ _expr_Parent_Child_effect = _$.effect(
+ "a4",
+ (_scope, { 11: Parent, 12: Child }) => {
+ for (const node of _scope[0].querySelectorAll("a"))
+ node.getAttribute("ns") !== node.namespaceURI &&
+ node.setAttribute("ns", node.namespaceURI);
+ },
+ ),
+ _expr_Parent_Child = _$.intersection(2, (_scope) => {
+ _expr_Parent_Child_effect(_scope);
+ }),
+ _Parent_input = _$.dynamicTagAttrs(5, _Parent_content),
+ _dynamicTagName3 = _$.conditional(
+ 5,
+ (_scope) => _Parent_input(_scope, () => ({})),
+ () => _Parent_input,
+ ),
+ _Child_input2 = _$.dynamicTagAttrs(4, _Child_content2),
+ _dynamicTagName2 = _$.conditional(
+ 4,
+ (_scope) => _Child_input2(_scope, () => ({ href: "#bar" })),
+ () => _Child_input2,
+ ),
+ _Child_input = _$.dynamicTagAttrs(2, _Child_content),
+ _dynamicTagName = _$.conditional(
+ 2,
+ (_scope) => _Child_input(_scope, () => ({ href: "#bar" })),
+ () => _Child_input,
+ ),
+ _Child_effect = _$.effect("a5", (_scope, { 12: Child }) =>
+ _$.on(_scope[7], "click", function () {
+ _Child(_scope, "a" === Child ? null : "a");
+ }),
+ ),
+ _Child = _$.state(
+ 12,
+ (_scope, Child) => {
+ _Child_effect(_scope),
+ _dynamicTagName(_scope, Child || _Child_content(_scope)),
+ _dynamicTagName2(_scope, Child || _Child_content2(_scope));
+ },
+ () =>
+ _$.intersections([_expr_Parent_Child, _dynamicTagName, _dynamicTagName2]),
+ ),
+ _Parent_effect = _$.effect("a6", (_scope, { 11: Parent }) =>
+ _$.on(_scope[6], "click", function () {
+ _Parent(_scope, "div" === Parent ? "svg" : "div");
+ }),
+ ),
+ _Parent = _$.state(
+ 11,
+ (_scope, Parent) => {
+ _Parent_effect(_scope),
+ _dynamicTagName3(_scope, Parent || _Parent_content(_scope));
+ },
+ () => _$.intersections([_expr_Parent_Child, _dynamicTagName3]),
+ );
+init();
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/dom.expected/template.js b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/dom.expected/template.js
new file mode 100644
index 0000000000..2f90aea6ec
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/dom.expected/template.js
@@ -0,0 +1,70 @@
+export const _template_ = "Toggle Parent Toggle Child
";
+export const _walks_ = /* get, next(2), replace, over(1), replace, out(1), next(1), replace, over(1), replace, out(1), replace, over(1), get, over(1), get, out(1) */" E%b%lD%b%l%b b l";
+import * as _$ from "@marko/runtime-tags/debug/dom";
+const _Child_content2 = _$.register("__tests__/template.marko_3_renderer", /* @__PURE__ */_$.createRendererWithOwner("Hi", ""));
+const _Child_content = _$.register("__tests__/template.marko_2_renderer", /* @__PURE__ */_$.createRendererWithOwner("Hi", ""));
+const _input_value$Parent_content = _$.registerSubscriber("__tests__/template.marko_1_input_value/subscriber", /* @__PURE__ */_$.dynamicClosure((_scope, input_value) => _$.html(_scope, input_value, "#text/0")));
+const _setup$Parent_content = _scope => {
+ _input_value$Parent_content._(_scope, _scope._["input_value"]);
+};
+const _Parent_content = _$.register("__tests__/template.marko_1_renderer", /* @__PURE__ */_$.createRendererWithOwner(" ", /* get */" ", _setup$Parent_content));
+const _expr_Parent_Child_effect = _$.effect("__tests__/template.marko_0_Parent_Child", (_scope, {
+ Parent,
+ Child
+}) => {
+ Parent;
+ Child;
+ for (const node of _scope["#div/0"].querySelectorAll("a")) {
+ if (node.getAttribute("ns") !== node.namespaceURI) {
+ node.setAttribute("ns", node.namespaceURI);
+ }
+ }
+});
+const _expr_Parent_Child = /* @__PURE__ */_$.intersection(2, _scope => {
+ const {
+ Parent,
+ Child
+ } = _scope;
+ _expr_Parent_Child_effect(_scope);
+});
+const _Parent_input = _$.dynamicTagAttrs("#text/5", _Parent_content);
+const _dynamicTagName3 = /* @__PURE__ */_$.conditional("#text/5", _scope => _Parent_input(_scope, () => ({})), () => _Parent_input);
+const _Child_input2 = _$.dynamicTagAttrs("#text/4", _Child_content2);
+const _dynamicTagName2 = /* @__PURE__ */_$.conditional("#text/4", _scope => _Child_input2(_scope, () => ({
+ href: "#bar"
+})), () => _Child_input2);
+const _Child_input = _$.dynamicTagAttrs("#text/2", _Child_content);
+const _dynamicTagName = /* @__PURE__ */_$.conditional("#text/2", _scope => _Child_input(_scope, () => ({
+ href: "#bar"
+})), () => _Child_input);
+const _Child_effect = _$.effect("__tests__/template.marko_0_Child", (_scope, {
+ Child
+}) => _$.on(_scope["#button/7"], "click", function () {
+ _Child(_scope, Child === "a" ? null : "a");
+}));
+const _Child = /* @__PURE__ */_$.state("Child", (_scope, Child) => {
+ _Child_effect(_scope);
+ _dynamicTagName(_scope, Child || _Child_content(_scope));
+ _dynamicTagName2(_scope, Child || _Child_content2(_scope));
+}, () => _$.intersections([_expr_Parent_Child, _dynamicTagName, _dynamicTagName2]));
+const _Parent_effect = _$.effect("__tests__/template.marko_0_Parent", (_scope, {
+ Parent
+}) => _$.on(_scope["#button/6"], "click", function () {
+ _Parent(_scope, Parent === "div" ? "svg" : "div");
+}));
+const _Parent = /* @__PURE__ */_$.state("Parent", (_scope, Parent) => {
+ _Parent_effect(_scope);
+ _dynamicTagName3(_scope, Parent || _Parent_content(_scope));
+}, () => _$.intersections([_expr_Parent_Child, _dynamicTagName3]));
+export const _input_value_ = /* @__PURE__ */_$.value("input_value", (_scope, input_value) => {
+ _$.html(_scope, input_value, "#text/1");
+ _$.html(_scope, input_value, "#text/3");
+ _input_value$Parent_content(_scope, input_value);
+});
+export const _input_ = /* @__PURE__ */_$.value("input", (_scope, input) => _input_value_(_scope, input.value));
+export const _params__ = /* @__PURE__ */_$.value("_params_", (_scope, _params_) => _input_(_scope, _params_[0]));
+export function _setup_(_scope) {
+ _Parent(_scope, "div");
+ _Child(_scope, "a");
+}
+export default /* @__PURE__ */_$.createTemplate("__tests__/template.marko", _template_, _walks_, _setup_, () => _params__);
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/html.expected/template.js b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/html.expected/template.js
new file mode 100644
index 0000000000..74e09bafa2
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/html.expected/template.js
@@ -0,0 +1,54 @@
+import * as _$ from "@marko/runtime-tags/debug/html";
+const _renderer = /* @__PURE__ */_$.createRenderer((input, _tagVar) => {
+ const _scope0_id = _$.nextScopeId();
+ const Parent = "div";
+ const Child = "a";
+ const el = _$.nodeRef();
+ _$.write(`${_$.toString(input.value)}${_$.markResumeNode(_scope0_id, "#text/1")}`);
+ const _dynamicScope = _$.peekNextScope();
+ _$.dynamicTagInput(_scope0_id, "#text/2", Child, {
+ href: "#bar"
+ }, _$.register(/* @__PURE__ */_$.createRenderer(() => {
+ const _scope2_id = _$.nextScopeId();
+ _$.write("Hi");
+ }), "__tests__/template.marko_2_renderer", _scope0_id));
+ _$.write(` ${_$.toString(input.value)}${_$.markResumeNode(_scope0_id, "#text/3")}`);
+ const _dynamicScope2 = _$.peekNextScope();
+ _$.dynamicTagInput(_scope0_id, "#text/4", Child, {
+ href: "#bar"
+ }, _$.register(/* @__PURE__ */_$.createRenderer(() => {
+ const _scope3_id = _$.nextScopeId();
+ _$.write("Hi");
+ }), "__tests__/template.marko_3_renderer", _scope0_id));
+ _$.write(" ");
+ const _dynamicScope3 = _$.peekNextScope();
+ _$.dynamicTagInput(_scope0_id, "#text/5", Parent, {}, _$.register(/* @__PURE__ */_$.createRenderer(() => {
+ const _scope1_id = _$.nextScopeId();
+ _$.write(`${_$.toString(input.value)}${_$.markResumeNode(_scope1_id, "#text/0")}`);
+ _$.writeEffect(_scope1_id, "__tests__/template.marko_1_input_value/subscriber");
+ _$.debug(_$.writeScope(_scope1_id, {
+ "_": _$.ensureScopeWithId(_scope0_id)
+ }), "__tests__/template.marko", "12:3");
+ _$.resumeClosestBranch(_scope1_id);
+ }), "__tests__/template.marko_1_renderer", _scope0_id));
+ _$.write(`Toggle Parent ${_$.markResumeNode(_scope0_id, "#button/6")}Toggle Child ${_$.markResumeNode(_scope0_id, "#button/7")}
${_$.markResumeNode(_scope0_id, "#div/0")}`);
+ _$.writeEffect(_scope0_id, "__tests__/template.marko_0_Parent_Child");
+ _$.writeEffect(_scope0_id, "__tests__/template.marko_0_Child");
+ _$.writeEffect(_scope0_id, "__tests__/template.marko_0_Parent");
+ _$.debug(_$.writeScope(_scope0_id, {
+ "input_value": input.value,
+ "Parent": Parent,
+ "Child": Child,
+ "#text/2!": _$.writeExistingScope(_dynamicScope),
+ "#text/2(": _$.normalizeDynamicRenderer(Child),
+ "#text/4!": _$.writeExistingScope(_dynamicScope2),
+ "#text/4(": _$.normalizeDynamicRenderer(Child),
+ "#text/5!": _$.writeExistingScope(_dynamicScope3),
+ "#text/5(": _$.normalizeDynamicRenderer(Parent)
+ }), "__tests__/template.marko", 0, {
+ "Parent": "1:5",
+ "Child": "2:5"
+ });
+ _$.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/namespaced-tags/__snapshots__/resume-sanitized.expected.md b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/resume-sanitized.expected.md
new file mode 100644
index 0000000000..52cc3776c7
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/resume-sanitized.expected.md
@@ -0,0 +1,326 @@
+# Render `{"value":" "}`
+
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+ Hi
+
+
+
+ Hi
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
+
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+ Hi
+
+
+
+ Hi
+
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/resume.expected.md b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/resume.expected.md
new file mode 100644
index 0000000000..04814f03ce
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/resume.expected.md
@@ -0,0 +1,503 @@
+# Render `{"value":" "}`
+
+```html
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+UPDATE html/body/div/svg/a0[ns] null => "http://www.w3.org/2000/svg"
+UPDATE html/body/div/svg/a1[ns] null => "http://www.w3.org/2000/svg"
+UPDATE html/body/div/math/a0[ns] null => "http://www.w3.org/1998/Math/MathML"
+UPDATE html/body/div/math/a1[ns] null => "http://www.w3.org/1998/Math/MathML"
+UPDATE html/body/div/div/a[ns] null => "http://www.w3.org/1999/xhtml"
+```
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/div/svg1
+REMOVE div after html/body/div/math
+INSERT #text
+INSERT html/body/div/svg1/a
+REMOVE #text after html/body/div/svg1/a
+UPDATE html/body/div/svg1/a[ns] null => "http://www.w3.org/2000/svg"
+```
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/div/div
+REMOVE svg after html/body/div/math
+INSERT #text
+INSERT html/body/div/div/a
+REMOVE #text after html/body/div/div/a
+UPDATE html/body/div/div/a[ns] null => "http://www.w3.org/1999/xhtml"
+```
+
+# Render
+```js
+container.querySelector(".toggle-parent").click();
+```
+```html
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/div/svg1
+REMOVE div after html/body/div/math
+INSERT #text
+INSERT html/body/div/svg1/a
+REMOVE #text after html/body/div/svg1/a
+UPDATE html/body/div/svg1/a[ns] null => "http://www.w3.org/2000/svg"
+```
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/div/svg0/#text
+REMOVE a after html/body/div/svg0/#comment0
+INSERT html/body/div/math/#text
+REMOVE a after html/body/div/math/#comment0
+```
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/div/svg0/a1
+REMOVE #text after html/body/div/svg0/#comment0
+INSERT html/body/div/svg0/a1/#text
+UPDATE html/body/div/svg0/a1[href] null => "#bar"
+INSERT html/body/div/math/a1
+REMOVE #text after html/body/div/math/#comment0
+INSERT html/body/div/math/a1/#text
+UPDATE html/body/div/math/a1[href] null => "#bar"
+UPDATE html/body/div/svg0/a1[ns] null => "http://www.w3.org/2000/svg"
+UPDATE html/body/div/math/a1[ns] null => "http://www.w3.org/1998/Math/MathML"
+```
+
+# Render
+```js
+container.querySelector(".toggle-child").click();
+```
+```html
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html/body/div/svg0/#text
+REMOVE a after html/body/div/svg0/#comment0
+INSERT html/body/div/math/#text
+REMOVE a after html/body/div/math/#comment0
+```
\ No newline at end of file
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/ssr-sanitized.expected.md b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/ssr-sanitized.expected.md
new file mode 100644
index 0000000000..4f2546fc1a
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/ssr-sanitized.expected.md
@@ -0,0 +1,40 @@
+# Render End
+```html
+
+
+
+
+ Hi
+
+
+
+
+
+ Hi
+
+
+
+
+ Toggle Parent
+
+
+ Toggle Child
+
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/ssr.expected.md b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/ssr.expected.md
new file mode 100644
index 0000000000..600e3d7a1f
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/__snapshots__/ssr.expected.md
@@ -0,0 +1,97 @@
+# Write
+```html
+ Hi Hi Toggle Parent Toggle Child
+```
+
+# Render End
+```html
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+
+ Hi
+
+
+
+
+
+
+ Toggle Parent
+
+
+
+ Toggle Child
+
+
+
+
+
+
+
+```
+
+# Mutations
+```
+INSERT html
+INSERT html/head
+INSERT html/body
+INSERT html/body/div
+INSERT html/body/div/svg
+INSERT html/body/div/svg/a0
+INSERT html/body/div/svg/#comment0
+INSERT html/body/div/svg/a1
+INSERT html/body/div/svg/a1/#text
+INSERT html/body/div/svg/#comment1
+INSERT html/body/div/math
+INSERT html/body/div/math/a0
+INSERT html/body/div/math/#comment0
+INSERT html/body/div/math/a1
+INSERT html/body/div/math/a1/#text
+INSERT html/body/div/math/#comment1
+INSERT html/body/div/div
+INSERT html/body/div/div/a
+INSERT html/body/div/div/#comment0
+INSERT html/body/div/div/#comment1
+INSERT html/body/div/#comment0
+INSERT html/body/div/button0
+INSERT html/body/div/button0/#text
+INSERT html/body/div/#comment1
+INSERT html/body/div/button1
+INSERT html/body/div/button1/#text
+INSERT html/body/div/#comment2
+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/namespaced-tags/template.marko b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/template.marko
new file mode 100644
index 0000000000..403a5ebd68
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/template.marko
@@ -0,0 +1,30 @@
+let/Parent = "div"
+let/Child = "a"
+div/el
+ svg
+ -- $!{input.value}
+ Child href="#bar" -- Hi
+
+ math
+ -- $!{input.value}
+ Child href="#bar" -- Hi
+
+ Parent --
+ $!{input.value}
+
+ button.toggle-parent onClick() {
+ Parent = Parent === "div" ? "svg" : "div";
+ } -- Toggle Parent
+
+ button.toggle-child onClick() {
+ Child = Child === "a" ? null : "a";
+ } -- Toggle Child
+
+script --
+ Parent;
+ Child;
+ for (const node of el().querySelectorAll("a")) {
+ if (node.getAttribute("ns") !== node.namespaceURI) {
+ node.setAttribute("ns", node.namespaceURI!);
+ }
+ }
diff --git a/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/test.ts b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/test.ts
new file mode 100644
index 0000000000..e3821e93a4
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/namespaced-tags/test.ts
@@ -0,0 +1,19 @@
+export const steps = [
+ {
+ value: " ",
+ },
+ clickParent,
+ clickParent,
+ clickParent,
+ clickChild,
+ clickChild,
+ clickChild,
+];
+
+function clickParent(container: Element) {
+ (container.querySelector(".toggle-parent") as HTMLButtonElement).click();
+}
+
+function clickChild(container: Element) {
+ (container.querySelector(".toggle-child") as HTMLButtonElement).click();
+}
diff --git a/packages/runtime-tags/src/dom/compat.ts b/packages/runtime-tags/src/dom/compat.ts
index def2302346..ceaf9ba071 100644
--- a/packages/runtime-tags/src/dom/compat.ts
+++ b/packages/runtime-tags/src/dom/compat.ts
@@ -4,12 +4,12 @@ import {
SET_SCOPE_REGISTER_ID,
} from "../common/compat-meta";
import type { BranchScope } from "../common/types";
-import { createScope } from "../dom";
import { patchConditionals } from "./control-flow";
+import { toInsertNode } from "./dom";
import { prepareEffects, queueEffect, runEffects } from "./queue";
import { createRenderer, initBranch, type Renderer } from "./renderer";
import { getRegisteredWithScope, register } from "./resume";
-import { destroyBranch } from "./scope";
+import { createScope, destroyBranch } from "./scope";
import { CLEAN, DIRTY, MARK } from "./signals";
const classIdToBranch = new Map();
@@ -101,7 +101,7 @@ export const compat = {
// TODO: this should be createBranch
branch = component.scope = createScope(out.global) as BranchScope;
branch._ = renderer.___owner;
- initBranch(renderer, branch);
+ initBranch(renderer, branch, document.body);
} else {
applyArgs(branch, MARK);
existing = true;
@@ -110,9 +110,7 @@ export const compat = {
});
if (!existing) {
- return branch.___startNode === branch.___endNode
- ? branch.___startNode
- : branch.___startNode.parentNode;
+ return toInsertNode(branch.___startNode, branch.___endNode);
}
},
};
diff --git a/packages/runtime-tags/src/dom/control-flow.ts b/packages/runtime-tags/src/dom/control-flow.ts
index 46317f8afe..8f71536a55 100644
--- a/packages/runtime-tags/src/dom/control-flow.ts
+++ b/packages/runtime-tags/src/dom/control-flow.ts
@@ -4,6 +4,7 @@ import {
type Accessor,
AccessorChar,
type BranchScope,
+ NodeType,
type Scope,
} from "../common/types";
import { reconcile } from "./reconcile";
@@ -73,7 +74,12 @@ export function setConditionalRenderer(
(scope[nodeAccessor + AccessorChar.ConditionalScope] as BranchScope) ||
getEmptyBranch(scope[nodeAccessor] as Comment);
const newBranch = newRenderer
- ? createBranchScopeWithTagNameOrRenderer(newRenderer, scope.$global, scope)
+ ? createBranchScopeWithTagNameOrRenderer(
+ newRenderer,
+ scope.$global,
+ scope,
+ prevBranch.___endNode.parentNode!,
+ )
: (getEmptyBranch(scope[nodeAccessor] as Comment) as BranchScope);
insertBranchBefore(
newBranch,
@@ -134,7 +140,12 @@ export function setConditionalRendererOnlyChild(
] as BranchScope;
const referenceNode = scope[nodeAccessor] as Element;
const newBranch = newRenderer
- ? createBranchScopeWithTagNameOrRenderer(newRenderer, scope.$global, scope)
+ ? createBranchScopeWithTagNameOrRenderer(
+ newRenderer,
+ scope.$global,
+ scope,
+ referenceNode,
+ )
: undefined;
referenceNode.textContent = "";
@@ -214,10 +225,7 @@ function loop(
}
const referenceNode = scope[nodeAccessor] as Element | Comment | Text;
- // TODO: compiler should use only comment so the text check can be removed
- const referenceIsMarker =
- referenceNode.nodeType === 8 /* Comment */ ||
- referenceNode.nodeType === 3; /* Text */
+ const referenceIsMarker = referenceNode.nodeType > NodeType.Element;
const oldMap =
(scope[nodeAccessor + AccessorChar.LoopScopeMap] as Map<
unknown,
@@ -226,16 +234,25 @@ function loop(
const oldArray =
(scope[nodeAccessor + AccessorChar.LoopScopeArray] as BranchScope[]) ||
Array.from(oldMap.values());
+ const parentNode = (
+ referenceIsMarker
+ ? referenceNode.parentNode || oldArray[0].___startNode.parentNode
+ : referenceNode
+ ) as Element;
let newMap!: Map;
let newArray!: BranchScope[];
let afterReference: Node | null;
- let parentNode: ParentNode;
let needsReconciliation = true;
forEach(valueOrOp, (key, args) => {
let branch = oldMap.get(key);
if (!branch) {
- branch = createBranchScopeWithRenderer(renderer, scope.$global, scope);
+ branch = createBranchScopeWithRenderer(
+ renderer,
+ scope.$global,
+ scope,
+ parentNode,
+ );
// TODO: once we can track moves
// needsReconciliation = true;
} else {
@@ -277,10 +294,8 @@ function loop(
}
const oldLastChild = oldArray[oldArray.length - 1];
afterReference = oldLastChild.___endNode.nextSibling;
- parentNode = oldLastChild.___startNode.parentNode!;
} else {
afterReference = null;
- parentNode = referenceNode as Element;
}
reconcile(parentNode, oldArray, newArray!, afterReference);
}
diff --git a/packages/runtime-tags/src/dom/dom.ts b/packages/runtime-tags/src/dom/dom.ts
index f6c9df15e2..ff71144bd3 100644
--- a/packages/runtime-tags/src/dom/dom.ts
+++ b/packages/runtime-tags/src/dom/dom.ts
@@ -8,7 +8,6 @@ import {
type Accessor,
AccessorChar,
ControlledType,
- NodeType,
type Scope,
} from "../common/types";
import { getAbortSignal } from "./abort-signal";
@@ -28,10 +27,6 @@ import {
import { on } from "./event";
import { parseHTML } from "./parse-html";
-export function isDocumentFragment(node: Node): node is DocumentFragment {
- return node.nodeType === NodeType.DocumentFragment;
-}
-
export function attr(element: Element, name: string, value: unknown) {
setAttribute(element, name, normalizeAttrValue(value));
}
@@ -261,17 +256,22 @@ export function attrsEvents(scope: Scope, nodeAccessor: Accessor) {
export function html(scope: Scope, value: unknown, accessor: Accessor) {
const firstChild = scope[accessor] as ChildNode;
+ const parentNode = firstChild.parentNode!;
const lastChild = (scope[
accessor + AccessorChar.DynamicPlaceholderLastChild
] || firstChild) as ChildNode;
const newContent = parseHTML(
- value || value === 0 ? value + "" : "", // TODO: is the comment needed
+ value || value === 0 ? value + "" : "",
+ (parentNode as Element).namespaceURI!,
);
- scope[accessor] = newContent.firstChild;
- scope[accessor + AccessorChar.DynamicPlaceholderLastChild] =
- newContent.lastChild;
- firstChild.parentNode!.insertBefore(newContent, firstChild);
+ insertChildNodes(
+ parentNode,
+ firstChild,
+ (scope[accessor] = newContent.firstChild),
+ (scope[accessor + AccessorChar.DynamicPlaceholderLastChild] =
+ newContent.lastChild),
+ );
removeChildNodes(firstChild, lastChild);
}
@@ -336,3 +336,26 @@ export function removeChildNodes(startNode: ChildNode, endNode: ChildNode) {
current = next!;
}
}
+
+export function insertChildNodes(
+ parentNode: ParentNode,
+ referenceNode: Node | null,
+ startNode: Node,
+ endNode: Node,
+) {
+ parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
+}
+
+export function toInsertNode(startNode: Node, endNode: Node) {
+ if (startNode === endNode) return startNode;
+ const parent = new DocumentFragment();
+ const stop = endNode.nextSibling;
+ let current = startNode;
+ while (current !== stop) {
+ const next = current.nextSibling;
+ parent.appendChild(current);
+ current = next!;
+ }
+
+ return parent;
+}
diff --git a/packages/runtime-tags/src/dom/parse-html.ts b/packages/runtime-tags/src/dom/parse-html.ts
index 7a22621be4..b9e7322c4b 100644
--- a/packages/runtime-tags/src/dom/parse-html.ts
+++ b/packages/runtime-tags/src/dom/parse-html.ts
@@ -1,27 +1,12 @@
-const parser = /* @__PURE__ */ document.createElement("template");
-
-export function parseHTML(html: string) {
- parser.innerHTML = html;
- return parser.content;
-}
-
-export function parseHTMLOrSingleNode(html: string) {
- const content = parseHTML(html);
- if (content.firstChild) {
- if (
- content.firstChild === content.lastChild &&
- // If the firstChild is a comment it's possible its
- // a single replaced node, in which case the walker can't replace
- // the node itself.
- content.firstChild.nodeType !== 8 /* Node.COMMENT_NODE */
- ) {
- return content.firstChild;
- }
-
- const fragment = new DocumentFragment();
- fragment.appendChild(content);
- return fragment;
- }
-
- return new Text();
+const parsers: Record = {};
+export function parseHTML(html: string, ns: string) {
+ const parser = (parsers[ns] ||= document.createElementNS(ns, "template"));
+ const content =
+ ((parser.innerHTML = html),
+ (parser as HTMLTemplateElement).content || parser);
+ if (!content.firstChild) content.appendChild(new Text());
+ return content as (DocumentFragment | Element) & {
+ firstChild: ChildNode;
+ lastChild: ChildNode;
+ };
}
diff --git a/packages/runtime-tags/src/dom/renderer.ts b/packages/runtime-tags/src/dom/renderer.ts
index ce43dc316b..eaa036f075 100644
--- a/packages/runtime-tags/src/dom/renderer.ts
+++ b/packages/runtime-tags/src/dom/renderer.ts
@@ -6,8 +6,8 @@ import {
type Scope,
} from "../common/types";
import { setConditionalRendererOnlyChild } from "./control-flow";
-import { attrs } from "./dom";
-import { parseHTMLOrSingleNode } from "./parse-html";
+import { attrs, insertChildNodes } from "./dom";
+import { parseHTML } from "./parse-html";
import { queueRender } from "./queue";
import { createScope } from "./scope";
import { CLEAN, DIRTY, MARK, type Signal, type SignalOp } from "./signals";
@@ -18,7 +18,7 @@ export type Renderer = {
___template: string;
___walks: string;
___setup: SetupFn | undefined;
- ___clone: () => Node;
+ ___clone: (ns: string) => ChildNode;
___sourceNode: Node | undefined;
___args: Signal | undefined;
___owner: Scope | undefined;
@@ -30,6 +30,7 @@ export function createBranchScopeWithRenderer(
renderer: Renderer,
$global: Scope["$global"],
parentScope: Scope,
+ parentNode: ParentNode,
) {
const branch = createBranch(
$global,
@@ -39,7 +40,7 @@ export function createBranchScopeWithRenderer(
if (MARKO_DEBUG) {
branch.___renderer = renderer;
}
- initBranch(renderer, branch);
+ initBranch(renderer, branch, parentNode);
return branch;
}
@@ -47,12 +48,14 @@ export function createBranchScopeWithTagNameOrRenderer(
tagNameOrRenderer: Renderer | string,
$global: Scope["$global"],
parentScope: Scope,
+ parentNode: ParentNode,
) {
if (typeof tagNameOrRenderer !== "string") {
return createBranchScopeWithRenderer(
tagNameOrRenderer,
$global,
parentScope,
+ parentNode,
);
}
@@ -60,7 +63,14 @@ export function createBranchScopeWithTagNameOrRenderer(
branch[MARKO_DEBUG ? `#${tagNameOrRenderer}/0` : 0] =
branch.___startNode =
branch.___endNode =
- document.createElement(tagNameOrRenderer);
+ document.createElementNS(
+ tagNameOrRenderer === "svg"
+ ? "http://www.w3.org/2000/svg"
+ : tagNameOrRenderer === "math"
+ ? "http://www.w3.org/1998/Math/MathML"
+ : (parentNode as Element).namespaceURI,
+ tagNameOrRenderer,
+ );
return branch;
}
@@ -85,25 +95,19 @@ function createBranch(
return branch;
}
-export function initBranch(renderer: Renderer, branch: BranchScope) {
- const dom = renderer.___clone();
- walk(
- dom.nodeType === NodeType.DocumentFragment ? dom.firstChild! : dom,
- renderer.___walks,
- branch,
- );
- branch.___startNode =
- dom.nodeType === NodeType.DocumentFragment
- ? dom.firstChild!
- : (dom as ChildNode);
- branch.___endNode =
- dom.nodeType === NodeType.DocumentFragment
- ? dom.lastChild!
- : (dom as ChildNode);
+export function initBranch(
+ renderer: Renderer,
+ branch: BranchScope,
+ parentNode: ParentNode,
+) {
+ const clone = renderer.___clone((parentNode as Element).namespaceURI!);
+ const cloneParent = clone.parentNode;
+ walk(cloneParent?.firstChild || clone, renderer.___walks, branch);
+ branch.___startNode = cloneParent?.firstChild || (clone as ChildNode);
+ branch.___endNode = cloneParent?.lastChild || (clone as ChildNode);
if (renderer.___setup) {
queueRender(branch, renderer.___setup);
}
- return dom;
}
export function dynamicTagAttrs(
@@ -194,8 +198,26 @@ export function createRenderer(
return createRendererWithOwner(template, walks, setup, getArgs)();
}
-function _clone(this: Renderer) {
- return (this.___sourceNode ||= parseHTMLOrSingleNode(
+function _clone(this: Renderer, ns: string) {
+ return ((cloneCache[ns] ||= {})[this.___template] ||= createCloneableHTML(
this.___template,
- )).cloneNode(true);
+ ns,
+ ))();
+}
+
+const cloneCache: Partial<
+ Record>>
+> = {};
+function createCloneableHTML(html: string, ns: string) {
+ const { firstChild, lastChild } = parseHTML(html, ns);
+ const parent = document.createElementNS(ns, "t") as ParentNode & {
+ firstChild: ChildNode;
+ lastChild: ChildNode;
+ };
+ insertChildNodes(parent, null, firstChild, lastChild);
+ return (
+ firstChild === lastChild && firstChild!.nodeType < NodeType.Comment
+ ? () => firstChild.cloneNode(true)
+ : () => parent.cloneNode(true).firstChild
+ ) as () => ChildNode;
}
diff --git a/packages/runtime-tags/src/dom/scope.ts b/packages/runtime-tags/src/dom/scope.ts
index 767891f9cc..e6253c4301 100644
--- a/packages/runtime-tags/src/dom/scope.ts
+++ b/packages/runtime-tags/src/dom/scope.ts
@@ -1,5 +1,5 @@
import type { BranchScope, Scope } from "../common/types";
-import { removeChildNodes } from "./dom";
+import { insertChildNodes, removeChildNodes } from "./dom";
let pendingScopes: Scope[] = [];
let debugID = 0;
@@ -58,11 +58,10 @@ export function insertBranchBefore(
parentNode: ParentNode,
nextSibling: Node | null,
) {
- let current = branch.___startNode as Node;
- const stop = branch.___endNode.nextSibling;
- while (current !== stop) {
- const next = current.nextSibling;
- parentNode.insertBefore(current, nextSibling);
- current = next!;
- }
+ insertChildNodes(
+ parentNode,
+ nextSibling,
+ branch.___startNode,
+ branch.___endNode,
+ );
}
diff --git a/packages/runtime-tags/src/dom/template.ts b/packages/runtime-tags/src/dom/template.ts
index 598b50e733..b19fa3ea20 100644
--- a/packages/runtime-tags/src/dom/template.ts
+++ b/packages/runtime-tags/src/dom/template.ts
@@ -5,6 +5,7 @@ import type {
TemplateInput,
TemplateInstance,
} from "../common/types";
+import { insertChildNodes } from "./dom";
import { prepareEffects, runEffects } from "./queue";
import { createRenderer, initBranch, type Renderer } from "./renderer";
import { register } from "./resume";
@@ -35,7 +36,7 @@ function mount(
reference: Node,
position?: InsertPosition,
): TemplateInstance {
- let branch!: BranchScope, dom!: Node;
+ let branch!: BranchScope;
let parentNode = reference as ParentNode;
let nextSibling: Node | null = null;
let { $global } = input;
@@ -78,11 +79,16 @@ function mount(
const args = this.___args;
const effects = prepareEffects(() => {
branch = createScope($global) as BranchScope;
- dom = initBranch(this, branch);
+ initBranch(this, branch, parentNode);
args?.(branch, [input]);
});
- parentNode.insertBefore(dom, nextSibling);
+ insertChildNodes(
+ parentNode,
+ nextSibling,
+ branch.___startNode,
+ branch.___endNode,
+ );
runEffects(effects);
return {