なっく日報

技術やら生活やらのメモ

estraverseを若干使いやすくする小技

生estraverseの問題

estraverseは素直に書くと

↓みたいになるのでif文が増えて結構見づらくなってきます。

estraverse.traverse(ast, {
    enter: function (node, parent) {
        if (node.type == 'FunctionExpression' || node.type == 'FunctionDeclaration')
            return estraverse.VisitorOption.Skip;
    },
    leave: function (node, parent) {
        if (node.type == 'VariableDeclarator')
          console.log(node.id.name);
    }
});

理想

ESLintのルールみたいにnodeのtype毎かつenter/leave時で処理を分けたい。

https://github.com/eslint/eslint/blob/master/lib/rules/complexity.js

module.exports = function(context) {
  // ...  
  // ESLintだと↓な感じで書ける
  return {
      "FunctionDeclaration": startFunction,
      "FunctionExpression": startFunction,
      "ArrowFunctionExpression": startFunction,
      "FunctionDeclaration:exit": endFunction,
      "FunctionExpression:exit": endFunction,
      "ArrowFunctionExpression:exit": endFunction,
      // ...
  };
};

解決案

ラップしてあげればいいんじゃないかと。

function walk(ast, visitors) {
  return estraverse.traverse(ast, {
    enter: function(node, parent) {
      var visitor = visitors[node.type];
      if (visitor) {
        return visitor.call(this, node, parent);
      }
    },
    leave: function(node, parent) {
      var visitor = visitors[node.type + ':exit'];
      if (visitor) {
        return visitor.call(this, node, parent);
      }
    },
  });
}

// ↓みたいに書ける
walk(ast, {
  "FunctionDeclaration": startFunction,
  "FunctionExpression": startFunction,
  "ArrowFunctionExpression": startFunction,
  "FunctionDeclaration:exit": endFunction,
  "FunctionExpression:exit": endFunction,
  "ArrowFunctionExpression:exit": endFunction
});

ちなみに

ESLint内部ではEventEmitterを使っていますが、

eslint/eslint.js at 7ebb027a12b4ec49fa6996abd05ef99dd91f5932 · eslint/eslint · GitHub

return this.skip()とかできた方が良いと思うので、この書き方で。