Browse Source

Fixed #34322 -- Made ES module support to ManifestStaticFilesStorage optional.

Co-authored-by: Author: Claude Paroz <claude@2xlibre.net>
Mariusz Felisiak 2 years ago
parent
commit
e10c1688f9

+ 30 - 22
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 = {}

+ 7 - 2
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.
 
@@ -342,8 +347,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
 

+ 4 - 2
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

+ 62 - 46
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"],
@@ -646,6 +600,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(