pax_global_header 0000666 0000000 0000000 00000000064 15244216674 0014525 g ustar 00root root 0000000 0000000 52 comment=c9efba387b2cb48eeb68e3f2a2c86e6322c27c71
feincms-django-js-asset-e73dd4d/ 0000775 0000000 0000000 00000000000 15244216674 0016640 5 ustar 00root root 0000000 0000000 feincms-django-js-asset-e73dd4d/.editorconfig 0000664 0000000 0000000 00000000310 15244216674 0021307 0 ustar 00root root 0000000 0000000 # top-most EditorConfig file
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[*.py]
indent_size = 4
feincms-django-js-asset-e73dd4d/.github/ 0000775 0000000 0000000 00000000000 15244216674 0020200 5 ustar 00root root 0000000 0000000 feincms-django-js-asset-e73dd4d/.github/dependabot.yml 0000664 0000000 0000000 00000000524 15244216674 0023031 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
github-actions:
patterns:
- "*"
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "monthly"
groups:
python-deps:
patterns:
- "*"
feincms-django-js-asset-e73dd4d/.github/workflows/ 0000775 0000000 0000000 00000000000 15244216674 0022235 5 ustar 00root root 0000000 0000000 feincms-django-js-asset-e73dd4d/.github/workflows/publish.yml 0000664 0000000 0000000 00000001417 15244216674 0024431 0 ustar 00root root 0000000 0000000 name: Publish to PyPI
on:
push:
tags:
- '*'
jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
if: github.event.repository.fork == false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: 3.x
- name: Build
run: |
python -m pip install --upgrade build
python -m build
- uses: actions/upload-artifact@v7
with:
name: dist
path: dist/
publish:
name: Publish to PyPI
needs: build
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v8
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
feincms-django-js-asset-e73dd4d/.github/workflows/tests.yml 0000664 0000000 0000000 00000001613 15244216674 0024123 0 ustar 00root root 0000000 0000000 name: Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: 37 1 1 * *
jobs:
tests:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version:
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- '3.14'
steps:
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip wheel setuptools tox
- name: Run tox targets for ${{ matrix.python-version }}
run: |
ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}")
TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox
feincms-django-js-asset-e73dd4d/.gitignore 0000664 0000000 0000000 00000000230 15244216674 0020623 0 ustar 00root root 0000000 0000000 *.py?
*~
*.sw?
\#*#
/secrets.py
.DS_Store
._*
/*.egg-info
/MANIFEST
/_build
/build
/dist
tests/test.zip
/docs/_build
/.eggs
.coverage
htmlcov
venv
.tox
feincms-django-js-asset-e73dd4d/.pre-commit-config.yaml 0000664 0000000 0000000 00000001671 15244216674 0023126 0 ustar 00root root 0000000 0000000 exclude: ".yarn/|yarn.lock|\\.min\\.(css|js)$"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-added-large-files
- id: check-builtin-literals
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: check-toml
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.32.0
hooks:
- id: django-upgrade
args: [--target-version, "3.2"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.16.4"
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.28.1
hooks:
- id: pyproject-fmt
- repo: https://github.com/abravalheri/validate-pyproject
rev: '0.26'
hooks:
- id: validate-pyproject
feincms-django-js-asset-e73dd4d/.readthedocs.yaml 0000664 0000000 0000000 00000000331 15244216674 0022064 0 ustar 00root root 0000000 0000000 # Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
build:
os: ubuntu-24.04
tools:
python: "3.13"
sphinx:
configuration: docs/conf.py
feincms-django-js-asset-e73dd4d/AGENTS.md 0000664 0000000 0000000 00000015027 15244216674 0020150 0 ustar 00root root 0000000 0000000 # django-js-asset — agent notes
`JS`, `CSS`, `JSON` support for `django.forms.Media`, plus importmap and CSP
nonce support.
## Running tests
Use **tox** (configured with `tox-uv`, so it provisions the right Python/Django
automatically — do not hand-roll a venv):
```bash
tox -e py312-dj42 # lowest supported combination
tox -e py314-dj62 # a recent combination
tox run-parallel # the whole matrix
```
The matrix lives in `tox.ini` (`tests/manage.py test testapp`).
## Compatibility (hard constraint)
- Python `>=3.10`, Django `>=4.2`. **Django 6.2+ is NOT an acceptable floor.**
- `JS`/`CSS` *produce* Django's own `Script`/`Stylesheet` (see Layout). Where
Django lacks them they are **backported** in `js_asset/_compat.py`, so the
4.2 floor holds — we provide the machinery, we don't *depend* on Django
having it. Availability in real Django: `Script` (5.2+), `Stylesheet` +
`MediaAsset.render(attrs=)` (6.1+), attribute-aware `__eq__`/`__hash__`
(6.2+). The `<5.2` backport mirrors the **6.2** contract.
- Don't rely on Django's `MediaAsset.render(attrs=...)` existing: it is absent
on 5.2/6.0. `media.py:_render_asset` injects the nonce itself by rebuilding
the tag from `element_template` + `flatatt`, which works on every version.
- Cross-version gotcha: Django >= 6.1 wraps bare js/css path strings into
`Script`/`Stylesheet` in `Media._js`/`._css`; older Django keeps raw strings.
`media.py:_render_{js,css}` wrap any leftover strings via `JS()`/`CSS()`, so
`_render_asset` always sees a `MediaAsset` (or `JSON`/`ImportMap`).
- **Never test an asset with `isinstance(item, str)`.** `SafeString` is a `str`
subclass, so `mark_safe('')` — a complete tag that
must render verbatim — would be resolved through `static()` and
percent-encoded. The predicate is `hasattr(item, "__html__")`; only bare
paths get wrapped. Django made exactly this mistake in 6.1 (ticket #37262,
fixed on its 6.1.x branch for 6.1.1), and we had it independently in
`_render_{js,css}`.
- Because Django 6.1 mangles such strings in `forms.Media.__init__` itself,
`media.py` also overrides `_normalize_{js,css}` with the `__html__`
predicate — those hooks are called through `self`, so a subclass can fix
them, and `_compat` gives us `Script`/`Stylesheet` on every supported Django
so the wrapped output is unchanged. This covers media built *through* our
class; assets adopted from a foreign `forms.Media` (`from_media`, `__add__`,
and Django's widget `media_property`, which always instantiates
`forms.Media`) arrive already normalized and cannot be recovered on 6.1.
`test_html_safe_strings_adopted_from_foreign_media` pins that boundary via
the `DJANGO_KEEPS_HTML_SAFE_STRINGS` probe.
- `tests/testapp/test_media.py` carries `JS_ASSETS`/`CSS_ASSETS`: one row per
asset kind, each with its exact rendering with and without a nonce. **Add a
row whenever a new asset kind appears** — that table is the guard against a
rendering branch quietly mishandling one of them.
`test_matches_django_rendering` compares us against stock `forms.Media` for
path assets; it is what catches Django changing its normalization or tag
format under our overrides.
## Layout
- `js_asset/_compat.py` — `MediaAsset`/`Script`/`Stylesheet`: imported from
Django where present, backported (with the 6.2 contract) below 5.2/6.1.
- `js_asset/js.py` — `JS`/`CSS` are **factories** (a `_ProducesAsset`
metaclass): calling them returns a Django `Script`/`Stylesheet`/`InlineStyle`
so they dedup in `forms.Media.merge` against native assets *and* bare path
strings; `isinstance(x, JS)` still works via `__instancecheck__`. `JSON` and
`ImportMap` have no Django counterpart and stay standalone `@html_safe`
objects with `render(*, nonce="")`. Output is byte-identical to native Django
assets (flatatt sorts attributes), which the exact-string tests depend on.
Equality is Django's, so dedup is attribute-aware on 4.2-5.1 + 6.2+ and
path-only on 5.2-6.1 (`test_set` derives its expectation from this).
`InlineStyle` renders its CSS **verbatim** (`path` returns `mark_safe`):
`