diff --git a/.gitattributes b/.gitattributes index 4bd6655ffda..170bbc2b548 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ # Normalize line endings to avoid spurious failures in the core test suite on Windows. *html text eol=lf *css text eol=lf +*js text eol=lf tests/staticfiles_tests/apps/test/static/test/*txt text eol=lf tests/staticfiles_tests/project/documents/test/*txt text eol=lf docs/releases/*.txt merge=union diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 8949969c587..0909f517244 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -17,6 +17,7 @@ from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage, get_storage_class from django.utils.encoding import force_bytes, force_text from django.utils.functional import LazyObject +from django.utils.six import iteritems from django.utils.six.moves.urllib.parse import ( unquote, urldefrag, urlsplit, urlunsplit, ) @@ -248,13 +249,14 @@ class HashedFilesMixin(object): # ..to apply each replacement pattern to the content if name in adjustable_paths: content = original_file.read().decode(settings.FILE_CHARSET) - for patterns in self._patterns.values(): - for pattern, template in patterns: - converter = self.url_converter(name, template) - try: - content = pattern.sub(converter, content) - except ValueError as exc: - yield name, None, exc + for extension, patterns in iteritems(self._patterns): + if matches_patterns(path, (extension,)): + for pattern, template in patterns: + converter = self.url_converter(name, template) + try: + content = pattern.sub(converter, content) + except ValueError as exc: + yield name, None, exc if hashed_file_exists: self.delete(hashed_name) # then save the processed result diff --git a/tests/staticfiles_tests/project/documents/cached/test.js b/tests/staticfiles_tests/project/documents/cached/test.js new file mode 100644 index 00000000000..3d1c431aff7 --- /dev/null +++ b/tests/staticfiles_tests/project/documents/cached/test.js @@ -0,0 +1 @@ +myVar = url("import.css"); diff --git a/tests/staticfiles_tests/storage.py b/tests/staticfiles_tests/storage.py index 06caebfcd28..8c474591f37 100644 --- a/tests/staticfiles_tests/storage.py +++ b/tests/staticfiles_tests/storage.py @@ -63,3 +63,17 @@ class SimpleCachedStaticFilesStorage(CachedStaticFilesStorage): def file_hash(self, name, content=None): return 'deploy12345' + + +class ExtraPatternsCachedStaticFilesStorage(CachedStaticFilesStorage): + """ + A storage class to test pattern substitutions with more than one pattern + entry. The added pattern rewrites strings like "url(...)" to JS_URL("..."). + """ + patterns = tuple(CachedStaticFilesStorage.patterns) + ( + ( + "*.js", ( + (r"""(url\(['"]{0,1}\s*(.*?)["']{0,1}\))""", 'JS_URL("%s")'), + ), + ), + ) diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py index bd636c4a8ce..318b26c635b 100644 --- a/tests/staticfiles_tests/test_storage.py +++ b/tests/staticfiles_tests/test_storage.py @@ -237,6 +237,38 @@ class TestCollectionCachedStorage(TestHashedFiles, CollectionTestCase): self.assertEqual(cache_key, 'staticfiles:821ea71ef36f95b3922a77f7364670e7') +@override_settings( + STATICFILES_STORAGE='staticfiles_tests.storage.ExtraPatternsCachedStaticFilesStorage', +) +class TestExtraPatternsCachedStorage(CollectionTestCase): + + def setUp(self): + storage.staticfiles_storage.hashed_files.clear() # avoid cache interference + super(TestExtraPatternsCachedStorage, self).setUp() + + def cached_file_path(self, path): + fullpath = self.render_template(self.static_template_snippet(path)) + return fullpath.replace(settings.STATIC_URL, '') + + def test_multi_extension_patterns(self): + """ + With storage classes having several file extension patterns, only the + files matching a specific file pattern should be affected by the + substitution (#19670). + """ + # CSS files shouldn't be touched by JS patterns. + relpath = self.cached_file_path("cached/import.css") + self.assertEqual(relpath, "cached/import.2b1d40b0bbd4.css") + with storage.staticfiles_storage.open(relpath) as relfile: + self.assertIn(b'import url("styles.bb84a0240107.css")', relfile.read()) + + # Confirm JS patterns have been applied to JS files. + relpath = self.cached_file_path("cached/test.js") + self.assertEqual(relpath, "cached/test.62789ffcd280.js") + with storage.staticfiles_storage.open(relpath) as relfile: + self.assertIn(b'JS_URL("import.2b1d40b0bbd4.css")', relfile.read()) + + @override_settings( STATICFILES_STORAGE='django.contrib.staticfiles.storage.ManifestStaticFilesStorage', ) @@ -320,6 +352,10 @@ class TestCollectionSimpleCachedStorage(CollectionTestCase): """ hashed_file_path = hashed_file_path + def setUp(self): + storage.staticfiles_storage.hashed_files.clear() # avoid cache interference + super(TestCollectionSimpleCachedStorage, self).setUp() + def test_template_tag_return(self): """ Test the CachedStaticFilesStorage backend.