Symfony2: Using CoffeeScript with Assetic

CoffeeScript is a scripting language which is compiled to run as JavaScript. It let you write simpler code with improvements in much of the JavaScript syntax, mainly inspired by Ruby and Python. It is served up to the end user as JavaScript so it requires nothing on the end users part.

If you are using Symfony2's asset manager Assetic then this can deal with the compilation of your CoffeeScript files into JavaScript for you. This offers the advantages of caching the end result so the compilation does not run every time whilst also automatically recompiling when ever the script changes during development.

Installation

Whilst Assetic's CoffeeScriptFilter will do the work for you it does not do the actual compilation, you will still need to install CoffeeScript itself along with node.js on which it runs. Details of how to do this for various platforms can be found on the CoffeeScript GitHub page. For me, running on Fedora 13, I downloaded the latest node.js tar and built it from source using:

sudo ./configure
sudo make
sudo make install

I then installed NPM the Node Package Manager with the following

su
curl http://npmjs.org/install.sh | sh

I had to su to root rather than sudo as the script run needed to run as root. I could then install coffee using:

sudo npm install -g coffee-script

Configuration

Then main configuration in the app's config.yml is just the paths to coffee and node. These default respectively to /usr/bin/coffee and /usr/bin/node. This was not where they had been installed for me so I needed the following config:

assetic:
    filters:
        coffee:
            bin: /usr/local/bin/coffee
            node: /usr/local/bin/node

Use

It is easy to configure what assets and filters Assetic should output from within Twig templates (Twig is Symfony2's default templating engine). I placed a test CoffeeScript file in my bundle and the following in my base.html.twig file in order to serve it up compiled into JavaScript:

{% javascripts output="/js/instant.js" filter="coffee"
    '@LimeThinkingSpringBundle/Resources/js/instant.coffee'
%}
    <script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

This is all was needed and the file was served up as regular JavaScript. You can combine multiple CoffeeScript files into a single output file by just listing them as such:

{% javascripts output="/js/instant.js" filter="coffee"
    '@LimeThinkingSpringBundle/Resources/js/instant.coffee'
    '@LimeThinkingSpringBundle/Resources/js/another.coffee'
%}
    <script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

Both the files will now be served up as a single file compiled into regular JavaScript.

Combining JavaScript and CoffeeScript files

One of the great advantages of using Assetic is reducing the number of asset files to lower HTTP requests. In order to make full use of this it would be good to combine all your JavaScript and CoffeeScript files together since they will ultimately all be served as JavaScript. Unfortunately just adding the JavaScript files to the files to be combined as above will not work as the regular JavaScript files will not survive the CoffeeScript compilation.

The good news though is that Kris Wallsmith has added a way round this to the latest version of Assetic, so that as of the beta 2 version of Symfony2 you will be able to configure the CoffeeScriptFilter to be run against any file with a specified extension. So changing my configuration to be the following:

assetic:
    filters:
        coffee:
            bin: /usr/local/bin/coffee
            node: /usr/local/bin/node
            apply_to: "\.coffee$"

means that he filter is automatically applied to my files as they have the .coffee extension.
So I can now remove specifying the filter in the Twig template and list any regular JavaScript files which will now be combined into the output file without having been run through coffee:

{% javascripts output="/js/instant.js"
    '@LimeThinkingSpringBundle/Resources/js/instant.coffee'
    '@LimeThinkingSpringBundle/Resources/js/another.coffee'
    '@LimeThinkingSpringBundle/Resources/js/plain.js'
%}
    <script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

You could of course then run this through the YUI Compressor using Assetic to reduce the size of the output file with the following:

{% javascripts output="/js/instant.js" filter="yui_js"
    '@LimeThinkingSpringBundle/Resources/js/instant.coffee'
    '@LimeThinkingSpringBundle/Resources/js/another.coffee'
    '@LimeThinkingSpringBundle/Resources/js/plain.js'
%}
    <script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

Please see my post Symfony2: Using Assetic from Twig for more details, its about using the YUI Compressor for CSS but there are only a few minor differences.