diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 267fe3a379..dfca0f4a2d 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -49,6 +49,9 @@ class HashedFilesMixin: r"""(url\(['"]{0,1}\s*(.*?)["']{0,1}\))""", (r"""(@import\s*["']\s*(.*?)["'])""", """@import url("%s")"""), )), + ('*.js', ( + (r'(?m)^(//# (?-i:sourceMappingURL)=(.*))$', '//# sourceMappingURL=%s'), + )), ) keep_intermediate_files = True diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt index 84be7d1c27..f22f32c78d 100644 --- a/docs/ref/contrib/staticfiles.txt +++ b/docs/ref/contrib/staticfiles.txt @@ -291,24 +291,30 @@ The storage backend automatically replaces the paths found in the saved files matching other saved files with the path of the cached copy (using the :meth:`~django.contrib.staticfiles.storage.StaticFilesStorage.post_process` method). The regular expressions used to find those paths -(``django.contrib.staticfiles.storage.HashedFilesMixin.patterns``) -by default covers the `@import`_ rule and `url()`_ statement of `Cascading -Style Sheets`_. For example, the ``'css/styles.css'`` file with the -content +(``django.contrib.staticfiles.storage.HashedFilesMixin.patterns``) cover: + +* The `@import`_ rule and `url()`_ statement of `Cascading Style Sheets`_. +* The `source map`_ comment in JavaScript. + +For example, the ``'css/styles.css'`` file with this content: .. code-block:: css @import url("../admin/css/base.css"); -would be replaced by calling the :meth:`~django.core.files.storage.Storage.url` -method of the ``ManifestStaticFilesStorage`` storage backend, ultimately -saving a ``'css/styles.55e7cbb9ba48.css'`` file with the following -content: +...would be replaced by calling the +:meth:`~django.core.files.storage.Storage.url` method of the +``ManifestStaticFilesStorage`` storage backend, ultimately saving a +``'css/styles.55e7cbb9ba48.css'`` file with the following content: .. code-block:: css @import url("../admin/css/base.27e20196a850.css"); +.. versionchanged:: 4.0 + + Support for finding paths in the 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 @@ -361,6 +367,7 @@ hashing algorithm. .. _`@import`: https://www.w3.org/TR/CSS2/cascade.html#at-import .. _`url()`: https://www.w3.org/TR/CSS2/syndata.html#uri .. _`Cascading Style Sheets`: https://www.w3.org/Style/CSS/ +.. _`source map`: https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map ``ManifestFilesMixin`` ---------------------- diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt index 516f15b95e..656cf23fa8 100644 --- a/docs/releases/4.0.txt +++ b/docs/releases/4.0.txt @@ -136,7 +136,9 @@ Minor features :mod:`django.contrib.staticfiles` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` now + replaces paths to JavaScript source map references with their hashed + counterparts. :mod:`django.contrib.syndication` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/staticfiles_tests/project/documents/cached/source_map.js b/tests/staticfiles_tests/project/documents/cached/source_map.js new file mode 100644 index 0000000000..b37c5e5701 --- /dev/null +++ b/tests/staticfiles_tests/project/documents/cached/source_map.js @@ -0,0 +1 @@ +//# sourceMappingURL=source_map.js.map diff --git a/tests/staticfiles_tests/project/documents/cached/source_map.js.map b/tests/staticfiles_tests/project/documents/cached/source_map.js.map new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/tests/staticfiles_tests/project/documents/cached/source_map.js.map @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/staticfiles_tests/project/documents/cached/source_map_sensitive.js b/tests/staticfiles_tests/project/documents/cached/source_map_sensitive.js new file mode 100644 index 0000000000..d60c76b4f6 --- /dev/null +++ b/tests/staticfiles_tests/project/documents/cached/source_map_sensitive.js @@ -0,0 +1 @@ +//# sOuRcEMaPpInGURL=source_map.js.map diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py index 7ae88e6c68..c6e2c48d93 100644 --- a/tests/staticfiles_tests/test_storage.py +++ b/tests/staticfiles_tests/test_storage.py @@ -216,6 +216,30 @@ class TestHashedFiles: self.assertIn(b"other.d41d8cd98f00.css", content) self.assertPostCondition() + def test_js_source_map(self): + relpath = self.hashed_file_path('cached/source_map.js') + self.assertEqual(relpath, 'cached/source_map.9371cbb02a26.js') + with storage.staticfiles_storage.open(relpath) as relfile: + content = relfile.read() + self.assertNotIn(b'//# sourceMappingURL=source_map.js.map', content) + self.assertIn( + b'//# sourceMappingURL=source_map.js.99914b932bd3.map', + content, + ) + self.assertPostCondition() + + def test_js_source_map_sensitive(self): + relpath = self.hashed_file_path('cached/source_map_sensitive.js') + self.assertEqual(relpath, 'cached/source_map_sensitive.5da96fdd3cb3.js') + with storage.staticfiles_storage.open(relpath) as relfile: + content = relfile.read() + self.assertIn(b'//# sOuRcEMaPpInGURL=source_map.js.map', content) + self.assertNotIn( + b'//# sourceMappingURL=source_map.js.99914b932bd3.map', + content, + ) + self.assertPostCondition() + @override_settings( STATICFILES_DIRS=[os.path.join(TEST_ROOT, 'project', 'faulty')], STATICFILES_FINDERS=['django.contrib.staticfiles.finders.FileSystemFinder'],