Projects

Archives

Node.js require() for your browser

Update: This article is obsolete. Check out my post “JS require() for browsers – better, faster, stronger” for an improved version, which is CommonJS compliant, more secure, handles relative paths correctly and supports module bundles. The require function described here is obsolete.

Everyone who has worked a bit with JavaScript will sooner or later wish for an easy way to load a script directly from another script (similar to #include in C or require() in PHP). Over time frameworks like RequireJS and some other code snippets bubbled up to the surface, but they all have some drawbacks: They either require quite a lot of extra code, only allow asynchronous loading (this is not always a good idea) or don’t separate the module from the rest of the code, which makes porting kinda difficult.

Then came Node.js with its easy to use module concept, which needs nothing more than a simple function call to load a JavaScript file into its own namespace. When I started to use Node.js this concept felt a bit odd at first, but once I got used to it I loved it an started to look for a way to bring Node.js’ require() to the browser. I stumbled upon Browserify pretty fast, but didn’t take a closer look, since it simply looked like a bit too much for my use-case and all this command-line stuff just didn’t feel right.

In the end I started to implement my own require function for browsers, which resembles most of the features implemented in Node.js and also allows asynchronous loading. For the lack of creativity I simply called the project Require(). However, I’m sure it lacks some features of the bigger frameworks, but it’s just a small script (ZIP archive) and pretty easy to use as you can see on the example page, so I think it’s worth a try.

The Node.js way

First of all I’ll give a short introduction into the way Node.js handles modules. I’m just explaining the very basics, so you can skip this section, if you’re already used to it.

As I’ve already mentioned require() is used to load files. For normal JavaScript files this means that they are executed when they are loaded for the first time and that’s it. The code will be executed in its own closure, so it won’t interfere with the rest of your code (e.g. identical variable names are no problem). The only way to return something to the outside world is by modifying a special object called exports, which will be the return value of the require() call. A JavaScript file, which uses exports to return a function or variable to the outside world, is called a module.

Well, that’s not the whole story, but everything you need to know to understand my require() implementation. If you want to know all details you should read the Node.js documentation on modules.

The browser way

The require implementation for browsers works pretty much the same way the Node.js implementation does. There are some smaller differences, but let me show how to use require() in your browser first:

The only file we need to load the usual way is require.js which defines window.require(). After that we can load the module greeting.js as we would do in Node.js. The variable greeting holds all stuff exported by the module so you can think of it as a namespace. As you can see calling an exported function, sayHello() in this case, works the same way as calling the method of an object.

The code in greeting.js could look like this:

exports.sayHello = function() {
alert('Hello World!');
}

Again no surprises here – everything works like in Node.js. We implement the sayHello() as a method of exports and that’s it.

Asynchronous loading

Asynchronous module loading is a feature that Node.js is – AFAIK – not capable of, but it’s quite handy in a lot of web-development situations. Luckily it’s quite easy to tell our little require function for the browser to load a module asynchronously:

As you can see the code hasn’t changed too much. All we have to do is to add the name of a callback-function as a second parameter to the call. require() will then load the module asynchronously and call the callback-function with the namespace-variable as its only parameter afterwards.

The module itself doen’t need any changes for asynchronous loading, so it contains the same code as in the first example.

Implicit path names

Node.js has some rules for module paths, which try to complement incomplete pathnames to a full path. require() tries to resemble most of these rules, but some won’t make a lot of sense in a web-development context so I dropped them. However, here a short overview of the implemented rewrite-rules:

/some/path/module â‡’ /some/path/module.js

/some/path/module.js â‡’ /some/path/module.js

/some/path/module/ â‡’ /some/path/module/index.js

../module â‡’ ../module.js

../module.js â‡’ ../module.js

../module/ â‡’ ../module/index.js

module â‡’ /js_modules/module.js

module.js â‡’ /js_modules/module.js

module/ â‡’ /js_modules/module/index.js

As you can see absolute and relative paths are possible and if no path is given require() will look for the module in the global js_modules directory. As long as you specify an extension the last part of the path will be interpreted as a file, otherwise it will be interpreted as a directory and require() will try to load the file index.js within this directory.

JSON data

Our require function is capable of handling JSON encoded data, too. Every response with the content-type application/json will be interpreted by the JSON parser. The server is responsible for sending the right handler, so you might have to some configuration stuff here. Joshua Gourneau’s post on how add a JSON type to an Apache/Ubuntu server might be a good hint here (it’s easy :)).

Apart from that you should remember that require() will always return the complete JSON object, so you don’t have to put your data in an exports child-object or something. Here’s an example:

Asynchrounous loading works of course too, so this is a nice way to access a database through a JSON interface like the one Thomas Frank described in his MySQL to JSON post.

Little helpers

You can call require.resolve(module) to get the actual full path of a module without loading the module.

You can access the module cache array via require.cache, a single module can be accessed via require.cache[path]. This is quite handy when you want to remove a module from the cache to ensure that the next require call reloads the module-file from the server.

Conclusion

I’ve used require() in two of my projects so far and even with dynamic page loading and other stuff I never had problems with it. In fact it changed a lot the way I use JavaScript in my projects now – and I’m pretty sure to a better way ;)

I’ve tried to make my implementation as compatible as possible to the Node.js version, but I’ve never tried to load a module written for Node.js in a browser. For that reason there might be some incompatibilities (especially with paths), but I think they should be fixable. It would be nice if you could post a comment here if you managed to load a Node.js module, so others might benefit from you experience.

21 thoughts on “Node.js require() for your browser”

I love the simplicity of this solution. Less is definitely more when it comes to code. However, in the simplicity there are inherent limits to your solution.
* use {} instead of new Object() – mostly a question of style, but no need to waste bytes.
* IE6 doesn’t have native XmlHttpRequest – but who cares nowadays.
* IE7 doesn’t have JSON.parse.
* The ‘use strict’ in your evaluation means all modules MUST be written in strict mode, which isn’t the sort of choice you want to make for others. – templating libraries in particular rely on with()
* Synchronous requests WILL freeze the browser window until the response comes, so this is not resilient to network bugs. Normally if one thing fails to load, the js breaks, but the basic html functionality continues to work. With this solution, one failed request can freeze the window, and perhaps the whole browser.
* This is missing functionality from the Modules spec. http://wiki.commonjs.org/wiki/Modules/1.1

First of all thanks for the feedback. I think the first three points are no big issues: “new Object()” will be reduced to “{}” in the minified version, JSON.parse support can be added to IE7 with Douglas Crockford json2 library (https://github.com/douglascrockford/JSON-js) and IE6 is – as you’ve said – too old to be cared about.

Thx for the strict mode hint, I use it almost everywhere now, so I simply didn’t thought about it. I improved the code so that you’re now able to toggle between strict and normal mode via “require.strict” (default is false).

Synchronous loading has indeed some problems in a network environment, but I implemented it anyway for compatibility reasons (it’s the way Node.js does it). However, you can load scripts asynchronously simply by providing a callback function as second parameter. However, I will try to find a solution to provide at least a timeout for synchronous loading.

The modules spec link looks interesting – never read about CommonJS before (shame on me ;)), but what functionality am I exactly missing? A first read gave me no clue…

The `module` variable is missing. That is the only thing in the spec you are missing. However for broader compatibility with other modules written for NodeJS or RequireJS, you may want to support assigning to `module.exports`, and/or `this`, or automatically exporting the return value. Many implementations also have a `global` variable.

With the sync requests, in practice I use synchronous require(), but I preload all the modules into the page (the concatenate, minify, and cache everything up front approach). https://github.com/thetalecrafter/modules

thanks for YOUR work! This sourceURL-stuff is great! Your code just don’t work, because the require-function didn’t use eval() but a new inserted script-tag to interpret the code. I went this way, since this gave me at least some line numbers with the error message. Anyway, I switched back to eval() now and sourceURL works like a charm. Never heart of it before, so thanks for the hint :)

Bai
Torben

PS: Using “/node_modules” as the module directory is IMHO not the best idea since require() is not 100% compatible with node.js and I don’t want to imply any false assumptions here. However, I’m thinking about implementing some kind of compatibility mode…

damn you for pointing me to a bug in my code right after I’ve finished eight hours of coding for work :D Anyway, I got it fixed – GIST and ZIP are updated.

I simply forgot to handle module names without a ‘.js’ at the end. Now ‘./module’ will resolve to ‘./module.js’ and ‘./module/’ to ‘./module/index.js’. Hope this doesn’t break the code of other people ;)

well it’s a bug actually ;) I’ll take a look on it during the next few days. Until then setting the path to ‘./lib/x.js’ or ‘../parentdirname/lib/x’ or ‘../parentdirname/lib/x.js’ might work around the issue…

Hmm, even later, but I think I finally know now what you’re talking about :D The require() described here always resolves relative paths from the path of the HTML document. Check out my improved require() version, if you need one which resolves relative module paths from the path of the calling module.

I am fairly new to node.js & javascript, I know a bit of java, but don’t much html either. I have installed node.js on my “raspberry pi”, and been using a module called rpi-gpio. I have been successful in putting a high signal to one of a gpio pin using node.js+rpi-gpio.
I have also been successful in creating a node.js server and access it using localhost:8080, but the part i don’t understand is; how to combine these two. Means i want to run the rpi-gpio module in the browser, so when i press a html button the module function gets called.
Can you give me a hello world equivalent example; where i can access a node.js function (){console.write(“hello world”)} in my browser when it press a html button.
thx in advance!!!!!!!!!

1) how to include file1.js ( that renders forms) in it and run it inside mainapp.js
2) how to do the same thing if sending to a route ( if we have a button inside mainapp.js ) say /adduser , so the mainapp.js will first render a form from the code in file1.js then enter it ?

Hej Den, as far as I understand you’re asking a more or less generic question about Node.js and not about my require implementation, which is for browsers. Therefore I think it would be better to ask it on a more general q’n’a site for developers. Like Stack Overflow for example.