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?
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.