forked from arcturo/library
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path05_compiling.html
133 lines (97 loc) · 7.8 KB
/
05_compiling.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>The Little Book on CoffeeScript - Compiling</title>
<link rel="stylesheet" href="site/site.css" type="text/css" charset="utf-8">
<link rel="stylesheet" href="site/highlight.css" type="text/css" charset="utf-8">
<script src="site/jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="site/highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="site/coffee-script.js" type="text/javascript" charset="utf-8"></script>
<script src="site/preview.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
hljs.initHighlightingOnLoad();
</script>
</head>
<body>
<div id="container">
<header>
<h1><a href="index.html">The Little Book on CoffeeScript</a></h1>
</header>
<div id="content">
<h1>Automating CoffeeScript compilation</h1>
<p>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 which can make the development cycle somewhat smoother.</p>
<p>As we covered in the first chapter, we can compile CoffeeScript files using the <code>coffee</code> executable:</p>
<pre><code>coffee --compile --output lib src
</code></pre>
<p>In fact in the example above, all the <code>.coffee</code> files in <code>src</code> will be compiled & their JavaScript outputted to the <code>lib</code> directory. Even calling that is a bit of a bore, so let's look into automating it.</p>
<h2>Cake</h2>
<p><a href="http://jashkenas.github.com/coffee-script/#cake">Cake</a> is a super simple build system along the lines of <a href="http://www.gnu.org/software/make/">Make</a> and <a href="http://rake.rubyforge.org/">Rake</a>. The library is bundled with the <code>coffee-script</code> npm package, and available via an executable called <code>cake</code>.</p>
<p>You can define tasks using CoffeeScript in a file called <code>Cakefile</code>. Cake will pick these up, and can be invoked by running <code>cake [task] [options]</code> from within the directory. To print a list of all the tasks and options, just type <code>cake</code>.</p>
<p>Tasks are defined using the <code>task()</code> function, passing a name, optional description and callback function. For example, create a file called <code>Cakefile</code>, and two directories, <code>lib</code> and <code>src</code>. Add the following to the <code>Cakefile</code>:</p>
<p><span class="csscript"></span></p>
<pre><code>fs = require 'fs'
{print} = require 'sys'
{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()
</code></pre>
<p>In the example above, we're defining a task called <code>build</code> that can be invoked by running: <code>cake build</code>. This runs the same command as the previous example, compiling all the CoffeeScript files in <code>src</code> to JavaScript in <code>lib</code>. You can now reference JavaScript files in the <code>lib</code> directory as per usual from your HTML:</p>
<p><span class="csscript"></span></p>
<pre><code><!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<script src="lib/app.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
</body>
</html>
</code></pre>
<p>We're still having to manually run <code>cake build</code> whenever our CoffeeScript code changes, which is far from ideal. Luckily, the <code>coffee</code> command takes another option, <code>--watch</code>, which instructs it to watch a directory for changes and re-compiling as necessary. Let's define another task using that:</p>
<p><span class="csscript"></span></p>
<pre><code> 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()
</code></pre>
<p>If one task relies on another, you can run other tasks using <code>invoke(name)</code>. Let's add a utility task to our <code>Cakefile</code> which is going to both open <code>index.html</code> and start watching the source for changes.</p>
<p><span class="csscript"></span></p>
<pre><code>task 'open', 'Open index.html', ->
# First open, then watch
spawn 'open', 'index.html'
invoke 'watch'
</code></pre>
<p>You can also define options for your task using the <code>option()</code> function, which takes a short name, long name and description.</p>
<p><span class="csscript"></span></p>
<pre><code>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()
</code></pre>
<p>As you can see, the task context now has access to an <code>options</code> object containing any data specified by the user. If we run <code>cake</code> without any other arguments, all the tasks and options will be listed.</p>
<p>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 <a href="http://jashkenas.github.com/coffee-script/documentation/docs/cake.html">Cake's source</a>, a great example of CoffeeScript's expressiveness and beautifully documented alongside the code comments.</p>
<h2>Server side support</h2>
<p>Using Cake for CoffeeScript compilation is fine for static sites, but for dynamic sites we might as well integrate CoffeeScript compilation into the request/response cycle. Various integration solutions already exist for the popular backend languages and frameworks, such as <a href="http://rubyonrails.org/">Rails</a> and <a href="https://www.djangoproject.com/">Django</a>.</p>
<p>When it comes to Rails 3.1, CoffeeScript support comes via <a href="https://github.com/sstephenson/sprockets">Sprockets & the asset pipeline</a>. Add your CoffeeScript files under <code>app/assets/javascripts</code>, and Rails is smart enough to pre-compile them when they're requested. JavaScript & CoffeeScript files are concatenated and bundled using special comment directives, meaning you can fetch all of your application's JavaScript with one request. When it comes to production, Rails will write the compiled output to disk, ensuring it's cached and fast to serve.</p>
<p>Other Ruby options include Rack servers such as 37signal's <a href="http://pow.cx/">Pow</a> and Joshua Peek's <a href="http://josh.github.com/nack/">Nack</a>, both highly recommended if your application doesn't need Rail's other features and associated overhead.</p>
<p>Django also has <a href="http://pypi.python.org/pypi/django-coffeescript/">support for CoffeeScript</a> through special template tags. It works with both inline code and external files.</p>
<p>Both Ruby and Python pipe out to Node and the CoffeeScript lib behind the scenes when compiling CoffeeScript, so you'll need to have those installed during development. If you're using Node directly as a backend for your site, CoffeeScript integration is even simpler and you can use it for both the backend and frontend code. We're going to talk more about this in the next chapter, using <a href="https://github.com/sstephenson/stitch">Stitch</a> to serve all our client-side CoffeeScript.</p>
</div>
</div>
</body>
</html>