我正在创建一个库,该库在全局命名空间(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
返回到一个大的调用(顺序并不重要,并且没有副作用)。
我尝试了什么:
我计划使用esprima
,estraverse
和escodegen
,这是我实际拥有的内容:
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并获得我想要的东西? 提前谢谢!