packages/babel-traverse/src/scope/lib/renamer.js
import Binding from "../binding";
import splitExportDeclaration from "@babel/helper-split-export-declaration";
import * as t from "@babel/types";
const renameVisitor = {
ReferencedIdentifier({ node }, state) {
if (node.name === state.oldName) {
node.name = state.newName;
}
},
Scope(path, state) {
if (
!path.scope.bindingIdentifierEquals(
state.oldName,
state.binding.identifier,
)
) {
path.skip();
}
},
"AssignmentExpression|Declaration|VariableDeclarator"(path, state) {
if (path.isVariableDeclaration()) return;
const ids = path.getOuterBindingIdentifiers();
for (const name in ids) {
if (name === state.oldName) ids[name].name = state.newName;
}
},
};
export default class Renamer {
constructor(binding: Binding, oldName: string, newName: string) {
this.newName = newName;
this.oldName = oldName;
this.binding = binding;
}
oldName: string;
newName: string;
binding: Binding;
maybeConvertFromExportDeclaration(parentDeclar) {
const maybeExportDeclar = parentDeclar.parentPath;
if (!maybeExportDeclar.isExportDeclaration()) {
return;
}
if (
maybeExportDeclar.isExportDefaultDeclaration() &&
!maybeExportDeclar.get("declaration").node.id
) {
return;
}
splitExportDeclaration(maybeExportDeclar);
}
maybeConvertFromClassFunctionDeclaration(path) {
return; // TODO
// retain the `name` of a class/function declaration
// eslint-disable-next-line no-unreachable
if (!path.isFunctionDeclaration() && !path.isClassDeclaration()) return;
if (this.binding.kind !== "hoisted") return;
path.node.id = t.identifier(this.oldName);
path.node._blockHoist = 3;
path.replaceWith(
t.variableDeclaration("let", [
t.variableDeclarator(
t.identifier(this.newName),
t.toExpression(path.node),
),
]),
);
}
maybeConvertFromClassFunctionExpression(path) {
return; // TODO
// retain the `name` of a class/function expression
// eslint-disable-next-line no-unreachable
if (!path.isFunctionExpression() && !path.isClassExpression()) return;
if (this.binding.kind !== "local") return;
path.node.id = t.identifier(this.oldName);
this.binding.scope.parent.push({
id: t.identifier(this.newName),
});
path.replaceWith(
t.assignmentExpression("=", t.identifier(this.newName), path.node),
);
}
rename(block?) {
const { binding, oldName, newName } = this;
const { scope, path } = binding;
const parentDeclar = path.find(
path =>
path.isDeclaration() ||
path.isFunctionExpression() ||
path.isClassExpression(),
);
if (parentDeclar) {
const bindingIds = parentDeclar.getOuterBindingIdentifiers();
if (bindingIds[oldName] === binding.identifier) {
// When we are renaming an exported identifier, we need to ensure that
// the exported binding keeps the old name.
this.maybeConvertFromExportDeclaration(parentDeclar);
}
}
scope.traverse(block || scope.block, renameVisitor, this);
if (!block) {
scope.removeOwnBinding(oldName);
scope.bindings[newName] = binding;
this.binding.identifier.name = newName;
}
if (binding.type === "hoisted") {
// https://github.com/babel/babel/issues/2435
// todo: hoist and convert function to a let
}
if (parentDeclar) {
this.maybeConvertFromClassFunctionDeclaration(parentDeclar);
this.maybeConvertFromClassFunctionExpression(parentDeclar);
}
}
}