Add cloneDeepWithoutLoc (#10680)
* feat: add cloneDeepWithoutLoc * fix: sort functions alphabetically * doc: add documentation for the withoutLoc parameter * test: add a test for shallow cloneWithoutLoc * test: add loc object to node and fix test * fix: set loc object on deeper node * test: check loc on deeper node is null * doc: adjust withoutLoc documentation * fix: add withoutLoc param to deep clones * fix: apply cloneIfNodeOrArray for leadingComments, innerComments and trailingComments * test: add test for leadingComments, innerComments and trailingComments * Cleanup PR Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
parent
f232d4d109
commit
286aaeadd9
@ -135,7 +135,8 @@ lines.push(
|
||||
// clone/
|
||||
`declare function clone<T>(n: T): T;`,
|
||||
`declare function cloneDeep<T>(n: T): T;`,
|
||||
`declare function cloneNode<T>(n: T, deep?: boolean): T;`,
|
||||
`declare function cloneDeepWithoutLoc<T>(n: T): T;`,
|
||||
`declare function cloneNode<T>(n: T, deep?: boolean, withoutLoc?: boolean): T;`,
|
||||
`declare function cloneWithoutLoc<T>(n: T): T;`,
|
||||
|
||||
// comments/
|
||||
|
||||
@ -154,7 +154,8 @@ lines.push(
|
||||
// clone/
|
||||
`export function clone<T extends Node>(n: T): T;`,
|
||||
`export function cloneDeep<T extends Node>(n: T): T;`,
|
||||
`export function cloneNode<T extends Node>(n: T, deep?: boolean): T;`,
|
||||
`export function cloneDeepWithoutLoc<T extends Node>(n: T): T;`,
|
||||
`export function cloneNode<T extends Node>(n: T, deep?: boolean, withoutLoc?: boolean): T;`,
|
||||
`export function cloneWithoutLoc<T extends Node>(n: T): T;`,
|
||||
|
||||
// comments/
|
||||
|
||||
10
packages/babel-types/src/clone/cloneDeepWithoutLoc.js
Normal file
10
packages/babel-types/src/clone/cloneDeepWithoutLoc.js
Normal file
@ -0,0 +1,10 @@
|
||||
// @flow
|
||||
import cloneNode from "./cloneNode";
|
||||
/**
|
||||
* Create a deep clone of a `node` and all of it's child nodes
|
||||
* including only properties belonging to the node.
|
||||
* excluding `_private` and location properties.
|
||||
*/
|
||||
export default function cloneDeepWithoutLoc<T: Object>(node: T): T {
|
||||
return cloneNode(node, /* deep */ true, /* withoutLoc */ true);
|
||||
}
|
||||
@ -2,33 +2,32 @@ import { NODE_FIELDS } from "../definitions";
|
||||
|
||||
const has = Function.call.bind(Object.prototype.hasOwnProperty);
|
||||
|
||||
function cloneIfNode(obj, deep) {
|
||||
if (
|
||||
obj &&
|
||||
typeof obj.type === "string" &&
|
||||
// CommentLine and CommentBlock are used in File#comments, but they are
|
||||
// not defined in babel-types
|
||||
obj.type !== "CommentLine" &&
|
||||
obj.type !== "CommentBlock"
|
||||
) {
|
||||
return cloneNode(obj, deep);
|
||||
// This function will never be called for comments, only for real nodes.
|
||||
function cloneIfNode(obj, deep, withoutLoc) {
|
||||
if (obj && typeof obj.type === "string") {
|
||||
return cloneNode(obj, deep, withoutLoc);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function cloneIfNodeOrArray(obj, deep) {
|
||||
function cloneIfNodeOrArray(obj, deep, withoutLoc) {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(node => cloneIfNode(node, deep));
|
||||
return obj.map(node => cloneIfNode(node, deep, withoutLoc));
|
||||
}
|
||||
return cloneIfNode(obj, deep);
|
||||
return cloneIfNode(obj, deep, withoutLoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of a `node` including only properties belonging to the node.
|
||||
* If the second parameter is `false`, cloneNode performs a shallow clone.
|
||||
* If the third parameter is true, the cloned nodes exclude location properties.
|
||||
*/
|
||||
export default function cloneNode<T: Object>(node: T, deep: boolean = true): T {
|
||||
export default function cloneNode<T: Object>(
|
||||
node: T,
|
||||
deep: boolean = true,
|
||||
withoutLoc: boolean = false,
|
||||
): T {
|
||||
if (!node) return node;
|
||||
|
||||
const { type } = node;
|
||||
@ -44,7 +43,7 @@ export default function cloneNode<T: Object>(node: T, deep: boolean = true): T {
|
||||
|
||||
if (has(node, "typeAnnotation")) {
|
||||
newNode.typeAnnotation = deep
|
||||
? cloneIfNodeOrArray(node.typeAnnotation, true)
|
||||
? cloneIfNodeOrArray(node.typeAnnotation, true, withoutLoc)
|
||||
: node.typeAnnotation;
|
||||
}
|
||||
} else if (!has(NODE_FIELDS, type)) {
|
||||
@ -52,24 +51,45 @@ export default function cloneNode<T: Object>(node: T, deep: boolean = true): T {
|
||||
} else {
|
||||
for (const field of Object.keys(NODE_FIELDS[type])) {
|
||||
if (has(node, field)) {
|
||||
newNode[field] = deep
|
||||
? cloneIfNodeOrArray(node[field], true)
|
||||
: node[field];
|
||||
if (deep) {
|
||||
newNode[field] =
|
||||
type === "File" && field === "comments"
|
||||
? maybeCloneComments(node.comments, deep, withoutLoc)
|
||||
: cloneIfNodeOrArray(node[field], true, withoutLoc);
|
||||
} else {
|
||||
newNode[field] = node[field];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has(node, "loc")) {
|
||||
newNode.loc = node.loc;
|
||||
if (withoutLoc) {
|
||||
newNode.loc = null;
|
||||
} else {
|
||||
newNode.loc = node.loc;
|
||||
}
|
||||
}
|
||||
if (has(node, "leadingComments")) {
|
||||
newNode.leadingComments = node.leadingComments;
|
||||
newNode.leadingComments = maybeCloneComments(
|
||||
node.leadingComments,
|
||||
deep,
|
||||
withoutLoc,
|
||||
);
|
||||
}
|
||||
if (has(node, "innerComments")) {
|
||||
newNode.innerComments = node.innerComments;
|
||||
newNode.innerComments = maybeCloneComments(
|
||||
node.innerComments,
|
||||
deep,
|
||||
withoutLoc,
|
||||
);
|
||||
}
|
||||
if (has(node, "trailingComments")) {
|
||||
newNode.trailingComments = node.trailingComments;
|
||||
newNode.trailingComments = maybeCloneComments(
|
||||
node.trailingComments,
|
||||
deep,
|
||||
withoutLoc,
|
||||
);
|
||||
}
|
||||
if (has(node, "extra")) {
|
||||
newNode.extra = {
|
||||
@ -79,3 +99,11 @@ export default function cloneNode<T: Object>(node: T, deep: boolean = true): T {
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
function cloneCommentsWithoutLoc<T: Object>(comments: T[]): T {
|
||||
return comments.map(({ type, value }) => ({ type, value, loc: null }));
|
||||
}
|
||||
|
||||
function maybeCloneComments(comments, deep, withoutLoc) {
|
||||
return deep && withoutLoc ? cloneCommentsWithoutLoc(comments) : comments;
|
||||
}
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
// @flow
|
||||
import clone from "./clone";
|
||||
|
||||
import cloneNode from "./cloneNode";
|
||||
/**
|
||||
* Create a shallow clone of a `node` excluding `_private` and location properties.
|
||||
*/
|
||||
export default function cloneWithoutLoc<T: Object>(node: T): T {
|
||||
const newNode = clone(node);
|
||||
newNode.loc = null;
|
||||
|
||||
return newNode;
|
||||
return cloneNode(node, /* deep */ false, /* withoutLoc */ true);
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ export * from "./builders/generated";
|
||||
export { default as cloneNode } from "./clone/cloneNode";
|
||||
export { default as clone } from "./clone/clone";
|
||||
export { default as cloneDeep } from "./clone/cloneDeep";
|
||||
export { default as cloneDeepWithoutLoc } from "./clone/cloneDeepWithoutLoc";
|
||||
export { default as cloneWithoutLoc } from "./clone/cloneWithoutLoc";
|
||||
|
||||
// comments
|
||||
|
||||
@ -72,4 +72,72 @@ describe("cloneNode", function() {
|
||||
node.declarations[0].id.typeAnnotation,
|
||||
);
|
||||
});
|
||||
|
||||
it("should support shallow cloning without loc", function() {
|
||||
const node = t.variableDeclaration("let", [
|
||||
t.variableDeclarator({
|
||||
...t.identifier("value"),
|
||||
typeAnnotation: t.anyTypeAnnotation(),
|
||||
}),
|
||||
]);
|
||||
node.loc = {};
|
||||
const cloned = t.cloneNode(node, /* deep */ false, /* withoutLoc */ true);
|
||||
expect(cloned.loc).toBeNull();
|
||||
});
|
||||
|
||||
it("should support deep cloning without loc", function() {
|
||||
const node = t.variableDeclaration("let", [
|
||||
t.variableDeclarator({
|
||||
...t.identifier("value"),
|
||||
typeAnnotation: t.anyTypeAnnotation(),
|
||||
}),
|
||||
]);
|
||||
node.loc = {};
|
||||
node.declarations[0].id.loc = {};
|
||||
const cloned = t.cloneNode(node, /* deep */ true, /* withoutLoc */ true);
|
||||
expect(cloned.loc).toBeNull();
|
||||
expect(cloned.declarations[0].id.loc).toBeNull();
|
||||
});
|
||||
|
||||
it("should support deep cloning for comments", function() {
|
||||
const node = t.variableDeclaration("let", [
|
||||
t.variableDeclarator({
|
||||
...t.identifier("value"),
|
||||
leadingComments: [{ loc: {} }],
|
||||
innerComments: [{ loc: {} }],
|
||||
trailingComments: [{ loc: {} }],
|
||||
}),
|
||||
]);
|
||||
node.loc = {};
|
||||
node.declarations[0].id.loc = {};
|
||||
|
||||
const cloned = t.cloneNode(node, /* deep */ true, /* withoutLoc */ false);
|
||||
expect(cloned.declarations[0].id.leadingComments[0].loc).toBe(
|
||||
node.declarations[0].id.leadingComments[0].loc,
|
||||
);
|
||||
expect(cloned.declarations[0].id.innerComments[0].loc).toBe(
|
||||
node.declarations[0].id.innerComments[0].loc,
|
||||
);
|
||||
expect(cloned.declarations[0].id.trailingComments[0].loc).toBe(
|
||||
node.declarations[0].id.trailingComments[0].loc,
|
||||
);
|
||||
});
|
||||
|
||||
it("should support deep cloning for comments without loc", function() {
|
||||
const node = t.variableDeclaration("let", [
|
||||
t.variableDeclarator({
|
||||
...t.identifier("value"),
|
||||
leadingComments: [{ loc: {} }],
|
||||
innerComments: [{ loc: {} }],
|
||||
trailingComments: [{ loc: {} }],
|
||||
}),
|
||||
]);
|
||||
node.loc = {};
|
||||
node.declarations[0].id.loc = {};
|
||||
|
||||
const cloned = t.cloneNode(node, /* deep */ true, /* withoutLoc */ true);
|
||||
expect(cloned.declarations[0].id.leadingComments[0].loc).toBe(null);
|
||||
expect(cloned.declarations[0].id.innerComments[0].loc).toBe(null);
|
||||
expect(cloned.declarations[0].id.trailingComments[0].loc).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user