What is broccoli?
Broccoli is a JavaScript build tool that allows you to define a build pipeline using a series of plugins. It is particularly well-suited for front-end development, enabling tasks such as file transformation, concatenation, and minification.
What are broccoli's main functionalities?
File Transformation
This code demonstrates how to use Broccoli to transform files. It uses the Funnel plugin to include only JavaScript files from the 'app' and 'vendor' directories and then merges these trees into a single output tree.
const Broccoli = require('broccoli');
const Funnel = require('broccoli-funnel');
const MergeTrees = require('broccoli-merge-trees');
let appTree = new Funnel('app', {
include: ['**/*.js']
});
let vendorTree = new Funnel('vendor', {
include: ['**/*.js']
});
let finalTree = new MergeTrees([appTree, vendorTree]);
module.exports = finalTree;
File Concatenation
This code demonstrates how to concatenate JavaScript files using the broccoli-concat plugin. It takes all JavaScript files in the 'app' directory and concatenates them into a single file, 'app.js', in the 'assets' directory.
const Broccoli = require('broccoli');
const Concat = require('broccoli-concat');
let appJs = new Concat('app', {
outputFile: '/assets/app.js',
inputFiles: ['**/*.js'],
sourceMapConfig: { enabled: true }
});
module.exports = appJs;
File Minification
This code demonstrates how to minify JavaScript files using the broccoli-uglify-sourcemap plugin. It takes all JavaScript files in the 'app' directory and minifies them, with source maps enabled.
const Broccoli = require('broccoli');
const UglifyJS = require('broccoli-uglify-sourcemap');
let appJs = new UglifyJS('app', {
mangle: true,
compress: true,
sourceMapConfig: { enabled: true }
});
module.exports = appJs;
Other packages similar to broccoli
gulp
Gulp is a toolkit for automating painful or time-consuming tasks in your development workflow. It uses a code-over-configuration approach, making it flexible and easy to use. Compared to Broccoli, Gulp has a larger ecosystem and more plugins available.
webpack
Webpack is a module bundler that takes modules with dependencies and generates static assets representing those modules. It is highly configurable and supports a wide range of plugins and loaders. Webpack is more powerful and complex compared to Broccoli, making it suitable for larger projects.
grunt
Grunt is a JavaScript task runner that automates repetitive tasks like minification, compilation, unit testing, and linting. It uses a configuration-over-code approach, which can be less flexible but easier to understand for beginners. Grunt has been largely superseded by Gulp and Webpack in recent years.
Broccoli
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. For background and architecture, see the
introductory blog post.
For the command line interface, see
broccoli-cli.
Installation
npm install --save-dev broccoli
npm install --global broccoli-cli
Brocfile.js
A Brocfile.js
file in the project root contains the build specification. It
should export a tree.
A tree can be any string representing a directory path, like 'app'
or
'src'
. Or a tree can be an object conforming to the Plugin API
Specification. A Brocfile.js
will usually
directly work with only directory paths, and then use the plugins in the
Plugins section to generate transformed trees.
The following simple Brocfile.js
would export the app/
subdirectory as a
tree:
module.exports = 'app'
With that Brocfile, the build result would equal the contents of the app
tree in your project folder. For example, say your project contains these
files:
app
├─ main.js
└─ helper.js
Brocfile.js
package.json
…
Running broccoli build the-output
(a command provided by
broccoli-cli) would generate
the following folder within your project folder:
the-output
├─ main.js
└─ helper.js
Using plugins in a Brocfile.js
The following Brocfile.js
exports the app/
subdirectory as appkit/
:
var Funnel = require('broccoli-funnel')
module.exports = new Funnel('app', {
destDir: 'appkit'
})
That example uses the plugin
broccoli-funnel
.
In order for the require
call to work, you must first put the plugin in
your devDependencies
and install it, with
npm install --save-dev broccoli-funnel
With the above Brocfile.js
and the file tree from the previous example,
running broccoli build the-output
would generate the following folder:
the-output
└─ appkit
├─ main.js
└─ helper.js
Plugins
You can find plugins under the broccoli-plugin keyword on npm.
Using Broccoli Programmatically
In addition to using Broccoli via the combination of broccoli-cli
and a Brocfile.js
, you can also use Broccoli programmatically to construct your own build output via the Builder
class. The Builder
is one of the core APIs in Broccoli, and is responsible for taking a graph of Broccoli nodes and producing an actual build artifact (i.e. the output usually found in your dist
directory after you run broccoli build
). The output of a Builder
's build
method is a Promise that resolves when all the operations in the graph are complete. You can use this promise to chain together additional operations (such as error handling or cleanup) that will execute once the build step is complete.
By way of example, let's assume we have a graph of Broccoli nodes constructed via a combination of Funnel
and MergeTrees
:
const html = new Funnel(appRoot, {
files: ['index.html'],
annotation: 'Index file'
})
const js = new Funnel(appRoot, {
files: ['app.js'],
destDir: '/assets',
annotation: 'JS Files'
});
const css = new Funnel(appRoot, {
srcDir: 'styles',
files: ['app.css'],
destDir: '/assets',
annotation: 'CSS Files'
});
const public = new Funnel(appRoot, {
annotation: 'Public Files'
});
const tree = new Merge([html, js, css, public]);
At this point, tree
is a graph of nodes, each of which can represent either an input or a transformation that we want to perform. In other words, tree
is an abstract set of operations, not a concrete set of output files.
In order to perform all the operations described in tree
, we need to do the following:
- construct a
Builder
instance, passing in the graph we constructed before - call the
build
method, which will traverse the graph, performing each operation and eventually writing the output to a temporary folder indicated by builder.outputPath
Since we typically want do more than write to a temporary folder, we'll also use a library called TreeSync
to sync the contents of the temp file with our desired output directory. Finally, we'll clean up the temporary folder once all our operations are complete:
const { Builder } = require('broccoli');
const TreeSync = require('tree-sync');
const tree = new Merge([html, js, css, public]);
const builder = new Builder(tree);
const outputDir = 'dist';
const outputTree = new TreeSync(builder.outputPath, outputDir);
builder.build()
.then(() => {
return outputTree.sync();
})
.then(() => {
return builder.cleanup();
})
.catch(err => {
console.log(err);
return builder.cleanup();
});
Running Broccoli, Directly or Through Other Tools
Helpers
Shared code for writing plugins.
Plugin API Specification
See docs/node-api.md.
Also see docs/broccoli-1-0-plugin-api.md on
how to upgrade from Broccoli 0.x to the Broccoli 1.x API.
Security
- Do not run
broccoli serve
on a production server. While this is
theoretically safe, it exposes a needlessly large amount of attack surface
just for serving static assets. Instead, use broccoli build
to precompile
your assets, and serve the static files from a web server of your choice.
Get Help
- IRC:
#broccolijs
on Freenode. Ask your question and stick around for a few
hours. Someone will see your message eventually. - Twitter: mention @jo_liss with your question
- GitHub: Open an issue on a specific plugin repository, or on this
repository for general questions.
License
Broccoli was originally written by Jo Liss and is
licensed under the MIT license.
The Broccoli logo was created by Samantha Penner
(Miric) and is licensed under CC0
1.0.