class: center, middle # Intro to Broccoli Simon Wade [@aexmachina](http://twitter.com/aexmachina) [http://aexmachina.info](http://aexmachina.info) _The code for the enclosed examples is available on this [Github repo](https://github.com/aexmachina/intro-to-broccoli)._ --- ## What is Broccoli? - A build tool! - Written in Node.js - Can be used to construct your own DIY asset pipeline - Static file server built-in - Watches your source files and performs incremental rebuilds as files change - Perfect for front-end development - Strong empahsis on speed and minimal design principles The Broccoli Github page provides the following summary: > A fast, reliable asset pipeline, supporting constant-time rebuilds and compact build definitions. Comparable to the Rails asset pipeline in scope, though it runs on Node and is backend-agnostic. --- ## Why the Name? The central concept in Broccoli is the tree, which can be thought of as similar to a filesystem directory, but: - Each tree consumes one or more trees as input and produces a single tree as output - This process may alter its input in some way as part of the process For example, a Broccoli plugin for compiling SASS will take one or more trees containing `.scss` files as input, and produce a single `.css` file as output. --- ## What about Grunt or Gulp? - Broccoli is a build tool, whereas... - Grunt and Gulp are task runners, in the tradition of make, rake and friends - In Rails speak Broccoli is the asset pipeline, and Grunt and Gulp are `rake` You can use Grunt or Gulp as a build tool, but it can be difficult to keep builds fast as the size of the project and complexity of the build process grows. - Ember App Kit went to extreme lengths to parallelize their build process, but it wasn't going to get any faster That's where Broccoli shines, its architecture allows for fast builds as the size and complexity of the project grows over time. --- ## Architecture A Broccoli tree has a remarkably simple API - it's an object that provides two methods: - `read(readTree)` is called when the tree should produce its output directory - `cleanup()` is called when the previous output directory can be cleaned up These two methods (in conjunction with [some simple rules](https://github.com/joliss/broccoli#plugin-api-specification)) are all that's required to integrate with Broccoli. - Broccoli is broken into the CLI and the tool itself --- ## Show Me the Code Here's an example of a very simple but entirely useful way to create a Broccoli tree: ``` function unwatchedTree(dir) { return { read: function() { return dir; }, cleanup: function() {} }; } ``` This function returns a tree containing the contents of `dir`, and doesn't watch for changes. --- ## A Simple Example The following example is simple, but actually pretty darn useful. Save the following code into `Brocfile.js` to serve the `public/` directory: ``` // Brocfile.js module.exports = 'public/'; ``` Any files that you put into `public/` will be available at `http://localhost:4200` when you run: ``` broccoli serve ``` _n.b. You'll need to use the LiveReload plugin for [Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en) or [Firefox](https://addons.mozilla.org/en-US/firefox/addon/livereload/) unless you want to add the LiveReload script snippet into your index.html._ --- ## `broccoli serve` Fires up a webserver that: - Watches your files for changes - Rebuilds on the fly - Has LiveReload support built-in ## `broccoli build` Run `broccoli build dist` to produce a build in `dist/` when you're ready to deploy. --- ### The Basics Okay, let's concatenate all our CSS and Javascript into single files. Here's the Brocfile (and the [full code here](https://github.com/aexmachina/intro-to-broccoli/tree/example-2)): ``` // Brocfile.js var concat = require('broccoli-concat'), pickFiles = require('broccoli-static-compiler'), mergeTrees = require('broccoli-merge-trees'); // concat the JS var scripts = concat('app/', { inputFiles: ['**/*.js'], outputFile: '/assets/scripts.js' }); // concat the CSS var styles = concat('app/styles', { inputFiles: ['**/*.css'], outputFile: '/assets/styles.css' }); // grap any static assets var public = pickFiles('public', { srcDir: '.', destDir: '.' }); // and merge all the trees together module.exports = mergeTrees([scripts, styles, public]); ``` --- ### Add a Pinch of SASS Now we're going to add the [broccoli-sass](https://www.npmjs.org/package/broccoli-sass) plugin, which uses [node-sass](https://www.npmjs.org/package/node-sass) for fast SASS compilation (and also provides support for source maps too). Now we just add `var compileSass = require('broccoli-sass')` to the requires at the top of our Brocfile and then replace the `var styles = ...` with the following ([code here](https://github.com/aexmachina/intro-to-broccoli/tree/example-3)): ``` var styles = compileSass(['app/styles'], 'app.scss', '/assets/styles.css'); ``` `compileSass()` takes an array of input trees (hence the `['app/styles']` argument), and this is also how you provide include paths to the SASS compiler. - Anyone want to see source maps in action? --- ### And a Dash of Sweet ES6 Why wait for the browsers to add... - [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) - [destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) - [class syntax](http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes) When you can start using them right now? - [jstransform](https://www.npmjs.org/package/jstransform) - [esnext](https://www.npmjs.org/package/esnext) I like to use jstransform because it's minimal and preserves line numbers. Now we just add `var compileES6 = require('broccoli-jstransform')` to the requires at the top of our Brocfile and then add the following after `var scripts = ...` ([code here](https://github.com/aexmachina/intro-to-broccoli/tree/example-4)): ``` scripts = compileES6(scripts); ``` Huzzah! 21st century JavaScript is here at last ;) --- ## In Summary I've found Broccoli to be a really great tool. It's got a wonderfully simple API and a wide range of good quality plugins. We use it for a variety of tasks including: - Building our Ember.js apps - Building modules that we use in our apps - Some of my open source modules are build using Broccoli ([ember-notify](https://github.com/aexmachina/ember-notify) and [select-transformer](https://github.com/aexmachina/select-transformer)) - Replacing Traceur in our Node.js code --- ## Parting thoughts - While Broccoli can be used directly, I think developers will interact with higher-level tools - Provide a predefined asset pipeline based on Broccoli, providing a set of conventions targeted at a specific use case. - This has already happened with [ember-cli](http://www.ember-cli.com/) - I expect we'll see similar things for AngularJS, React and friends. --- class: center, middle # Thanks! Simon Wade [@aexmachina](http://twitter.com/aexmachina)