Bootstrapping Dojo in Node

posted January 10th, 2010, 5 comments, tagged , ,

Being a Dojo hacker I’ve been wanting to use Dojo in combination with Node for a while now. So on a recent flight to London I added support for the Node environment to Dojo. I cleaned it up yesterday and – after figuring out Git – the code is now available on my Dojo fork.

Dojo is available for other environments than browsers, although the pre-compiled Dojo Base code you might have downloaded will only work in a browser. Other supported environments are Firefox extensions, the Rhino JavaScript engine and the Spidermonkey JavaScript engine. And now, Node.

Dojo is able to support multiple environments by dynamically bootstrapping itself for the given environment. In the non-compiled code, the dojo.js file detects its environment and loads the appropriate hostenv_*.js file. The biggest task of the various hostenv files is to fix the loader code to ensure dojo.require() works. Depending on the environment the file might also add some other features, for example the Rhino environment adds an implementation for setTimeout.

Right now I’m detecting the Node environment by seeing if the process object exists in the global scope. This object provides various utility methods to Node and is, as far as I know, unique to just the Node environment.

We briefly need to interrupt this explanation by discussing how we invoke Dojo within Node. As far as I can tell Dojo needs to exist in the global scope, and the only way to accomplish this is by loading the Dojo code when Node starts. Therefore you’d run node dojo.js to kick things off.

Based on this invocation we can use the path to dojo.js to calculate the root location of the Dojo files. I use process.ARGV[1].replace(/[^\/]+\.js$/, "") to do this. The primary reason for calculating the root is because the Dojo bootstrapping code uses the root location to load the host environment file. Technically I could have left it empty but that’s a bit silly.

Finally we use require() to load further bootstrapping code, the loader, the host environment and Dojo Base. As a special trick for the Node environment, dojo.js invokes dojo._loadInit() to signal that the environment is ready for use. In browsers this function would be invoked on DOM ready, but of course that’s not possible in Node.

The Node host environment file (hostenv_node.js) patches the loader so we can use dojo.require() in combination with Node’s module loading support. I’ve also added support for passing djConfig properties to Dojo. Simply pass the JSON object to the --djConfig argument: node dojo.js --djConfig='{isDebug:true}'.

Because we have to start Node with the dojo.js file we need a way to load additional JavaScript code into Node. This is accomplished by passing the -s or --script argument, which should point to a JavaScript file to be loaded into Node with require().

Currently the Node environment is largely untested, and it is quite probable that there are parts of Dojo that are depending on other features that haven’t yet been ported. I did try implementing the XMLHttpRequest object however, as a wrapper around Node’s HTTP libraries, to make it easier to execute HTTP requests through dojo.xhr().

Check out the code at Github. Some examples are also available. See the documentation on usage.

5 responses

  1. Kris Zyp says:

    Will you system work on other CommonJS platforms? It would be great if this fork worked on Narwhal, for example.

  2. mark says:

    I suppose it would, perhaps not this fork but the approach should work. I’m not sure though how much I’m relying on Node quirks.

  3. [...] Continue reading on Supercollider. [...]

  4. I’m curious, why’d you decide to implement XMLHttpRequest rather than implement dojo.xhr() using require(“http”) directly?

  5. Mark Wubben says:

    Internally Dojo checks for readyState, so my implementation needed to support at least that. I didn’t want to rewrite Dojo’s XHR code, so writing an XHR shim was the obvious solution. I might have gone a bit too far with it though, but it was an interesting exercise nonetheless.