Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Css file names do not change when images within the file change content #3

Open
tomaslin opened this issue May 2, 2012 · 3 comments

Comments

@tomaslin
Copy link

tomaslin commented May 2, 2012

We have a css file that embeds an image.

We see the css name is:

gHBlnyf50JPSKJjENrIhdQ2ovgr5iOOi2uvPwWjINoe.css

And the file content is:

.search {
       background: transparent url(eq8jTXq1Uk56HvVkeeKRKnaxeJGgzqCJ1iSxDFhVgFr.png) 0 0 no-repeat;
}

We change the image to another image in the file system, keeping the same name.

this leads to new css content:

.search {
       background: transparent url(0tX3g00nnE9AAvZVWiuiny5wpFAHdKpQvybjKsKx4CX.png) 0 0 no-repeat;
}

However, this does not change the name of the css file. This causes a staleness issue in clients that have already downloaded the css file.

The correct behaviour should generate a new css name when the contents in the css have changed.

@tednaleid
Copy link

Just hit this issue myself. The problem here is that the MapperPhases conflate hashing images and hashing css files into the same phase.

Here's the detail:

  1. first the css files are searched for any images that they have in them and a placeholder value is stuck in there (MapperPhase.LINKNORMALIZATION)
  2. now the images and css files are run through hash and cache with these static placeholder values, this means that the css files have static, non-hashed urls in them when they are hashed (MapperPhase.RENAMING)
  3. next the css files have the image placeholders replaced with the new hashed value for the image path now that it's determined (MapperPhase.LINKREALIZATION)

The problem is that the css file has changed, and potentially the image has changed where it's located, but that doesn't change the generated css path because it was hashed before the updated image url was inserted (so it had the non-hashed image url in it when hashed). If that image url has changed we need to rehash the css filename to represent this.

The solution (or A solution) is to re-hash the css files after they've got the hashed image urls in them. This could fall down if css files are including other css files. The fix for that would be to change the original hashandcache method to ignore css files and only hashandcache them here.

This can be done relatively easily by adding this class to your app (though it really belongs in this plugin), I'd submit a patch if it'll get accepted.

package com.myapp

import org.grails.plugin.resource.mapper.MapperPhase
import org.grails.plugin.cachedresources.HashAndCacheResourceMapper

class ReHashAndCacheCssResourceMapper extends HashAndCacheResourceMapper {

    static defaultIncludes = [
            "**/*.css"
    ]

    static phase = MapperPhase.ALTERNATEREPRESENTATION
}

All this does is run the hash stuff on CSS files after they've had image files with hashed representations placed in them (so the next phase).

I've successfully used this with some branding logic in my app so that I can have the user upload replacement images for the defaults that are packaged in the war via a resourceMapper that works in the MapperPhase.GENERATION phase and looks for assets as binary strings in redis and replaces them.

If a user uploads a new asset, I stick it in redis, then evict() all the items in the grailsResourceProcessor.resourceInfo and then call grailsResourceProcessor.reloadAll(). Works great once I figured out WTF was going on with css files not getting rehashed.

@tomaslin
Copy link
Author

We ran into an issue with this approach and javascript files that reference a css file ( using the jQuery supersized plugin, for example ). Seems that there needs to be an extra ordering where we process "/*.css" files and then "/*.js" files.

@tednaleid
Copy link

@tomaslin I haven't hit that, but I'd think you could fix that by making another mapper like I've got above that is in the ALTERNATEREPRESENTATION phase, but gets run after the css one with the mapper ordering stuff that resources provides.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants