将多个呼叫转换为一个呼叫链

时间:2017-02-07 05:13:08

标签: javascript node.js abstract-syntax-tree static-analysis esprima

我正在创建一个库,该库在全局命名空间(make)上导出一个函数(app),用于定义模块或引用它,类似于angular.module

当我调用它时,我确保不存储对make(name, [deps])调用的任何引用,因此它将始终使用主app对象。

用法是这样的:

// Define 'hello'
app.make('hello', []);

// Define 'one' in 'hello'
app.make('hello').
data('one', 'thing');

// Multiple calls
app.make('hello').
data('two', 'thing').
attr('third', 'no').
data('four', 'empty');

我希望上面的代码变成它:

app.make('hello', []).
data('one', 'thing').
data('two', 'thing').
attr('third', 'no').
data('four', 'empty');

因此它应该将多个单独的调用转换为从make返回到一个大的调用(顺序并不重要,并且没有副作用)。

我尝试了什么:

我计划使用esprimaestraverseescodegen,这是我实际拥有的内容:

const fs         = require('fs'),
      esprima    = require('esprima'),
      estraverse = require('estraverse');


const modules = Object.create(null);


fs.readFile('sample.js', 'utf8', (err, data) => {
  const tree = esprima.parse(data);

  // Find module definitions
  estraverse.traverse(tree, {
    enter(node) {
      const args = node.arguments;

      if (isDefinitionCall(node) && args.length == 2) {
        modules[args[0].value] = {
          node,
          childs: []
        };
      }
    }
  });

  // Find module usages
  estraverse.traverse(tree, {
    enter(node) {
      if (isGetterCall(node)) {

        // What to store here?
        // And how to modify the AST to turn
        // everything into just chained call?
      }
    }
  });

  console.log('Modules found: ' + Object.keys(modules).join(', '));
});


function isDefinitionCall(node) {
  return node.type == 'CallExpression' &&
         node.callee &&
         node.callee.object &&
         node.callee.property &&
         node.callee.type == 'MemberExpression' &&
         node.callee.object.name == 'app' &&
         node.callee.object.type == 'Identifier' &&
         node.callee.property.type == 'Identifier' &&
         node.callee.property.name == 'make';
}

function isGetterCall(node) {
  return node.type == 'CallExpression' &&
         node.callee &&
         node.callee.object &&
         isDefinitionCall(node.callee.object);
}

我的问题是:如何移动AST并获得我想要的东西? 提前谢谢!

0 个答案:

没有答案