Compilers and Minifiers

Fanstatic supports running external programs to create transformations of resource files. There are two use cases for this: The first use case is compiling files written in languages like CoffeeScript or SASS into JavaScript and CSS, respectively. The second use case is automatically generating minified versions of JS and CSS files. We call programs for the first case compilers and those for the second case minifiers, and use compiling as the encompassing term.

Running compilers

There are two ways of running compilers, one is manually via the command-line program fanstatic-compile. The other way is on-the-fly when processing a request: When the compile option is set to True (see Configuration options), Fanstatic will check on each request whether the source file is older than the compiled file, and invoke the compiler if needed.

Usage: fanstatic-compile my.package.name
Compiles and minifies all Resources declared in the given package.

Fanstatic also provides a hook into setuptools to run compilers during sdist creation, so you can package and deploy the compiled resources and don’t need any of the compilers in the production environment. To use this, add the following to the setup() call in your package’s setup.py:

setup(
  ...
  cmdclass={'sdist': fanstatic.sdist_compile},
  ...
)

Then, run python setup.py sdist as usual to create your sdist.

Note: If you are using version control plugins (e.g. setuptools_hg) to collect the files to include in your sdist, and do not check in the compiled/minified files, they will not be included in the sdist. In that case, you will need to create a MANIFEST.in file to pick them up, for example:

recursive-include src *.css *.js

Configuring compilers

Compilers work by creating the resource file from a source file. For example, the CoffeeScript compiler creates foo.js from foo.coffee. This is configured like so:

from fanstatic import Library, Resource

js_library = Library('js', 'js_resources')

a = Resource(js_library, 'a.js', compiler='coffee', source='a.coffee')

When compilation is run and a.js is not present, or older than a.coffee, Fanstatic will run the CoffeeScript compiler on a.coffee to produce a.js.

Compilers can have knowledge what the source files are typically named, so usually you don’t have to specify that explicitly on each Resource (if you do specify a source that of course is used, overriding what the Compiler thought).

You can also configure compilers on the level of the Library, so they apply to all Resources with a given extension:

from fanstatic import Library, Resource

coffee_library = Library('coffee', 'coffee_resources',
                         compilers={'.js': 'coffee'})

a = Resource(coffee_library, 'b.js')
b = Resource(coffee_library, 'plain.js', compiler=None)

Note that individual Resources can override the compiler set on the Library.

Configuring minifiers

Minifiers work by creating a minified version of the resource file. For example, jsmin creates foo.min.js from foo.js. This is configured like so:

from fanstatic import Library, Resource

js_library = Library('js', 'js_resources')

a = Resource(js_library, 'a.js', minified='a.min.js', minifier='jsmin')

Minifiers can have a built-in rule what the target filename looks like, so usually you don’t have to explicitly specify minified=.

You can also configure minifiers on the level of the Library, so they apply to all Resources with a given extension:

from fanstatic import Library, Resource

js_library = Library('js', 'js_resources', minifiers={'.js': 'jsmin'})

a = Resource(js_library, 'a.js')
b = Resource(js_library, 'tricky.js', minifier=None, minified='tricky.min.js')

Note that individual Resources can override the minifier set on the Library.

Pre-packaged compilers

Fanstatic includes the following compilers:

coffee:CoffeeScript, a little language that compiles to JavaScript, requires the coffee binary (npm install -g coffeescript)
less:LESS, the dynamic stylesheet language, requires the lessc binary (npm install -g less)
sass:SASS, Syntactically Awesome Stylesheets, requires the sass binary (gem install sass)

Fanstatic includes the following minifiers:

cssmin:cssmin, A Python port of the YUI CSS compression algorithm, requires the cssmin package. Use the extras requirement fanstatic[cssmin] to install this dependency.
jsmin:jsmin, A Python port of Douglas Crockford’s jsmin, requires the jsmin package. Use the extras requirement fanstatic[jsmin] to install this dependency.
closure:closure, A Python wrapper around the Google Closure Compiler. Use the extras requirement fanstatic[closure] to install this dependency.

Hiding source files

You can prevent the Fanstatic publisher from serving the source files in by using the ignores configuration option.

Writing compilers

A compiler is a class that conforms to the following interface:

class fanstatic.compiler.Compiler

Generates a target file from a source file.

__call__(resource, force=False)

Perform compilation of resource.

Parameters:force – If True, always perform compilation. If False (default), only perform compilation if should_process returns True.
__weakref__

list of weak references to the object (if defined)

available

Whether this compiler is available, i.e. necessary dependencies like external commands or third-party packages are installed.

should_process(source, target)

Determine whether to process the resource, based on the mtime of the target and source.

source_path(resource)

Return an absolute path to the source file (to use as input for compilation)

target_path(resource)

Return an absolute path to the target file (to use as output for compilation)

Fanstatic provides generic base classes for both compilers and minifiers, as well as helper classes for compilers that run external commands or depend on other Python packages (fanstatic.compiler.CommandlineBase, fanstatic.compiler.PythonPackageBase).

To make a compiler or minifier known to Fanstatic, it needs to be declared as an entry point in its packages’ setup.py:

entry_points={
    'fanstatic.compilers': [
        'coffee = fanstatic.compiler:COFFEE_COMPILER',
        ],
    'fanstatic.minifiers': [
        'jsmin = fanstatic.compiler:JSMIN_MINIFIER',
        ],
    },