我正在开发一个 JavaScript 项目,我需要能够以编程方式重命名函数参数。我面临的具体挑战是区分函数参数和函数体内同名的其他变量。我的目标是仅重命名参数,而不影响函数范围内可能具有相同名称的任何其他标识符。
这是我尝试过的片段:
JSFiddle:https://jsfiddle.net/jfbhzLrp/
window.onload = function() {
const code = `function process(x, y) { return { z: x * y, x: 4 }; }`;
const result = Babel.transform(code, {
plugins: [{
visitor: {
Identifier(path) {
if (path.node.name === 'x' && path.scope.getBinding(path.node.name).kind === 'param') {
path.node.name = 'a';
}
},
FunctionDeclaration(path) {
path.node.params.forEach(param => {
if (param.name === 'x') {
param.name = 'a';
}
});
},
}
}]
});
console.log(result.code);
};
在上面的脚本中,我想确保只有函数声明中的参数
x
及其在函数体内的用法被重命名为a
,但函数体内名为x
的任何变量/对象键保持不变。
这就是我所期望的:
function process(a, y) {
return {
z: a * y,
x: 4
};
}
但这就是我得到的:
function process(a, y) {
return {
z: a * y,
a: 4
};
}
正如您所看到的,返回对象中的
x
键也已转换为 a
。path.scope.getBinding(path.node.name).kind === 'param'
会起作用,但事实并非如此。
我感谢任何关于如何使用 Babel 实现这一目标的建议或示例。谢谢!
JSFiddle:https://jsfiddle.net/jfbhzLrp/
经过大量的尝试和错误,我能够实现这个功能。希望它对其他人有帮助。
function renameFunctionParam(func, paramName, newName) {
if (typeof func !== 'string') {
console.error('\'func\' not a string', func, paramName, newName);
throw new Error('\'func\' not a string');
}
let topLevelPath;
const result = Babel.transform(func, {
plugins: [{
visitor: {
Identifier(path) {
const isFunctionParam = path.scope.getBinding(paramName)?.kind === 'param';
const isObjectProperty = path.parent.type === 'ObjectProperty' && path.parent.key === path.node;
const isNestedFunction = path.scope.parent?.block?.type === 'FunctionExpression' ||
path.scope.parent?.block?.type === 'ArrowFunctionExpression';
const isSameScope = path.scope.block === topLevelPath.scope.block;
if (path.node.name === paramName && isFunctionParam && !isObjectProperty && !isNestedFunction && isSameScope) {
path.node.name = newName;
}
},
FunctionDeclaration(path) {
if (topLevelPath)
return;
topLevelPath = path;
path.node.params.forEach(param => {
if (param.name === paramName) {
param.name = newName;
}
});
},
FunctionExpression(path) {
if (topLevelPath)
return;
topLevelPath = path;
path.node.params.forEach(param => {
if (param.name === paramName) {
param.name = newName;
}
});
},
ArrowFunctionExpression(path) {
if (topLevelPath)
return;
topLevelPath = path;
path.node.params.forEach(param => {
if (param.name === paramName) {
param.name = newName;
}
});
},
},
}],
});
return result.code;
}
使用方法
const code = renameFunctionParam(`function process(x, y) {
const localVariable = x - 2;
const nestedFunc = (x) => x * 2;
return { z: x * y, x: 4 };
}`, 'x', 'a')
它应该产生:
function process(a, y) {
const localVariable = a - 2;
const nestedFunc = x => x * 2;
return {
z: a * y,
x: 4
};
}