From eea3065039428bde0e88a773032957b5d97719f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 24 Nov 2020 15:07:20 -0500 Subject: [PATCH] Update pathCache in `NodePath#_replaceWith` (#12391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add tests on regression 12386 * fix: update cache on _replaceWith * fix: pathCache in replaceWithMultiple could be nullish * Update packages/babel-traverse/src/path/replacement.js Co-authored-by: Nicolò Ribaudo * test: add replaceWith test to traverse Co-authored-by: Brian Ng Co-authored-by: Nicolò Ribaudo --- .../non-null-in-optional-chain/input.ts | 2 ++ .../non-null-in-optional-chain/options.json | 3 +++ .../non-null-in-optional-chain/output.js | 7 +++++ .../babel-traverse/src/path/replacement.js | 3 ++- packages/babel-traverse/test/conversion.js | 8 ++++++ packages/babel-traverse/test/replacement.js | 27 +++++++++++++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/non-null-in-optional-chain/output.js 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", () => {