Skip to content

Commit e185cc3

Browse files
committed
Fix ESM exports based on latest Node.js implementation
1 parent a7b96d4 commit e185cc3

File tree

4 files changed

+42
-24
lines changed

4 files changed

+42
-24
lines changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ INSTALLED_APPS = [
3030
]
3131
```
3232

33-
Next, add the node_modules directory to your staticfiles finders:
33+
Next, add the `node_modules` directory to your staticfiles directories:
3434

3535
```python
3636
# settings.py
@@ -61,11 +61,26 @@ You can now import JavaScript modules in your Django templates:
6161

6262
```html
6363
<!-- index.html -->
64-
6564
{% block content %}
6665
<script type="module">
6766
import "htmx.org"
6867
htmx.logAll()
6968
</script>
7069
{% endblock %}
7170
```
71+
72+
## How it works
73+
74+
Django ESM works via native JavaScript module support in modern browsers.
75+
It uses the [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap)
76+
to map module names to their location on the server.
77+
78+
Here is an example import map:
79+
80+
```json
81+
{
82+
"imports": {
83+
"htmx.org": "/static/htmx.org/dist/htmx.min.js"
84+
}
85+
}
86+
```

django_esm/utils.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
from django.contrib.staticfiles.storage import staticfiles_storage
55

6+
# There is a long history how ESM is supported in Node.js
7+
# So we implement some fallbacks, see also: https://nodejs.org/api/packages.html#exports
8+
ESM_KEYS = ["exports", "module", "main"]
9+
610

711
def parse_package_json(path: Path = None, node_modules: Path = None):
812
"""Parse a project main package.json and return a dict of importmap entries."""
@@ -12,10 +16,17 @@ def parse_package_json(path: Path = None, node_modules: Path = None):
1216
package_json = json.load(f)
1317
name = package_json["name"]
1418
dependencies = package_json.get("dependencies", {})
15-
main = package_json.get("main", None)
16-
if main:
17-
yield name, staticfiles_storage.url(
18-
str((path / main).relative_to(node_modules))
19-
)
19+
for key in ESM_KEYS:
20+
export = package_json.get(key, None)
21+
if export:
22+
try:
23+
for module_name, module in export.items():
24+
yield str(Path(name) / module_name), staticfiles_storage.url(
25+
str((path / module["default"]).relative_to(node_modules))
26+
)
27+
except AttributeError:
28+
yield name, staticfiles_storage.url(
29+
str((path / export).relative_to(node_modules))
30+
)
2031
for dep_name, dep_version in dependencies.items():
2132
yield from parse_package_json(node_modules / dep_name, node_modules)

tests/test_utils.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33

44
def test_parse_package_json(package_json):
5-
assert dict(utils.parse_package_json(package_json.parent)) == {
6-
"htmx.org": "/static/htmx.org/dist/htmx.min.js",
7-
"lit": "/static/lit/index.js",
8-
"@lit/reactive-element": "/static/%40lit/reactive-element/reactive-element.js",
9-
"@lit-labs/ssr-dom-shim": "/static/%40lit-labs/ssr-dom-shim/index.js",
10-
"lit-element": "/static/lit-element/index.js",
11-
"lit-html": "/static/lit-html/lit-html.js",
12-
}
5+
import_map = dict(utils.parse_package_json(package_json.parent))
6+
assert import_map["htmx.org"] == "/static/htmx.org/dist/htmx.min.js"
7+
assert import_map["lit"] == "/static/lit/index.js"
8+
assert (
9+
import_map["@lit/reactive-element"]
10+
== "/static/%40lit/reactive-element/reactive-element.js"
11+
)
12+
assert import_map["lit-html"] == "/static/lit-html/lit-html.js"

tests/test_views.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ def test_importmap(page, live_server):
66
page.goto(live_server.url)
77
assert (
88
"""<script type="importmap">{
9-
"imports": {
10-
"htmx.org": "/static/htmx.org/dist/htmx.min.js",
11-
"lit": "/static/lit/index.js",
12-
"@lit/reactive-element": "/static/%40lit/reactive-element/reactive-element.js",
13-
"@lit-labs/ssr-dom-shim": "/static/%40lit-labs/ssr-dom-shim/index.js",
14-
"lit-element": "/static/lit-element/index.js",
15-
"lit-html": "/static/lit-html/lit-html.js"
16-
}
17-
}</script>"""
9+
"imports": {"""
1810
in page.content()
1911
)

0 commit comments

Comments
 (0)