marko 6.3.29 → 6.3.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,6 +23,7 @@ export declare const PlaceholderBranch = "P";
23
23
  export declare const PlaceholderContent = "Q";
24
24
  export declare const Renderer = "R";
25
25
  export declare const StartNode = "S";
26
+ export declare const Subscriptions = "Z";
26
27
  export declare const TagVariable = "T";
27
28
  export declare const TagVariableChange = "U";
28
29
  type Self = typeof import("./accessor-prop");
@@ -23,6 +23,7 @@ export declare const PlaceholderBranch = "#PlaceholderBranch";
23
23
  export declare const PlaceholderContent = "#PlaceholderContent";
24
24
  export declare const Renderer = "#Renderer";
25
25
  export declare const StartNode = "#StartNode";
26
+ export declare const Subscriptions = "#Subscriptions";
26
27
  export declare const TagVariable = "#TagVariable";
27
28
  export declare const TagVariableChange = "#TagVariableChange";
28
29
  type Self = typeof import("./accessor-prop.debug");
@@ -29,6 +29,7 @@ export interface Scope {
29
29
  [AccessorProp.AbortControllers]: Record<string | number, AbortController | void> | undefined;
30
30
  [AccessorProp.ClosestBranch]: BranchScope | undefined;
31
31
  [AccessorProp.ClosestBranchId]: number | undefined;
32
+ [AccessorProp.Subscriptions]: Set<Scope>[] | undefined;
32
33
  [x: `___${string}`]: never;
33
34
  [x: string | number]: any;
34
35
  }
package/dist/debug/dom.js CHANGED
@@ -60,6 +60,7 @@ const PlaceholderBranch = "#PlaceholderBranch";
60
60
  const PlaceholderContent = "#PlaceholderContent";
61
61
  const Renderer = "#Renderer";
62
62
  const StartNode = "#StartNode";
63
+ const Subscriptions = "#Subscriptions";
63
64
  const TagVariable = "#TagVariable";
64
65
  const TagVariableChange = "#TagVariableChange";
65
66
  //#endregion
@@ -400,17 +401,21 @@ function destroyBranch(branch) {
400
401
  function destroyScope(scope) {
401
402
  if (scope["#Gen"]) {
402
403
  destroyNestedScopes(scope);
403
- resetControllers(scope);
404
+ cleanupScope(scope);
404
405
  }
405
406
  }
406
407
  const destroyNestedScopes = function destroyNestedScopes(scope) {
407
408
  scope[Gen$1] = 0;
408
409
  scope[BranchScopes]?.forEach(destroyNestedScopes);
409
- scope[AbortScopes]?.forEach(resetControllers);
410
+ scope[AbortScopes]?.forEach(cleanupScope);
410
411
  };
411
- function resetControllers(scope) {
412
+ function cleanupScope(scope) {
413
+ scope[Subscriptions]?.forEach(unsubscribe, scope);
412
414
  for (const id in scope[AbortControllers]) $signalReset(scope, id);
413
415
  }
416
+ function unsubscribe(subscribers) {
417
+ subscribers.delete(this);
418
+ }
414
419
  function removeAndDestroyBranch(branch) {
415
420
  destroyBranch(branch);
416
421
  removeChildNodes(branch[StartNode], branch[EndNode]);
@@ -565,10 +570,8 @@ function _if_closure(ownerConditionalNodeAccessor, branch, fn) {
565
570
  }
566
571
  function subscribeToScopeSet(ownerScope, accessor, scope) {
567
572
  const subscribers = ownerScope[accessor] ||= /* @__PURE__ */ new Set();
568
- if (!subscribers.has(scope)) {
569
- subscribers.add(scope);
570
- $signal(scope, -1).addEventListener("abort", () => ownerScope[accessor].delete(scope));
571
- }
573
+ const { size } = subscribers;
574
+ if (subscribers.add(scope).size !== size) trackCleanup(scope, subscribers);
572
575
  }
573
576
  function _closure(...closureSignals) {
574
577
  const [firstSignal] = closureSignals;
@@ -647,9 +650,10 @@ function _hoist_resume(id, ...path) {
647
650
  }
648
651
  //#endregion
649
652
  //#region src/dom/walker.ts
650
- const walker = /* @__PURE__ */ document.createTreeWalker(document);
653
+ /** Cloned templates are small, where a TreeWalker's per-step cost dominates. */
654
+ let currentNode;
651
655
  function walk(startNode, walkCodes, branch) {
652
- walker.currentNode = startNode;
656
+ currentNode = startNode;
653
657
  walkInternal(0, walkCodes, branch);
654
658
  }
655
659
  const walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
@@ -661,11 +665,9 @@ const walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
661
665
  value = walkCodes.charCodeAt(currentWalkIndex++);
662
666
  currentMultiplier = storedMultiplier;
663
667
  storedMultiplier = 0;
664
- if (value === 32) {
665
- const node = walker.currentNode;
666
- scope[getDebugKey(currentScopeIndex++, node)] = node;
667
- } else if (value === 37 || value === 49) {
668
- walker.currentNode.replaceWith(walker.currentNode = scope[getDebugKey(currentScopeIndex++, "#text")] = new Text());
668
+ if (value === 32) scope[getDebugKey(currentScopeIndex++, currentNode)] = currentNode;
669
+ else if (value === 37 || value === 49) {
670
+ currentNode.replaceWith(currentNode = scope[getDebugKey(currentScopeIndex++, "#text")] = new Text());
669
671
  if (value === 49) scope[getDebugKey(currentScopeIndex++, "#scopeOffset")] = skipScope();
670
672
  } else if (value === 38) return currentWalkIndex;
671
673
  else if (value === 47 || value === 48) {
@@ -673,14 +675,14 @@ const walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
673
675
  if (value === 48) scope[getDebugKey(currentScopeIndex++, "#scopeOffset")] = skipScope();
674
676
  } else if (value < 92) {
675
677
  value = 25 * currentMultiplier + value - 67;
676
- while (value--) walker.nextNode();
678
+ while (value--) walkNextNode();
677
679
  } else if (value < 107) {
678
680
  value = 10 * currentMultiplier + value - 97;
679
- while (value--) walker.nextSibling();
681
+ while (value--) walkNextSibling();
680
682
  } else if (value < 117) {
681
683
  value = 10 * currentMultiplier + value - 107;
682
- while (value--) walker.parentNode();
683
- walker.nextSibling();
684
+ while (value--) currentNode = currentNode.parentNode || currentNode;
685
+ walkNextSibling();
684
686
  } else {
685
687
  if (value < 117 || value > 126) throw new Error(`Unknown walk code: ${value}`);
686
688
  storedMultiplier = currentMultiplier * 10 + value - 117;
@@ -694,6 +696,12 @@ function getDebugKey(index, node) {
694
696
  else if (node.nodeType === 1) return `#${node.tagName.toLowerCase()}/${index}`;
695
697
  return index;
696
698
  }
699
+ const walkNextNode = () => {
700
+ if (currentNode.firstChild) return currentNode = currentNode.firstChild;
701
+ while (!currentNode.nextSibling && currentNode.parentNode) currentNode = currentNode.parentNode;
702
+ walkNextSibling();
703
+ };
704
+ const walkNextSibling = () => currentNode = currentNode.nextSibling || currentNode;
697
705
  //#endregion
698
706
  //#region src/dom/resume.ts
699
707
  const registeredValues = {};
@@ -1160,18 +1168,19 @@ function removeChildNodes(startNode, endNode) {
1160
1168
  }
1161
1169
  }
1162
1170
  function insertChildNodes(parentNode, referenceNode, startNode, endNode) {
1163
- parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
1171
+ if (parentNode.isConnected) parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
1172
+ else {
1173
+ const stop = endNode.nextSibling;
1174
+ while (startNode !== stop) {
1175
+ const next = startNode.nextSibling;
1176
+ parentNode.insertBefore(startNode, referenceNode);
1177
+ startNode = next;
1178
+ }
1179
+ }
1180
+ return parentNode;
1164
1181
  }
1165
1182
  function toInsertNode(startNode, endNode) {
1166
- if (startNode === endNode) return startNode;
1167
- const parent = new DocumentFragment();
1168
- const stop = endNode.nextSibling;
1169
- while (startNode !== stop) {
1170
- const next = startNode.nextSibling;
1171
- parent.appendChild(startNode);
1172
- startNode = next;
1173
- }
1174
- return parent;
1183
+ return startNode === endNode ? startNode : insertChildNodes(new DocumentFragment(), null, startNode, endNode);
1175
1184
  }
1176
1185
  //#endregion
1177
1186
  //#region src/dom/resolve-cursor-position.ts
@@ -2090,9 +2099,15 @@ function $signalReset(scope, id) {
2090
2099
  }
2091
2100
  }
2092
2101
  function $signal(scope, id) {
2093
- if (scope["#ClosestBranch"]) (scope[ClosestBranch][AbortScopes] ||= /* @__PURE__ */ new Set()).add(scope);
2102
+ trackCleanup(scope);
2094
2103
  return ((scope[AbortControllers] ||= {})[id] ||= new AbortController()).signal;
2095
2104
  }
2105
+ /** Enrols `scope` with its branch so destroying the branch cleans it up. */
2106
+ function trackCleanup(scope, subscribers) {
2107
+ const branch = scope[ClosestBranch];
2108
+ if (branch) (branch[AbortScopes] ||= /* @__PURE__ */ new Set()).add(scope);
2109
+ if (subscribers) (scope[Subscriptions] ||= []).push(subscribers);
2110
+ }
2096
2111
  function abort(ctrl) {
2097
2112
  ctrl.abort();
2098
2113
  }
@@ -58,6 +58,7 @@ const PlaceholderBranch = "#PlaceholderBranch";
58
58
  const PlaceholderContent = "#PlaceholderContent";
59
59
  const Renderer = "#Renderer";
60
60
  const StartNode = "#StartNode";
61
+ const Subscriptions = "#Subscriptions";
61
62
  const TagVariable = "#TagVariable";
62
63
  const TagVariableChange = "#TagVariableChange";
63
64
  //#endregion
@@ -398,17 +399,21 @@ function destroyBranch(branch) {
398
399
  function destroyScope(scope) {
399
400
  if (scope["#Gen"]) {
400
401
  destroyNestedScopes(scope);
401
- resetControllers(scope);
402
+ cleanupScope(scope);
402
403
  }
403
404
  }
404
405
  const destroyNestedScopes = function destroyNestedScopes(scope) {
405
406
  scope[Gen$1] = 0;
406
407
  scope[BranchScopes]?.forEach(destroyNestedScopes);
407
- scope[AbortScopes]?.forEach(resetControllers);
408
+ scope[AbortScopes]?.forEach(cleanupScope);
408
409
  };
409
- function resetControllers(scope) {
410
+ function cleanupScope(scope) {
411
+ scope[Subscriptions]?.forEach(unsubscribe, scope);
410
412
  for (const id in scope[AbortControllers]) $signalReset(scope, id);
411
413
  }
414
+ function unsubscribe(subscribers) {
415
+ subscribers.delete(this);
416
+ }
412
417
  function removeAndDestroyBranch(branch) {
413
418
  destroyBranch(branch);
414
419
  removeChildNodes(branch[StartNode], branch[EndNode]);
@@ -563,10 +568,8 @@ function _if_closure(ownerConditionalNodeAccessor, branch, fn) {
563
568
  }
564
569
  function subscribeToScopeSet(ownerScope, accessor, scope) {
565
570
  const subscribers = ownerScope[accessor] ||= /* @__PURE__ */ new Set();
566
- if (!subscribers.has(scope)) {
567
- subscribers.add(scope);
568
- $signal(scope, -1).addEventListener("abort", () => ownerScope[accessor].delete(scope));
569
- }
571
+ const { size } = subscribers;
572
+ if (subscribers.add(scope).size !== size) trackCleanup(scope, subscribers);
570
573
  }
571
574
  function _closure(...closureSignals) {
572
575
  const [firstSignal] = closureSignals;
@@ -645,9 +648,10 @@ function _hoist_resume(id, ...path) {
645
648
  }
646
649
  //#endregion
647
650
  //#region src/dom/walker.ts
648
- const walker = /* @__PURE__ */ document.createTreeWalker(document);
651
+ /** Cloned templates are small, where a TreeWalker's per-step cost dominates. */
652
+ let currentNode;
649
653
  function walk(startNode, walkCodes, branch) {
650
- walker.currentNode = startNode;
654
+ currentNode = startNode;
651
655
  walkInternal(0, walkCodes, branch);
652
656
  }
653
657
  const walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
@@ -659,11 +663,9 @@ const walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
659
663
  value = walkCodes.charCodeAt(currentWalkIndex++);
660
664
  currentMultiplier = storedMultiplier;
661
665
  storedMultiplier = 0;
662
- if (value === 32) {
663
- const node = walker.currentNode;
664
- scope[getDebugKey(currentScopeIndex++, node)] = node;
665
- } else if (value === 37 || value === 49) {
666
- walker.currentNode.replaceWith(walker.currentNode = scope[getDebugKey(currentScopeIndex++, "#text")] = new Text());
666
+ if (value === 32) scope[getDebugKey(currentScopeIndex++, currentNode)] = currentNode;
667
+ else if (value === 37 || value === 49) {
668
+ currentNode.replaceWith(currentNode = scope[getDebugKey(currentScopeIndex++, "#text")] = new Text());
667
669
  if (value === 49) scope[getDebugKey(currentScopeIndex++, "#scopeOffset")] = skipScope();
668
670
  } else if (value === 38) return currentWalkIndex;
669
671
  else if (value === 47 || value === 48) {
@@ -671,14 +673,14 @@ const walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
671
673
  if (value === 48) scope[getDebugKey(currentScopeIndex++, "#scopeOffset")] = skipScope();
672
674
  } else if (value < 92) {
673
675
  value = 25 * currentMultiplier + value - 67;
674
- while (value--) walker.nextNode();
676
+ while (value--) walkNextNode();
675
677
  } else if (value < 107) {
676
678
  value = 10 * currentMultiplier + value - 97;
677
- while (value--) walker.nextSibling();
679
+ while (value--) walkNextSibling();
678
680
  } else if (value < 117) {
679
681
  value = 10 * currentMultiplier + value - 107;
680
- while (value--) walker.parentNode();
681
- walker.nextSibling();
682
+ while (value--) currentNode = currentNode.parentNode || currentNode;
683
+ walkNextSibling();
682
684
  } else {
683
685
  if (value < 117 || value > 126) throw new Error(`Unknown walk code: ${value}`);
684
686
  storedMultiplier = currentMultiplier * 10 + value - 117;
@@ -692,6 +694,12 @@ function getDebugKey(index, node) {
692
694
  else if (node.nodeType === 1) return `#${node.tagName.toLowerCase()}/${index}`;
693
695
  return index;
694
696
  }
697
+ const walkNextNode = () => {
698
+ if (currentNode.firstChild) return currentNode = currentNode.firstChild;
699
+ while (!currentNode.nextSibling && currentNode.parentNode) currentNode = currentNode.parentNode;
700
+ walkNextSibling();
701
+ };
702
+ const walkNextSibling = () => currentNode = currentNode.nextSibling || currentNode;
695
703
  //#endregion
696
704
  //#region src/dom/resume.ts
697
705
  const registeredValues = {};
@@ -1158,18 +1166,19 @@ function removeChildNodes(startNode, endNode) {
1158
1166
  }
1159
1167
  }
1160
1168
  function insertChildNodes(parentNode, referenceNode, startNode, endNode) {
1161
- parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
1169
+ if (parentNode.isConnected) parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
1170
+ else {
1171
+ const stop = endNode.nextSibling;
1172
+ while (startNode !== stop) {
1173
+ const next = startNode.nextSibling;
1174
+ parentNode.insertBefore(startNode, referenceNode);
1175
+ startNode = next;
1176
+ }
1177
+ }
1178
+ return parentNode;
1162
1179
  }
1163
1180
  function toInsertNode(startNode, endNode) {
1164
- if (startNode === endNode) return startNode;
1165
- const parent = new DocumentFragment();
1166
- const stop = endNode.nextSibling;
1167
- while (startNode !== stop) {
1168
- const next = startNode.nextSibling;
1169
- parent.appendChild(startNode);
1170
- startNode = next;
1171
- }
1172
- return parent;
1181
+ return startNode === endNode ? startNode : insertChildNodes(new DocumentFragment(), null, startNode, endNode);
1173
1182
  }
1174
1183
  //#endregion
1175
1184
  //#region src/dom/resolve-cursor-position.ts
@@ -2088,9 +2097,15 @@ function $signalReset(scope, id) {
2088
2097
  }
2089
2098
  }
2090
2099
  function $signal(scope, id) {
2091
- if (scope["#ClosestBranch"]) (scope[ClosestBranch][AbortScopes] ||= /* @__PURE__ */ new Set()).add(scope);
2100
+ trackCleanup(scope);
2092
2101
  return ((scope[AbortControllers] ||= {})[id] ||= new AbortController()).signal;
2093
2102
  }
2103
+ /** Enrols `scope` with its branch so destroying the branch cleans it up. */
2104
+ function trackCleanup(scope, subscribers) {
2105
+ const branch = scope[ClosestBranch];
2106
+ if (branch) (branch[AbortScopes] ||= /* @__PURE__ */ new Set()).add(scope);
2107
+ if (subscribers) (scope[Subscriptions] ||= []).push(subscribers);
2108
+ }
2094
2109
  function abort(ctrl) {
2095
2110
  ctrl.abort();
2096
2111
  }
@@ -1,3 +1,5 @@
1
1
  import { type Scope } from "../common/types";
2
2
  export declare function $signalReset(scope: Scope, id: string | number): void;
3
3
  export declare function $signal(scope: Scope, id: string | number): AbortSignal;
4
+ /** Enrols `scope` with its branch so destroying the branch cleans it up. */
5
+ export declare function trackCleanup(scope: Scope, subscribers?: Set<Scope>): void;
package/dist/dom/dom.d.ts CHANGED
@@ -27,5 +27,5 @@ export declare function _lifecycle(scope: Scope, thisObj: Record<string, unknown
27
27
  onDestroy?: (this: unknown) => void;
28
28
  }, index?: number): void;
29
29
  export declare function removeChildNodes(startNode: ChildNode, endNode: ChildNode): void;
30
- export declare function insertChildNodes(parentNode: ParentNode, referenceNode: Node | null, startNode: Node, endNode: Node): void;
30
+ export declare function insertChildNodes(parentNode: ParentNode, referenceNode: Node | null, startNode: Node, endNode: Node): ParentNode;
31
31
  export declare function toInsertNode(startNode: Node, endNode: Node): Node;
@@ -1,4 +1,3 @@
1
1
  import { type BranchScope } from "../common/types";
2
- export declare const walker: TreeWalker;
3
2
  export declare function walk(startNode: Node, walkCodes: string, branch: BranchScope): void;
4
3
  export declare function getDebugKey(index: number, node: Node | string): string | number;
package/dist/dom.js CHANGED
@@ -5,22 +5,24 @@ let empty = [], rest = Symbol(), unsafeStyleAttrReg = /[\\;]/g, replaceUnsafeSty
5
5
  else for (let name in val) part = stringify(name, val[name]), part && (str += sep + part, sep = delimiter);
6
6
  return str;
7
7
  }, decodeAccessor = (num) => (num + (num < 26 ? 10 : num < 962 ? 334 : 11998)).toString(36), delegate = (type, handler) => handler[type] ||= (document.addEventListener(type, handler, !0), 1), parsers = {}, nextScopeId = 1e6, collectingScopes, destroyNestedScopes = function destroyNestedScopes(scope) {
8
- scope.H = 0, scope.D?.forEach(destroyNestedScopes), scope.B?.forEach(resetControllers);
9
- }, isScheduled, channel, _return = (scope, value) => scope.T?.(value), _var_change = (scope, value) => scope.U?.(value), tagIdsByGlobal = /* @__PURE__ */ new WeakMap(), walker = /* @__PURE__ */ document.createTreeWalker(document), walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
8
+ scope.H = 0, scope.D?.forEach(destroyNestedScopes), scope.B?.forEach(cleanupScope);
9
+ }, isScheduled, channel, _return = (scope, value) => scope.T?.(value), _var_change = (scope, value) => scope.U?.(value), tagIdsByGlobal = /* @__PURE__ */ new WeakMap(), currentNode, walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
10
10
  let value, currentMultiplier, storedMultiplier = 0, currentScopeIndex = 0;
11
- for (; currentWalkIndex < walkCodes.length;) if (value = walkCodes.charCodeAt(currentWalkIndex++), currentMultiplier = storedMultiplier, storedMultiplier = 0, value === 32) {
12
- let node = walker.currentNode;
13
- scope[decodeAccessor(currentScopeIndex++)] = node;
14
- } else if (value === 37 || value === 49) walker.currentNode.replaceWith(walker.currentNode = scope[decodeAccessor(currentScopeIndex++)] = new Text()), value === 49 && (scope[decodeAccessor(currentScopeIndex++)] = skipScope());
11
+ for (; currentWalkIndex < walkCodes.length;) if (value = walkCodes.charCodeAt(currentWalkIndex++), currentMultiplier = storedMultiplier, storedMultiplier = 0, value === 32) scope[decodeAccessor(currentScopeIndex++)] = currentNode;
12
+ else if (value === 37 || value === 49) currentNode.replaceWith(currentNode = scope[decodeAccessor(currentScopeIndex++)] = new Text()), value === 49 && (scope[decodeAccessor(currentScopeIndex++)] = skipScope());
15
13
  else if (value === 38) return currentWalkIndex;
16
14
  else if (value === 47 || value === 48) currentWalkIndex = walkInternal(currentWalkIndex, walkCodes, scope[decodeAccessor(currentScopeIndex++)] = createScope(scope.$, scope.F)), value === 48 && (scope[decodeAccessor(currentScopeIndex++)] = skipScope());
17
- else if (value < 92) for (value = 25 * currentMultiplier + value - 67; value--;) walker.nextNode();
18
- else if (value < 107) for (value = 10 * currentMultiplier + value - 97; value--;) walker.nextSibling();
15
+ else if (value < 92) for (value = 25 * currentMultiplier + value - 67; value--;) walkNextNode();
16
+ else if (value < 107) for (value = 10 * currentMultiplier + value - 97; value--;) walkNextSibling();
19
17
  else if (value < 117) {
20
- for (value = 10 * currentMultiplier + value - 107; value--;) walker.parentNode();
21
- walker.nextSibling();
18
+ for (value = 10 * currentMultiplier + value - 107; value--;) currentNode = currentNode.parentNode || currentNode;
19
+ walkNextSibling();
22
20
  } else storedMultiplier = currentMultiplier * 10 + value - 117;
23
- }, registeredValues = {}, curRenders, branchesEnabled, embedRenders, readyIds, isResuming, cloneCache = {}, R = /[^\p{L}\p{N}]/gu, inputType = "", controllableScripts = {}, controllableRenders = {}, _dynamic_tag = function(nodeAccessor, getContent, getTagVar, inputIsArgs) {
21
+ }, walkNextNode = () => {
22
+ if (currentNode.firstChild) return currentNode = currentNode.firstChild;
23
+ for (; !currentNode.nextSibling && currentNode.parentNode;) currentNode = currentNode.parentNode;
24
+ walkNextSibling();
25
+ }, walkNextSibling = () => currentNode = currentNode.nextSibling || currentNode, registeredValues = {}, curRenders, branchesEnabled, embedRenders, readyIds, isResuming, cloneCache = {}, R = /[^\p{L}\p{N}]/gu, inputType = "", controllableScripts = {}, controllableRenders = {}, _dynamic_tag = function(nodeAccessor, getContent, getTagVar, inputIsArgs) {
24
26
  nodeAccessor = decodeAccessor(nodeAccessor);
25
27
  let childScopeAccessor = "A" + nodeAccessor, rendererAccessor = "D" + nodeAccessor;
26
28
  return enableBranches(), (scope, newRenderer, getInput) => {
@@ -250,11 +252,15 @@ function destroyBranch(branch) {
250
252
  branch.N?.D?.delete(branch), destroyNestedScopes(branch);
251
253
  }
252
254
  function destroyScope(scope) {
253
- scope.H && (destroyNestedScopes(scope), resetControllers(scope));
255
+ scope.H && (destroyNestedScopes(scope), cleanupScope(scope));
254
256
  }
255
- function resetControllers(scope) {
257
+ function cleanupScope(scope) {
258
+ scope.Z?.forEach(unsubscribe, scope);
256
259
  for (let id in scope.A) $signalReset(scope, id);
257
260
  }
261
+ function unsubscribe(subscribers) {
262
+ subscribers.delete(this);
263
+ }
258
264
  function removeAndDestroyBranch(branch) {
259
265
  destroyBranch(branch), removeChildNodes(branch.S, branch.K);
260
266
  }
@@ -347,8 +353,8 @@ function _if_closure(ownerConditionalNodeAccessor, branch, fn) {
347
353
  return ownerSignal._ = fn, ownerSignal;
348
354
  }
349
355
  function subscribeToScopeSet(ownerScope, accessor, scope) {
350
- let subscribers = ownerScope[accessor] ||= /* @__PURE__ */ new Set();
351
- subscribers.has(scope) || (subscribers.add(scope), $signal(scope, -1).addEventListener("abort", () => ownerScope[accessor].delete(scope)));
356
+ let subscribers = ownerScope[accessor] ||= /* @__PURE__ */ new Set(), { size } = subscribers;
357
+ subscribers.add(scope).size !== size && trackCleanup(scope, subscribers);
352
358
  }
353
359
  function _closure(...closureSignals) {
354
360
  let [firstSignal] = closureSignals, scopeInstances = firstSignal.a, signalIndex = firstSignal.b;
@@ -405,8 +411,9 @@ function _hoist_resume(id, ...path) {
405
411
  }
406
412
  //#endregion
407
413
  //#region src/dom/walker.ts
414
+ /** Cloned templates are small, where a TreeWalker's per-step cost dominates. */
408
415
  function walk(startNode, walkCodes, branch) {
409
- walker.currentNode = startNode, walkInternal(0, walkCodes, branch);
416
+ currentNode = startNode, walkInternal(0, walkCodes, branch);
410
417
  }
411
418
  //#endregion
412
419
  //#region src/dom/resume.ts
@@ -702,16 +709,18 @@ function removeChildNodes(startNode, endNode) {
702
709
  }
703
710
  }
704
711
  function insertChildNodes(parentNode, referenceNode, startNode, endNode) {
705
- parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
712
+ if (parentNode.isConnected) parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
713
+ else {
714
+ let stop = endNode.nextSibling;
715
+ for (; startNode !== stop;) {
716
+ let next = startNode.nextSibling;
717
+ parentNode.insertBefore(startNode, referenceNode), startNode = next;
718
+ }
719
+ }
720
+ return parentNode;
706
721
  }
707
722
  function toInsertNode(startNode, endNode) {
708
- if (startNode === endNode) return startNode;
709
- let parent = new DocumentFragment(), stop = endNode.nextSibling;
710
- for (; startNode !== stop;) {
711
- let next = startNode.nextSibling;
712
- parent.appendChild(startNode), startNode = next;
713
- }
714
- return parent;
723
+ return startNode === endNode ? startNode : insertChildNodes(new DocumentFragment(), null, startNode, endNode);
715
724
  }
716
725
  //#endregion
717
726
  //#region src/dom/resolve-cursor-position.ts
@@ -1242,7 +1251,12 @@ function $signalReset(scope, id) {
1242
1251
  ctrl && (scope.A[id] = void 0, rendering ? queueEffect(ctrl, abort) : abort(ctrl));
1243
1252
  }
1244
1253
  function $signal(scope, id) {
1245
- return scope.F && (scope.F.B ||= /* @__PURE__ */ new Set()).add(scope), ((scope.A ||= {})[id] ||= new AbortController()).signal;
1254
+ return trackCleanup(scope), ((scope.A ||= {})[id] ||= new AbortController()).signal;
1255
+ }
1256
+ /** Enrols `scope` with its branch so destroying the branch cleans it up. */
1257
+ function trackCleanup(scope, subscribers) {
1258
+ let branch = scope.F;
1259
+ branch && (branch.B ||= /* @__PURE__ */ new Set()).add(scope), subscribers && (scope.Z ||= []).push(subscribers);
1246
1260
  }
1247
1261
  function abort(ctrl) {
1248
1262
  ctrl.abort();
package/dist/dom.mjs CHANGED
@@ -5,22 +5,24 @@ let empty = [], rest = Symbol(), unsafeStyleAttrReg = /[\\;]/g, replaceUnsafeSty
5
5
  else for (let name in val) part = stringify(name, val[name]), part && (str += sep + part, sep = delimiter);
6
6
  return str;
7
7
  }, decodeAccessor = (num) => (num + (num < 26 ? 10 : num < 962 ? 334 : 11998)).toString(36), delegate = (type, handler) => handler[type] ||= (document.addEventListener(type, handler, !0), 1), parsers = {}, nextScopeId = 1e6, collectingScopes, destroyNestedScopes = function destroyNestedScopes(scope) {
8
- scope.H = 0, scope.D?.forEach(destroyNestedScopes), scope.B?.forEach(resetControllers);
9
- }, isScheduled, channel, _return = (scope, value) => scope.T?.(value), _var_change = (scope, value) => scope.U?.(value), tagIdsByGlobal = /* @__PURE__ */ new WeakMap(), walker = /* @__PURE__ */ document.createTreeWalker(document), walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
8
+ scope.H = 0, scope.D?.forEach(destroyNestedScopes), scope.B?.forEach(cleanupScope);
9
+ }, isScheduled, channel, _return = (scope, value) => scope.T?.(value), _var_change = (scope, value) => scope.U?.(value), tagIdsByGlobal = /* @__PURE__ */ new WeakMap(), currentNode, walkInternal = function walkInternal(currentWalkIndex, walkCodes, scope) {
10
10
  let value, currentMultiplier, storedMultiplier = 0, currentScopeIndex = 0;
11
- for (; currentWalkIndex < walkCodes.length;) if (value = walkCodes.charCodeAt(currentWalkIndex++), currentMultiplier = storedMultiplier, storedMultiplier = 0, value === 32) {
12
- let node = walker.currentNode;
13
- scope[decodeAccessor(currentScopeIndex++)] = node;
14
- } else if (value === 37 || value === 49) walker.currentNode.replaceWith(walker.currentNode = scope[decodeAccessor(currentScopeIndex++)] = new Text()), value === 49 && (scope[decodeAccessor(currentScopeIndex++)] = skipScope());
11
+ for (; currentWalkIndex < walkCodes.length;) if (value = walkCodes.charCodeAt(currentWalkIndex++), currentMultiplier = storedMultiplier, storedMultiplier = 0, value === 32) scope[decodeAccessor(currentScopeIndex++)] = currentNode;
12
+ else if (value === 37 || value === 49) currentNode.replaceWith(currentNode = scope[decodeAccessor(currentScopeIndex++)] = new Text()), value === 49 && (scope[decodeAccessor(currentScopeIndex++)] = skipScope());
15
13
  else if (value === 38) return currentWalkIndex;
16
14
  else if (value === 47 || value === 48) currentWalkIndex = walkInternal(currentWalkIndex, walkCodes, scope[decodeAccessor(currentScopeIndex++)] = createScope(scope.$, scope.F)), value === 48 && (scope[decodeAccessor(currentScopeIndex++)] = skipScope());
17
- else if (value < 92) for (value = 25 * currentMultiplier + value - 67; value--;) walker.nextNode();
18
- else if (value < 107) for (value = 10 * currentMultiplier + value - 97; value--;) walker.nextSibling();
15
+ else if (value < 92) for (value = 25 * currentMultiplier + value - 67; value--;) walkNextNode();
16
+ else if (value < 107) for (value = 10 * currentMultiplier + value - 97; value--;) walkNextSibling();
19
17
  else if (value < 117) {
20
- for (value = 10 * currentMultiplier + value - 107; value--;) walker.parentNode();
21
- walker.nextSibling();
18
+ for (value = 10 * currentMultiplier + value - 107; value--;) currentNode = currentNode.parentNode || currentNode;
19
+ walkNextSibling();
22
20
  } else storedMultiplier = currentMultiplier * 10 + value - 117;
23
- }, registeredValues = {}, curRenders, branchesEnabled, embedRenders, readyIds, isResuming, cloneCache = {}, R = /[^\p{L}\p{N}]/gu, inputType = "", controllableScripts = {}, controllableRenders = {}, _dynamic_tag = function(nodeAccessor, getContent, getTagVar, inputIsArgs) {
21
+ }, walkNextNode = () => {
22
+ if (currentNode.firstChild) return currentNode = currentNode.firstChild;
23
+ for (; !currentNode.nextSibling && currentNode.parentNode;) currentNode = currentNode.parentNode;
24
+ walkNextSibling();
25
+ }, walkNextSibling = () => currentNode = currentNode.nextSibling || currentNode, registeredValues = {}, curRenders, branchesEnabled, embedRenders, readyIds, isResuming, cloneCache = {}, R = /[^\p{L}\p{N}]/gu, inputType = "", controllableScripts = {}, controllableRenders = {}, _dynamic_tag = function(nodeAccessor, getContent, getTagVar, inputIsArgs) {
24
26
  nodeAccessor = decodeAccessor(nodeAccessor);
25
27
  let childScopeAccessor = "A" + nodeAccessor, rendererAccessor = "D" + nodeAccessor;
26
28
  return enableBranches(), (scope, newRenderer, getInput) => {
@@ -249,11 +251,15 @@ function destroyBranch(branch) {
249
251
  branch.N?.D?.delete(branch), destroyNestedScopes(branch);
250
252
  }
251
253
  function destroyScope(scope) {
252
- scope.H && (destroyNestedScopes(scope), resetControllers(scope));
254
+ scope.H && (destroyNestedScopes(scope), cleanupScope(scope));
253
255
  }
254
- function resetControllers(scope) {
256
+ function cleanupScope(scope) {
257
+ scope.Z?.forEach(unsubscribe, scope);
255
258
  for (let id in scope.A) $signalReset(scope, id);
256
259
  }
260
+ function unsubscribe(subscribers) {
261
+ subscribers.delete(this);
262
+ }
257
263
  function removeAndDestroyBranch(branch) {
258
264
  destroyBranch(branch), removeChildNodes(branch.S, branch.K);
259
265
  }
@@ -346,8 +352,8 @@ function _if_closure(ownerConditionalNodeAccessor, branch, fn) {
346
352
  return ownerSignal._ = fn, ownerSignal;
347
353
  }
348
354
  function subscribeToScopeSet(ownerScope, accessor, scope) {
349
- let subscribers = ownerScope[accessor] ||= /* @__PURE__ */ new Set();
350
- subscribers.has(scope) || (subscribers.add(scope), $signal(scope, -1).addEventListener("abort", () => ownerScope[accessor].delete(scope)));
355
+ let subscribers = ownerScope[accessor] ||= /* @__PURE__ */ new Set(), { size } = subscribers;
356
+ subscribers.add(scope).size !== size && trackCleanup(scope, subscribers);
351
357
  }
352
358
  function _closure(...closureSignals) {
353
359
  let [firstSignal] = closureSignals, scopeInstances = firstSignal.a, signalIndex = firstSignal.b;
@@ -404,8 +410,9 @@ function _hoist_resume(id, ...path) {
404
410
  }
405
411
  //#endregion
406
412
  //#region src/dom/walker.ts
413
+ /** Cloned templates are small, where a TreeWalker's per-step cost dominates. */
407
414
  function walk(startNode, walkCodes, branch) {
408
- walker.currentNode = startNode, walkInternal(0, walkCodes, branch);
415
+ currentNode = startNode, walkInternal(0, walkCodes, branch);
409
416
  }
410
417
  //#endregion
411
418
  //#region src/dom/resume.ts
@@ -701,16 +708,18 @@ function removeChildNodes(startNode, endNode) {
701
708
  }
702
709
  }
703
710
  function insertChildNodes(parentNode, referenceNode, startNode, endNode) {
704
- parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
711
+ if (parentNode.isConnected) parentNode.insertBefore(toInsertNode(startNode, endNode), referenceNode);
712
+ else {
713
+ let stop = endNode.nextSibling;
714
+ for (; startNode !== stop;) {
715
+ let next = startNode.nextSibling;
716
+ parentNode.insertBefore(startNode, referenceNode), startNode = next;
717
+ }
718
+ }
719
+ return parentNode;
705
720
  }
706
721
  function toInsertNode(startNode, endNode) {
707
- if (startNode === endNode) return startNode;
708
- let parent = new DocumentFragment(), stop = endNode.nextSibling;
709
- for (; startNode !== stop;) {
710
- let next = startNode.nextSibling;
711
- parent.appendChild(startNode), startNode = next;
712
- }
713
- return parent;
722
+ return startNode === endNode ? startNode : insertChildNodes(new DocumentFragment(), null, startNode, endNode);
714
723
  }
715
724
  //#endregion
716
725
  //#region src/dom/resolve-cursor-position.ts
@@ -1241,7 +1250,12 @@ function $signalReset(scope, id) {
1241
1250
  ctrl && (scope.A[id] = void 0, rendering ? queueEffect(ctrl, abort) : abort(ctrl));
1242
1251
  }
1243
1252
  function $signal(scope, id) {
1244
- return scope.F && (scope.F.B ||= /* @__PURE__ */ new Set()).add(scope), ((scope.A ||= {})[id] ||= new AbortController()).signal;
1253
+ return trackCleanup(scope), ((scope.A ||= {})[id] ||= new AbortController()).signal;
1254
+ }
1255
+ /** Enrols `scope` with its branch so destroying the branch cleans it up. */
1256
+ function trackCleanup(scope, subscribers) {
1257
+ let branch = scope.F;
1258
+ branch && (branch.B ||= /* @__PURE__ */ new Set()).add(scope), subscribers && (scope.Z ||= []).push(subscribers);
1245
1259
  }
1246
1260
  function abort(ctrl) {
1247
1261
  ctrl.abort();
@@ -162,6 +162,7 @@ var accessor_prop_debug_exports = /* @__PURE__ */ __exportAll({
162
162
  PlaceholderContent: () => PlaceholderContent$1,
163
163
  Renderer: () => Renderer$1,
164
164
  StartNode: () => StartNode$1,
165
+ Subscriptions: () => Subscriptions$1,
165
166
  TagVariable: () => TagVariable$1,
166
167
  TagVariableChange: () => TagVariableChange$2
167
168
  });
@@ -188,6 +189,7 @@ const PlaceholderBranch$1 = "#PlaceholderBranch";
188
189
  const PlaceholderContent$1 = "#PlaceholderContent";
189
190
  const Renderer$1 = "#Renderer";
190
191
  const StartNode$1 = "#StartNode";
192
+ const Subscriptions$1 = "#Subscriptions";
191
193
  const TagVariable$1 = "#TagVariable";
192
194
  const TagVariableChange$2 = "#TagVariableChange";
193
195
  //#endregion
@@ -948,6 +950,7 @@ var accessor_prop_exports = /* @__PURE__ */ __exportAll({
948
950
  PlaceholderContent: () => "Q",
949
951
  Renderer: () => "R",
950
952
  StartNode: () => "S",
953
+ Subscriptions: () => "Z",
951
954
  TagVariable: () => "T",
952
955
  TagVariableChange: () => "U"
953
956
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marko",
3
- "version": "6.3.29",
3
+ "version": "6.3.31",
4
4
  "description": "Optimized runtime for Marko templates.",
5
5
  "keywords": [
6
6
  "api",
@@ -54,7 +54,7 @@
54
54
  "magic-string": "^0.30.21"
55
55
  },
56
56
  "devDependencies": {
57
- "@marko/runtime-tags": "npm:marko@6.3.29",
57
+ "@marko/runtime-tags": "npm:marko@6.3.31",
58
58
  "marko": "5.39.30"
59
59
  },
60
60
  "engines": {