diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 32232c059f..5b492abd75 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -51,6 +51,10 @@ class HashedFilesMixin: r"""(?P@import\s*["']\s*(?P.*?)["'])""", """@import url("%(url)s")""", ), + ( + r'(?m)(?P)^(/\*# (?-i:sourceMappingURL)=(?P.*) \*/)$', + '/*# sourceMappingURL=%(url)s */', + ), )), ('*.js', ( ( diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt index eb6703685b..a0b466ad28 100644 --- a/docs/ref/contrib/staticfiles.txt +++ b/docs/ref/contrib/staticfiles.txt @@ -294,7 +294,7 @@ method). The regular expressions used to find those paths (``django.contrib.staticfiles.storage.HashedFilesMixin.patterns``) cover: * The `@import`_ rule and `url()`_ statement of `Cascading Style Sheets`_. -* The `source map`_ comment in JavaScript. +* `Source map`_ comments in CSS and JavaScript files. For example, the ``'css/styles.css'`` file with this content: @@ -327,10 +327,14 @@ argument. For example:: .. versionchanged:: 4.0 - Support for finding paths in the source map comments was added. + Support for finding paths in JavaScript source map comments was added. The ``manifest_storage`` argument was added. +.. versionchanged:: 4.1 + + Support for finding paths in CSS source map comments was added. + .. attribute:: storage.ManifestStaticFilesStorage.max_post_process_passes Since static files might reference other static files that need to have their diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 32e816b940..b6112fe29e 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -139,7 +139,8 @@ Minor features :mod:`django.contrib.staticfiles` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` now + replaces paths to CSS source map references with their hashed counterparts. :mod:`django.contrib.syndication` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/staticfiles_tests/project/documents/cached/source_map.css b/tests/staticfiles_tests/project/documents/cached/source_map.css new file mode 100644 index 0000000000..dcd65da596 --- /dev/null +++ b/tests/staticfiles_tests/project/documents/cached/source_map.css @@ -0,0 +1,2 @@ +* {outline: 1px solid red;} +/*# sourceMappingURL=source_map.css.map */ diff --git a/tests/staticfiles_tests/project/documents/cached/source_map.css.map b/tests/staticfiles_tests/project/documents/cached/source_map.css.map new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/tests/staticfiles_tests/project/documents/cached/source_map.css.map @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/staticfiles_tests/project/documents/cached/source_map_sensitive.css b/tests/staticfiles_tests/project/documents/cached/source_map_sensitive.css new file mode 100644 index 0000000000..a1b23bb316 --- /dev/null +++ b/tests/staticfiles_tests/project/documents/cached/source_map_sensitive.css @@ -0,0 +1,2 @@ +* {outline: 1px solid red;} +/*# sOuRcEMaPpInGURL=source_map.css.map */ diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py index 41d98d4332..62484205b7 100644 --- a/tests/staticfiles_tests/test_storage.py +++ b/tests/staticfiles_tests/test_storage.py @@ -212,6 +212,30 @@ class TestHashedFiles: self.assertIn(b"other.d41d8cd98f00.css", content) self.assertPostCondition() + def test_css_source_map(self): + relpath = self.hashed_file_path('cached/source_map.css') + self.assertEqual(relpath, 'cached/source_map.b2fceaf426aa.css') + with storage.staticfiles_storage.open(relpath) as relfile: + content = relfile.read() + self.assertNotIn(b'/*# sourceMappingURL=source_map.css.map */', content) + self.assertIn( + b'/*# sourceMappingURL=source_map.css.99914b932bd3.map */', + content, + ) + self.assertPostCondition() + + def test_css_source_map_sensitive(self): + relpath = self.hashed_file_path('cached/source_map_sensitive.css') + self.assertEqual(relpath, 'cached/source_map_sensitive.456683f2106f.css') + with storage.staticfiles_storage.open(relpath) as relfile: + content = relfile.read() + self.assertIn(b'/*# sOuRcEMaPpInGURL=source_map.css.map */', content) + self.assertNotIn( + b'/*# sourceMappingURL=source_map.css.99914b932bd3.map */', + content, + ) + self.assertPostCondition() + def test_js_source_map(self): relpath = self.hashed_file_path('cached/source_map.js') self.assertEqual(relpath, 'cached/source_map.cd45b8534a87.js')