Skip to content

Fix detached node morphing and cover edge cases#43

Closed
joeldrapper wants to merge 1 commit intomainfrom
fix/detached-node-morphing
Closed

Fix detached node morphing and cover edge cases#43
joeldrapper wants to merge 1 commit intomainfrom
fix/detached-node-morphing

Conversation

@joeldrapper
Copy link
Copy Markdown
Collaborator

This fixes an extreme edge case and brings us up to 100% line and branch coverage.

Copilot AI review requested due to automatic review settings March 9, 2026 18:38
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets hard-to-hit edge cases in Morphlex’s DOM morphing logic (notably around detached nodes) and adds regression tests to reach full line/branch coverage.

Changes:

  • Adjust morphing internals to better handle detached nodes during replacement and one-to-many morphs.
  • Refactor ID-mapping/dirty-flagging helpers to operate on Element nodes (via isElementNode), and simplify a few matching paths.
  • Add a dedicated regression test suite for previously uncovered/edge scenarios, plus V8 coverage ignores where needed.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/morphlex.ts Fixes detached-node handling and refactors node-type helpers; adds a few V8 ignore annotations.
test/new/coverage.test.ts Adds targeted regression tests covering detached morphs, callback behavior, id matching, whitespace, and textarea/option edge cases.
test/new/utils.ts Marks helper utilities file as ignored for V8 coverage reporting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/morphlex.ts
Comment on lines 329 to 331
if (from.nodeType === to.nodeType && from.nodeValue !== null && to.nodeValue !== null) {
if (from.nodeValue !== to.nodeValue) {
from.nodeValue = to.nodeValue
}
from.nodeValue = to.nodeValue
} else {
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #morphOtherNode, from.nodeValue is assigned unconditionally whenever node types match. Even when the value is unchanged, this can generate unnecessary DOM mutations (and potentially extra MutationObserver characterData records), and it removes a previous micro-optimization. Consider restoring the if (from.nodeValue !== to.nodeValue) guard so identical text/comment nodes don’t incur a write.

Copilot uses AI. Check for mistakes.
Comment thread src/morphlex.ts
Comment on lines 772 to 782
#replaceNode(node: ChildNode, newNode: ChildNode): void {
const parent = node.parentNode || document
const parent = node.parentNode
const callbackParent = parent || document
const insertionPoint = node
// Check if both removal and addition are allowed before starting the replacement
if (
(this.#options.beforeNodeRemoved?.(node) ?? true) &&
(this.#options.beforeNodeAdded?.(parent, newNode, insertionPoint) ?? true)
(this.#options.beforeNodeAdded?.(callbackParent, newNode, insertionPoint) ?? true)
) {
if (!parent) return
parent.insertBefore(newNode, insertionPoint)
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#replaceNode may invoke beforeNodeRemoved/beforeNodeAdded for a detached node (no parentNode), then return early without actually inserting/removing anything. This breaks the stated callback contract (“before removed/added to the DOM”) and can cause user code to run side effects assuming a real replacement will occur. Consider short-circuiting when parent is null (or at least skipping beforeNodeRemoved / after* callbacks) while still allowing the detached-parent behavior you want for beforeNodeAdded (e.g., call beforeNodeAdded(document, ...) but avoid calling beforeNodeRemoved when nothing can be removed).

Copilot uses AI. Check for mistakes.
@joeldrapper joeldrapper deleted the fix/detached-node-morphing branch March 10, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants