Managing Transpiled Resources in Django

With the rise in popularity of languages and pre-processors like EcmaScript6 and Stylus, a somewhat annoying step was added to the development cycle: compiling your front-end scripts into a browser friendly form.

One way to handle this is to perform these tasks by hand. Another is to have the compilation scripts watch for changes themselves. However, we can build this functionality into our django application and forget about the issue all together.

note: this guide assumes you have already configured django to serve static files as described here.

Getting started with django-compressor

Let's begin by downloading django-compressor from pip:

pip install django-compressor

and add 'compressor' to your INSTALLED_APPS setting.

One of the many nice features of django-compressor is very sensible defaults which allow you to get going with very little configuration. Some things to note about the default settings:

  • it looks for static files in the same directory as django
  • javascript files are minified
  • urls are normalized in css files
  • when DEBUG == False, the files are concatenated into a single one

If you want to change the default behavior of js/css processing, you can set a list of the filters that are used. See the documentation for more details.

In order to know what files to run over, django-compressor provides the template tag compress which performs the necessary actions. For example, to have django-compressor process the javascript of my homepage:

{% load staticfiles %}
{% load compress %}
...
{% compress js %}
    <script src="{% static 'thirdparty/jquery/dist/jquery.js' %}"></script>
    <script src="{% static 'thirdparty/underscore/underscore.js' %}"></script>
    <script src="{% static 'scripts/core.js' %}"></script>
{% endcompress %}

In place of that block, the rendered template would have a single <script> element whose src points to a concatenation and minifacation of those three files (yes, it's cached).

In order for django-compressor to be able to find your static files when it comes time to run ./manage.py collectstatic, you have to add compressor.finders.CompressorFinder to your STATICFILES_FINDERS setting.

Handling additional languages

Besides various file optimizations, django-compressor will also handle various any sort of pre-compilation that we need.

To teach django-compressor which types correspond to which commands, define a variable in your settings file called COMPRESS_PRECOMPILERS. This setting associates the value of the type attribute of an element with a command to run in order to produce the desired output. For example, to run the commandstylus on any linked file with type="text/stylus", the configuration would look something like

COMPRESS_PRECOMPILERS = (
    ('text/stylus', 'stylus'),
)

By default, django-compressor passes the command the contents of the file to stdio and expects the response to come through stdin. Therefore, with this configuation an element of the form,

<link type="text/stylus" href="foo.styl"/>

is compiled into css before joining the rest of the files as described above.

However, sometimes the executable does not expect the source from stdin and instead wants a filename. To handle this, django-compressor uses the strings {infile} and {outfile} as placeholders for the appropriate names when executing the command. Therefore, to allow support for es6 using babel, the configuration would now look like:

COMPRESS_PRECOMPILERS = (
    ('text/stylus', 'stylus'),
    ('text/es6', 'babel {infile} -o {outfile}'),
)

Additional configurations

Here are configurations for each of the pre-processors I use:

  • coffeescript: ('text/coffeescript', 'coffee --compile --stdio')
  • stylus: ('text/stylus', 'stylus')
  • coffee-react-transform: ('text/cjsx', 'cjsx-transform {infile} | coffee --compile --stdio -b')
  • es6: ('text/es6', 'babel {infile} -o {outfile}')
blog comments powered by Disqus