django-components-lite 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_components_lite-0.1.0/.gitignore +31 -0
- django_components_lite-0.1.0/LICENSE +22 -0
- django_components_lite-0.1.0/PKG-INFO +128 -0
- django_components_lite-0.1.0/README.md +101 -0
- django_components_lite-0.1.0/django_components_lite/__init__.py +72 -0
- django_components_lite-0.1.0/django_components_lite/app_settings.py +316 -0
- django_components_lite-0.1.0/django_components_lite/apps.py +46 -0
- django_components_lite-0.1.0/django_components_lite/attributes.py +434 -0
- django_components_lite-0.1.0/django_components_lite/autodiscovery.py +45 -0
- django_components_lite-0.1.0/django_components_lite/component.py +2051 -0
- django_components_lite-0.1.0/django_components_lite/component_media.py +70 -0
- django_components_lite-0.1.0/django_components_lite/component_registry.py +540 -0
- django_components_lite-0.1.0/django_components_lite/components/__init__.py +1 -0
- django_components_lite-0.1.0/django_components_lite/constants.py +3 -0
- django_components_lite-0.1.0/django_components_lite/context.py +42 -0
- django_components_lite-0.1.0/django_components_lite/dependencies.py +34 -0
- django_components_lite-0.1.0/django_components_lite/finders.py +166 -0
- django_components_lite-0.1.0/django_components_lite/library.py +47 -0
- django_components_lite-0.1.0/django_components_lite/node.py +594 -0
- django_components_lite-0.1.0/django_components_lite/py.typed +0 -0
- django_components_lite-0.1.0/django_components_lite/slots.py +1513 -0
- django_components_lite-0.1.0/django_components_lite/template.py +320 -0
- django_components_lite-0.1.0/django_components_lite/template_loader.py +32 -0
- django_components_lite-0.1.0/django_components_lite/templatetags/__init__.py +0 -0
- django_components_lite-0.1.0/django_components_lite/templatetags/component_tags.py +42 -0
- django_components_lite-0.1.0/django_components_lite/util/__init__.py +0 -0
- django_components_lite-0.1.0/django_components_lite/util/cache.py +115 -0
- django_components_lite-0.1.0/django_components_lite/util/context.py +151 -0
- django_components_lite-0.1.0/django_components_lite/util/django_monkeypatch.py +172 -0
- django_components_lite-0.1.0/django_components_lite/util/exception.py +78 -0
- django_components_lite-0.1.0/django_components_lite/util/loader.py +228 -0
- django_components_lite-0.1.0/django_components_lite/util/logger.py +104 -0
- django_components_lite-0.1.0/django_components_lite/util/misc.py +316 -0
- django_components_lite-0.1.0/django_components_lite/util/nanoid.py +28 -0
- django_components_lite-0.1.0/django_components_lite/util/template_parser.py +219 -0
- django_components_lite-0.1.0/django_components_lite/util/template_tag.py +103 -0
- django_components_lite-0.1.0/django_components_lite/util/types.py +28 -0
- django_components_lite-0.1.0/django_components_lite/util/weakref.py +28 -0
- django_components_lite-0.1.0/pyproject.toml +177 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# Unit test / coverage reports
|
|
13
|
+
htmlcov/
|
|
14
|
+
.coverage
|
|
15
|
+
.coverage.*
|
|
16
|
+
coverage.xml
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
|
|
19
|
+
# Django
|
|
20
|
+
*.log
|
|
21
|
+
*.sqlite3
|
|
22
|
+
|
|
23
|
+
# Python environment
|
|
24
|
+
.venv/
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
.ruff_cache/
|
|
27
|
+
|
|
28
|
+
# Build artifacts
|
|
29
|
+
site/
|
|
30
|
+
.DS_Store
|
|
31
|
+
.claude/worktrees/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Emil Stenström
|
|
4
|
+
Copyright (c) 2026 Oliver Haas
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-components-lite
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight reusable template components for Django. An exploratory fork of django-components.
|
|
5
|
+
Project-URL: Homepage, https://github.com/oliverhaas/django-components-lite
|
|
6
|
+
Project-URL: Documentation, https://oliverhaas.github.io/django-components-lite/
|
|
7
|
+
Project-URL: Issues, https://github.com/oliverhaas/django-components-lite/issues
|
|
8
|
+
Author-email: Oliver Haas <ohaas@e1plus.de>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: components,css,django,html,js
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: Django
|
|
14
|
+
Classifier: Framework :: Django :: 5.2
|
|
15
|
+
Classifier: Framework :: Django :: 6.0
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: django<7,>=5.2
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# django-components-lite
|
|
29
|
+
|
|
30
|
+
**An exploratory, lightweight fork of [django-components](https://github.com/django-components/django-components).**
|
|
31
|
+
|
|
32
|
+
This package strips django-components down to its core: simple, reusable template components for Django, just templates with some optional python logic. The goal is to see how a minimal django-components feels in practice.
|
|
33
|
+
|
|
34
|
+
## Attribution
|
|
35
|
+
|
|
36
|
+
This project is built on the excellent work of the **[django-components](https://github.com/django-components/django-components)** project by **[Emil Stenström](https://github.com/EmilStenstrom)**, **[Juro Oravec](https://github.com/JuroOravec)**, and [all contributors](https://github.com/django-components/django-components/graphs/contributors). Their years of work made this possible.
|
|
37
|
+
|
|
38
|
+
**If you're looking for a mature, full-featured, and battle-tested component library for Django, use [django-components](https://github.com/django-components/django-components).** It has an active community, extensive documentation, and a rich feature set.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
What django-components-lite keeps from django-components:
|
|
44
|
+
|
|
45
|
+
- Component classes with Python logic and Django templates
|
|
46
|
+
- `{% component %}` / `{% endcomponent %}` template tags
|
|
47
|
+
- Slots and fills (`{% slot %}`, `{% fill %}`)
|
|
48
|
+
- Component autodiscovery
|
|
49
|
+
- Component registry
|
|
50
|
+
- Static file handling (JS/CSS)
|
|
51
|
+
- Isolated component context
|
|
52
|
+
- HTML attribute rendering utilities
|
|
53
|
+
|
|
54
|
+
## What's removed?
|
|
55
|
+
|
|
56
|
+
Compared to django-components, the following have been stripped out:
|
|
57
|
+
|
|
58
|
+
- Extension system
|
|
59
|
+
- Built-in components (DynamicComponent, ErrorFallback)
|
|
60
|
+
- Component caching
|
|
61
|
+
- Provide/Inject system
|
|
62
|
+
- Template expressions
|
|
63
|
+
- Management commands
|
|
64
|
+
- JS/CSS data methods and dependency management
|
|
65
|
+
- Type validation (Args/Kwargs/Slots/TemplateData)
|
|
66
|
+
- `on_render()` generator system and deferred rendering
|
|
67
|
+
- `context_behavior` setting (always isolated, like Django's `inclusion_tag`)
|
|
68
|
+
- Tag formatters
|
|
69
|
+
- Component views and URLs
|
|
70
|
+
- `libraries` setting and `import_libraries()`
|
|
71
|
+
- `reload_on_file_change` setting
|
|
72
|
+
- All deprecated setting aliases
|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install django-components-lite
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Add to your Django settings:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
INSTALLED_APPS = [
|
|
84
|
+
# ...
|
|
85
|
+
"django_components_lite",
|
|
86
|
+
]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Quick example
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# myapp/components/greeting/greeting.py
|
|
93
|
+
from django_components_lite import Component
|
|
94
|
+
|
|
95
|
+
class Greeting(Component):
|
|
96
|
+
template_name = "greeting/greeting.html"
|
|
97
|
+
|
|
98
|
+
def get_context_data(self, name):
|
|
99
|
+
return {"name": name}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<!-- myapp/components/greeting/greeting.html -->
|
|
104
|
+
<div class="greeting">
|
|
105
|
+
Hello, {{ name }}!
|
|
106
|
+
{% slot "extra" %}{% endslot %}
|
|
107
|
+
</div>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<!-- In any template -->
|
|
112
|
+
{% load component_tags %}
|
|
113
|
+
{% component "greeting" name="World" %}
|
|
114
|
+
{% fill "extra" %}
|
|
115
|
+
<p>Welcome!</p>
|
|
116
|
+
{% endfill %}
|
|
117
|
+
{% endcomponent %}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Links
|
|
121
|
+
|
|
122
|
+
- [django-components (original)](https://github.com/django-components/django-components) - the full-featured upstream project
|
|
123
|
+
- [Documentation](https://oliverhaas.github.io/django-components-lite/)
|
|
124
|
+
- [Issues](https://github.com/oliverhaas/django-components-lite/issues)
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT - see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# django-components-lite
|
|
2
|
+
|
|
3
|
+
**An exploratory, lightweight fork of [django-components](https://github.com/django-components/django-components).**
|
|
4
|
+
|
|
5
|
+
This package strips django-components down to its core: simple, reusable template components for Django, just templates with some optional python logic. The goal is to see how a minimal django-components feels in practice.
|
|
6
|
+
|
|
7
|
+
## Attribution
|
|
8
|
+
|
|
9
|
+
This project is built on the excellent work of the **[django-components](https://github.com/django-components/django-components)** project by **[Emil Stenström](https://github.com/EmilStenstrom)**, **[Juro Oravec](https://github.com/JuroOravec)**, and [all contributors](https://github.com/django-components/django-components/graphs/contributors). Their years of work made this possible.
|
|
10
|
+
|
|
11
|
+
**If you're looking for a mature, full-featured, and battle-tested component library for Django, use [django-components](https://github.com/django-components/django-components).** It has an active community, extensive documentation, and a rich feature set.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
What django-components-lite keeps from django-components:
|
|
17
|
+
|
|
18
|
+
- Component classes with Python logic and Django templates
|
|
19
|
+
- `{% component %}` / `{% endcomponent %}` template tags
|
|
20
|
+
- Slots and fills (`{% slot %}`, `{% fill %}`)
|
|
21
|
+
- Component autodiscovery
|
|
22
|
+
- Component registry
|
|
23
|
+
- Static file handling (JS/CSS)
|
|
24
|
+
- Isolated component context
|
|
25
|
+
- HTML attribute rendering utilities
|
|
26
|
+
|
|
27
|
+
## What's removed?
|
|
28
|
+
|
|
29
|
+
Compared to django-components, the following have been stripped out:
|
|
30
|
+
|
|
31
|
+
- Extension system
|
|
32
|
+
- Built-in components (DynamicComponent, ErrorFallback)
|
|
33
|
+
- Component caching
|
|
34
|
+
- Provide/Inject system
|
|
35
|
+
- Template expressions
|
|
36
|
+
- Management commands
|
|
37
|
+
- JS/CSS data methods and dependency management
|
|
38
|
+
- Type validation (Args/Kwargs/Slots/TemplateData)
|
|
39
|
+
- `on_render()` generator system and deferred rendering
|
|
40
|
+
- `context_behavior` setting (always isolated, like Django's `inclusion_tag`)
|
|
41
|
+
- Tag formatters
|
|
42
|
+
- Component views and URLs
|
|
43
|
+
- `libraries` setting and `import_libraries()`
|
|
44
|
+
- `reload_on_file_change` setting
|
|
45
|
+
- All deprecated setting aliases
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install django-components-lite
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Add to your Django settings:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
INSTALLED_APPS = [
|
|
57
|
+
# ...
|
|
58
|
+
"django_components_lite",
|
|
59
|
+
]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick example
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# myapp/components/greeting/greeting.py
|
|
66
|
+
from django_components_lite import Component
|
|
67
|
+
|
|
68
|
+
class Greeting(Component):
|
|
69
|
+
template_name = "greeting/greeting.html"
|
|
70
|
+
|
|
71
|
+
def get_context_data(self, name):
|
|
72
|
+
return {"name": name}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<!-- myapp/components/greeting/greeting.html -->
|
|
77
|
+
<div class="greeting">
|
|
78
|
+
Hello, {{ name }}!
|
|
79
|
+
{% slot "extra" %}{% endslot %}
|
|
80
|
+
</div>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<!-- In any template -->
|
|
85
|
+
{% load component_tags %}
|
|
86
|
+
{% component "greeting" name="World" %}
|
|
87
|
+
{% fill "extra" %}
|
|
88
|
+
<p>Welcome!</p>
|
|
89
|
+
{% endfill %}
|
|
90
|
+
{% endcomponent %}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Links
|
|
94
|
+
|
|
95
|
+
- [django-components (original)](https://github.com/django-components/django-components) - the full-featured upstream project
|
|
96
|
+
- [Documentation](https://oliverhaas.github.io/django-components-lite/)
|
|
97
|
+
- [Issues](https://github.com/oliverhaas/django-components-lite/issues)
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT - see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Main package for Django Components."""
|
|
2
|
+
|
|
3
|
+
# Public API
|
|
4
|
+
# isort: off
|
|
5
|
+
from django_components_lite.app_settings import ComponentsSettings
|
|
6
|
+
from django_components_lite.attributes import format_attributes, merge_attributes
|
|
7
|
+
from django_components_lite.autodiscovery import autodiscover
|
|
8
|
+
from django_components_lite.component import (
|
|
9
|
+
Component,
|
|
10
|
+
ComponentNode,
|
|
11
|
+
ComponentVars,
|
|
12
|
+
all_components,
|
|
13
|
+
get_component_by_class_id,
|
|
14
|
+
)
|
|
15
|
+
from django_components_lite.component_registry import (
|
|
16
|
+
AlreadyRegisteredError,
|
|
17
|
+
ComponentRegistry,
|
|
18
|
+
NotRegisteredError,
|
|
19
|
+
RegistrySettings,
|
|
20
|
+
register,
|
|
21
|
+
registry,
|
|
22
|
+
all_registries,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from django_components_lite.library import TagProtectedError
|
|
26
|
+
from django_components_lite.slots import (
|
|
27
|
+
FillNode,
|
|
28
|
+
Slot,
|
|
29
|
+
SlotContext,
|
|
30
|
+
SlotFallback,
|
|
31
|
+
SlotFunc,
|
|
32
|
+
SlotInput,
|
|
33
|
+
SlotNode,
|
|
34
|
+
SlotResult,
|
|
35
|
+
)
|
|
36
|
+
from django_components_lite.util.loader import ComponentFileEntry, get_component_dirs, get_component_files
|
|
37
|
+
from django_components_lite.util.types import Empty
|
|
38
|
+
|
|
39
|
+
# isort: on
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"AlreadyRegisteredError",
|
|
44
|
+
"Component",
|
|
45
|
+
"ComponentFileEntry",
|
|
46
|
+
"ComponentNode",
|
|
47
|
+
"ComponentRegistry",
|
|
48
|
+
"ComponentVars",
|
|
49
|
+
"ComponentsSettings",
|
|
50
|
+
"Empty",
|
|
51
|
+
"FillNode",
|
|
52
|
+
"NotRegisteredError",
|
|
53
|
+
"RegistrySettings",
|
|
54
|
+
"Slot",
|
|
55
|
+
"SlotContext",
|
|
56
|
+
"SlotFallback",
|
|
57
|
+
"SlotFunc",
|
|
58
|
+
"SlotInput",
|
|
59
|
+
"SlotNode",
|
|
60
|
+
"SlotResult",
|
|
61
|
+
"TagProtectedError",
|
|
62
|
+
"all_components",
|
|
63
|
+
"all_registries",
|
|
64
|
+
"autodiscover",
|
|
65
|
+
"format_attributes",
|
|
66
|
+
"get_component_by_class_id",
|
|
67
|
+
"get_component_dirs",
|
|
68
|
+
"get_component_files",
|
|
69
|
+
"merge_attributes",
|
|
70
|
+
"register",
|
|
71
|
+
"registry",
|
|
72
|
+
]
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# ruff: noqa: N802
|
|
2
|
+
import re
|
|
3
|
+
from collections.abc import Callable, Sequence
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from os import PathLike
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import (
|
|
8
|
+
NamedTuple,
|
|
9
|
+
TypeVar,
|
|
10
|
+
cast,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from django.conf import settings
|
|
14
|
+
|
|
15
|
+
from django_components_lite.util.misc import default
|
|
16
|
+
|
|
17
|
+
T = TypeVar("T")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# This is the source of truth for the settings that are available. If the documentation
|
|
21
|
+
# or the defaults do NOT match this, they should be updated.
|
|
22
|
+
class ComponentsSettings(NamedTuple):
|
|
23
|
+
"""
|
|
24
|
+
Settings available for django_components_lite.
|
|
25
|
+
|
|
26
|
+
**Example:**
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
COMPONENTS = ComponentsSettings(
|
|
30
|
+
autodiscover=False,
|
|
31
|
+
dirs = [BASE_DIR / "components"],
|
|
32
|
+
)
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
autodiscover: bool | None = None
|
|
37
|
+
"""
|
|
38
|
+
Toggle whether to run [autodiscovery](../concepts/fundamentals/autodiscovery.md) at the Django server startup.
|
|
39
|
+
|
|
40
|
+
Defaults to `True`
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
COMPONENTS = ComponentsSettings(
|
|
44
|
+
autodiscover=False,
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
dirs: Sequence[str | PathLike | tuple[str, str] | tuple[str, PathLike]] | None = None
|
|
50
|
+
"""
|
|
51
|
+
Specify the directories that contain your components.
|
|
52
|
+
|
|
53
|
+
Defaults to `[Path(settings.BASE_DIR) / "components"]`. That is, the root `components/` app.
|
|
54
|
+
|
|
55
|
+
Directories must be full paths, same as with
|
|
56
|
+
[STATICFILES_DIRS](https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STATICFILES_DIRS).
|
|
57
|
+
|
|
58
|
+
These locations are searched during [autodiscovery](../concepts/fundamentals/autodiscovery.md),
|
|
59
|
+
or when you [define HTML, JS, or CSS as separate files](../concepts/fundamentals/html_js_css_files.md).
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
COMPONENTS = ComponentsSettings(
|
|
63
|
+
dirs=[BASE_DIR / "components"],
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Set to empty list to disable global components directories:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
COMPONENTS = ComponentsSettings(
|
|
71
|
+
dirs=[],
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
app_dirs: Sequence[str] | None = None
|
|
77
|
+
"""
|
|
78
|
+
Specify the app-level directories that contain your components.
|
|
79
|
+
|
|
80
|
+
Defaults to `["components"]`. That is, for each Django app, we search `<app>/components/` for components.
|
|
81
|
+
|
|
82
|
+
The paths must be relative to app, e.g.:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
COMPONENTS = ComponentsSettings(
|
|
86
|
+
app_dirs=["my_comps"],
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
To search for `<app>/my_comps/`.
|
|
91
|
+
|
|
92
|
+
These locations are searched during [autodiscovery](../concepts/fundamentals/autodiscovery.md),
|
|
93
|
+
or when you [define HTML, JS, or CSS as separate files](../concepts/fundamentals/html_js_css_files.md).
|
|
94
|
+
|
|
95
|
+
Set to empty list to disable app-level components:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
COMPONENTS = ComponentsSettings(
|
|
99
|
+
app_dirs=[],
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
multiline_tags: bool | None = None
|
|
105
|
+
"""
|
|
106
|
+
Enable / disable
|
|
107
|
+
[multiline support for template tags](../concepts/fundamentals/template_tag_syntax.md#multiline-tags).
|
|
108
|
+
If `True`, template tags like `{% component %}` or `{{ my_var }}` can span multiple lines.
|
|
109
|
+
|
|
110
|
+
Defaults to `True`.
|
|
111
|
+
|
|
112
|
+
Disable this setting if you are making custom modifications to Django's
|
|
113
|
+
regular expression for parsing templates at `django.template.base.tag_re`.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
COMPONENTS = ComponentsSettings(
|
|
117
|
+
multiline_tags=False,
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
static_files_allowed: list[str | re.Pattern] | None = None
|
|
123
|
+
"""
|
|
124
|
+
A list of file extensions (including the leading dot) that define which files within
|
|
125
|
+
[`COMPONENTS.dirs`](./settings.md#django_components_lite.app_settings.ComponentsSettings.dirs)
|
|
126
|
+
or
|
|
127
|
+
[`COMPONENTS.app_dirs`](./settings.md#django_components_lite.app_settings.ComponentsSettings.app_dirs)
|
|
128
|
+
are treated as [static files](https://docs.djangoproject.com/en/5.2/howto/static-files/).
|
|
129
|
+
|
|
130
|
+
If a file is matched against any of the patterns, it's considered a static file. Such files are collected
|
|
131
|
+
when running [`collectstatic`](https://docs.djangoproject.com/en/5.2/ref/contrib/staticfiles/#collectstatic),
|
|
132
|
+
and can be accessed under the
|
|
133
|
+
[static file endpoint](https://docs.djangoproject.com/en/5.2/ref/settings/#static-url).
|
|
134
|
+
|
|
135
|
+
You can also pass in compiled regexes ([`re.Pattern`](https://docs.python.org/3/library/re.html#re.Pattern))
|
|
136
|
+
for more advanced patterns.
|
|
137
|
+
|
|
138
|
+
By default, JS, CSS, and common image and font file formats are considered static files:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
COMPONENTS = ComponentsSettings(
|
|
142
|
+
static_files_allowed=[
|
|
143
|
+
".css",
|
|
144
|
+
".js", ".jsx", ".ts", ".tsx",
|
|
145
|
+
# Images
|
|
146
|
+
".apng", ".png", ".avif", ".gif", ".jpg",
|
|
147
|
+
".jpeg", ".jfif", ".pjpeg", ".pjp", ".svg",
|
|
148
|
+
".webp", ".bmp", ".ico", ".cur", ".tif", ".tiff",
|
|
149
|
+
# Fonts
|
|
150
|
+
".eot", ".ttf", ".woff", ".otf", ".svg",
|
|
151
|
+
],
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
!!! warning
|
|
156
|
+
|
|
157
|
+
Exposing your Python files can be a security vulnerability.
|
|
158
|
+
See [Security notes](../overview/security_notes.md).
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
static_files_forbidden: list[str | re.Pattern] | None = None
|
|
162
|
+
"""
|
|
163
|
+
A list of file extensions (including the leading dot) that define which files within
|
|
164
|
+
[`COMPONENTS.dirs`](./settings.md#django_components_lite.app_settings.ComponentsSettings.dirs)
|
|
165
|
+
or
|
|
166
|
+
[`COMPONENTS.app_dirs`](./settings.md#django_components_lite.app_settings.ComponentsSettings.app_dirs)
|
|
167
|
+
will NEVER be treated as [static files](https://docs.djangoproject.com/en/5.2/howto/static-files/).
|
|
168
|
+
|
|
169
|
+
If a file is matched against any of the patterns, it will never be considered a static file,
|
|
170
|
+
even if the file matches a pattern in
|
|
171
|
+
[`static_files_allowed`](./settings.md#django_components_lite.app_settings.ComponentsSettings.static_files_allowed).
|
|
172
|
+
|
|
173
|
+
Use this setting together with
|
|
174
|
+
[`static_files_allowed`](./settings.md#django_components_lite.app_settings.ComponentsSettings.static_files_allowed)
|
|
175
|
+
for a fine control over what file types will be exposed.
|
|
176
|
+
|
|
177
|
+
You can also pass in compiled regexes ([`re.Pattern`](https://docs.python.org/3/library/re.html#re.Pattern))
|
|
178
|
+
for more advanced patterns.
|
|
179
|
+
|
|
180
|
+
By default, any HTML and Python are considered NOT static files:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
COMPONENTS = ComponentsSettings(
|
|
184
|
+
static_files_forbidden=[
|
|
185
|
+
".html", ".django", ".dj", ".tpl",
|
|
186
|
+
# Python files
|
|
187
|
+
".py", ".pyc",
|
|
188
|
+
],
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
!!! warning
|
|
193
|
+
|
|
194
|
+
Exposing your Python files can be a security vulnerability.
|
|
195
|
+
See [Security notes](../overview/security_notes.md).
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# NOTE: Some defaults depend on the Django settings, which may not yet be
|
|
200
|
+
# initialized at the time that these settings are generated. For such cases
|
|
201
|
+
# we define the defaults as a factory function, and use the `Dynamic` class to
|
|
202
|
+
# mark such fields.
|
|
203
|
+
@dataclass(frozen=True)
|
|
204
|
+
class Dynamic[T]:
|
|
205
|
+
getter: Callable[[], T]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# This is the source of truth for the settings defaults. If the documentation
|
|
209
|
+
# does NOT match it, the documentation should be updated.
|
|
210
|
+
#
|
|
211
|
+
# NOTE: Because we need to access Django settings to generate default dirs
|
|
212
|
+
# for `COMPONENTS.dirs`, we do it lazily.
|
|
213
|
+
# NOTE 2: We show the defaults in the documentation, together with the comments
|
|
214
|
+
# (except for the `Dynamic` instances and comments like `type: ignore`).
|
|
215
|
+
# So `fmt: off` turns off Black/Ruff formatting and `snippet:defaults` allows
|
|
216
|
+
# us to extract the snippet from the file.
|
|
217
|
+
#
|
|
218
|
+
# fmt: off
|
|
219
|
+
# --snippet:defaults--
|
|
220
|
+
defaults = ComponentsSettings(
|
|
221
|
+
autodiscover=True,
|
|
222
|
+
# Root-level "components" dirs, e.g. `/path/to/proj/components/`
|
|
223
|
+
dirs=Dynamic(lambda: [Path(settings.BASE_DIR) / "components"]), # type: ignore[arg-type]
|
|
224
|
+
# App-level "components" dirs, e.g. `[app]/components/`
|
|
225
|
+
app_dirs=["components"],
|
|
226
|
+
multiline_tags=True,
|
|
227
|
+
static_files_allowed=[
|
|
228
|
+
".css",
|
|
229
|
+
".js", ".jsx", ".ts", ".tsx",
|
|
230
|
+
# Images
|
|
231
|
+
".apng", ".png", ".avif", ".gif", ".jpg",
|
|
232
|
+
".jpeg", ".jfif", ".pjpeg", ".pjp", ".svg",
|
|
233
|
+
".webp", ".bmp", ".ico", ".cur", ".tif", ".tiff",
|
|
234
|
+
# Fonts
|
|
235
|
+
".eot", ".ttf", ".woff", ".otf", ".svg",
|
|
236
|
+
],
|
|
237
|
+
static_files_forbidden=[
|
|
238
|
+
# See https://marketplace.visualstudio.com/items?itemName=junstyle.vscode-django-support
|
|
239
|
+
".html", ".django", ".dj", ".tpl",
|
|
240
|
+
# Python files
|
|
241
|
+
".py", ".pyc",
|
|
242
|
+
],
|
|
243
|
+
)
|
|
244
|
+
# --endsnippet:defaults--
|
|
245
|
+
# fmt: on
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# Interface through which we access the settings.
|
|
249
|
+
#
|
|
250
|
+
# This is the only place where we actually access the settings.
|
|
251
|
+
# The settings are merged with defaults, and then validated.
|
|
252
|
+
#
|
|
253
|
+
# The settings are then available through the `app_settings` object.
|
|
254
|
+
#
|
|
255
|
+
# Settings are loaded from Django settings only once, at `apps.py` in `ready()`.
|
|
256
|
+
class InternalSettings:
|
|
257
|
+
def __init__(self) -> None:
|
|
258
|
+
self._settings: ComponentsSettings | None = None
|
|
259
|
+
|
|
260
|
+
def _load_settings(self) -> None:
|
|
261
|
+
data = getattr(settings, "COMPONENTS", {})
|
|
262
|
+
components_settings = ComponentsSettings(**data) if not isinstance(data, ComponentsSettings) else data
|
|
263
|
+
|
|
264
|
+
# Merge we defaults and otherwise initialize if necessary
|
|
265
|
+
|
|
266
|
+
# For DIRS setting, we use a getter for the default value, because the default value
|
|
267
|
+
# uses Django settings, which may not yet be initialized at the time these settings are generated.
|
|
268
|
+
dirs_default_fn = cast("Dynamic[Sequence[str | tuple[str, str]]]", defaults.dirs)
|
|
269
|
+
dirs_default = dirs_default_fn.getter()
|
|
270
|
+
|
|
271
|
+
self._settings = ComponentsSettings(
|
|
272
|
+
autodiscover=default(components_settings.autodiscover, defaults.autodiscover),
|
|
273
|
+
dirs=default(components_settings.dirs, dirs_default),
|
|
274
|
+
app_dirs=default(components_settings.app_dirs, defaults.app_dirs),
|
|
275
|
+
multiline_tags=default(components_settings.multiline_tags, defaults.multiline_tags),
|
|
276
|
+
static_files_allowed=default(components_settings.static_files_allowed, defaults.static_files_allowed),
|
|
277
|
+
static_files_forbidden=self._prepare_static_files_forbidden(components_settings),
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
def _get_settings(self) -> ComponentsSettings:
|
|
281
|
+
if self._settings is None:
|
|
282
|
+
self._load_settings()
|
|
283
|
+
return cast("ComponentsSettings", self._settings)
|
|
284
|
+
|
|
285
|
+
def _prepare_static_files_forbidden(self, new_settings: ComponentsSettings) -> list[str | re.Pattern]:
|
|
286
|
+
return default(
|
|
287
|
+
new_settings.static_files_forbidden,
|
|
288
|
+
cast("list[str | re.Pattern]", defaults.static_files_forbidden),
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def AUTODISCOVER(self) -> bool:
|
|
293
|
+
return self._get_settings().autodiscover # type: ignore[return-value]
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def DIRS(self) -> Sequence[str | PathLike | tuple[str, str] | tuple[str, PathLike]]:
|
|
297
|
+
return self._get_settings().dirs # type: ignore[return-value]
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def APP_DIRS(self) -> Sequence[str]:
|
|
301
|
+
return self._get_settings().app_dirs # type: ignore[return-value]
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def MULTILINE_TAGS(self) -> bool:
|
|
305
|
+
return self._get_settings().multiline_tags # type: ignore[return-value]
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def STATIC_FILES_ALLOWED(self) -> Sequence[str | re.Pattern]:
|
|
309
|
+
return self._get_settings().static_files_allowed # type: ignore[return-value]
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def STATIC_FILES_FORBIDDEN(self) -> Sequence[str | re.Pattern]:
|
|
313
|
+
return self._get_settings().static_files_forbidden # type: ignore[return-value]
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
app_settings = InternalSettings()
|