######################## ### 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()