与社区流行工具对比

与编写 babel 插件对比

对于下面这段 js 代码

import a from 'a';
console.log('get A');
var b = console.log();
console.log.bind();
var c = console.log;
console.log = func;

我们希望对不同的 console.log 做不同的处理,变成下面这样

import a from 'a';
var b = void 0;
console.log.bind()
var c = function(){};
console.log = func
  • Babel 插件实现的核心代码:
// 代码来源:https://zhuanlan.zhihu.com/p/32189701
module.exports = function({ types: t }) {
return {
    name: "transform-remove-console",
    visitor: {
    CallExpression(path, state) {
        const callee = path.get("callee");

        if (!callee.isMemberExpression()) return;

        if (isIncludedConsole(callee, state.opts.exclude)) {
        // console.log()
        if (path.parentPath.isExpressionStatement()) {
            path.remove();
        } else {
        //var a = console.log()
            path.replaceWith(createVoid0());
        }
        } else if (isIncludedConsoleBind(callee, state.opts.exclude)) {
        // console.log.bind()
            path.replaceWith(createNoop());
        }
    },
    MemberExpression: {
        exit(path, state) {
        if (
            isIncludedConsole(path, state.opts.exclude) &&
            !path.parentPath.isMemberExpression()
        ) {
        //console.log = func
            if (
            path.parentPath.isAssignmentExpression() &&
            path.parentKey === "left"
            ) {
            path.parentPath.get("right").replaceWith(createNoop());
            } else {
            //var a = console.log
            path.replaceWith(createNoop());
            }
        }
        }
    }
    }
};

其中 isIncludedConsole、isIncludedConsoleBind、createNoop 等方法还需额外开发引入

  • 用 GoGoCode 实现:
$(code)
  .replace(`var $_$ = console.log()`, `var $_$ = void 0`)
  .replace(`var $_$ = console.log`, `var $_$ = function(){}`)
  .find(`console.log()`)
  .remove()
  .generate();

几行代码即可完成,可读性非常强

与 jscodeshift 工具对比

对于下面这段代码:

import car from 'car';

const suv = car.factory('white', 'Kia', 'Sorento', 2010, 50000, null, true);
const truck = car.factory(
  'silver',
  'Toyota',
  'Tacoma',
  2006,
  100000,
  true,
  true
);

我们希望将函数的多个入参封装为一个对象传入,变成下面这样

import car from 'car';
const suv = car.factory({
  color: 'white',
  make: 'Kia',
  model: 'Sorento',
  year: 2010,
  miles: 50000,
  bedliner: null,
  alarm: true
});
const truck = car.factory({
  color: 'silver',
  make: 'Toyota',
  model: 'Tacoma',
  year: 2006,
  miles: 100000,
  bedliner: true,
  alarm: true
});
  • 用 jscodeshift 实现的核心代码:
// 代码来源:https://www.toptal.com/javascript/write-code-to-rewrite-your-code
export default (fileInfo, api) => {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  // find declaration for "car" import
  const importDeclaration = root.find(j.ImportDeclaration, {
    source: {
      type: 'Literal',
      value: 'car'
    }
  });

  // get the local name for the imported module
  const localName = importDeclaration.find(j.Identifier).get(0).node.name;

  // current order of arguments
  const argKeys = [
    'color',
    'make',
    'model',
    'year',
    'miles',
    'bedliner',
    'alarm'
  ];

  // find where `.factory` is being called
  return (
    root
      .find(j.CallExpression, {
        callee: {
          type: 'MemberExpression',
          object: {
            name: localName
          },
          property: {
            name: 'factory'
          }
        }
      })
      .replaceWith(nodePath => {
        const { node } = nodePath;

        // use a builder to create the ObjectExpression
        const argumentsAsObject = j.objectExpression(
          // map the arguments to an Array of Property Nodes
          node.arguments.map((arg, i) =>
            j.property('init', j.identifier(argKeys[i]), j.literal(arg.value))
          )
        );

        // replace the arguments with our new ObjectExpression
        node.arguments = [argumentsAsObject];

        return node;
      })

      // specify print options for recast
      .toSource({ quote: 'single', trailingComma: true })
  );
};
  • 用 GoGoCode 实现:
const argKeys = [
  'color',
  'make',
  'model',
  'year',
  'miles',
  'bedliner',
  'alarm'
];
const argObj = {};
$(code)
  .find(`const $_$1 = car.factory($_$2);`)
  .each(item => {
    const variableName = item.match[1][0].value;
    item.match[2].forEach((match, j) => {
        argObj[argKeys[j]] = match.value;
    });
    item.replaceBy(
        $(`const ${variableName} = car.factory(${JSON.stringify(argObj)})`)
    );
  })
  .root()
  .generate()