1

I'm making a library which exports one function (make) on global namespace (app) for defining a module or referencing it, similar to what angular.module does.

When I call it, I make sure to not store any reference to the make(name, [deps]) call so it will always use the main app object.

Usage is like this:

// 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');

And I want the above code turn into it:

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

So it should turn multiple separated calls to the return from make into just a big one (order doesn't matter and there are no side effects).

What I tried:

I'm planning to use esprima, estraverse and escodegen, here's what I actually have:

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);
}

My question is: how can I move around the AST and get what I want done? Thanks in advance!

Bryan Horna
  • 167
  • 2
  • 12
  • 2
    Re "*there are no side effects*": given your calls don't have any return values (or at least you don't use them), what are they actually doing? – Bergi Feb 07 '17 at 05:27

0 Answers0