Skip to content
This repository

COMPRESS_PRECOMPILERS break css url() #226

Open
domoritz opened this Issue March 04, 2012 · 24 comments
Dominik Moritz

Since the precompiled files are served from another location, relative links in css files do not work anymore.

The Problem can be solved by setting the output dir to an empty string.

COMPRESS_OUTPUT_DIR = ''

However, it would be great to have processed urls in the css file that link to the right locations of images.

Jannis Leidel
Owner

I'm not sure I understand correctly, can you provide a test case showing the problem?

Dominik Moritz

I have a scss precompiler for my scss files. It has to be executed even in development since the browser can't handle scss files.

COMPRESS_PRECOMPILERS = (
    ('text/x-scss', 'pyscss -C {infile}'),
)

As a result I get a precompiled css for each scss file. So far so good. however, the problem occurs when I reference images with url() inside my scss files.Since these are not processed, relative urls, such as ../images/foo.png won't work anymore because the precompiled css files are served from another location and the images cannot be accessed. The problem is for example that urls will be relative to the CACHE location and not the static location in case you are using django.contrib.staticfiles.

My config looks like this:

DEBUG = True
STATIC_URL = '/static/'
COMPRESS_ENABLED = False
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    # other finders..
    'compressor.finders.CompressorFinder',
)

this works:

COMPRESS_OUTPUT_DIR = ''

this does not:

COMPRESS_OUTPUT_DIR = 'CACHE'
Reinout van Rees

Yes, this is an issue (at least, I have the same problem). I'm using twitter bootstrap as a css framework. That contains less stylesheets and I handle those with compressor.

But the sprite file with icons that's included in twitter bootstrap is missing when setting COMPRESS_ENABLED=False (as I'm developing) because the icon .png file isn't moved to the CACHE/ directory.

Dominik Moritz

It would be a solution to serve all static content from the CACHE location while DEBUG=True.

Jannis Leidel
Owner

Well, for the record, that's why the CssAbsoluteFilter exists, to convert relative URLs to absolute ones. If you don't want to use it, it's perfectly fine to set COMPRESS_OUTPUT_DIR to an empty string. Serving files from that directory is out of scope of compressor.

Jannis Leidel jezdez closed this March 17, 2012
Roland van Laar

CssAbsoluteFilter does not run when COMPRESS_ENABLED = False.

In my use case I want to run the precompressor and the CssAbsoluteFilter, but I don't want to compress the css.
This is not possible since in base.py:Compressor.hunks: enabled = settings.COMPRESS_ENABLED or forced
And self.filters where is not called because enabled = False.

Is it possible to use CssAbsoluteFilter without compressing the css?

Jannis Leidel
Owner

It's not possible right now, no.

Craig Nagy

I've encountered this issue as well, and it makes development a bit troublesome. Setting the COMPRESS_OUTPUT_DIR to an empty string might work, but only in simple cases; any sort of deeper directory structure breaks all of the url paths in CSS files, since the pre-compilers output to COMPRESS_OUTPUT_DIR/css regardless of source location. With so many css urls being relative, you're forced to turn compression on or abandon pre-compilers.

One alternative I was brainstorming, was detecting in the file to be pre-compiled, whether or not compression is enabled and adjusting the image paths in that file...which would be an ugly hack if it was possible.

Craig Nagy

Scratch my comment. It does still apply, but I think one of the pull requests (specific commit below), will allow for the CssAbsoluteFilter, thus avoiding the issue entirely. Nice.

matthewwithanm@05f3d8c

Jannis Leidel jezdez reopened this June 07, 2012
Reinout van Rees

Setting COMPRESS_OUTPUT_DIR to an empty directory just removes CACHE from the resulting filename. So the file still ends up in the COMPRESS_ROOT.

Setting COMPRESS_ROOT to the empty string places CACHE/css/... in the directory where you started django.

So there's no way to currently get django-compressor to create the output file in the same directory as the source file.

Jesús Barbero Rodríguez

Same problem here, I think it is important

Will Hardy

My workaround for this was to add the following to the bottom of my settings:

if not COMPRESS_ENABLED:
    COMPRESS_ENABLED = True
    COMPRESS_CSS_FILTERS = ['compressor.filters.css_default.CssAbsoluteFilter']
    COMPRESS_JS_FILTERS = []

The settings are saying don't apply filters, but you want this one to apply anyway. You want some filters to get some sort of special status that makes them run whether or not COMPRESS_ENABLED is true. I imagine an attribute on the relevant filter's class might be able to tell django-compressor to always use it. This isn't ideal, because the developer won't expect things to be changed. The above mentioned approach of attaching filters to precompiler definitions might be nicer.

Antti Hirvonen

I'm doing the same as @willhardy at the moment. It works, but as the compressor runs, the JS files also are concatenated and that makes the debugging a bit harder, as the resulting file can be quite long.

It should be possible to run some filters even when COMPRESS_ENABLED = False. Or should the CssAbsoluteFilter be run as a precompiler, as it really doesn't do any compressing? The pull request #291 is somewhat related, as it would allow class-based precompilers.

Greg Ball

Sorry to leave a "me too" comment; but I too am doing the same as @willhardy. It would be nice not to have JS or CSS files concatenated while debugging; and it's also nice if SASS/SCSS/LESS files are compiled into something with a filename that indicates the source.

Dan LaMotte

+1 on this bug. Took me a while to understand this, but here is the problem.

<link type="text/less" href="/static/less/main.less">

.myclass {
    background: url(../img/myimg.png);
}

The above attempts to reference this location: /static/img/myimg.png And this would work, if the generated css was served from /static/css/main.css. However, the COMPRESSOR_OUTPUT_DIR = 'CACHE' ends up adding a directory level because something that gets precompiled gets served from the COMPRESSOR_OUTPUT_DIR.

So, the above less gets precompiled and served from:
<link type="text/less" href="/static/CACHE/css/main.css"

Which ends up looking in the full path: /static/CACHE/img/myimg.png which is pretty much guaranteed to not exist.

So, this is actually a special case for css IMO. A CSS Absolute filter is an absolute requirement when it comes to precompilers only because of where a precompiled file is served from.

This problem gets compounded with the current absolute filter. Trying to account for the CACHE directory in main.less ends up breaking things very bad.

.myclass {
    background: url(../../img/myimg.png);
}

The above attempts to account for the extra CACHE dir, but when CSS Absolute Filter comes through, it'll make the full path: /img/myimg.png (notice no /static prefix) which is actually correct given the path to the less file /static/less/main.less. However, it's not given the files final resting place in the CACHE directory.

Hopefully I've helped clear up any misunderstandings if there were any.

Greg Ball

I have just submitted a pull request to address this issue, which is a big one for me too.

Trey Hunner

I just started using SCSS with Django Compressor and this is a big problem for me also.

I'm using @willhardy's hack at the moment but it creates issues with when the value of COMPRESS_ENABLED is changed in a local settings file.

Dustin Farris

Can the absolute URL filter just be implicit, and disabled by an optional setting? e.g.

COMPRESS_CSS_ABSOLUTE_URL_FILTER = False

in the rare instance someone wouldn't want this functionality.

Chris Roby

+1, having this issue myself as well

Antti Hirvonen

I'm currently using a custom Css absolute filter + SCSS precompiler combination to get around this issue and it works without any problems. The main point here is that the CssAbsoluteFilter is used during precompilation, so I don't need to enable compression for it to work. This keeps the original SCSS/JS files separate and the names of the files are not obfuscated hashes, which makes for happier development :-)

(The implementation is adapted from somone's LESS-based implementation, but I can't find it right now. Anyhow, the code is very easy to convert to LESS if that's your preprocessor choice.)

In lib/scssabsolutefilter.py:

from compressor.filters.base import CompilerFilter
from compressor.filters.css_default import CssAbsoluteFilter

class SCSSFilter(CompilerFilter):
    def __init__(self, content, attrs, **kwargs):
        super(SCSSFilter, self).__init__(content, command='scss {infile} {outfile}', **kwargs)

    def input(self, **kwargs):
        content = super(SCSSFilter, self).input(**kwargs)
        return CssAbsoluteFilter(content).input(**kwargs)

and in settings

COMPRESS_PRECOMPILERS = (
    ('text/scss', 'lib.scssabsolutefilter.SCSSFilter'),
)

Anyway, it would be nice if there was a way to do this correctly without any extra code.

sunshineo

+1 for this issue and I'm not even using any LESS or SASS
Simply relative url in CSS file and I have to pick between "../../images/" for production to work or "../images" for development to work.
Well I picked for production to work of course then debugging is hard and localhost looks bad.

sunshineo

Also I'm on Amazon S3 so I cannot make a sim link like suggested at yourcelf/btb#20

Danilo Bargen

Same issue here. django-cms, django-sekizai, django-compressor and pyscss.

andre da palma andrefsp referenced this issue from a commit in andrefsp/django-oscar June 10, 2013
David Winterbottom Change compressor settings to fix relative URL issue
When the LESS files are served but compression is disabled, the relative
URL between the generated CSS and other assets is wrong (using the
default compressor settings).  This fixes changes the output directory
name to match the path of Oscar's assets, which fixes this issue.

Related to django-compressor/django-compressor#226

Fixes #649
a4b8365
Joni Bekenstein
honi commented March 06, 2014

Same issue here.

CssAbsoluteFilter should be a compressor option, as @dustinfarris said (which could be implemented as a filter internally). You already know that compressor will generate a css in a different location than the original file, so it is very likely that your relative paths are going to break. When would you not want to fix your relative paths?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.