An issue with CoffeeScript is that it puts another layer between you and JavaScript, and having to manually compile CoffeeScript files whenever they change quickly gets old. Fortunately, CoffeeScript has some alternative forms of compilation that can make the development cycle somewhat smoother.
As we covered in Initial Setup, we can compile CoffeeScript files using the coffee executable:
coffee --compile --output lib src
However, calling that whenever a source file changes is a bit of a bore, so letâs look into automating it.
Cake is a
super simple build system along the lines of Make and Rake. The library is bundled with
the coffee-script
npm package, and
available via an executable called cake
.
You can define tasks using CoffeeScript in a file called Cakefile
. Cake will pick these up, and can be
invoked by running cake [task]
[options]
from within the directory. To print a list of all the
tasks and options, just type cake
.
Tasks are defined using the task()
function, passing a name, optional
description, and callback function. For example, create a file called
Cakefile
, and two directories, lib
and src
.
Add the following to the Cakefile
:
fs = require 'fs' {print} = require 'util' {spawn} = require 'child_process' build = (callback) -> coffee = spawn 'coffee', ['-c', '-o', 'lib', 'src'] coffee.stderr.on 'data', (data) -> process.stderr.write data.toString() coffee.stdout.on 'data', (data) -> print data.toString() coffee.on 'exit', (code) -> callback?() if code is 0 task 'build', 'Build lib/ from src/', -> build()
In the example above, weâre defining a task called build
that can be invoked by running cake build
. This runs the same command as the
previous example, compiling all the CoffeeScript files in src
to JavaScript in lib
. You can now reference JavaScript files in
the lib
directory as per usual from
your HTML:
<script src="lib/app.js" type="text/javascript" charset="utf-8"></script>
Weâre still having to manually run cake
build
whenever our CoffeeScript code changes, which is far from
ideal. Luckily, the coffee
command
takes another option, --watch
, which
instructs it to watch a directory for changes and re-compiling as
necessary. Letâs define another task using that:
task 'watch', 'Watch src/ for changes', -> coffee = spawn 'coffee', ['-w', '-c', '-o', 'lib', 'src'] coffee.stderr.on 'data', (data) -> process.stderr.write data.toString() coffee.stdout.on 'data', (data) -> print data.toString()
If one task relies on another, you can run other tasks using
invoke(name)
. Letâs add a utility task
to our Cakefile
which is going to both
open index.html
and start watching the
source for changes:
task 'open', 'Open index.html', -> # First open, then watch spawn 'open', 'index.html' invoke 'watch'
You can also define options for your task using the option()
function, which takes a short name,
long name, and description:
option '-o', '--output [DIR]', 'output dir' task 'build', 'Build lib/ from src/', -> # Now we have access to a `options` object coffee = spawn 'coffee', ['-c', '-o', options.output or 'lib', 'src'] coffee.stderr.on 'data', (data) -> process.stderr.write data.toString() coffee.stdout.on 'data', (data) -> print data.toString()
As you can see, the task context now has access to an options
object containing any data specified by
the user. If we run cake
without any
other arguments, all the tasks and options will be listed.
Cakeâs a great way of automating common tasks such as compiling CoffeeScript without going to the hassle of using bash or Makefiles. Itâs also worth taking a look at Cakeâs source, a great example of CoffeeScriptâs expressiveness and beautifully documented alongside the code comments.
Get The Little Book on CoffeeScript now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.