I've been catching up with the modern client-side JS ecosystem and reading up on CommonJS and AMD (incl. associated tools - browserify, requirejs, onejs, jam, dozens of others). If I'm writing a Javascript library, how do I modularize/package it such that it can be most broadly accessible (ideally by users who swear by CommonJS, AMD, and especially neither)?

Popular libraries like jQuery seem to just use old-school file concatenation for building itself and dynamically detect whether it should write to an exports or the global context. I'm currently doing the same thing, but the main downside is that if I (unlike jQuery) depend on a few libraries, it's nice to not have to ask users to manually pre-include the transitive set. (Though I currently just have two dependencies.) And of course global namespace pollution.

Or perhaps it's cleanest to generate multiple versions of my library, for each context?

I'm also wondering about packaging and publishing. There are several systems, but I believe the major one is bower, which is easy to deal with since all it does is fetch. However, I'm wondering if I should also be targeting other package systems like component (which requires CommonJS).

Are there other relevant aspects I should be aware of? Are there any good example projects to follow for all of this?

  • This is an awesome tutorial: youtube.com/watch?v=USk1ie30z5k The guy mentions requirejs (r.js), node, bower, backbone, ...
    – user39685
    May 16 '13 at 19:27
  • @MattFenwick I've used all of the mentioned tools; the video doesn't answer any of my questions.
    – Yang
    May 16 '13 at 19:31
  • Have you watched it? I seem to remember the guy walking us through a library and explaining the specific lines of code that made it work with multiple module systems without requiring any of them.
    – user39685
    May 16 '13 at 19:46

I've always used to use build files but since I started my first nodejs project I started using browserify. With browerify and other similar libraries your code is your build file. I am taking advantage of a client and server library that can run on both but it can also work with purely client code. To sum it up browserify gives you all the benefits of writing code in node (no anon functions to avoid globals, npm, simple requires) and it allows you to package that code to run on the client with one command and only load one file.

With browserify you can do something like (named app.js):

var MyLib = require('../myLib');

if(typeof window !== 'undefined') {
    window.MyLib = MyLib;
    window._ = require('underscore');
    window.$ = require('$');
    window.MyLib.myCan = require('./3rdParty/can/can');

browserify app.js > client.js

Would produce something like:

    window.MyLib = //MyLib code
     window._ = //_ code
    window.$ = //$ code
    window.MyLib.myCan = //can code

The file that you would define could have all your 3rd party libs included and not clash with any of your developers that use it.

--Edit in respond to the comment (and a complete miss on the question)

I guess that would depend on your dependencies and how much time you want to spend making sure they work across all versions and libs. If your dependencies are common and follow the same api from version to version you could go the Backbone route and just require the user to have $ and _. I would suggest putting the more obscure libs as part of the bundled file. The options don't have to be cut and dry either. You could offer a pre-built or build your own package.

  • +1 for browserify, more people need to know about that tool May 16 '13 at 17:43
  • @BenjaminGruenbaum It is a really great tool. I am very fortunate I checked it out again. I initially ignored it cause it used to load the files async which could trigger N# of file loads in the browser. Now there is only one and source maps can be enabled.
    – pllee
    May 16 '13 at 17:57
  • 1
    See, here's the problem - I'm asking about how to publish a library. I actually know about browserify/onejs/other CommonJS-based systems, but if I start using require() in my code, that means it will no longer be accessible to users unless they too change their own projects to use CommonJS. If I release a compiled script, then it will include potentially include dependencies redundant with their own project's and potentially enormously bloat the deliverable (e.g. multiple jquery).
    – Yang
    May 16 '13 at 19:24

Kinds of client-side libraries:

  1. Touches DOM
  2. Doesn't touch DOM

With the first kind (UI widgets etc), you will typically assume jQuery is present. You can also write "DOM library agnostic" and have it work with the less popular ones as well but I don't bother.

With the second kind. First of all, don't make it a jQuery plugin, for example "jQuery cookie plugin" is ridiculous but such a library actually exists.

Both of these kinds might have no dependencies, small dependencies or huge dependencies - with a dom library not counting as a dependency in this sense. With the first 2, you would just concatenate them inside your library scope and not worry about possible duplication. For example, jQuery concatenates an internal isArrayLike function, even though the user might have his own already included from some random utility belt library.

I only have one personal experience with a huge dependency when developing a library (actually a language) - moment.js. In this case I would provide 2 builds, one with moment.js concatenated and one without, where the user is responsible for including it. I don't know if this is a good solution though.

And yes, in every case, the jQuery approach of building one final big file that just works is taken. It has the module boilerplate at bottom (require/AMD/global etc detection).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.