commits
I don’t think this is very useful and it makes the code more complex.
This fixes an issue where defaultValues were not being morphed correctly.
Reduce repeated scans in the exact-id matching pass so keyed reorders spend less time searching candidates without changing morph behavior.
Use pre-sized LIS buffers and Int32Array predecessor tracking to reduce allocations and avoid unshift-heavy reconstruction in the reordering hot path while preserving behavior.
Replace querySelectorAll("[id]") scans in ID mapping with a TreeWalker-based descendant pass to reduce selector overhead in hot morph paths while keeping matching behavior unchanged.
Queue direct morphs for the focused element and flush them when focus moves away, while preserving user-typed input value by updating only default value/attribute during the deferred pass.
Allow active elements and their ancestors to be moved or replaced while still skipping direct updates/replacement/removal of the active node itself when preserveActiveElement is enabled. Keep focused tests and docs aligned with the new behavior.
Replace set-based candidate tracking with contiguous index arrays and typed active flags in visitChildNodes to reduce hash overhead while preserving matching order and behavior.
Remove dead sequence writes and replace spread NodeList copies with indexed array conversion to reduce per-morph allocations without changing matching behavior.
Introduce a scriptable benchmark runner with warmup, fixed fixtures, and thorough mode so performance changes can be measured consistently over time.
Precompute the active element ancestor chain so replacement and removal guards use O(1) membership checks while still protecting focused descendants during morphing.
Introduce preserveActiveElement to protect the focused element from moves, replacement, and removal so typing state can stay stable when requested without changing default behavior.
Elements with IDs can only match by their ID - they are semantically
distinct entities. Previously, if an element with an ID failed to match
in the ID matching phase, it could still incorrectly match in the
heuristics or tagName phases.
This change separates elements with IDs into a dedicated `idElements`
pool at build time, so they:
1. Skip the expensive `isEqualNode` check entirely
2. Only participate in ID matching
3. Get removed if no ID match is found
The idSet matching phase (for elements matched by descendant IDs) now
correctly iterates over `candidateElements` since elements with idSets
may not have IDs themselves.
This is both a correctness fix and a performance optimization.
* Only flag dirty inputs if preserveChanges is disabled
* Use idSets for one side
* Simplify attribute loops
* Bump deps
Elements with IDs can only match by their ID - they are semantically
distinct entities. Previously, if an element with an ID failed to match
in the ID matching phase, it could still incorrectly match in the
heuristics or tagName phases.
This change separates elements with IDs into a dedicated `idElements`
pool at build time, so they:
1. Skip the expensive `isEqualNode` check entirely
2. Only participate in ID matching
3. Get removed if no ID match is found
The idSet matching phase (for elements matched by descendant IDs) now
correctly iterates over `candidateElements` since elements with idSets
may not have IDs themselves.
This is both a correctness fix and a performance optimization.