Skip to content

Commit 97a4b19

Browse files
authored
Use use WSGI to serve static files (#49)
1 parent 732eacf commit 97a4b19

File tree

113 files changed

+1195
-76
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+1195
-76
lines changed

.github/workflows/ci.yml

+21-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ jobs:
6464
python-version: ${{ matrix.python-version }}
6565
- run: python -m pip install .[test]
6666
- run: python -m pip install django~=${{ matrix.django-version }}.0
67-
- run: python -m playwright install
6867
- run: python -m pytest
6968
- uses: codecov/codecov-action@v5
7069
with:
@@ -91,12 +90,32 @@ jobs:
9190
python-version: ${{ matrix.python-version }}
9291
- run: python -m pip install .[test]
9392
- run: python -m pip install django~=${{ matrix.django-version }}.0
94-
- run: python -m playwright install
9593
- run: python -m pytest
9694
- uses: codecov/codecov-action@v5
9795
with:
9896
flags: dj${{ matrix.django-version }}
9997

98+
pytest-extras:
99+
name: PyTest
100+
needs:
101+
- lint
102+
strategy:
103+
matrix:
104+
extras:
105+
- "whitenoise"
106+
runs-on: ubuntu-latest
107+
steps:
108+
- uses: actions/checkout@v4
109+
- name: Set up Python ${{ matrix.python-version }}
110+
uses: actions/setup-python@v5
111+
with:
112+
python-version: 3.x
113+
- run: python -m pip install .[test,${{ matrix.extras }}]
114+
- run: python -m pytest
115+
- uses: codecov/codecov-action@v5
116+
with:
117+
flags: dj${{ matrix.extras }}
118+
100119
codeql:
101120
name: CodeQL
102121
needs: [ dist, pytest-python, pytest-django ]

README.md

+18-32
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ NextGen JavaScript ESM module support for Django.
1919
Install the package and add it to your `INSTALLED_APPS` setting:
2020

2121
```bash
22-
pip install django-esm
22+
pip install django-esm[whitenoise]
2323
```
2424

25+
First, add `django_esm` to your `INSTALLED_APPS` settings:
26+
2527
```python
2628
# settings.py
2729
INSTALLED_APPS = [
@@ -31,25 +33,26 @@ INSTALLED_APPS = [
3133
]
3234
```
3335

34-
Next, lets configure Django-ESM:
36+
Optionally: If you are using whitenoise you will need to modify your WSGI application.
3537

3638
```python
37-
# settings.py
38-
from pathlib import Path
39+
import os
40+
import pathlib
3941

40-
# add BASE_DIR setting (if not already present)
41-
BASE_DIR = Path(__file__).resolve().parent.parent
42+
from django.core.wsgi import get_wsgi_application
4243

43-
ESM = {
44-
"PACKAGE_DIR": BASE_DIR, # path to a directory containing a package.json file
45-
"STATIC_DIR": BASE_DIR / "esm", # target directory to collect ES modules into
46-
"STATIC_PREFIX": "esm", # prefix for the ES module URLs
47-
}
44+
from django_esm.wsgi import ESM
4845

49-
STATICFILES_DIRS = [
50-
#
51-
(ESM["STATIC_PREFIX"], ESM["STATIC_DIR"]),
52-
]
46+
BASE_DIR = pathlib.Path(__file__).parent.parent
47+
48+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
49+
50+
application = get_wsgi_application()
51+
application = ESM(
52+
application,
53+
root=BASE_DIR / "staticfiles" / "esm",
54+
prefix="esm",
55+
)
5356
```
5457

5558
Finally, add the import map to your base template:
@@ -108,23 +111,6 @@ You need to define your private modules in your `package.json` file:
108111
}
109112
```
110113

111-
### Testing (with Jest)
112-
113-
You can use the `django_esm` package to test your JavaScript modules with Jest.
114-
Jest v27.4 and upwards will honor `imports` in your `package.json` file.
115-
116-
Before v27.4 that, you can try to use a custom `moduleNameMapper`, like so:
117-
118-
```js
119-
// jest.config.js
120-
module.exports = {
121-
//
122-
moduleNameMapper: {
123-
'^#(.*)$': '<rootDir>/staticfiles/js/$1' // @todo: remove this with Jest >=29.4
124-
},
125-
}
126-
```
127-
128114
## How it works
129115

130116
Django ESM works via native JavaScript module support in modern browsers.

django_esm/checks.py

+2-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import pathlib
22

3-
from django.conf import settings
43
from django.core.checks import Error, Tags, Warning, register
54

65
from . import conf
@@ -66,23 +65,12 @@ def check_esm_settings(app_configs, **kwargs):
6665
)
6766
)
6867

69-
if (
70-
conf.get_settings().STATIC_PREFIX,
71-
conf.get_settings().STATIC_DIR,
72-
) not in settings.STATICFILES_DIRS:
73-
errors.append(
74-
Error(
75-
'\'(ESM["STATIC_PREFIX"], ESM["STATIC_DIR"]),\' must be in STATICFILES_DIRS.',
76-
id="esm.E006",
77-
)
78-
)
79-
8068
if not (pathlib.Path(conf.get_settings().PACKAGE_DIR) / "package.json").exists():
8169
errors.append(
8270
Error(
8371
f"package.json file not found in: {conf.get_settings().PACKAGE_DIR}",
8472
hint='Make sure check your ESM["PACKAGE_DIR"] setting.',
85-
id="esm.E007",
73+
id="esm.E006",
8674
)
8775
)
8876

@@ -111,7 +99,7 @@ def check_deployment(app_configs, **kwargs):
11199
hint=(
112100
'Make sure check your ESM["STATIC_DIR"] setting and to run the "esm" management command to generate the importmap.json file.'
113101
),
114-
id="esm.E008",
102+
id="esm.E007",
115103
)
116104
)
117105

django_esm/conf.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from pathlib import Path
2+
13
from django.conf import settings
24

35
__all__ = ["get_settings"]
@@ -8,9 +10,9 @@ def get_settings():
810
"Settings",
911
(),
1012
{
11-
"PACKAGE_DIR": "",
12-
"STATIC_DIR": "",
13-
"STATIC_PREFIX": "",
13+
"PACKAGE_DIR": Path(getattr(settings, "BASE_DIR", "")),
14+
"STATIC_DIR": Path(getattr(settings, "STATIC_ROOT")) / "esm",
15+
"STATIC_PREFIX": "esm",
1416
**getattr(settings, "ESM", {}),
1517
},
1618
)

django_esm/management/commands/collectstatic.py

+15
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,19 @@ def handle(self, **options):
2929
stdout=sys.stdout,
3030
stderr=sys.stderr,
3131
)
32+
try:
33+
import whitenoise # noqa
34+
except ImportError:
35+
pass
36+
else:
37+
subprocess.check_call( # nosec
38+
[
39+
"python3",
40+
"-m",
41+
"whitenoise.compress",
42+
get_settings().STATIC_DIR,
43+
],
44+
stdout=sys.stdout,
45+
stderr=sys.stderr,
46+
)
3247
super().handle(**options)

django_esm/management/commands/esm.py

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ def add_arguments(self, parser):
1818
action="store_true",
1919
help="Watch for changes in the package directory and re-run collect files.",
2020
)
21+
parser.add_argument(
22+
"-s",
23+
"--serve",
24+
action="store_true",
25+
help="Serve the files using esimport.",
26+
)
2127

2228
def handle(self, *args, **options):
2329
subprocess.check_call( # nosec
@@ -30,6 +36,7 @@ def handle(self, *args, **options):
3036
get_settings().STATIC_DIR,
3137
]
3238
+ (["--watch"] if options["watch"] else [])
39+
+ (["--serve"] if options["serve"] else [])
3340
),
3441
stdout=sys.stdout,
3542
stderr=sys.stderr,

django_esm/templatetags/esm.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import json
22
import pathlib
3+
import re
34

45
from django import template
56
from django.conf import settings
6-
from django.contrib.staticfiles.storage import staticfiles_storage
77
from django.utils.safestring import mark_safe
88

99
from .. import conf
@@ -19,9 +19,12 @@ def _resolve_importmap_urls(raw_importmap):
1919
"integrity": {},
2020
}
2121
for entry_pointy, filename in raw_importmap["imports"].items():
22-
static_url = staticfiles_storage.url(
23-
str(pathlib.Path(conf.get_settings().STATIC_PREFIX) / filename)
24-
)
22+
if re.match("^https?://", filename):
23+
static_url = filename
24+
else:
25+
static_url = str(
26+
pathlib.Path("/") / conf.get_settings().STATIC_PREFIX / filename
27+
)
2528
full_importmap["imports"][entry_pointy] = static_url
2629
full_importmap["integrity"][static_url] = raw_importmap["integrity"][filename]
2730
return json.dumps(

django_esm/wsgi.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from whitenoise import WhiteNoise
2+
3+
4+
class ESM(WhiteNoise):
5+
def immutable_file_test(self, path, url):
6+
return True

pyproject.toml

+6-2
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@ classifiers = [
3333
"Framework :: Django :: 5.2",
3434
]
3535
requires-python = ">=3.9"
36-
dependencies = ["django>=4.2.0"]
36+
dependencies = [
37+
"django>=4.2.0",
38+
]
3739

3840
[project.optional-dependencies]
41+
whitenoise = [
42+
"whitenoise>=6.0",
43+
]
3944
test = [
4045
"pytest",
4146
"pytest-cov",
4247
"pytest-django",
43-
"pytest-playwright",
4448
]
4549
lint = [
4650
"bandit==1.8.3",

tests/esm/importmap.json

-1
This file was deleted.

tests/esm/testapp/static/js/components/index-SNYMGT5D.js

-1
This file was deleted.

tests/esm/testapp/static/js/index-STJ35Y2M.js

-1
This file was deleted.

0 commit comments

Comments
 (0)