From bfd13c707db4d26629da8eef918f7a768ce466b3 Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Sat, 14 May 2022 21:46:42 +0800 Subject: [PATCH 1/3] fix cloneNode with comments --- packages/babel-types/src/clone/cloneNode.ts | 22 ++++++++++++++++++--- packages/babel-types/test/cloning.js | 21 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/babel-types/src/clone/cloneNode.ts b/packages/babel-types/src/clone/cloneNode.ts index 0e6506a0c752..1a5631cd7d7a 100644 --- a/packages/babel-types/src/clone/cloneNode.ts +++ b/packages/babel-types/src/clone/cloneNode.ts @@ -3,6 +3,7 @@ import type * as t from ".."; import { isFile, isIdentifier } from "../validators/generated"; const has = Function.call.bind(Object.prototype.hasOwnProperty); +const commentsCache: Map = new Map(); // This function will never be called for comments, only for real nodes. function cloneIfNode(obj, deep, withoutLoc) { @@ -32,6 +33,9 @@ export default function cloneNode( ): T { if (!node) return node; + const isTop = !commentsCache.get("inCloning"); + if (isTop) commentsCache.set("inCloning", true); + const { type } = node; const newNode: any = { type: node.type }; @@ -49,6 +53,7 @@ export default function cloneNode( : node.typeAnnotation; } } else if (!has(NODE_FIELDS, type)) { + if (isTop) commentsCache.clear(); throw new Error(`Unknown node type: "${type}"`); } else { for (const field of Object.keys(NODE_FIELDS[type])) { @@ -99,6 +104,8 @@ export default function cloneNode( }; } + if (isTop) commentsCache.clear(); + return newNode; } @@ -110,10 +117,19 @@ function maybeCloneComments( if (!comments || !deep) { return comments; } - return comments.map(({ type, value, loc }) => { + return comments.map(comment => { + const cache = commentsCache.get(comment); + if (cache) return cache; + + const { type, value, loc } = comment; + + const ret = { type, value, loc } as T; if (withoutLoc) { - return { type, value, loc: null } as T; + ret.loc = null; } - return { type, value, loc } as T; + + commentsCache.set(comment, ret); + + return ret; }); } diff --git a/packages/babel-types/test/cloning.js b/packages/babel-types/test/cloning.js index 9ab9151d1842..b9d60d9dd9db 100644 --- a/packages/babel-types/test/cloning.js +++ b/packages/babel-types/test/cloning.js @@ -1,5 +1,6 @@ import * as t from "../lib/index.js"; import { parse } from "@babel/parser"; +import { CodeGenerator } from "@babel/generator"; describe("cloneNode", function () { it("should handle undefined", function () { @@ -151,4 +152,24 @@ describe("cloneNode", function () { expect(cloned.declarations[0].id.innerComments[0].loc).toBe(null); expect(cloned.declarations[0].id.trailingComments[0].loc).toBe(null); }); + + it("should same code after deep cloning", function () { + let code = `//test1 + /*test2*/var/*test3*/ a = 1/*test4*/;//test5 + //test6 + var b; + `; + code = new CodeGenerator(parse(code), { retainLines: true }).generate() + .code; + + const ast = t.cloneNode( + parse(code), + /* deep */ true, + /* withoutLoc */ false, + ); + const newCode = new CodeGenerator(ast, { retainLines: true }).generate() + .code; + + expect(code).toBe(newCode); + }); }); From 582bb23551d9dd22d69979ee5e7618094b83448a Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Tue, 17 May 2022 00:22:47 +0800 Subject: [PATCH 2/3] review --- packages/babel-types/src/clone/cloneNode.ts | 51 +++++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/packages/babel-types/src/clone/cloneNode.ts b/packages/babel-types/src/clone/cloneNode.ts index 1a5631cd7d7a..2f2ac74e2b56 100644 --- a/packages/babel-types/src/clone/cloneNode.ts +++ b/packages/babel-types/src/clone/cloneNode.ts @@ -3,22 +3,21 @@ import type * as t from ".."; import { isFile, isIdentifier } from "../validators/generated"; const has = Function.call.bind(Object.prototype.hasOwnProperty); -const commentsCache: Map = new Map(); // This function will never be called for comments, only for real nodes. -function cloneIfNode(obj, deep, withoutLoc) { +function cloneIfNode(obj, deep, withoutLoc, commentsCache) { if (obj && typeof obj.type === "string") { - return cloneNode(obj, deep, withoutLoc); + return cloneNodeInternal(obj, deep, withoutLoc, commentsCache); } return obj; } -function cloneIfNodeOrArray(obj, deep, withoutLoc) { +function cloneIfNodeOrArray(obj, deep, withoutLoc, commentsCache) { if (Array.isArray(obj)) { - return obj.map(node => cloneIfNode(node, deep, withoutLoc)); + return obj.map(node => cloneIfNode(node, deep, withoutLoc, commentsCache)); } - return cloneIfNode(obj, deep, withoutLoc); + return cloneIfNode(obj, deep, withoutLoc, commentsCache); } /** @@ -31,10 +30,16 @@ export default function cloneNode( deep: boolean = true, withoutLoc: boolean = false, ): T { - if (!node) return node; + return cloneNodeInternal(node, deep, withoutLoc, new Map()); +} - const isTop = !commentsCache.get("inCloning"); - if (isTop) commentsCache.set("inCloning", true); +function cloneNodeInternal( + node: T, + deep: boolean = true, + withoutLoc: boolean = false, + commentsCache: Map, +): T { + if (!node) return node; const { type } = node; const newNode: any = { type: node.type }; @@ -49,11 +54,15 @@ export default function cloneNode( if (has(node, "typeAnnotation")) { newNode.typeAnnotation = deep - ? cloneIfNodeOrArray(node.typeAnnotation, true, withoutLoc) + ? cloneIfNodeOrArray( + node.typeAnnotation, + true, + withoutLoc, + commentsCache, + ) : node.typeAnnotation; } } else if (!has(NODE_FIELDS, type)) { - if (isTop) commentsCache.clear(); throw new Error(`Unknown node type: "${type}"`); } else { for (const field of Object.keys(NODE_FIELDS[type])) { @@ -61,8 +70,18 @@ export default function cloneNode( if (deep) { newNode[field] = isFile(node) && field === "comments" - ? maybeCloneComments(node.comments, deep, withoutLoc) - : cloneIfNodeOrArray(node[field], true, withoutLoc); + ? maybeCloneComments( + node.comments, + deep, + withoutLoc, + commentsCache, + ) + : cloneIfNodeOrArray( + node[field], + true, + withoutLoc, + commentsCache, + ); } else { newNode[field] = node[field]; } @@ -82,6 +101,7 @@ export default function cloneNode( node.leadingComments, deep, withoutLoc, + commentsCache, ); } if (has(node, "innerComments")) { @@ -89,6 +109,7 @@ export default function cloneNode( node.innerComments, deep, withoutLoc, + commentsCache, ); } if (has(node, "trailingComments")) { @@ -96,6 +117,7 @@ export default function cloneNode( node.trailingComments, deep, withoutLoc, + commentsCache, ); } if (has(node, "extra")) { @@ -104,8 +126,6 @@ export default function cloneNode( }; } - if (isTop) commentsCache.clear(); - return newNode; } @@ -113,6 +133,7 @@ function maybeCloneComments( comments: ReadonlyArray | null, deep: boolean, withoutLoc: boolean, + commentsCache: Map, ): ReadonlyArray | null { if (!comments || !deep) { return comments; From 81f540c9c4783e0825c90dac9ed83584968e29ef Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Tue, 17 May 2022 05:49:39 +0800 Subject: [PATCH 3/3] fix typo --- packages/babel-types/test/cloning.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-types/test/cloning.js b/packages/babel-types/test/cloning.js index b9d60d9dd9db..5a3895c28d03 100644 --- a/packages/babel-types/test/cloning.js +++ b/packages/babel-types/test/cloning.js @@ -153,7 +153,7 @@ describe("cloneNode", function () { expect(cloned.declarations[0].id.trailingComments[0].loc).toBe(null); }); - it("should same code after deep cloning", function () { + it("should generate same code after deep cloning", function () { let code = `//test1 /*test2*/var/*test3*/ a = 1/*test4*/;//test5 //test6 @@ -170,6 +170,6 @@ describe("cloneNode", function () { const newCode = new CodeGenerator(ast, { retainLines: true }).generate() .code; - expect(code).toBe(newCode); + expect(newCode).toBe(code); }); });