The module property is undefined when using vm.runInThisContext - node.js

Here is a function from jasmine 2.0.0 standalone project:
function getJasmineRequireObj() {
if (typeof module !== "undefined" && module.exports) {
return exports;
} else {
window.jasmineRequire = window.jasmineRequire || {};
return window.jasmineRequire;
}
}
I guess that if I would use the standard require method the module property will be define.
When I load this file using the VM module as follows the module global property is undefined:
var fs = require('fs');
var vm = require('vm');
var jasmineFile = fs.readFileSync(__dirname + '/jasmine.js');
vm.runInThisContext(src, jasmineFile);
Is this the expected behavior of the VM module or a defect?

It is the expected behaviour. Your code is evaluated in the same context, but not in the same scope, so module, exports and whatnot are undefined. You can do something like this:
var m = require('module')
var src = 'module.exports = 42'
var res = require('vm').runInThisContext(m.wrap(src))(exports, require, module, __filename, __dirname)
console.log(module.exports)
but there is no much point in doing that, because it's basically what require does

Related

How does exports object reference module.exports in Node.js?

In Node.js, module object contains an exports property, that is an empty object. This object can be used to reference module.exports (exports.a = "A";), unless it is reassigned (module.exports = "one";).
My question is - what makes this exports object reference module.exports?
CommonJS modules are actually pretty simple: you take all the code in a file, and just wrap it in a function. Execute the function, and return the value of module.exports after execution to the caller.
You can see this function's header in the node.js source code:
const wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
The wrapper is applied to the code in the require'd file and then called like this:
const exports = this.exports;
const thisValue = exports;
const module = this;
if (requireDepth === 0) statCache = new Map();
if (inspectorWrapper) {
result = inspectorWrapper(compiledWrapper, thisValue, exports,
require, module, filename, dirname);
} else {
result = compiledWrapper.call(thisValue, exports, require, module,
filename, dirname);
}
As you can see it's pretty straightforward. const exports = this.exports, and then exports is passed as an argument to the wrapper function - thus they initially point to the same value, but if you reassign either then they no longer do.

fs.js module in nodejs requiring itself?

I'm currently learning nodeJS and running version v6.10.2, I was taking a look at the fs.js module source code located in the graceful-js folder and noticed this code in it:
'use strict'
var fs = require('fs')
module.exports = clone(fs)
function clone (obj) {
if (obj === null || typeof obj !== 'object')
return obj
if (obj instanceof Object)
var copy = { __proto__: obj.__proto__ }
else
var copy = Object.create(null)
Object.getOwnPropertyNames(obj).forEach(function (key) {
Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key))
})
return copy
}
How can a module require itself does it have to do with the clone keyword? I couldn't find any other module named fs.js either.
You're looking at part of the code for the graceful-fs package which is a wrapper around the builtin fs module in NodeJS. So this code is not requiring itself - the require statement imports the builtin fs module and the rest of the code clones it so that other parts of the graceful-fs package can override some of the default fs functionality without affecting the original module.

How to include a file which is not a module in Node.js (to make a module of it)?

Purpose: making a Node.js module or a Requirejs AMD module, from a plain old JavaScript file like this:
var foo = function () {};
I have no control and I can't edit foo.js.
Whyfoo is is a plain empty object inside the first if? How can I "include" a plain JavaScript file in my module? File mymodule.js:
(function(root) {
if(typeof exports === 'object') { // Node.js
var foo = require('./foo');
console.log(foo); // { }
} else if (typeof define === 'function' && define.amd) { // RequireJS AMD
define(['foo'], function () {
return foo;
});
}
}());
Node modules loaded with require must populate an exports object with everything that the module wants to make public. In your case, when you require the file nothing is added to exports, hence it shows up as empty. So the short answer is that you need to modify the contents of foo.js somehow. If you can't do it manually, then you need to do it programmatically. Why do you have no control over it?
The easiest being that you programmatically wrap the contents if foo.js in the code needed to make it a proper module.
// Add an 'exports' line to the end of the module before loading it.
var originalFoo = fs.readFileSync('./foo.js', 'utf8');
fs.writeFileSync('./foo-module.js', originalFoo + "\nexports.foo = foo;");
var foo = require('./foo-module.js');
You Could Try:
var fs = require('fs');
var foo = fs.readFileSync('foo.js', 'utf8') // Or request it somehow, just get it as a string
foo += "module.exports.foo = module.exports = foo;";
fs.writeFileSync('fooModule.js',foo);
var foo = require('./fooModule');
// foo() and foo.foo() are both the same
Note: This requires node.

How to stub out express after you require it with jasmine?

I'm trying to get the code below under test when occurred to me that I already included express at the top of this file. Can you some how monkey patch the express object after it's already loaded?
var express = require('express')
Helper = (function() {
var HelperObject = function(params) {
this.directories = params.directories;
};
HelperObject.prototype.addStaticPath = function(app) {
for(i = 0; i < this.directories.length; i++) {
var static = express.static('/public');
app.use(static);
}
};
return HelperObject;
})();
The problem is that when you create a node module the required modul is bound in the closure of the module and you can't start spying on it cause it isn't visible in your test.
There is Gently where you can override require but it will sprinkle your code with boilerplate test related code.
From the docs:
Returns a new require functions that catches a reference to all
required modules into gently.hijacked.
To use this function, include a line like this in your 'my-module.js'.
if (global.GENTLY) require = GENTLY.hijack(require);
var sys = require('sys');
exports.hello = function() {
sys.log('world');
};
Now you can write a test for the module above:
var gently = global.GENTLY = new (require('gently'))
, myModule = require('./my-module');
gently.expect(gently.hijacked.sys, 'log', function(str) {
assert.equal(str, 'world');
});
myModule.hello();

How should I pass options to a node module?

If I have a node module (I wrote) and I want to pass it a value, I could do this:
var someValue process.env.SomeKey || '';
var someModule = require('./someModule');
someModule.setOption({ 'SomeKey' : someValue });
but it feels like I am reinventing the wheel.
Is there a better way to do this or is it totally subjective?
In general, you simply export a function from the module:
module.exports = function(opts){
return {
// module instance
};
}
then in the requiring page:
var mod = require('module')({ someOpt: 'val' });
But in reality, do it however you want. There's no set-in-stone standard.
I generally build modules that have similar components, sometimes just one class, or even just a selections of methods.
(function () {
var myClass = function (opts) {
this.opts = opts;
};
myClass.prototype.blah = function () {
console.log('blah');
};
exports.myClass = myClass;
})();
Then in your file that is using that module.
var mymodule = require('./mymodule');
var myInstance = new mymodule.myClass({opt1: 'blah'});
myInstance.blah();
Of course you don't need to just pass around an object of options :)
Yes, it is totally subjective.
Doing it the way you demonstrated is fine. You can also just export a function or a class by assigning it to module.exports.

Resources