summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Bligh <618250+blighj@users.noreply.github.com>2022-12-07 09:56:00 +0000
committerGitHub <noreply@github.com>2022-12-07 10:56:00 +0100
commite44d348c99f0a449180399045ac54b3909121a03 (patch)
treed808d453195d7c206bc2094313e8c7d8b7c499a5
parent9ac97e7eb5a74f813012715c7598c8608e78e178 (diff)
Fixed #32319 -- Added ES module support to ManifestStaticFilesStorage.
Co-authored-by: James Bligh <james.bligh@silvercloudhealth.com>
-rw-r--r--django/contrib/staticfiles/storage.py22
-rw-r--r--docs/ref/contrib/staticfiles.txt9
-rw-r--r--docs/releases/4.2.txt4
-rw-r--r--tests/staticfiles_tests/project/documents/absolute_root.js2
-rw-r--r--tests/staticfiles_tests/project/documents/cached/module.js22
-rw-r--r--tests/staticfiles_tests/project/documents/cached/module_test.js5
-rw-r--r--tests/staticfiles_tests/project/documents/nested/js/nested.js1
-rw-r--r--tests/staticfiles_tests/test_storage.py46
8 files changed, 110 insertions, 1 deletions
diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py
index 5c0159c0b4..eae25ba737 100644
--- a/django/contrib/staticfiles/storage.py
+++ b/django/contrib/staticfiles/storage.py
@@ -72,6 +72,28 @@ 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")""",
+ ),
),
),
)
diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt
index 152838736c..0f0e8d8001 100644
--- a/docs/ref/contrib/staticfiles.txt
+++ b/docs/ref/contrib/staticfiles.txt
@@ -295,6 +295,8 @@ 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.
+* The `modules import`_ in JavaScript.
+* The `modules aggregation`_ in JavaScript.
For example, the ``'css/styles.css'`` file with this content:
@@ -329,6 +331,11 @@ argument. For example::
Support for finding paths in CSS source map comments was added.
+.. versionchanged:: 4.2
+
+ Support for finding paths to JavaScript modules in ``import`` and
+ ``export`` statements was added.
+
.. attribute:: storage.ManifestStaticFilesStorage.max_post_process_passes
Since static files might reference other static files that need to have their
@@ -382,6 +389,8 @@ hashing algorithm.
.. _`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
+.. _`modules import`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#importing_features_into_your_script
+.. _`modules aggregation`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#aggregating_modules
``ManifestFilesMixin``
----------------------
diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt
index 7e2aa6e5d6..77d4c8594c 100644
--- a/docs/releases/4.2.txt
+++ b/docs/releases/4.2.txt
@@ -131,7 +131,9 @@ Minor features
:mod:`django.contrib.staticfiles`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* ...
+* :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage` now
+ replaces paths to JavaScript modules in ``import`` and ``export`` statements
+ with their hashed counterparts.
:mod:`django.contrib.syndication`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/staticfiles_tests/project/documents/absolute_root.js b/tests/staticfiles_tests/project/documents/absolute_root.js
new file mode 100644
index 0000000000..4561b0389b
--- /dev/null
+++ b/tests/staticfiles_tests/project/documents/absolute_root.js
@@ -0,0 +1,2 @@
+const rootConst = "root";
+export default rootConst;
diff --git a/tests/staticfiles_tests/project/documents/cached/module.js b/tests/staticfiles_tests/project/documents/cached/module.js
new file mode 100644
index 0000000000..7764e740d6
--- /dev/null
+++ b/tests/staticfiles_tests/project/documents/cached/module.js
@@ -0,0 +1,22 @@
+// Static imports.
+import rootConst from "/static/absolute_root.js";
+import testConst from "./module_test.js";
+import * as NewModule from "./module_test.js";
+import { testConst as alias } from "./module_test.js";
+import { firstConst, secondConst } from "./module_test.js";
+import {
+ firstVar1 as firstVarAlias,
+ $second_var_2 as secondVarAlias
+} from "./module_test.js";
+import relativeModule from "../nested/js/nested.js";
+
+// Dynamic imports.
+const dynamicModule = import("./module_test.js");
+
+// Modules exports to aggregate modules.
+export * from "./module_test.js";
+export { testConst } from "./module_test.js";
+export {
+ firstVar as firstVarAlias,
+ secondVar as secondVarAlias
+} from "./module_test.js";
diff --git a/tests/staticfiles_tests/project/documents/cached/module_test.js b/tests/staticfiles_tests/project/documents/cached/module_test.js
new file mode 100644
index 0000000000..219372f891
--- /dev/null
+++ b/tests/staticfiles_tests/project/documents/cached/module_test.js
@@ -0,0 +1,5 @@
+export const testConst = "test";
+export const firstConst = "first";
+export const secondConst = "second";
+export var firstVar1 = "test_1";
+export var SecondVar2 = "test_2";
diff --git a/tests/staticfiles_tests/project/documents/nested/js/nested.js b/tests/staticfiles_tests/project/documents/nested/js/nested.js
new file mode 100644
index 0000000000..7646bbd17d
--- /dev/null
+++ b/tests/staticfiles_tests/project/documents/nested/js/nested.js
@@ -0,0 +1 @@
+export default null;
diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py
index 16bb556d4f..077d14bcc4 100644
--- a/tests/staticfiles_tests/test_storage.py
+++ b/tests/staticfiles_tests/test_storage.py
@@ -177,6 +177,52 @@ 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"],