Search code examples
djangoleafletdjango-staticfilesstatic-files

How to exclude paths from hashing with ManifestStaticFilesStorage


I am involved in an application that uses whitenoise to serve static files. It is configured to use CompressedManifestStaticFilesStorage which in turn uses ManifestStaticFilesStorage.

By static files, I mean static files we provide, and libraries such as leaflet from third parties.

The ManifestStaticFilesStorage class, provided by Django, will rename filenames to include a hash. For cachebusting. It filters resources and changes unhashed references to files to include the hash.

This in turn breaks a function in leaflet:

_detectIconPath: function () {
    var el = DomUtil.create('div',  'leaflet-default-icon-path', document.body);
    var path = DomUtil.getStyle(el, 'background-image') ||
               DomUtil.getStyle(el, 'backgroundImage'); // IE8

    document.body.removeChild(el);

    return path.indexOf('url') === 0 ?
        path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : '';
}

The problem being the value of path looks something like:

url("https://example.org/static/path/leaflet/dist/images/marker-icon.2273e3d8ad92.png")

As the filename does not match, the 2nd replace will not do anything. This in turn will return:

https://example.org/static/path/leaflet/dist/images/marker-icon.2273e3d8ad92.png")

When leaflet tries to append a filename to this "directory", we get:

https://example.org/static/path/leaflet/dist/images/marker-icon.2273e3d8ad92.png")marker-icon.png

Which it obviously wrong.

So what is the solution? I have been advised that we shouldn't be trying to hash filenames of third party packages, there could be other breakages. However I don't see any options for ManifestStaticFilesStorage to exclude certain directories from being hashed.

I created the following class to try to work around this. I reference this class in the settings instead of CompressedManifestStaticFilesStorage.

class MyStorage(CompressedManifestStaticFilesStorage):
    """ This class overrides the built in class and turns off file name hashing for selected directories. """

    def _nonhashed_name_func(self, name, hashed_files=None):
        name = posixpath.normpath(name)
        cleaned_name = self.clean_name(name)
        return cleaned_name

    def _url(self, hashed_name_func, name, force=False, hashed_files=None):
        split = name.split("/")
        if split[0] in ['bower_components', 'node_modules']:
            hashed_name_func = self._nonhashed_name_func
        return super()._url(hashed_name_func=hashed_name_func, name=name, force=force, hashed_files=hashed_files)

This works, however seems like a somewhat inelegant solution. Does anybody here have any better suggestions?


Solution

  • I found this page which describes similar problems, in the context of Angular2:

    https://www.npmjs.com/package/@asymmetrik/angular2-leaflet#a-note-about-markers

    In the specific case of leaflet, the solution seems to be to override the default icon path. As I am using vue2-leaflet this can be done using:

    <v-icondefault :image-path="path"></v-icondefault>
    

    As per the example supplied with the source code.