Add t.cloneNode and deprecate t.clone and t.cloneDeep (#7149)

This commit is contained in:
Nicolò Ribaudo 2018-01-11 19:31:48 +01:00
parent dde9274986
commit 63ae923987
24 changed files with 170 additions and 123 deletions

View File

@ -69,7 +69,7 @@ function buildModule(whitelist) {
t.exportNamedDeclaration(
null,
Object.keys(refs).map(name => {
return t.exportSpecifier(t.clone(refs[name]), t.identifier(name));
return t.exportSpecifier(t.cloneNode(refs[name]), t.identifier(name));
}),
),
);

View File

@ -50,7 +50,7 @@ export default class ImportBuilder {
assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0);
statement.specifiers = [t.importNamespaceSpecifier(name)];
this._resultName = t.clone(name);
this._resultName = t.cloneNode(name);
return this;
}
default(name) {
@ -59,7 +59,7 @@ export default class ImportBuilder {
assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0);
statement.specifiers = [t.importDefaultSpecifier(name)];
this._resultName = t.clone(name);
this._resultName = t.cloneNode(name);
return this;
}
named(name, importName) {
@ -70,7 +70,7 @@ export default class ImportBuilder {
assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0);
statement.specifiers = [t.importSpecifier(name, t.identifier(importName))];
this._resultName = t.clone(name);
this._resultName = t.cloneNode(name);
return this;
}
@ -86,7 +86,7 @@ export default class ImportBuilder {
"var",
[t.variableDeclarator(name, statement.expression)],
);
this._resultName = t.clone(name);
this._resultName = t.cloneNode(name);
return this;
}

View File

@ -128,7 +128,7 @@ export function buildNamespaceInitStatements(
statements.push(
template.statement`var NAME = SOURCE;`({
NAME: localName,
SOURCE: t.cloneDeep(srcNamespace),
SOURCE: t.cloneNode(srcNamespace),
}),
);
}
@ -150,14 +150,14 @@ export function buildNamespaceInitStatements(
: template.statement`EXPORTS.NAME = NAMESPACE;`)({
EXPORTS: metadata.exportName,
NAME: exportName,
NAMESPACE: t.cloneDeep(srcNamespace),
NAMESPACE: t.cloneNode(srcNamespace),
}),
);
}
if (sourceMetadata.reexportAll) {
const statement = buildNamespaceReexport(
metadata,
t.cloneDeep(srcNamespace),
t.cloneNode(srcNamespace),
loose,
);
statement.loc = sourceMetadata.reexportAll.loc;
@ -191,7 +191,7 @@ const buildReexportsFromMeta = (meta, metadata, loose) => {
templateForCurrentMode({
EXPORTS: meta.exportName,
EXPORT_NAME: exportName,
NAMESPACE: t.cloneDeep(namespace),
NAMESPACE: t.cloneNode(namespace),
IMPORT_NAME: importName,
}),
);

View File

@ -309,7 +309,7 @@ function checkDuplicatedNodes(ast) {
if (isByRegenerator(node)) return;
if (nodes.has(node)) {
throw new Error(
"Do not reuse nodes. Use `t.clone` or `t.cloneDeep` to copy them.\n" +
"Do not reuse nodes. Use `t.cloneNode` to copy them.\n" +
JSON.stringify(node, hidePrivateProperties, 2) +
"\nParent:\n" +
JSON.stringify(parents.get(node), hidePrivateProperties, 2),

View File

@ -215,7 +215,7 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
for (const path of imps) path.remove();
for (const path of impsBindingRefs) {
const node = t.cloneDeep(dependenciesRefs[path.node.name]);
const node = t.cloneNode(dependenciesRefs[path.node.name]);
path.replaceWith(node);
}

View File

@ -128,7 +128,7 @@ export default function(api, options) {
t.variableDeclarator(ident, computedNode.key),
]),
);
computedNode.key = t.clone(ident);
computedNode.key = t.cloneNode(ident);
}
}

View File

@ -17,7 +17,7 @@ export default function() {
function inferBindContext(bind, scope) {
const staticContext = getStaticContext(bind, scope);
if (staticContext) return t.cloneDeep(staticContext);
if (staticContext) return t.cloneNode(staticContext);
const tempId = getTempId(scope);
if (bind.object) {

View File

@ -15,7 +15,11 @@ export default function(api, { loose = false }) {
const ref = scope.generateUidIdentifierBasedOnNode(node.left);
scope.push({ id: ref });
const assignment = t.assignmentExpression("=", t.clone(ref), node.left);
const assignment = t.assignmentExpression(
"=",
t.cloneNode(ref),
node.left,
);
path.replaceWith(
t.conditionalExpression(
@ -28,11 +32,11 @@ export default function(api, { loose = false }) {
t.binaryExpression("!==", assignment, t.nullLiteral()),
t.binaryExpression(
"!==",
t.clone(ref),
t.cloneNode(ref),
scope.buildUndefinedNode(),
),
),
t.clone(ref),
t.cloneNode(ref),
node.right,
),
);

View File

@ -74,7 +74,7 @@ export default function(api, opts) {
const props = path.get("properties");
const last = props[props.length - 1];
t.assertRestElement(last.node);
const restElement = t.clone(last.node);
const restElement = t.cloneNode(last.node);
last.remove();
const impureComputedPropertyDeclarators = replaceImpureComputedKeys(path);
@ -291,7 +291,7 @@ export default function(api, opts) {
);
}
const nodeWithoutSpread = t.clone(path.node);
const nodeWithoutSpread = t.cloneNode(path.node);
nodeWithoutSpread.right = ref;
nodes.push(t.expressionStatement(nodeWithoutSpread));
nodes.push(

View File

@ -73,13 +73,13 @@ export default function(api, options) {
replacementPath.replaceWith(
t.conditionalExpression(
loose
? t.binaryExpression("==", t.clone(check), t.nullLiteral())
? t.binaryExpression("==", t.cloneNode(check), t.nullLiteral())
: t.logicalExpression(
"||",
t.binaryExpression("===", t.clone(check), t.nullLiteral()),
t.binaryExpression("===", t.cloneNode(check), t.nullLiteral()),
t.binaryExpression(
"===",
t.clone(ref),
t.cloneNode(ref),
scope.buildUndefinedNode(),
),
),

View File

@ -13,7 +13,7 @@ export default function(api, options) {
let wrapAsync = state.methodWrapper;
if (wrapAsync) {
wrapAsync = t.cloneDeep(wrapAsync);
wrapAsync = t.cloneNode(wrapAsync);
} else {
wrapAsync = state.methodWrapper = addNamed(path, method, module);
}

View File

@ -525,7 +525,7 @@ class BlockScoping {
// turn outsideLetReferences into an array
const args = values(outsideRefs);
const params = args.map(id => t.clone(id));
const params = args.map(id => t.cloneNode(id));
const isSwitch = this.blockPath.isSwitchStatement();

View File

@ -221,7 +221,7 @@ export default function(api, options) {
if (t.isRestElement(prop)) {
this.pushObjectRest(pattern, objRef, prop, i);
} else {
this.pushObjectProperty(prop, t.clone(objRef));
this.pushObjectProperty(prop, t.cloneNode(objRef));
}
}
}

View File

@ -25,7 +25,7 @@ export default function(api, options) {
array = right;
}
const item = t.memberExpression(array, t.clone(i), true);
const item = t.memberExpression(array, t.cloneNode(i), true);
let assignment;
if (t.isVariableDeclaration(left)) {
assignment = left;
@ -44,10 +44,10 @@ export default function(api, options) {
t.variableDeclaration("let", inits),
t.binaryExpression(
"<",
t.clone(i),
t.memberExpression(t.clone(array), t.identifier("length")),
t.cloneNode(i),
t.memberExpression(t.cloneNode(array), t.identifier("length")),
),
t.updateExpression("++", t.clone(i)),
t.updateExpression("++", t.cloneNode(i)),
block,
),
);

View File

@ -63,7 +63,7 @@ export default function(api, options) {
let cached = cache.get(key);
if (cached) {
cached = t.cloneDeep(cached);
cached = t.cloneNode(cached);
} else {
cached = addDefault(file.path, source, {
importedInterop: "uncompiled",

View File

@ -107,7 +107,7 @@ export default function(api, options) {
callee.object = t.assignmentExpression("=", temp, callee.object);
contextLiteral = temp;
} else {
contextLiteral = t.cloneDeep(callee.object);
contextLiteral = t.cloneNode(callee.object);
}
t.appendToMemberExpression(callee, t.identifier("apply"));
} else {

View File

@ -72,7 +72,7 @@ export default function(api, options) {
let templateObject = this.templates.get(name);
if (templateObject) {
templateObject = t.clone(templateObject);
templateObject = t.cloneNode(templateObject);
} else {
const programPath = path.find(p => p.isProgram());
templateObject = programPath.scope.generateUidIdentifier(

View File

@ -8,7 +8,7 @@ export default function populatePlaceholders(
metadata: Metadata,
replacements: TemplateReplacements,
): BabelNodeFile {
const ast = t.cloneDeep(metadata.ast);
const ast = t.cloneNode(metadata.ast);
if (replacements) {
metadata.placeholders.forEach(placeholder => {
@ -57,9 +57,9 @@ function applyReplacement(
// once to avoid injecting the same node multiple times.
if (placeholder.isDuplicate) {
if (Array.isArray(replacement)) {
replacement = replacement.map(node => t.cloneDeep(node));
replacement = replacement.map(node => t.cloneNode(node));
} else if (typeof replacement === "object") {
replacement = t.cloneDeep(replacement);
replacement = t.cloneNode(replacement);
}
}

View File

@ -1,16 +1,12 @@
// @flow
import cloneNode from "./cloneNode";
/**
* Create a shallow clone of a `node` excluding `_private` properties.
* Create a shallow clone of a `node`, including only
* properties belonging to the node.
* @deprecated Use t.cloneNode instead.
*/
export default function clone<T: Object>(node: T): T {
if (!node) return node;
const newNode = (({}: any): T);
Object.keys(node).forEach(key => {
if (key[0] === "_") return;
newNode[key] = node[key];
});
return newNode;
return cloneNode(node, /* deep */ false);
}

View File

@ -1,28 +1,12 @@
// @flow
import cloneNode from "./cloneNode";
/**
* Create a deep clone of a `node` and all of it's child nodes
* excluding `_private` properties.
* including only properties belonging to the node.
* @deprecated Use t.cloneNode instead.
*/
export default function cloneDeep<T: Object>(node: T): T {
if (!node) return node;
const newNode = (({}: any): T);
Object.keys(node).forEach(key => {
if (key[0] === "_") return;
let val = node[key];
if (val) {
if (val.type) {
val = cloneDeep(val);
} else if (Array.isArray(val)) {
val = val.map(cloneDeep);
}
}
newNode[key] = val;
});
return newNode;
return cloneNode(node);
}

View File

@ -0,0 +1,69 @@
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);
}
return obj;
}
function cloneIfNodeOrArray(obj, deep) {
if (Array.isArray(obj)) {
return obj.map(node => cloneIfNode(node, deep));
}
return cloneIfNode(obj, deep);
}
/**
* Create a clone of a `node` including only properties belonging to the node.
* If the second parameter is `false`, cloneNode performs a shallow clone.
*/
export default function cloneNode<T: Object>(node: T, deep: boolean = true): T {
if (!node) return node;
const { type } = node;
const newNode = (({ type }: any): T);
// Special-case identifiers since they are the most cloned nodes.
if (type === "Identifier") {
newNode.name = node.name;
} else if (!has(NODE_FIELDS, type)) {
throw new Error(`Unknown node type: "${type}"`);
} else {
for (const field of Object.keys(NODE_FIELDS[type])) {
if (has(node, field)) {
newNode[field] = deep
? cloneIfNodeOrArray(node[field], true)
: node[field];
}
}
}
if (has(node, "loc")) {
newNode.loc = node.loc;
}
if (has(node, "leadingComments")) {
newNode.leadingComments = node.leadingComments;
}
if (has(node, "innerComments")) {
newNode.innerComments = node.innerCmments;
}
if (has(node, "trailingComments")) {
newNode.trailingComments = node.trailingComments;
}
if (has(node, "extra")) {
newNode.extra = Object.assign({}, node.extra);
}
return newNode;
}

View File

@ -1,6 +1,6 @@
// @flow
import { isIdentifier, isStringLiteral } from "../validators/generated";
import cloneDeep from "../clone/cloneDeep";
import cloneNode from "../clone/cloneNode";
import removePropertiesDeep from "../modifications/removePropertiesDeep";
export default function toKeyAlias(
@ -16,7 +16,7 @@ export default function toKeyAlias(
} else if (isStringLiteral(key)) {
alias = JSON.stringify(key.value);
} else {
alias = JSON.stringify(removePropertiesDeep(cloneDeep(key)));
alias = JSON.stringify(removePropertiesDeep(cloneNode(key)));
}
if (node.computed) {

View File

@ -17,6 +17,7 @@ export {
export * from "./builders/generated";
// clone
export { default as cloneNode } from "./clone/cloneNode";
export { default as clone } from "./clone/clone";
export { default as cloneDeep } from "./clone/cloneDeep";
export { default as cloneWithoutLoc } from "./clone/cloneWithoutLoc";

View File

@ -2,68 +2,61 @@ import * as t from "../lib";
import assert from "assert";
import { parse } from "babylon";
suite("cloning", function() {
suite("clone", function() {
it("should handle undefined", function() {
const node = undefined;
const cloned = t.clone(node);
assert(cloned === undefined);
});
it("should handle null", function() {
const node = null;
const cloned = t.clone(node);
assert(cloned === null);
});
it("should handle simple cases", function() {
const node = t.arrayExpression([null, t.identifier("a")]);
const cloned = t.clone(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
suite("cloneNode", function() {
it("should handle undefined", function() {
const node = undefined;
const cloned = t.cloneNode(node);
assert(cloned === undefined);
});
suite("cloneDeep", function() {
it("should handle undefined", function() {
const node = undefined;
const cloned = t.cloneDeep(node);
assert(cloned === undefined);
});
it("should handle null", function() {
const node = null;
const cloned = t.cloneNode(node);
assert(cloned === null);
});
it("should handle null", function() {
const node = null;
const cloned = t.cloneDeep(node);
assert(cloned === null);
});
it("should handle simple cases", function() {
const node = t.identifier("a");
const cloned = t.cloneNode(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
it("should handle simple cases", function() {
const node = t.arrayExpression([null, t.identifier("a")]);
const cloned = t.cloneDeep(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
it("should handle full programs", function() {
const file = parse("1 + 1");
const cloned = t.cloneNode(file);
assert(file !== cloned);
assert(
file.program.body[0].expression.right !==
cloned.program.body[0].expression.right,
);
assert(
file.program.body[0].expression.left !==
cloned.program.body[0].expression.left,
);
assert(t.isNodesEquivalent(file, cloned) === true);
});
it("should handle full programs", function() {
const node = parse("1 + 1");
const cloned = t.cloneDeep(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
it("should handle complex programs", function() {
const program = "'use strict'; function lol() { wow();return 1; }";
const node = parse(program);
const cloned = t.cloneNode(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
it("should handle complex programs", function() {
const program = "'use strict'; function lol() { wow();return 1; }";
const node = parse(program);
const cloned = t.cloneDeep(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
it("should handle missing array element", function() {
const node = parse("[,0]");
const cloned = t.cloneNode(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
it("should handle missing array element", function() {
const node = parse("[,0]");
const cloned = t.cloneDeep(node);
assert(node !== cloned);
assert(t.isNodesEquivalent(node, cloned) === true);
});
it("should support shallow cloning", function() {
const node = t.memberExpression(t.identifier("foo"), t.identifier("bar"));
const cloned = t.cloneNode(node, /* deep */ false);
assert.notStrictEqual(node, cloned);
assert.strictEqual(node.object, cloned.object);
assert.strictEqual(node.property, cloned.property);
});
});