[Breaking Change][lexical][lexical-utils][lexical-selection][lexical-website] Bug Fix: LexicalNode.replace no longer re-homes named slots - #8938
Merged
etrepum merged 1 commit intoAug 7, 2026
Conversation
… change: LexicalNode.replace no longer re-homes named slots ## Description Previously, `LexicalNode.replace` moved the replaced node's named slots onto the replacement node. Slots are tightly bound to their host node and are not necessarily portable across node types, so this re-homing is removed: the replaced node keeps its slot map. If it is reattached in the same update (the `$wrapNodeInElement` pattern) its slots come with it, and if it stays detached its slot subtrees are garbage-collected with it via the existing dual-channel slot GC. Anything built on `replace()`, such as `$setBlocksType`, now produces a slot-less replacement block. This also fixes the bug where `$wrapNodeInElement` stripped the wrapped node's slots, because `replace()` re-homed them onto the wrapper. - Remove the re-home block (and its invariant) from `LexicalNode.replace` - Repoint the LexicalSlot tests that pinned the re-home at the new semantics, and add a wrap-pattern regression test - Add a `$wrapNodeInElement` slot-preservation test in `@lexical/utils` - Pin the new `$setBlocksType` behavior (converted hosts drop slots) - Document the node-bound slot lifecycle in the named-slots concepts doc Closes facebook#8936 ## Test plan ### Before `host.replace(other)` moved every named slot onto `other`; `$wrapNodeInElement(host, ...)` left the wrapped host slot-less. The tests `replace(includeChildren) carries slots onto the replacement` and `replace carries slots onto a decorator host without includeChildren` pinned that behavior. ### After Slots stay on the replaced node. Covered by the updated LexicalSlot tests (no transfer + GC of the detached host's slots, wrap pattern keeps slots), the new `LexicalWrapNodeInElementSlots` regression test, and the new `$setBlocksType` slot-host test. Full unit suite, browser tests for core/utils/selection, tsc, eslint, and prettier all pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
etrepum
marked this pull request as ready for review
August 6, 2026 06:06
etrepum
requested review from
acywatson,
fantactuka,
ivailop7,
potatowagon and
zurfyx
as code owners
August 6, 2026 06:06
zurfyx
approved these changes
Aug 6, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Previously,
LexicalNode.replacemoved the replaced node's named slots onto the replacement node. Slots are tightly bound to their host node and are not necessarily portable across node types, so this re-homing is removed: the replaced node keeps its slot map. If it is reattached in the same update (the$wrapNodeInElementpattern) its slots come with it, and if it stays detached its slot subtrees are garbage-collected with it via the existing dual-channel slot GC. Anything built onreplace(), such as$setBlocksType, now produces a slot-less replacement block.This also fixes the bug where
$wrapNodeInElementstripped the wrapped node's slots, becausereplace()re-homed them onto the wrapper.LexicalNode.replace$wrapNodeInElementslot-preservation test in@lexical/utils$setBlocksTypebehavior (converted hosts drop slots)Closes #8936
Test plan
Before
host.replace(other)moved every named slot ontoother;$wrapNodeInElement(host, ...)left the wrapped host slot-less. The testsreplace(includeChildren) carries slots onto the replacementandreplace carries slots onto a decorator host without includeChildrenpinned that behavior.After
Slots stay on the replaced node. Covered by the updated LexicalSlot tests (no transfer + GC of the detached host's slots, wrap pattern keeps slots), the new
LexicalWrapNodeInElementSlotsregression test, and the new$setBlocksTypeslot-host test. Full unit suite, browser tests for core/utils/selection, tsc, eslint, and prettier all pass.