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( t.exportNamedDeclaration(
null, null,
Object.keys(refs).map(name => { 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.type === "ImportDeclaration");
assert(statement.specifiers.length === 0); assert(statement.specifiers.length === 0);
statement.specifiers = [t.importNamespaceSpecifier(name)]; statement.specifiers = [t.importNamespaceSpecifier(name)];
this._resultName = t.clone(name); this._resultName = t.cloneNode(name);
return this; return this;
} }
default(name) { default(name) {
@ -59,7 +59,7 @@ export default class ImportBuilder {
assert(statement.type === "ImportDeclaration"); assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0); assert(statement.specifiers.length === 0);
statement.specifiers = [t.importDefaultSpecifier(name)]; statement.specifiers = [t.importDefaultSpecifier(name)];
this._resultName = t.clone(name); this._resultName = t.cloneNode(name);
return this; return this;
} }
named(name, importName) { named(name, importName) {
@ -70,7 +70,7 @@ export default class ImportBuilder {
assert(statement.type === "ImportDeclaration"); assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0); assert(statement.specifiers.length === 0);
statement.specifiers = [t.importSpecifier(name, t.identifier(importName))]; statement.specifiers = [t.importSpecifier(name, t.identifier(importName))];
this._resultName = t.clone(name); this._resultName = t.cloneNode(name);
return this; return this;
} }
@ -86,7 +86,7 @@ export default class ImportBuilder {
"var", "var",
[t.variableDeclarator(name, statement.expression)], [t.variableDeclarator(name, statement.expression)],
); );
this._resultName = t.clone(name); this._resultName = t.cloneNode(name);
return this; return this;
} }

View File

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

View File

@ -309,7 +309,7 @@ function checkDuplicatedNodes(ast) {
if (isByRegenerator(node)) return; if (isByRegenerator(node)) return;
if (nodes.has(node)) { if (nodes.has(node)) {
throw new Error( 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) + JSON.stringify(node, hidePrivateProperties, 2) +
"\nParent:\n" + "\nParent:\n" +
JSON.stringify(parents.get(node), hidePrivateProperties, 2), 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 imps) path.remove();
for (const path of impsBindingRefs) { for (const path of impsBindingRefs) {
const node = t.cloneDeep(dependenciesRefs[path.node.name]); const node = t.cloneNode(dependenciesRefs[path.node.name]);
path.replaceWith(node); path.replaceWith(node);
} }

View File

@ -128,7 +128,7 @@ export default function(api, options) {
t.variableDeclarator(ident, computedNode.key), 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) { function inferBindContext(bind, scope) {
const staticContext = getStaticContext(bind, scope); const staticContext = getStaticContext(bind, scope);
if (staticContext) return t.cloneDeep(staticContext); if (staticContext) return t.cloneNode(staticContext);
const tempId = getTempId(scope); const tempId = getTempId(scope);
if (bind.object) { if (bind.object) {

View File

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

View File

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

View File

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

View File

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

View File

@ -525,7 +525,7 @@ class BlockScoping {
// turn outsideLetReferences into an array // turn outsideLetReferences into an array
const args = values(outsideRefs); 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(); const isSwitch = this.blockPath.isSwitchStatement();

View File

@ -221,7 +221,7 @@ export default function(api, options) {
if (t.isRestElement(prop)) { if (t.isRestElement(prop)) {
this.pushObjectRest(pattern, objRef, prop, i); this.pushObjectRest(pattern, objRef, prop, i);
} else { } 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; array = right;
} }
const item = t.memberExpression(array, t.clone(i), true); const item = t.memberExpression(array, t.cloneNode(i), true);
let assignment; let assignment;
if (t.isVariableDeclaration(left)) { if (t.isVariableDeclaration(left)) {
assignment = left; assignment = left;
@ -44,10 +44,10 @@ export default function(api, options) {
t.variableDeclaration("let", inits), t.variableDeclaration("let", inits),
t.binaryExpression( t.binaryExpression(
"<", "<",
t.clone(i), t.cloneNode(i),
t.memberExpression(t.clone(array), t.identifier("length")), t.memberExpression(t.cloneNode(array), t.identifier("length")),
), ),
t.updateExpression("++", t.clone(i)), t.updateExpression("++", t.cloneNode(i)),
block, block,
), ),
); );

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,12 @@
// @flow // @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 { export default function clone<T: Object>(node: T): T {
if (!node) return node; return cloneNode(node, /* deep */ false);
const newNode = (({}: any): T);
Object.keys(node).forEach(key => {
if (key[0] === "_") return;
newNode[key] = node[key];
});
return newNode;
} }

View File

@ -1,28 +1,12 @@
// @flow // @flow
import cloneNode from "./cloneNode";
/** /**
* Create a deep clone of a `node` and all of it's child nodes * 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 { export default function cloneDeep<T: Object>(node: T): T {
if (!node) return node; return cloneNode(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;
} }

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

View File

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

View File

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