I'm experimenting with nodeJs vm. This code works :
server.js
var fs = require('fs');
var vm = require('vm');
var app = fs.readFileSync(__dirname + '/' + 'app.js');
vm.runInThisContext(app);
var http = require('http');
var server = http.createServer(onRequest);
server.listen(8080, '127.0.0.1');
app.js
function onRequest(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('Hello Node.js\n');
}
Now if I change app.js into
function onRequest(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(fs.readFileSync(__dirname + '/index.html'));
}
It does not work anymore : browser would print "This webpage is not available"
How to make it work maybe by somehow binding fs.readFileSync to the local context of onRequest ?
While the answer posted by JohnKiller is technically correct, I would like to point out a solution that uses vm.runInContext
and is more robust in my opinion.
// app.js
var fs = require('fs');
module.exports = function onRequest(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(fs.readFileSync(__dirname + '/index.html'));
}
// server.js
var appFile = __dirname + '/' + 'app.js';
var app = fs.readFileSync(appFile);
var context = vm.createContext({
__filename: appFile,
__dirname: __dirname,
require: require,
module: { exports: {} }
});
context.exports = context.module.exports;
vm.runInContext(app, context, { filename: appFile });
var onRequest = context.module.exports;
var http = require('http');
var server = http.createServer(onRequest);
server.listen(8080, '127.0.0.1');
I see the following major benefits:
1) The global context is not polluted with additional variables used only by the loaded script file.
2) The code loaded from the external file is sandboxed, it cannot directly change the scope variables of the caller. It is very clear what variables are available to the external file.
3) The code in the external file is self-encapsulated and does not depend on any external modules being provided in the context. In fact, it is a regular Node.js file that can be loaded directly via require('./app.js')
Quoting from the docs:
Running code does not have access to local scope, but does have access to the current global object.
So, variable __dirname
and module fs
are undefined
in that context.
To solve the problem, use the global
object:
server.js
var fs = require('fs');
var vm = require('vm');
global.fs = fs;
global.__dirname = __dirname;
var app = fs.readFileSync(__dirname + '/' + 'app.js');
vm.runInThisContext(app);
var http = require('http');
var server = http.createServer(onRequest);
server.listen(8080, '127.0.0.1');
app.js
function onRequest(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(global.fs.readFileSync(global.__dirname + '/index.html'));
}
wtf, why?
var app = fs.readFileSync(__dirname + '/' + 'app.js');
vm.runInThisContext(app);
just do:
require('./app.js')
Sooo you entire app looks like:
server.js
var http = require('http');
// use './' for relative paths
var app = require('./app.js');
// load app.js
var server = http.createServer(app);
server.listen(8080, '127.0.0.1');
app.js
// app.js has to load `fs` for itself
var fs = require('fs');
module.exports = function app(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(global.fs.readFileSync('./index.html'));
}
Done
But, if for some reason you REALLY want to read, compile and run app.js
with vm
instead of simple old require
server.js
var http = require('http');
// use './' for relative paths
var app = vm.runInThisContext('./app.js');
// load app.js
var server = http.createServer(app);
server.listen(8080, '127.0.0.1');
app.js
// app.js _still_ has to load `fs` for itself
var fs = require('fs');
// last value is returned
function app(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(global.fs.readFileSync('./index.html'));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With