JavaScript

Pack It Up, Pack It In

Let me begin with a list of usual suspects up to no good in the browser:

I mean, because, you know, writing in HTML/JS/CSS is so 2000-late.

All this 74 72 61 6e s p i l a t i o n.

A typical layout. Something like this:

assets/
    css/
    img/
    js/
    html/
    index.html
src/
    coffee/
    img/
    jade/
    less/
Gruntfile.js // or gulpfile.js
package.json
server.js
// ...

You’re using Grunt or its less-loquacious cousin, Gulp or — mind-blown — npm(!) for builds… I mean running tasks… I mean running build tasks. Did I mention you’re modularizing with CommonJS? Well, in this blog post you are. We’ll forego tests for brevity’s sake. Because, of course you’re testing…

The cadre of /G(runt|ulp)/ transpiler plugins: contrib-coffee, contrib-less, contrib-jade, gulp-coffee, gulp-less, gulp-jade — configured. I said CommonJS, didn’t I? You might be tempted to use Browerserify, so multiply things by ^ify.

And you don’t want to repeat yourself. So you DRY-ly declare the chain of project directories in your Gruntfile.js or gulpfile.js.

var src = [__dirname, 'src'].join('/');
var srcCss = [src, 'css'].join('/');
var srcHtml = [src, 'html'].join('/');
var srcImg = [src, 'img'].join('/');
var srcJs = [src, 'js'].join('/');
 
var dest = [__dirname, 'assets'].join('/');
// ...

“Like I said, you don’t want to repeat yourself,” he said, dryly.

And maybe you want some hot/live reloading? Maybe you didn’t, but in this blog post, you do. Some client frameworks are more naturally suited than others (React and Flux) but at any rate, you orchestrate the delicate swan dance of watchers and reloaders in your package.json and build file.

So you’ve got at least six packages declared — at least — for this scheme, and at least a hundred well-formatted lines in your build file for the plug-ins alone, plus variable declarations. That’s a lot of wiring for a modern web app dev environment.

What if I told you you could get all of that in a cohesive manner? In a framework that will take care of all that wiring for you? Enter webpack! Gone are the variable declarations! (Mostly) gone are the lengthy plug-in configurations, the watch and reload courtship! Imagine all the digital trees saved!

webpack

webpack is a module builder and runtime module loader (like RequireJS). It’s not RequireJS. It’s not CommonJS. But it understands both. It will bundle both. In its fundamental design, however, what makes webpack different is: everything is a bundle. Wait.. CSS? Yep. Fonts? Uh-huh. HTML templates? You got it. Images? Alright, now you’re just being annoying.

Yes, you will be “require”-ing images. Of all the resources that become bundles themselves, in the webpack way of things, images are maybe the strangest, because their usage at runtime are in languages (HTML, and CSS by way of) that, in their DNA, expect to be served URL-y. So there are design considerations.

For one, anything beyond a truly single-page trivial application, you will be using a client framework. For another, with the help of said framework, you’ll be doing client-side routing, relegating the web server to a web service (and static asset directory, maybe). You see where this is heading: templating and JavaScript (transpiled-to). Be it a Handlebars template or, e.g., an Angular directive:

var angular = require('angular');
var img = require('./../img/foo.png');
 
var app = angular.module('app', []);
app.directive('bindImg', function () {
    return {
        link: function (scope) {
            scope.photo = img;
        },
        template: '<img data-ng-src="/assets/{{photo}}">',
        transclude: true
    };
});

(magic hands) And Bob’s your uncle. I could’ve even `require`-ed the HTML (as Jade) in the above `template` declaration.

But I’m off-point here. The focus of this post isn’t a tutorial over webpack. There are some good ones out there. It’s also not to document webpack. It has its own gh pages for that (decent, and hopefully gets better, more consistent, more in-depth and yet more accessible). No, this post is to convince you to offload your transpilations, your assets-building, to webpack. Drop the configurations and wiring in your build files. Where the building and wiring of assets are concerned, let webpack handle it. Forget the traditional bundling mechanisms of your modular frameworks.

1. You lose all those brittle declared meta-variables.
2. You lose the Gulp/Grunt plugins you fed them to.
3. See below

A webpack config file for this phantom project could look like this:

'use strict';
 
var config = {
    entry: [
        [__dirname, 'src', 'js', 'Application.js'].join('/')
    ],
    module: {
        loaders: [
            {
                loader: 'style-loader!css-loader',
                test: /\.css$/
            },
            {
                loader: 'coffee-loader',
                test: /\.coffee$/
            },
            {
                loader: 'html',
                test: /\.html$/
            },
            {
                loader: 'jade-loader',
                test: /\.jade$/
            },
            {
                loader: 'file-loader',
                test: /\.(png|jpg)$/
            },
            {
                loader: 'style-loader!css-loader!less-loader',
                test: /\.less$/
            },
            {
                loader: 'file-loader',
                test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/
            },
            {
                loader: 'url-loader?limit=10000&mimetype=application/font-woff',
                test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/
            }
        ]
    },
    output: {
        filename: 'bundle.js',
        path: [__dirname, 'assets'].join('/')
    }
};
 
module.exports = config;

Defining an entry point and configuring the loaders responsible for each filetype required (you will need to install these), plus an output directory/file. Pretty simple and should feel conceptually familiar to RequireJS users. You can get more sophisticated with “chunking,” or breaking apart, your assets as you see fit. Out of the gate, you get one file for non-binary assets and a file-per binary.

The key thing is that the naming, wiring, and retrieval of all assets is handled by the webpack runtime, embedded in the output. Actually key number two (maybe I’m a janitor?) is, it all goes in one directory (out-of-the-box). It’s a blob. To you at runtime, it’s a blob. To you at dev-time, it’s a blob. To you — and this is the pudding — at build-time, it’s a blob. Just declare your dependencies at the file-level. webpack simplifies things.

So, you say, “OK. That config snippet looks simple and kinda familiar. Not much configuration. My Grunt/Gulp tasks become dedicated runners vs. builders…” (yes, I just put words in your mouth) “…but, I’m a curmudgeon and it wouldn’t be very curmudgeonly of me to just accept a streamlined and consistent approach to building projects and declaring all dependencies, now would it?” Well, dagnabbit, if we din’t up ‘n fergit hot reload (slaps prospector’s hat on knee)!

webpack-dev-server

Although, strictly-speaking, not a part of webpack proper, webpack-dev-server is an easy-peasy-lemon-squeezy +1. Here is where the webpack runtime really flexes its muscles. Once configured, the runtime will serve through the dev-server (over a websocket) only the changed delta in-page. If you think about it, it’s a natural application of webpack’s design choices: it’s the runtime loader, it chunks and wires as it sees fit — substitutions in this model should be theoretically trivial vs. the watchers of old, binding file globs to tasks.

And for the most part, it just works. Occasionally, you may have to restart the server or its buffer, eventually, after long periods of uptime, overflows. But you can easily set up, for dev-time, say, an Express server serving API, offloading static assets to the dev-server and be ripping through your style minutiae and route tunings, seamlessly and lickety-split-ily(?). Again, there’s plenty of examples out there to guide you.

Conclusion

I’ve only touched the surface of webpack. Try it out.

I’m sure you’ll appreciate the cruft removed from your task runner, webpack’s minimal configuration, its everything-is-a-module boon to testing, and its rapid development dev-server (esp. if you’re using React — its shadow dom, itself, a lattice-like state machine). Its maintainers have made it easier to suggest documentation improvements (where I think is its weak point — including plug-ins) so I’m sure things will get better on that front (nudge, nudge YOU). But you can get going and do a lot in spite of it.

Now, get packin’!

Reference: Pack It Up, Pack It In from our WCG partner Keyhole Software at the Keyhole Software blog.

Keyhole Software

Keyhole is a midwest-based consulting firm with a tight-knit technical team. We work primarily with Java, JavaScript and .NET technologies, specializing in application development. We love the challenge that comes in consulting and blog often regarding some of the technical situations and technologies we face.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button