使用 Babel 重命名函数参数而不更改其他相同的标识符

问题描述 投票:0回答:1

我正在开发一个 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

这是我第一次使用 Babel,我以为条件
path.scope.getBinding(path.node.name).kind === 'param'
会起作用,但事实并非如此。

我感谢任何关于如何使用 Babel 实现这一目标的建议或示例。谢谢!

JSFiddle:https://jsfiddle.net/jfbhzLrp/

javascript babeljs abstract-syntax-tree code-transformation
1个回答
0
投票

经过大量的尝试和错误,我能够实现这个功能。希望它对其他人有帮助。

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
  };
}
© www.soinside.com 2019 - 2024. All rights reserved.