Metadata-Version: 2.4
Name: django-suppressor
Version: 0.1.1
Summary: HTML asset capture and runtime bundling for Django
Project-URL: Homepage, https://github.com/fran/django-suppressor
Project-URL: Issues, https://github.com/fran/django-suppressor/issues
Author: Fran
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.12
Requires-Dist: django>=4.2
Description-Content-Type: text/markdown

# django-suppressor

HTML asset capture and runtime bundling for Django.

Suppresses individual `<link>` and `<script>` tags in your templates and replaces them with bundled equivalents at runtime.

## Installation

```bash
pip install django-suppressor
```

## Rationale

Django templates scatter `<link>` and `<script>` tags across base templates, child templates, and includes. Each tag means a separate HTTP request. Existing bundling tools either require you to rewrite all asset references as custom tags or operate as offline build steps disconnected from the template system.

Suppressor takes a different approach: wrap a template region, let Django render it normally, then parse the resulting HTML fragment to find asset tags. Captured tags are removed from the fragment and their contents are concatenated into a single content-addressed bundle served at runtime. Child templates keep writing plain HTML — no special asset registration required.

This is deliberately scoped to explicit template regions rather than full-page rewriting: safer, faster, predictable ordering, and easy to debug.

## Quick Start

1. Add `suppressor` to your installed apps and middleware:

```python
INSTALLED_APPS = [
    "django.contrib.staticfiles",
    "suppressor",
    ...
]

MIDDLEWARE = [
    ...
    "suppressor.middleware.SuppressorMiddleware",  # near the end
]
```

2. Include the suppressor URLs in your root URL config:

```python
from django.urls import include, path

urlpatterns = [
    path("", include("suppressor.urls")),
    ...
]
```

3. Wrap asset regions in your base template:

```django
{% load suppressor_capture %}

<head>
    {% suppressor_emit_css "head" %}

    {% suppressor_capture "head" only_css %}
        <link rel="stylesheet" href="{% static 'css/base.css' %}">
        <link rel="stylesheet" href="{% static 'css/theme.css' %}">
        {% block extra_css %}{% endblock %}
    {% endsuppressor_capture %}
</head>

<body>
    ...

    {% suppressor_capture "body_js" only_js %}
        <script src="{% static 'js/vendor.js' %}"></script>
        <script src="{% static 'js/app.js' %}"></script>
        {% block extra_js %}{% endblock %}
    {% endsuppressor_capture %}

    {% suppressor_emit_js "body_js" %}
</body>
```

Child templates keep using plain `<link>` and `<script>` tags inside the inherited blocks. Suppressor captures them, bundles by type, and emits a single content-addressed URL per region.

4. Run `collectstatic` before use — suppressor reads from staticfiles storage, not finders:

```bash
python manage.py collectstatic
```

## Configuration

Bundling is controlled by `SUPPRESSOR_ENABLED`, which defaults to `not DEBUG`. Override it to test bundling locally:

```python
SUPPRESSOR_ENABLED = True
```

Bundle content is stored using Django's cache framework under the `suppressor` alias. A `FileBasedCache` default is auto-configured if you don't define one. To use a different backend:

```python
CACHES = {
    ...
    "suppressor": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "127.0.0.1:11211",
    },
}
```

## Limitations

- Only processes explicitly wrapped template regions (no full-page rewriting)
- Supports classic external `<script src>` and `<link rel="stylesheet">` tags only
- Tags with `async`, `defer`, `type="module"`, `nomodule`, `integrity`, or cross-origin URLs are preserved unchanged
- CSS relative `url(...)` references are rewritten to absolute paths; bare `@import "file.css"` (without `url()`) is not rewritten
- `StreamingHttpResponse` is not processed
- See also "TODO" section below.
- Currently only checked on Python 3.14 and Django 6.0

## TODO

 - improve template ergonomics
 - tests
 - dependency matrix
 

## Status

Under development. See `SPEC-0-1-0.md` for the design specification and a roadmap.
