Better Builds Begin with Broccoli

   Front End Development
Better Builds Begin with Broccoli

Build tools have come a long way since the days of writing bash scripts. The newest member to the ever growing community of build tools is called Broccoli and it’s written by the excellent open-source developer @jo_liss. Its aim is to not only deliver < 0.5 second rebuild times but to also simplify your build script. It accomplishes this by implementing an asset pipeline (a-la Rails) and by doing incremental builds instead of building the entire project (like Grunt).

Not a Replacement for Grunt

Note that Broccoli does not attempt to replace Grunt at all. In fact, it’s a grunt plugin. The idea here is to let Grunt do what it does best – run tasks and leave the building of your app to a tool that was designed to build – Broccoli. Simply install the plugin and replace all of the “hoop-jumping-multiple-tempfile-slow-building” mess in your Gruntfile with one fast, straightforward build task. Then you can use Grunt for the things it’s better at, like scaffolding and deploying.

Why Broccoli is Good for You

If you’ve spent any time on a medium sized project with Grunt then you are familiar with how slow builds can become. After a few months of development your productivity begins to shrink. One day, you change a single file, refresh the browser, and wait… and wait… 12 seconds later, your app loads. What was once a responsive feedback loop has become an intolerable double digit delay.

Never fear, Broccoli will get that to under 0.5 seconds. Do you have 100+ JavaScript files? No problem. But wait. “We use CoffeeScript, Sass, and ES6 Modules in our app. That stuff understandably takes time to build”, you say. Not anymore they don’t.

The beauty of Broccoli lies in three places:

  1. Trees. Instead of passing around File System Directories (Grunt) or Memory Streams (Gulp), it passes around Trees. Broccoli isn’t built around individual files. Its unit of work (input and output) is a tree. This makes n:1 compilers like Sass a breeze to deal with (tree pun).

  2. Chainable Plugins. Since all plugins return a tree, they can be easily chained.

    var tree = broccoli.makeTree('build');
    tree = filterCoffeeScript(tree);
    tree = compileSass(tree);
    return tree;
  1. Caching. Broccoli’s approach to incremental builds is pretty simple. Rather than do a partial rebuild (Rails, Brunch), it gets out of the way of the plugins and allows them to cache their output as appropriate. Broccoli simply does a fresh rebuild every time and the plugins return the majority of the build from their caches.

Where Broccoli Fits in

There are some places where Broccoli might not be the best choice and/or might not be a fit at all. If you’re building Sencha Ext JS or Sencha Touch apps then Broccoli will be a poor fit for that ecosystem.

However, if you are already using Grunt to do your builds then Broccoli could be a great fit. Also (and obviously), if you are using Ember then Broccoli is the perfect, if not the de-facto choice in build tools. That’s especially true given that the new Ember CLI uses the broccoli pipeline for asset compilation.

So, let’s go ahead and see what an actual build file looks like.

How Do I Use Broccoli

Installation is very straightforward. Since Broccoli runs on Node, we can use npm to install what we need. Note that you may need to run the broccoli-cli install as root/Administrator:

    npm install --save-dev broccoli
    npm install --global broccoli-cli

Then install any broccoli plugins you intend to use in your build file:

    npm install --save-dev broccoli-static-compiler
    npm install --save-dev broccoli-concat

The build file for Broccoli is called Brocfile.js and should obviously live in the project root folder. A very simple one to minify your JS and compile your Sass might look like this:

   var compileSass = require('broccoli-sass'),
        concatenate = require('broccoli-concat'),
        mergeTrees  = require('broccoli-merge-trees'),
        pickFiles   = require('broccoli-static-compiler'),
        uglifyJs    = require('broccoli-uglify-js'),
        app = 'app',
        appCss,
        appHtml,
        appJs;

    /** 
     * move the index.html file from the project /app folder
     * into the build production folder
     */
    appHtml = pickFiles(app, {
        srcDir  : '/',
        files   : ['index.html'],
        destDir : '/production'
    });

    /**
     * concatenate and compress all of our JavaScript files in 
     * the project /app folder into a single app.js file in 
     * the build production folder
     */
    appJs = concatenate(app, {
        inputFiles : ['**/*.js'],
        outputFile : '/production/app.js',
        header     : '/** Copyright Modus Create Inc. 2014 **/'
    });
    appJs = uglifyJs(appJs, {
        compress: true
    });

    /**
     * compile all of the SASS in the project /resources folder into 
     * a single app.css file in the build production/resources folder
     */
    appCss = compileSass(
        ['resources/sass'],
        '/app.scss',
        'production/resources/app.css'
    );

    // merge HTML, JavaScript and CSS trees into a single tree and export it
    module.exports = mergeTrees([appHtml, appJs, appCss]);

Now you have two choices:

  1. You can either start up the built-in watch server with broccoli serve and it will automatically know what needs to be rebuilt and when to rebuild it. Point your browser to http://localhost:4200 and you will see your newly built app.

  2. You can use the CLI to build your app into whatever directory you choose. For example – broccoli build ‘public’ will build your app into the ‘public’ directory of your workspace.

<

p>Broccoli is currently in its Beta release (0.12.3) and although it’s still a bit rough around the edges, the tool itself is extremely fast and the community around it is growing. I think we should all get out there and give Broccoli a try. Our refresh buttons will thank us.


Like What You See?

Got any questions?