In [8]:
var parse = require('acorn').parse;

var objectKeys = Object.keys || function (obj) {
    var keys = [];
    for (var key in obj) keys.push(key);
    return keys;
};
var forEach = function (xs, fn) {
    if (xs.forEach) return xs.forEach(fn);
    for (var i = 0; i < xs.length; i++) {
        fn.call(xs, xs[i], i, xs);
    }
};

var isArray = Array.isArray || function (xs) {
    return Object.prototype.toString.call(xs) === '[object Array]';
};

function falafel(src, opts, fn) {
    if (typeof opts === 'function') {
        fn = opts;
        opts = {};
    }
    if (typeof src === 'object') {
        opts = src;
        src = opts.source;
        delete opts.source;
    }
    src = src === undefined ? opts.source : src;
    if (typeof src !== 'string') src = String(src);
    var ast = parse(src, opts);
    
    var result = {
        chunks : src.split(''),
        toString : function () { return result.chunks.join('') },
        inspect : function () { return result.toString() }
    };
    var index = 0;
    
    (function walk (node, parent) {
//         console.log(node.type, node.start)
        insertHelpers(node, parent, result.chunks);
        
        forEach(objectKeys(node), function (key) {
            if (key === 'parent') return;
            
            var child = node[key];
            if (isArray(child)) {
                forEach(child, function (c) {
                    if (c && typeof c.type === 'string') {
                        walk(c, node);
                    }
                });
            }
            else if (child && typeof child.type === 'string') {
                walk(child, node);
            }
        });
        fn(node);
    })(ast, undefined);
    
    return result;
};
 
function insertHelpers (node, parent, chunks) {
    node.parent = parent;
    
    node.source = function () {
        return chunks.slice(node.start, node.end).join('');
    };
    
    if (node.update && typeof node.update === 'object') {
        var prev = node.update;
        forEach(objectKeys(prev), function (key) {
            update[key] = prev[key];
        });
        node.update = update;
    }
    else {
        node.update = update;
    }
    
    function update (s) {
        chunks[node.start] = s;
        for (var i = node.start + 1; i < node.end; i++) {
            chunks[i] = '';
        }
    }
}


Out[8]:
undefined

In [54]:
var src = '(' + function () {
    function merp(a, b){
        console.log(a / b)
    }
    var xs = [ 1, 2, [ 3, 4 ] ];
    var ys = [ 5, 6 ];
    console.dir([ xs, ys ]);
    for(var i = 0; i < 100; i++){
        console.log(i)
    }
    ;[1,2,3,4,5,6,7].forEach(function(derp, flerp){
        console.log(derp)
        function blah(){
            
        }
    }).map(function(asdf){
    })
} + ')()';
 
var output = falafel(src, function (node) {
    if (node.type === 'ArrayExpression') {
//         node.update('fn(' + node.source() + ')');
    }else if(node.type === 'ForStatement'){
//         console.log(node)
//         node.update()
        if(node.test.type == 'BinaryExpression' && node.test.right.type == 'Literal' && node.test.left.type == 'Identifier'){
//             console.log(node.test.left)
            node.update.update(node.update.source() + ', merp(' + node.test.left.name + ', ' + node.test.right.value + ')')
        }

    }else if(node.type === 'CallExpression'){
        if(node.callee.type == 'MemberExpression' && ['forEach', 'map'].indexOf(node.callee.property.name) != -1) {
//             console.log(node.arguments[0].body.source())
            var thing = node.arguments[0].body;
//             thing.update(thing.source())
            thing.update('{ merp(arguments[1], arguments[2].length);' + thing.source().slice(1))
        }
    }
//     }else if(node.type === 'MemberExpression' && node.property.name == 'forEach'){ // TODO: support map
//         console.dir(node.parent.arguments[0].body)
//     }
});
console.log(output);


(function () {
    function merp(a, b){
        console.log(a / b)
    }
    var xs = [ 1, 2, [ 3, 4 ] ];
    var ys = [ 5, 6 ];
    console.dir([ xs, ys ]);
    for(var i = 0; i < 100; i++, merp(i, 100)){
        console.log(i)
    }
    ;[1,2,3,4,5,6,7].forEach(function(derp, flerp){ merp(arguments[1], arguments[2].length);
        console.log(derp)
        function blah(){
            
        }
    }).map(function(asdf){ merp(arguments[1], arguments[2].length);
    })
})()
Out[54]:
undefined

In [ ]: