From fa21565558bd5560cae65752958202e76a6b8c68 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 15 Mar 2020 04:43:28 -0400 Subject: [PATCH] Fix bug with traversing detached node tree --- .../src/index.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/babel-helper-member-expression-to-functions/src/index.js b/packages/babel-helper-member-expression-to-functions/src/index.js index 258dc53d2554..b7482bcdd46c 100644 --- a/packages/babel-helper-member-expression-to-functions/src/index.js +++ b/packages/babel-helper-member-expression-to-functions/src/index.js @@ -56,6 +56,28 @@ function toNonOptional(path, base) { return path.node; } +// Determines if the current path is in a detached tree. This can happen when +// we are iterating on a path, and replace an ancestor with a new node. Babel +// doesn't always stop traversing the old node tree, and that can cause +// inconsistencies. +function isInDetachedTree(path) { + while (path) { + if (path.isProgram()) break; + + const { parentPath, container, listKey } = path; + const parentNode = parentPath.node; + if (listKey) { + if (container !== parentNode[listKey]) return true; + } else { + if (container !== parentNode) return true; + } + + path = parentPath; + } + + return false; +} + const handle = { memoise() { // noop. @@ -65,6 +87,9 @@ const handle = { const { node, parent, parentPath } = member; if (member.isOptionalMemberExpression()) { + // Transforming optional chaining requires we replace ancestors. + if (isInDetachedTree(member)) return; + // We're looking for the end of _this_ optional chain, which is actually // the "rightmost" property access of the chain. This is because // everything up to that property access is "optional".