Manage JavaScript dependencies with Fanstatic

Until the beginning of this year, we were using Fanstatic to manage dependencies to external JavaScript libraries. In case you are not familiar with Fanstatic, here is a short overview. I will discuss benefits and drawbacks later on.

How it works

Imagine you want to use jQuery in one of your projects. Therefore you build an integration package, say js.jquery, which contains the jquery.js file. You also add a resource.py which would look like this:

from fanstatic import Library, Resource

library = Library('jquery', 'resources')
jquery = Resource(library, 'jquery.js', minified='jquery.min.js')

To finish up, you state the version of the downloaded jquery.js in the setup.py and upload the package to PyPI.

After wrapping your app with Fanstatic, you can declare JavaScript dependencies by listing the integration package inside your setup.py. To use them, you write js.jquery.need() inside any view. Of course you can also add a resource.py and declare a local resource with dependencies:

import fanstatic
import js.jquery

library = fanstatic.Library('custom', 'resources')
my_awesome_js_code = fanstatic.Resource(
    library, 'js/my_awesome_js_code.js',
    minified='js/my_awesome_js_code.min.js',
    depends=[js.jquery.jquery])

Every time you call my_awesome_js_code.need() inside a view, it will also load jQuery.

Benefits

With Fanstatic you no longer need to copy the JavaScript files inside your project and link them statically, you just declare them as a dependency to your own JavaScript code and call my_awesome_js_code.need() inside a view.

The preparation described above can be tedious, but the assumption is, that someone else has built the integration package before, so you can reuse it. Installing a dependency therefore is as easy as adding a line to your setup.py and declare it as a dependency of one of your resources.

Fanstatic makes it a lot easier to update external libraries, since you only need to increase the version number in your setup.py. You can also make sure to use the same version across many projects, e.g. by sharing some buildout configuration across projects.

And besides many small nice features, Fanstatic can also create bundles, i.e. it will merge my_awesome_js_code and all of its dependencies into one big file and deliver it to the client, rather delivering all files separately. This usually makes the initial page load much faster.

Drawbacks

Despite all the benefits Fanstatic offers, we were getting more and more frustrated with it. The main reason is, that we feel a decline in the activity of the Fanstatic user group. With fewer people using Fanstatic, we often have to build the integration packages ourselves. This eliminates one of the main benefits, i.e. that we could reuse the integration packages of others.

For example, in a big project we did last year, we used integration packages for js.jquery, js.classy, js.chosen and many more. Those are pretty popular packages and the integration worked out of the box.

However, even those packages have issues: The integration package for jQuery does not offer version 2.0 or higher and Chosen is only available in a single (outdated) version. We often stumbled upon similar issues with other libraries, e.g. the integration package for js.modernizr is more than 2 years behind.

In addition, some newly developed libraries may not be available at all. Of course we could write the integration packages for those or update the jQuery integration package. But this would mean to find out where the code for an integration package is hosted, write a pull request and wait for a merge, as well as a new release on PyPI. In case the owner is not active anymore, you are out of luck and must add a duplicate package to PyPI. Tedious and unsatisfactory.

This is why we started to look for an alternative to Fanstatic. And maybe we have found it, but this is a story for another day, where I will talk about our experiences with Bower and Bowerstatic.