Login

Coffeescript compilation

Author:
delfick
Posted:
January 26, 2013
Language:
Python
Version:
1.3
Tags:
development staticfiles coffeescript collectstatic
Score:
0 (after 0 ratings)

All I wanted was for one to one compilation of coffeescript to javascript.

  • Without special templatetags
  • Without specifying explicit bundles
  • Served dynamically in development
  • Compiled by collectstatic for producton

This code is the minimum required for this.

There are two things to take into account:

  • list method to find coffeescript files to compile for collectstatic
  • find method to find coffeescript equivalent for a js file for django.contrib.staticfiles.views.serve.

The list method will use the list method on all finders that come before it in STATICFILES_FINDERS to find all the files that end with .coffee and will return the equivalent .js path with a storage class that knows how to compile the coffeescript.

The find method will use the find method on all finders that come before it in STATICFILES_FINDERS to locate the coffeescript file that is actually being requested.

It will then compile the coffeescript into a file in settings.CACHE_DIR before serving that file.

 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
########################
###   myapp.finders.py
########################

from django.contrib.staticfiles.finders import BaseStorageFinder
from django.contrib.staticfiles.finders import get_finders
from django.core.files.storage import FileSystemStorage
from contextlib import contextmanager
from django.core.files import File
from django.conf import settings
from itertools import takewhile
import subprocess
import tempfile
import operator
import shlex
import os

class CoffeeStorage(FileSystemStorage):
    def path(self, path):
        """Get path to the actual coffeescript file"""
        coffee_path = "{}.coffee".format(path[:-3])
        return os.path.join(self.location, coffee_path)

    def compile(self, src):
        """Let's compile some coffeescript!"""
        cmd = 'coffee -c -p {}'.format(src)

        proc = subprocess.Popen(shlex.split(cmd)
            , stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE
            )
        out, err = proc.communicate()

        if proc.returncode != 0 or err:
            raise Exception('%s\n\n%s' % (err, out))
        else:
            return out

    @contextmanager
    def open(self, path):
        """Open the coffeescript file as a javascript file"""
        with tempfile.NamedTemporaryFile() as f:
            f.write(self.compile(self.path(path)))
            yield File(f)

class CoffeescriptFinder(BaseStorageFinder):
    storage = CoffeeStorage

    def other_finders(self):
        """Get all the finders that come before this one"""
        not_self = lambda finder : operator.is_not(self, finder)
        return takewhile(not_self, get_finders())

    def list(self, ignore_patterns):
        """Find all the coffeescript files and say they have js equivalents"""
        for finder in self.other_finders():
            for path, _ in finder.list(ignore_patterns):
                if path.endswith(".coffee"):
                    js_path = "{}.js".format(path[:-7])
                    location = finder.find(path)[:-len(path)]
                    yield js_path, CoffeeStorage(location=location)

    def find(self, path, all=False):
        """Find the coffeescript file"""
        if path.endswith(".js"):
            coffee_path = "{}.coffee".format(path[:-3])
            for finder in self.other_finders():
                found = finder.find(coffee_path)
                if found:
                    storage = CoffeeStorage(location=found[:-len(path)])
                    destination = os.path.join(settings.CACHE_DIR, path)
                    if not os.path.exists(destination):
                        os.makedirs(os.path.dirname(destination))

                    with open(destination, 'w') as dest:
                        dest.write(storage.compile(found))

                    return destination

        # Nothing found
        return all and None or []

########################
###   settings.py
########################

CACHE_DIR = "some/path/to/where/coffeescript/is/compiled/for/development"

# Make sure coffeescript finder is after other finders
# It uses these to actually find the coffeescript files to compile
STATICFILES_FINDERS = (
      'django.contrib.staticfiles.finders.FileSystemFinder'
    , 'django.contrib.staticfiles.finders.AppDirectoriesFinder'
    , 'myapp.finders.CoffeescriptFinder'
    )

# And serving assets in development
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += staticfiles_urlpatterns()

More like this

  1. Collapsed stacked inlines by Aneon 6 years, 1 month ago
  2. Improved Pickled Object Field by taavi223 5 years, 10 months ago
  3. staticview for app by limodou 8 years ago
  4. Google Closure support in django-compress by fabrice.bonny 5 years, 7 months ago
  5. Extended cacheable callables and properties by gsakkis 5 years ago

Comments

Please login first before commenting.