Monday, January 12, 2015

Browserifying all the things

To quote a famous author,who if this blog post is to believed has started writing Javascript:
        "It is a truth universally acknowledged, that a good Javascript developer who writes           Node.js code  loves the CommonJS module format."
Before you read the rest of this article,if you are unfamiliar with browserify read the browserify handbook , if you are interested in figuring out more about alternate module systems such as requirejs .If you are unfamiliar with the module pattern go here and if you want to know why polluting the global module system is bad go here. I would rather you use these resources than have me butcher them while trying to explain them.If you are already using either the ES6 or System.js modules ,there is a ton of things you could teach me,so go on gloat over it.

Another interesting aspect is that managing your project's dependencies is easier because each dependency is listed in the package.json,this means that anyone else who works use your code , work with as either a developer,a plugin/middleware creator or as an enduser will understand the dependencies on the project and will be able to add their own in a way that is entirely transparent.

But there is more to it than just the explicit listing of dependencies,you do not need to add these dependencies to a git repository , thereby keeping it minimal , you could use npm install to install all these dependencies just as easily.

Now, imagine a scenario where you can use npm install to build all your server-side and client-side dependencies all at the same time. Ofcourse , this will also setup your grunt or gulp task runner along with any of the required tasks as well. This frees a lot of your time from chasing after dependencies that you already know you want to use or version mismatches which cause works on my system bugs when setting up a new project or setting up development environment on a virtual machine (beware of the virtualbox bug that does not allow symlinks and makes it difficult to setup cli tools) or a coworker's environment.

This is , I believe the appeal of using browserify which allows you to use the time-tested CommonJS module system , bundles your code and all of your actual dependencies(just the ones you need , not the ones you decieded to install but never use) into a single file with source map support for debugging.

And then there are transforms which allow you to dynamically compile only the handlebars template you require just before bundling , allow you to require that are built for amd only or available with bower only , compile coffeescript to javascript , compile from either SASS or LESS to CSS. Take a look at all the transforms available here . 

But , you have an understandable question , how would I load jQuery and jQuery plugins with it , certain CSS frameworks and Backbone need jQuery to be available either as jQuery or $ on the global scope.This does not solve any of my problems , it does not work for me ,it looks like an individual selling encyclopedias on the sidewalk , well dressed but offering a product that is of absolutely no use to me , you might say.

Before you slam the door in our collective faces(mine and browserify's,just because it is a module loader does not mean it does not have feelings) , listen to this , you can shim modules that are not written to take complete advantage of the commonjs module format or modules that are required by other modules to be available on the global scope using a transform called browserify-shim . 

(1) Loading Bootstrap/Semantic-UI and jQuery

jQuery has been published as an npm module that does not require shimming for a while,so you can just:

        npm install jquery --save

and 
     
       var $=require('jQuery');

Bootstrap is also available as an npm module,you can add the css to your html the same way you usually do it or you can require it using cssify or browserify-css(which supports concatenating and minifying css into a single style tag with a data-href attribute).

To shim the javascript,use this config which is an excerpt from my package.json:

 "dependencies": {  
   "bootstrap": "^3.3.1",  
   "jquery": "^2.1.3"  
  },  
  "browserify": {  
   "transform": [  
    "browserify-shim"  
   ]  
  },  
  "browser": {  
   "bootstrap": "./node_modules/bootstrap/dist/js/bootstrap.js"  
  },  
  "browserify-shim": {  
   "bootstrap": {  
    "depends": [  
     "jquery:jQuery"  
    ]  
   }  

The browser property allows you to create aliases to javascript files that you need,now go ahead and use these files within the browserify-shim property.Bootstrap expects to find jquery on the global scope as jQuery,this is specified as shown above,it depends(an array because there can be more than one dependency) on the jquery module and expects to find jQuery module,this is specified as moduleName:globalName.You can load semantic-ui in a similar manner replacing the file to be shimmed with semantic-ui instead of bootstrap.

You can either specify your transforms in the browserify command or you can preconfigure them inside transforms within package.json.I have configured the hbsfy and browserify-shim transforms to be executed whenever I browserify my code.

 "dependencies": {  
   "backbone": "^1.1.2",  
   "bootstrap": "^3.2.0",  
   "jquery": "^2.1.3"  
  },  
  "browserify": {  
   "transform": [  
    "browserify-shim",  
    "hbsfy"  
   ]  
  },  
  "browser": {  
   "bootstrap": "./node_modules/bootstrap/dist/js/bootstrap.js",  
   "backbone": "./node_modules/backbone/backbone.js",  
   "bootstrap-validator": "./bootstrap-validator-master/dist/validator.js",  
   "bootcards": "./bootcards-master/dist/js/bootcards.js",  
   "Backbone.actAs.Paginatable": "./Backbone.actAs.Paginatable-master/src/Backbone.actAs.Paginatable.js",  
   "backbone-query": "./backbone_query-master/backbone-query.js"  
  },  
  "browserify-shim": {  
   "bootstrap": {  
    "depends": [  
     "jquery:jQuery"  
    ]  
   },  
   "backbone": {  
    "depends": [  
     "jquery:$"  
    ],  
    "exports": "Backbone"  
   },  
   "bootstrap-validator": {  
    "depends": [  
     "jquery:jQuery",  
     "bootstrap"  
    ]  
   },  
   "bootcards": {  
    "depends": [  
     "jquery:$",  
     "bootstrap"  
    ]  
   },  
   "Backbone.actAs.Paginatable": {  
    "depends": [  
     "backbone:Backbone"  
    ]  
   },  
   "backbone-query": {  
    "depends": [  
     "backbone:Backbone"  
    ]  
   }  
  }  


Wait,one last thing before you leave,how would you figure out if your shim config is working,for all you know,the file you specified for the alias might not exist,maybe you specified the wrong dependencies , a ton of things could go wrong when you specify a shim config(I have hated shims ever since I first used requirejs,they are a nessecary evil caused by differing opinions and a need to support all of them),you could add a flag to inform you of all the gory details when you run browserify:

BROWSERIFYSHIM_DIAGNOSTICS=1 browserify -d . -o js/bundle.js

No comments:

Post a Comment