JeramieH
JeramieH

Reputation: 129

Node VM - require() on first level OK, fails on second level

So I'm trying to use Node's VM to dynamically load an external script that generates an object. The object is three prototypes deep (Grandparent->Parent->Child).

Everything works fine if all three prototypes are in the same file.

Everything works fine in two files, with child.js and parent-grandparent.js. I add "module" and "require" to my VM sandbox, and provide a custom requireShim and saw that it fired correctly... child.js correctly requested parent-grandparent.js and everything worked right.

However, it doesn't work when using 3 separate files. child.js still correctly requests parent.js through requestShim, but on the next level up parent.js ignores the provided shim and uses an internal require() for grandparent.js directly (and for some reason can never locate the file) (and for some reason only works if all files are in the app's root folder, refusing to handle subdirectories)

Here's my VM code. It runs an external script and expects the new object to be found in result.

app.js

var child = getScriptObject("./scripts/child.js")

function getScriptObject(scriptFilename)
    {
    var sandbox = { "module": module, "result": false, 
                    "require":requireShim, "console":console };

    var script = vm.createScript( fs.readFileSync(scriptFilename) );
    script.runInNewContext(sandbox);
    return sandbox.result;
    }

function requireShim (x)
    {
    console.log("requireShim: "+x);
    return require(x);
    }

grandparent.js

function Grandparent() {};
module.exports = Grandparent

parent.js

console.log("Parent requiring Grandparent...");
var Grandparent = require('./scripts/grandparent.js');

function Parent() { Grandparent.call(this); };
Parent.prototype = Object.create(Grandparent.prototype);    
Parent.prototype.constructor = Parent;

module.exports = Parent

child.js

console.log("Child requiring Parent...");
var Parent = require('./scripts/parent.js');

function Child() { Parent.call(this); };
Child.prototype = Object.create(Parent.prototype);  
Child.prototype.constructor = Child;

result = new Child();

With the following output:

Child requiring Parent...
requireShim: ./scripts/parent.js
Parent requiring Grandparent...

module.js:322
    throw err;
          ^
Error: Cannot find module './scripts/grandparent.js'
    at Function.Module._resolveFilename (module.js:320:15)
    at Function.Module._load (module.js:262:25)
    at Module.require (module.js:349:17)
    at require (module.js:368:17)
    at Object.<anonymous> (C:\RemHaz2\zone\scripts\parent.js:2:19)
    at Module._compile (module.js:444:26)
    at Object.Module._extensions..js (module.js:462:10)
    at Module.load (module.js:339:32)
    at Function.Module._load (module.js:294:12)
    at Module.require (module.js:349:17)

Note that Child->Parent worked fine, but Parent->Grandparent ignored the shim and could not handle the subdirectory for some inexplicable reason.

Upvotes: 0

Views: 157

Answers (1)

Dan D.
Dan D.

Reputation: 74655

If parent.js is in the same directory as child.js, then:

var Parent = require('./scripts/parent.js');

Should be:

var Parent = require('../scripts/parent.js');

Or:

var Parent = require('./parent.js');

Likewise for:

var Grandparent = require('./scripts/grandparent.js');

Whereas "./scripts/child.js" from app.js works as app.js is a sibling of the scripts directory that is that they share the same parent directory.

Upvotes: 1

Related Questions