@miaskiewicz/turbo-dom 0.4.0 → 0.5.0

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.
package/Cargo.lock CHANGED
@@ -522,7 +522,7 @@ dependencies = [
522
522
 
523
523
  [[package]]
524
524
  name = "turbo-dom"
525
- version = "0.4.0"
525
+ version = "0.5.0"
526
526
  dependencies = [
527
527
  "html5ever",
528
528
  "markup5ever_rcdom",
@@ -533,7 +533,7 @@ dependencies = [
533
533
 
534
534
  [[package]]
535
535
  name = "turbo-dom-parser"
536
- version = "0.4.0"
536
+ version = "0.5.0"
537
537
  dependencies = [
538
538
  "html5ever",
539
539
  "markup5ever_rcdom",
package/Cargo.toml CHANGED
@@ -5,7 +5,7 @@ members = ["crates/turbo-dom"]
5
5
 
6
6
  [package]
7
7
  name = "turbo-dom-parser"
8
- version = "0.4.0"
8
+ version = "0.5.0"
9
9
  edition = "2021"
10
10
  description = "html5ever-backed native parser for turbo-dom (Layer 1)"
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miaskiewicz/turbo-dom",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Faster, more spec-correct DOM for test runners — native html5ever (Rust/WASM) parser + lazy copy-on-write DOM. A drop-in-style alternative to jsdom/happy-dom for vitest & jest.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
Binary file
Binary file
@@ -219,7 +219,7 @@ export class Node extends EventTarget {
219
219
  // and feed MutationObservers, like every other childList mutation path.
220
220
  if (changed) notifyMutation(this, 'childList', null, null, null);
221
221
  }
222
- replaceChildren(...nodes) { this.__kids = []; this.__touch(); for (const n of nodes) this.appendChild(typeof n === 'string' ? this.ownerDocument.createTextNode(n) : n); }
222
+ replaceChildren(...nodes) { this.__detachKids(); this.__kids = []; this.__touch(); for (const n of nodes) this.appendChild(typeof n === 'string' ? this.ownerDocument.createTextNode(n) : n); }
223
223
  // bitmask: 1 DISCONNECTED, 2 PRECEDING, 4 FOLLOWING, 8 CONTAINS, 16 CONTAINED_BY
224
224
  compareDocumentPosition(other) {
225
225
  if (other === this) return 0;
@@ -268,6 +268,7 @@ export class Node extends EventTarget {
268
268
  return s;
269
269
  }
270
270
  set textContent(value) {
271
+ this.__detachKids();
271
272
  this.__kids = [];
272
273
  this.__touch();
273
274
  if (value !== '') this.appendChild(this.ownerDocument.createTextNode(String(value)));
@@ -278,6 +279,18 @@ export class Node extends EventTarget {
278
279
  // insertBefore/removeChild.
279
280
  __touch() { const d = this.ownerDocument; if (d) d.__version = (d.__version || 0) + 1; }
280
281
 
282
+ // Clear the outgoing children's parentNode before a wholesale `__kids = []` rewrite
283
+ // (innerHTML/textContent/replaceChildren) — removeChild does this one at a time, these
284
+ // paths bypass it. Without it a reference held into the replaced subtree keeps reporting
285
+ // the (now unrelated) old parent and walks back up into the live tree.
286
+ // Reads __kids DIRECTLY, never __children(): an un-inflated buffer-backed child has no
287
+ // node object yet, so nothing can be holding it — inflating a whole child list just to
288
+ // null out parent pointers would put an allocation on the React container-clear path.
289
+ __detachKids() {
290
+ const kids = this.__kids;
291
+ if (kids) for (let i = 0; i < kids.length; i++) kids[i].parentNode = null;
292
+ }
293
+
281
294
  // Element/Document/Fragment nodeValue is null per spec (CharacterData overrides).
282
295
  get nodeValue() { return null; }
283
296
  set nodeValue(_v) { /* no-op for non-CharacterData nodes, per spec */ }
@@ -706,7 +719,7 @@ export class Element extends Node {
706
719
  before(...nodes) { const p = this.parentNode; if (!p) return; for (const n of nodes) p.insertBefore(toNode(this.ownerDocument, n), this); }
707
720
  after(...nodes) { const p = this.parentNode; if (!p) return; const ref = viableNextSibling(this, nodes); for (const n of nodes) p.insertBefore(toNode(this.ownerDocument, n), ref); }
708
721
  replaceWith(...nodes) { const p = this.parentNode; if (!p) return; const ref = viableNextSibling(this, nodes); for (const n of nodes) if (n !== this) p.insertBefore(toNode(this.ownerDocument, n), ref); this.remove(); }
709
- replaceChildren(...nodes) { this.__kids = []; this.__touch(); for (const n of nodes) this.appendChild(toNode(this.ownerDocument, n)); }
722
+ replaceChildren(...nodes) { this.__detachKids(); this.__kids = []; this.__touch(); for (const n of nodes) this.appendChild(toNode(this.ownerDocument, n)); }
710
723
 
711
724
  // ---- queries ----
712
725
  matches(sel) { return matchesSelector(this, sel); }
@@ -728,6 +741,7 @@ export class Element extends Node {
728
741
  get innerHTML() { return serializeInner(this); }
729
742
  set innerHTML(html) {
730
743
  const frag = getParser().parseFragment(String(html), this.__ns ? `${this.__ns} ${this.localName}` : this.localName);
744
+ this.__detachKids();
731
745
  this.__kids = [];
732
746
  this.__touch();
733
747
  for (const rawChild of frag.children) {
@@ -1050,6 +1064,7 @@ export class ShadowRoot extends DocumentFragment {
1050
1064
  get innerHTML() { return serializeInner(this); }
1051
1065
  set innerHTML(html) {
1052
1066
  const frag = getParser().parseFragment(String(html), ''); // empty context → body
1067
+ this.__detachKids();
1053
1068
  this.__kids = [];
1054
1069
  this.__touch();
1055
1070
  for (const rawChild of frag.children) {
Binary file
Binary file