Is there a good way to use up-to-date sass (dart sass) in a django project? Searching pypi, I find loads of packages that uses libsass, but so far I haven’t found anything using dart sass. Am I just bad at searching?
The sass language is still being developed, but mainly in dart sass, and libsass is deperecated and quickly falling behind. It seems libsass can no longer be used for up-to-date bootstrap, so it seems strange if I’m the only one having this problem?
I already use django-compressor, so that should fit me fine. Didn’t realize I can configure it to use an external command rather than a “python-packaged” sass.
Thanks about the tip! It helped a lot. But I was still having two minor problems with Compress:
It was running SASS compilation for every request. And you can’t add SASS to COMPRESS_CACHEABLE_PRECOMPILERS, because Compress can’t tell if an imported SASS file changed.
It was generating hundreds of cached files in development, one for each request.
Since this thread is in the top results for “django dart sass”, in an attempt to contribute to the Django SASS workflow, I’ll post the alternative I’ve found. I’m new to Django Compress, and I started using the code bellow just yesterday, so use with care. Also, my use case is quite simple: I use Compressor only for SASS, and with only one main.scss file importing all the others.
import os
from pathlib import Path
from compressor.filters.base import CachedCompilerFilter
from django.conf import settings
class SassCachedCompilerFilter(CachedCompilerFilter):
'''
An attempt to improve SASS handling for development:
- Use as cache key the most recent mtime among all SASSs files in the
folder and subfolders of the target SASS file. Generally, the target file
is the one that imports all the others.
- Delete all related cached files, even the most recent, because Compressor
seems to always output the content to the file, even when cached. This
avoids accumulation of files in the CACHE folder.
WARNING: Bad COMPRESS_ROOT, COMPRESS_OUTPUT_DIR or tag href configuration
may result in unintended file loss.
'''
command = 'sass {infile} {outfile}'
# Max number of files that can be deleted automatically.
# This is an attempt to reduce the chance and size of unintended damage.
max_allowed_delete = 1
# Suffix of the glob used for file deletion.
# Being specific, this should reduce the chance of uninteded damage.
delete_suffix_pattern = '.*.css'
def __init__(self, *args, **kwargs):
super().__init__('text/x-sass', *args, **kwargs)
max_mtime = self.max_mtime(kwargs['filename'])
self.content = max_mtime
self.delete_cached_files(kwargs['attrs']['href'], max_mtime)
def max_mtime(self, filename) -> float:
'''Returns the most recent modification time among all original files.'''
return max(os.path.getmtime(f) for f in Path(filename).parent.glob('**/*.s[ac]ss'))
def delete_cached_files(self, href, max_mtime):
'''
Delete all related cached files, because Compressor always outputs the
content to the file, even when cached.
'''
# e.g.: <full local path>/static/CACHE
cache_root = Path(settings.COMPRESS_ROOT) / settings.COMPRESS_OUTPUT_DIR
# e.g.: css/main.*.css
pattern = Path(href).relative_to(settings.STATIC_URL).with_suffix(self.delete_suffix_pattern)
cached_files = list(cache_root.glob(pattern))
assert len(cached_files) <= self.max_allowed_delete, f'Too many files to delete! {cached_files}'
# Delete cached files.
for cached_file in cached_files:
cached_file.unlink()