diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/input.ts new file mode 100644 index 000000000000..778dc9064277 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/input.ts @@ -0,0 +1,2 @@ +const a = 1; +const b = () => a?.b?.c!.d; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/options.json new file mode 100644 index 000000000000..57204f16510c --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-typescript", "proposal-optional-chaining"] +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/output.js new file mode 100644 index 000000000000..a2a72afdb108 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/output.js @@ -0,0 +1,7 @@ +const a = 1; + +const b = () => { + var _a$b; + + return a === null || a === void 0 ? void 0 : (_a$b = a.b) === null || _a$b === void 0 ? void 0 : _a$b.c.d; +}; diff --git a/packages/babel-traverse/src/path/replacement.js b/packages/babel-traverse/src/path/replacement.js index c3add1418745..dc1512805172 100644 --- a/packages/babel-traverse/src/path/replacement.js +++ b/packages/babel-traverse/src/path/replacement.js @@ -50,7 +50,7 @@ export function replaceWithMultiple(nodes: Array) { nodes = this._verifyNodeList(nodes); t.inheritLeadingComments(nodes[0], this.node); t.inheritTrailingComments(nodes[nodes.length - 1], this.node); - pathCache.get(this.parent).delete(this.node); + pathCache.get(this.parent)?.delete(this.node); this.node = this.container[this.key] = null; const paths = this.insertAfter(nodes); @@ -199,6 +199,7 @@ export function _replaceWith(node) { } this.debug(`Replace with ${node?.type}`); + pathCache.get(this.parent)?.set(node, this).delete(this.node); this.node = this.container[this.key] = node; } diff --git a/packages/babel-traverse/test/conversion.js b/packages/babel-traverse/test/conversion.js index 97bd0d4cdc53..54f90cab2666 100644 --- a/packages/babel-traverse/test/conversion.js +++ b/packages/babel-traverse/test/conversion.js @@ -51,6 +51,14 @@ describe("conversion", function () { expect(generateCode(rootPath)).toBe("() => {\n return false;\n};"); }); + it("preserves arrow function body's context", function () { + const rootPath = getPath("() => true").get("expression"); + const body = rootPath.get("body"); + rootPath.ensureBlock(); + body.replaceWithMultiple([t.booleanLiteral(false), t.emptyStatement()]); + expect(generateCode(rootPath)).toBe("() => {\n return false;\n};"); + }); + it("converts for loop with statement body to block", function () { const rootPath = getPath("for (;;) true;"); rootPath.ensureBlock(); diff --git a/packages/babel-traverse/test/replacement.js b/packages/babel-traverse/test/replacement.js index 0109e8cf04c2..e04d831e99f3 100644 --- a/packages/babel-traverse/test/replacement.js +++ b/packages/babel-traverse/test/replacement.js @@ -112,6 +112,33 @@ describe("path/replacement", function () { }); expect(visitCounter).toBe(1); }); + + // https://github.com/babel/babel/issues/12386 + it("updates pathCache with the replaced node", () => { + const ast = parse(`() => (a?.b)?.c`, { + createParenthesizedExpressions: true, + }); + traverse(ast, { + OptionalMemberExpression(path) { + path.node.type = "MemberExpression"; + // force `replaceWith` to replace `path.node` + path.replaceWith(path.node.__clone()); + path.parentPath.ensureBlock(); + + const aQuestionDotBNode = path.node.object.expression; + // avoid traversing to a?.b + aQuestionDotBNode.type = "MemberExpression"; + }, + ParenthesizedExpression(path) { + path.replaceWith(path.node.expression); + }, + }); + expect(generate(ast).code).toMatchInlineSnapshot(` + "() => { + return a.b.c; + };" + `); + }); }); describe("replaceWithMultiple", () => { it("does not add extra parentheses for a JSXElement with a JSXElement parent", () => {