Sometimes you want to work on your AMD code as a single bundle; the same way it will
be deployed in production, with automatic bundling and live reload. Thanks to
Chris Dickinson's recent work on the beefy development
server, it's now easier to use r.js as a custom bundler.
Let’s get it configured.
What is beefy?
It was made to work with browserify by default, but if you use the
command line argument, you can specify r.js as your bundler of choice.
We are going to use this to run r.js each time our
main.js file is requested.
Get beefy by using the command:
npm install -g beefy
What is r.js?
r.js is a command line utility designed for running
AMD-based projects in node.js, Rhino, and xpcshell.
It also includes an optimizer for combining your AMD files into a bundle.
By using the optimizer functionality of r.js, in combination with beefy, we can
We need to install r.js into our project using the command:
npm install requirejs
If you have a
package.json and want to add it to your devDependencies, add
to the command:
npm install --save-dev requirejs
Minimum Viable Config
There are seven options we need to set in our r.js config file that will allow us to use
it with beefy (actually, two are just nice to have). They are
logLevel and each one is explained below.
These options will be set in a file named
config.js, which will be passed to the beefy
You can find an example project at https://github.com/iceddev/beefy-requirejs-example
As with every Require.js project, you are going to want to set your
This could be any place you want to begin resolving. Typically, it will be
your vendor or application directory. Since we are doing a simple example,
let's just use our current directory and we will put our JS files at the root
of our project.
To specify the module (and all of its dependencies) we want to optimize,
we use the
name option. This option indicates an entry point for r.js
to begin resolving dependencies.
We are going to name our entry module
main.js, an AMD package convention,
and it will be in the root of our project.
Create a file in the root of your project named
This file will be the entry point for your applicaton, and will be wrapped
define function call.
console.log('another-module dependency:', another);
Create another file in the root of your project, but name this one
This file is a dependency of your
main.js module and is just used to demonstrate that
we are, in fact, bundling all the dependencies together.
something: 'yup, another module'
We can specify the
insertRequire option, to insert a
require function call at the bottom of
our bundle, which will initialize your application.
Note: This isn't needed if your
data-main filename is the same as the module entrypoint name
because Require.js will do an implicit
require. We will add it here because it doesn't hurt
anything and will help in situations like naming the files differently or using something like
During development, you probably want to disable optimization/uglification, so you will
be able to debug your bundle and builds will happen quicker. This is done by setting
optimize option to
useSourceUrl - Source Maps
When using the
optimize: 'none' option, we can get source maps, using
useSourceUrl option to
true will auto insert these for you, but it should
be disabled when bundling for production.
The typical way r.js is used is to output a file, determined by providing
a filename string as the
beefy doesn't operate on files, and instead expects to receive data on
out option can also take a function that will receive the output
text as its only parameter.
We are going to leverage the
out function to redirect the r.js output
By default, r.js logs info about the build process. This gets intercepted
by beefy on
process.stdout and is added to the output served.
r.js provides a
logLevel option that can be used to disable logging. Log level 3 is
the level that logs only errors.
The last thing we need is an
index.html file that includes Require.js. If an
file doesn't exist, beefy serves up a default page that just injects a script tag for your
entrypoint file. This won't work with the workflow outlined above because we assume the
require machinery will be available.
index.html file in the root of your project that contains:
<title>Beefy + Require.js</title>
The beefy command takes a filename as the first argument, or an input filename/output filename combination
in the form of
input-filename.js:output-filename.js. If you don't specify an input filename but specify an
output filename, in the format
:output-filename.js, beefy won't pass a filename to the bundler, but it will
still make the result of the bundler command available as
r.js assumes it is supposed to run a file if one is passed as the first argument to the command, and skips the optimization
tool. To avoid this, we will specify the first beefy argument as
Next, we want to reference the r.js compiler as the bundler:
Finally, r.js expects the
-o config.js argument to start the optimize tool with the
config.js file. Anything
-- argument to beefy is passed directly to the bundler command.
Putting it all together, the command looks like:
beefy :main.js --bundler ./node_modules/.bin/r.js -- -o config.js
Accessing your bundle
Open your browser and go to the location that the beefy command said it is listening on,
listening on http://localhost:9966/.
You should see logging in your console if your modules loaded correctly. You should also
be able to view the individual files in the
sources pane, probably under (no domain)
since we were using the
What else can we do?
This configuration will allow you easily to swap dependencies with a single line change.
Can be changed to:
And your underscore references will resolve to Lo-Dash on the next refresh of your page.
Watch out for part two of this article for some advanced techniques and any other interesting
stuff I find related to AMD and beefy.