Don't clone root-less child nodes in container constructor#2097
Merged
Conversation
The container constructor cloned every real Node passed in `nodes`, so a freshly created (parent-less) node was adopted as a copy and the caller's original reference never had its `parent` set — later operating on it threw `Cannot read properties of undefined`. The clone was added only to keep the source tree intact when moving nodes between roots, so it's skipped when the node has no parent. Nodes that already belong to another tree are still cloned, preserving the existing behavior. Closes postcss#1987.
Member
|
Thanks! |
Member
|
Released in 8.5.16. |
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.
Closes #1987.
Problem
Passing a freshly created (parent-less) node as a child in a container constructor clones it, so the caller's original reference never gets a
parentand later operations on it throw:The constructor unconditionally clones any real
Nodechild:That clone was added in 9d19bce only to keep the source tree intact when moving nodes between two roots — but it also fires for brand-new, parent-less nodes that aren't part of any tree, where there's nothing to protect.
Fix
As you suggested on the issue, only clone when the node already belongs to another tree (
node.parentis set); otherwise adopt the instance directly soappend()sets itsparent:Nodes that already have a parent (e.g.
new Root({ nodes: root1.nodes })) are still cloned, so the move-between-trees behavior from 9d19bce is unchanged.Tests
Adds a regression test (
adopts root-less nodes in constructor instead of cloning them) that builds a container from a parent-less node and asserts the original instance is adopted (decl.parentis the new container,container.first === decl) and that operating on the original reference no longer throws. It fails on the current code and passes with this change. The two existing tests from 9d19bce (updates parent in overrides.nodes in constructor,allows to clone nodes) still pass — the full unit suite (653 tests) is green and ESLint is clean.