summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2023-03-18 14:05:41 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-03-18 14:06:09 +0100
commitf2923306f152ed4e385e0544f8bc615d7d65c797 (patch)
tree399599a3b4df04a515f570738accd4766a5b83bc
parenteff2ba3f8d2f172c64b9df08db8d188e11bae855 (diff)
downloaddjango-f2923306f152ed4e385e0544f8bc615d7d65c797.tar.gz
[4.2.x] Fixed #34322 -- Made ES module support to ManifestStaticFilesStorage optional.
Co-authored-by: Author: Claude Paroz <claude@2xlibre.net> Backport of e10c1688f96e3b2d202fe401472b7b25f6105969 from main
-rw-r--r--django/contrib/staticfiles/storage.py52
-rw-r--r--docs/ref/contrib/staticfiles.txt9
-rw-r--r--docs/releases/4.2.txt6
-rw-r--r--tests/staticfiles_tests/test_storage.py108
4 files changed, 103 insertions, 72 deletions
diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py
index 445cf6b954..b388abe575 100644
--- a/django/contrib/staticfiles/storage.py
+++ b/django/contrib/staticfiles/storage.py
@@ -47,6 +47,34 @@ class StaticFilesStorage(FileSystemStorage):
class HashedFilesMixin:
default_template = """url("%(url)s")"""
max_post_process_passes = 5
+ support_js_module_import_aggregation = False
+ _js_module_import_aggregation_patterns = (
+ "*.js",
+ (
+ (
+ (
+ r"""(?P<matched>import(?s:(?P<import>[\s\{].*?))"""
+ r"""\s*from\s*['"](?P<url>[\.\/].*?)["']\s*;)"""
+ ),
+ """import%(import)s from "%(url)s";""",
+ ),
+ (
+ (
+ r"""(?P<matched>export(?s:(?P<exports>[\s\{].*?))"""
+ r"""\s*from\s*["'](?P<url>[\.\/].*?)["']\s*;)"""
+ ),
+ """export%(exports)s from "%(url)s";""",
+ ),
+ (
+ r"""(?P<matched>import\s*['"](?P<url>[\.\/].*?)["']\s*;)""",
+ """import"%(url)s";""",
+ ),
+ (
+ r"""(?P<matched>import\(["'](?P<url>.*?)["']\))""",
+ """import("%(url)s")""",
+ ),
+ ),
+ )
patterns = (
(
"*.css",
@@ -72,34 +100,14 @@ class HashedFilesMixin:
r"(?m)(?P<matched>)^(//# (?-i:sourceMappingURL)=(?P<url>.*))$",
"//# sourceMappingURL=%(url)s",
),
- (
- (
- r"""(?P<matched>import(?s:(?P<import>[\s\{].*?))"""
- r"""\s*from\s*['"](?P<url>[\.\/].*?)["']\s*;)"""
- ),
- """import%(import)s from "%(url)s";""",
- ),
- (
- (
- r"""(?P<matched>export(?s:(?P<exports>[\s\{].*?))"""
- r"""\s*from\s*["'](?P<url>[\.\/].*?)["']\s*;)"""
- ),
- """export%(exports)s from "%(url)s";""",
- ),
- (
- r"""(?P<matched>import\s*['"](?P<url>[\.\/].*?)["']\s*;)""",
- """import"%(url)s";""",
- ),
- (
- r"""(?P<matched>import\(["'](?P<url>.*?)["']\))""",
- """import("%(url)s")""",
- ),
),
),
)
keep_intermediate_files = True
def __init__(self, *args, **kwargs):
+ if self.support_js_module_import_aggregation:
+ self.patterns += (self._js_module_import_aggregation_patterns,)
super().__init__(*args, **kwargs)
self._patterns = {}
self.hashed_files = {}
diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt
index 61ebd89d5e..94806d39a8 100644
--- a/docs/ref/contrib/staticfiles.txt
+++ b/docs/ref/contrib/staticfiles.txt
@@ -300,6 +300,11 @@ method). The regular expressions used to find those paths
* The `@import`_ rule and `url()`_ statement of `Cascading Style Sheets`_.
* `Source map`_ comments in CSS and JavaScript files.
+
+Subclass ``ManifestStaticFilesStorage`` and set the
+``support_js_module_import_aggregation`` attribute to ``True``, if you want to
+use the experimental regular expressions to cover:
+
* The `modules import`_ in JavaScript.
* The `modules aggregation`_ in JavaScript.
@@ -346,8 +351,8 @@ argument. For example::
.. versionchanged:: 4.2
- Support for finding paths to JavaScript modules in ``import`` and
- ``export`` statements was added.
+ Experimental optional support for finding paths to JavaScript modules in
+ ``import`` and ``export`` statements was added.
.. attribute:: storage.ManifestStaticFilesStorage.manifest_hash
diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt
index be47767bf6..0fb3e4a2d9 100644
--- a/docs/releases/4.2.txt
+++ b/docs/releases/4.2.txt
@@ -202,8 +202,10 @@ Minor features
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` now
- replaces paths to JavaScript modules in ``import`` and ``export`` statements
- with their hashed counterparts.
+ has experimental support for replacing paths to JavaScript modules in
+ ``import`` and ``export`` statements with their hashed counterparts. If you
+ want to try it, subclass ``ManifestStaticFilesStorage`` and set the
+ ``support_js_module_import_aggregation`` attribute to ``True``.
* The new :attr:`.ManifestStaticFilesStorage.manifest_hash` attribute provides
a hash over all files in the manifest and changes whenever one of the files
diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py
index 06df05e196..4474c09d75 100644
--- a/tests/staticfiles_tests/test_storage.py
+++ b/tests/staticfiles_tests/test_storage.py
@@ -177,52 +177,6 @@ class TestHashedFiles:
self.assertIn(b"https://", relfile.read())
self.assertPostCondition()
- def test_module_import(self):
- relpath = self.hashed_file_path("cached/module.js")
- self.assertEqual(relpath, "cached/module.55fd6938fbc5.js")
- tests = [
- # Relative imports.
- b'import testConst from "./module_test.477bbebe77f0.js";',
- b'import relativeModule from "../nested/js/nested.866475c46bb4.js";',
- b'import { firstConst, secondConst } from "./module_test.477bbebe77f0.js";',
- # Absolute import.
- b'import rootConst from "/static/absolute_root.5586327fe78c.js";',
- # Dynamic import.
- b'const dynamicModule = import("./module_test.477bbebe77f0.js");',
- # Creating a module object.
- b'import * as NewModule from "./module_test.477bbebe77f0.js";',
- # Aliases.
- b'import { testConst as alias } from "./module_test.477bbebe77f0.js";',
- b"import {\n"
- b" firstVar1 as firstVarAlias,\n"
- b" $second_var_2 as secondVarAlias\n"
- b'} from "./module_test.477bbebe77f0.js";',
- ]
- with storage.staticfiles_storage.open(relpath) as relfile:
- content = relfile.read()
- for module_import in tests:
- with self.subTest(module_import=module_import):
- self.assertIn(module_import, content)
- self.assertPostCondition()
-
- def test_aggregating_modules(self):
- relpath = self.hashed_file_path("cached/module.js")
- self.assertEqual(relpath, "cached/module.55fd6938fbc5.js")
- tests = [
- b'export * from "./module_test.477bbebe77f0.js";',
- b'export { testConst } from "./module_test.477bbebe77f0.js";',
- b"export {\n"
- b" firstVar as firstVarAlias,\n"
- b" secondVar as secondVarAlias\n"
- b'} from "./module_test.477bbebe77f0.js";',
- ]
- with storage.staticfiles_storage.open(relpath) as relfile:
- content = relfile.read()
- for module_import in tests:
- with self.subTest(module_import=module_import):
- self.assertIn(module_import, content)
- self.assertPostCondition()
-
@override_settings(
STATICFILES_DIRS=[os.path.join(TEST_ROOT, "project", "loop")],
STATICFILES_FINDERS=["django.contrib.staticfiles.finders.FileSystemFinder"],
@@ -634,6 +588,68 @@ class TestCollectionSimpleStorage(CollectionTestCase):
self.assertIn(b"other.deploy12345.css", content)
+class JSModuleImportAggregationManifestStorage(storage.ManifestStaticFilesStorage):
+ support_js_module_import_aggregation = True
+
+
+@override_settings(
+ STORAGES={
+ STATICFILES_STORAGE_ALIAS: {
+ "BACKEND": (
+ "staticfiles_tests.test_storage."
+ "JSModuleImportAggregationManifestStorage"
+ ),
+ },
+ }
+)
+class TestCollectionJSModuleImportAggregationManifestStorage(CollectionTestCase):
+ hashed_file_path = hashed_file_path
+
+ def test_module_import(self):
+ relpath = self.hashed_file_path("cached/module.js")
+ self.assertEqual(relpath, "cached/module.55fd6938fbc5.js")
+ tests = [
+ # Relative imports.
+ b'import testConst from "./module_test.477bbebe77f0.js";',
+ b'import relativeModule from "../nested/js/nested.866475c46bb4.js";',
+ b'import { firstConst, secondConst } from "./module_test.477bbebe77f0.js";',
+ # Absolute import.
+ b'import rootConst from "/static/absolute_root.5586327fe78c.js";',
+ # Dynamic import.
+ b'const dynamicModule = import("./module_test.477bbebe77f0.js");',
+ # Creating a module object.
+ b'import * as NewModule from "./module_test.477bbebe77f0.js";',
+ # Aliases.
+ b'import { testConst as alias } from "./module_test.477bbebe77f0.js";',
+ b"import {\n"
+ b" firstVar1 as firstVarAlias,\n"
+ b" $second_var_2 as secondVarAlias\n"
+ b'} from "./module_test.477bbebe77f0.js";',
+ ]
+ with storage.staticfiles_storage.open(relpath) as relfile:
+ content = relfile.read()
+ for module_import in tests:
+ with self.subTest(module_import=module_import):
+ self.assertIn(module_import, content)
+
+ def test_aggregating_modules(self):
+ relpath = self.hashed_file_path("cached/module.js")
+ self.assertEqual(relpath, "cached/module.55fd6938fbc5.js")
+ tests = [
+ b'export * from "./module_test.477bbebe77f0.js";',
+ b'export { testConst } from "./module_test.477bbebe77f0.js";',
+ b"export {\n"
+ b" firstVar as firstVarAlias,\n"
+ b" secondVar as secondVarAlias\n"
+ b'} from "./module_test.477bbebe77f0.js";',
+ ]
+ with storage.staticfiles_storage.open(relpath) as relfile:
+ content = relfile.read()
+ for module_import in tests:
+ with self.subTest(module_import=module_import):
+ self.assertIn(module_import, content)
+
+
class CustomManifestStorage(storage.ManifestStaticFilesStorage):
def __init__(self, *args, manifest_storage=None, **kwargs):
manifest_storage = storage.StaticFilesStorage(