node.js: Accessing local variables from another module

fridojet picture fridojet · Dec 24, 2012 · Viewed 14.5k times · Source

My Problem

I'm writing a node module called a, which require()s a module b (written by a stranger). Unfortunately, a doesn't only need to access the public members - it also needs to access local variables declared in the scope of the module.

// a
var b = require('b');

console.log(b.public);
console.log(b.private); // undefined


// b
var c = require('c');
var stdin = process.stdin;

exports.public = true;
var private = true;

My Solution

// a
var b = require('b');
var srcPath = require.resolve('b');

console.log(b.public);
fs.readFile(srcPath, 'utf-8', function (err, src) {
    var box = {};
    var res = vm.runInNewContext(src, box, srcPath);
    console.log(box.private);
});

But vm doesn't run b as a module, so require() etc. aren't accessible from the context of the vm. So there are ReferenceErrors like:

    var res = vm.runInNewContext(src, box, scPath);
                 ^
ReferenceError: require is not defined
    at <module b>
    at <module a>
    at fs.readFile (fs.js:176:14)
    at Object.oncomplete (fs.js:297:15)

My Question

Which is the cleanest way to get the value of a local variable declared in another module? Ideas?

Thanks for your help.

Answer

Moritz picture Moritz · Aug 26, 2014

you should probably mostly never have to do this, but there might be reasons.

you can hook the loader and inject javascript code to export what you want.

// let's say you have node_modules/foreignmodule/index.js
// and in that script there is a local (not-exported) function foreignfunction().

var path = require('path');
_oldLoader = require.extensions['.js'];
require.extensions['.js'] = function(mod, filename) {
    if (filename == path.resolve(path.dirname(module.filename), 'node_modules/foreignmodule/index.js')) {
        var content = require('fs').readFileSync(filename, 'utf8');
        content += "module.exports.foreignfunction=foreignfunction;\n";
        mod._compile(content, filename);
    } else {
        _oldLoader(mod, filename);
    }
};

require('foreignmodule').foreignfunction();