litestar-vite 0.9.0__tar.gz → 0.11.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.
Potentially problematic release.
This version of litestar-vite might be problematic. Click here for more details.
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/PKG-INFO +6 -4
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/README.md +5 -3
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/pyproject.toml +4 -2
- litestar_vite-0.11.0/src/py/litestar_vite/__init__.py +8 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/cli.py +1 -1
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/commands.py +1 -1
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/config.py +1 -67
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/middleware.py +1 -2
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/response.py +8 -9
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/loader.py +66 -3
- litestar_vite-0.11.0/src/py/litestar_vite/plugin.py +196 -0
- litestar_vite-0.11.0/src/py/tests/conftest.py +39 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/app.py +8 -2
- litestar_vite-0.11.0/src/py/tests/test_asset_loader.py +153 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_cli/test_init.py +16 -7
- litestar_vite-0.11.0/src/py/tests/test_inertia/conftest.py +33 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_inertia/test_inertia_request.py +27 -7
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_inertia/test_inertia_response.py +33 -9
- litestar_vite-0.9.0/src/py/litestar_vite/__init__.py +0 -9
- litestar_vite-0.9.0/src/py/litestar_vite/plugin.py +0 -136
- litestar_vite-0.9.0/src/py/litestar_vite/template_engine.py +0 -103
- litestar_vite-0.9.0/src/py/tests/conftest.py +0 -27
- litestar_vite-0.9.0/src/py/tests/test_inertia/conftest.py +0 -30
- litestar_vite-0.9.0/src/py/tests/test_template_engine.py +0 -148
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/.gitignore +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/LICENSE +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/LICENSE +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/Makefile +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/NOTICE +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/README.md +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/src/dev-server-index.html +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/src/index.ts +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/src/inertia-helpers/index.ts +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/tests/__data__/dummy.ts +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/tests/index.test.ts +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/tsconfig.inertia-helpers.json +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/tsconfig.json +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/vitest.config.ts +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/js/vitest.workspace.ts +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/__metadata__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/_utils.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/config.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/exception_handler.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/plugin.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/request.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/routes.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/inertia/types.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/py.typed +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/templates/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/templates/index.html.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/templates/main.ts.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/templates/package.json.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/templates/styles.css.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/templates/tsconfig.json.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/litestar_vite/templates/vite.config.ts.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/templates/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/templates/index.html.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/public/.gitkeep +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/public/assets/main-l0sNRNKZ.js +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/public/assets/styles-l0sNRNKZ.js +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/public/manifest.json +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/resources/.gitkeep +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/resources/main.ts +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/resources/styles.css +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/templates/.gitkeep +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_app/web/templates/index.html +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_cli/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_cli/conftest.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_commands.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_config.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_inertia/__init__.py +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_inertia/templates/index.html.j2 +0 -0
- {litestar_vite-0.9.0 → litestar_vite-0.11.0}/src/py/tests/test_inertia/test_routes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: litestar-vite
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Vite plugin for Litestar
|
|
5
5
|
Project-URL: Changelog, https://cofin.github.io/litestar-vite/latest/changelog
|
|
6
6
|
Project-URL: Discord, https://discord.gg/X3FJqy8d2j
|
|
@@ -53,6 +53,8 @@ from pathlib import Path
|
|
|
53
53
|
from litestar import Controller, get, Litestar
|
|
54
54
|
from litestar.response import Template
|
|
55
55
|
from litestar.status_codes import HTTP_200_OK
|
|
56
|
+
from litestar.template.config import TemplateConfig
|
|
57
|
+
from litestar.contrib.jinja import JinjaTemplateEngine
|
|
56
58
|
from litestar_vite import ViteConfig, VitePlugin
|
|
57
59
|
|
|
58
60
|
class WebController(Controller):
|
|
@@ -64,9 +66,9 @@ class WebController(Controller):
|
|
|
64
66
|
async def index(self) -> Template:
|
|
65
67
|
return Template(template_name="index.html.j2")
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
vite = VitePlugin(config=ViteConfig(
|
|
69
|
-
app = Litestar(plugins=[vite], route_handlers=[WebController])
|
|
69
|
+
template_config = TemplateConfig(engine=JinjaTemplateEngine(directory='templates/'))
|
|
70
|
+
vite = VitePlugin(config=ViteConfig())
|
|
71
|
+
app = Litestar(plugins=[vite], template_config=template_config, route_handlers=[WebController])
|
|
70
72
|
|
|
71
73
|
```
|
|
72
74
|
|
|
@@ -18,6 +18,8 @@ from pathlib import Path
|
|
|
18
18
|
from litestar import Controller, get, Litestar
|
|
19
19
|
from litestar.response import Template
|
|
20
20
|
from litestar.status_codes import HTTP_200_OK
|
|
21
|
+
from litestar.template.config import TemplateConfig
|
|
22
|
+
from litestar.contrib.jinja import JinjaTemplateEngine
|
|
21
23
|
from litestar_vite import ViteConfig, VitePlugin
|
|
22
24
|
|
|
23
25
|
class WebController(Controller):
|
|
@@ -29,9 +31,9 @@ class WebController(Controller):
|
|
|
29
31
|
async def index(self) -> Template:
|
|
30
32
|
return Template(template_name="index.html.j2")
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
vite = VitePlugin(config=ViteConfig(
|
|
34
|
-
app = Litestar(plugins=[vite], route_handlers=[WebController])
|
|
34
|
+
template_config = TemplateConfig(engine=JinjaTemplateEngine(directory='templates/'))
|
|
35
|
+
vite = VitePlugin(config=ViteConfig())
|
|
36
|
+
app = Litestar(plugins=[vite], template_config=template_config, route_handlers=[WebController])
|
|
35
37
|
|
|
36
38
|
```
|
|
37
39
|
|
|
@@ -25,7 +25,7 @@ license = { text = "MIT" }
|
|
|
25
25
|
name = "litestar-vite"
|
|
26
26
|
readme = "README.md"
|
|
27
27
|
requires-python = ">=3.8"
|
|
28
|
-
version = "0.
|
|
28
|
+
version = "0.11.0"
|
|
29
29
|
|
|
30
30
|
[project.urls]
|
|
31
31
|
Changelog = "https://cofin.github.io/litestar-vite/latest/changelog"
|
|
@@ -85,7 +85,7 @@ test = [
|
|
|
85
85
|
allow_dirty = true
|
|
86
86
|
commit = true
|
|
87
87
|
commit_args = "--no-verify"
|
|
88
|
-
current_version = "0.
|
|
88
|
+
current_version = "0.11.0"
|
|
89
89
|
ignore_missing_files = false
|
|
90
90
|
ignore_missing_version = false
|
|
91
91
|
message = "chore(release): bump to v{new_version}"
|
|
@@ -267,6 +267,8 @@ lint.select = ["ALL"]
|
|
|
267
267
|
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
268
268
|
src = ["src/py/litestar_vite", "src/py/tests"]
|
|
269
269
|
target-version = "py38"
|
|
270
|
+
unsafe-fixes = true
|
|
271
|
+
|
|
270
272
|
|
|
271
273
|
[tool.ruff.lint.pydocstyle]
|
|
272
274
|
convention = "google"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from litestar_vite import inertia
|
|
4
|
+
from litestar_vite.config import ViteConfig
|
|
5
|
+
from litestar_vite.loader import ViteAssetLoader
|
|
6
|
+
from litestar_vite.plugin import VitePlugin
|
|
7
|
+
|
|
8
|
+
__all__ = ("ViteAssetLoader", "ViteConfig", "VitePlugin", "inertia")
|
|
@@ -113,8 +113,8 @@ def vite_init(
|
|
|
113
113
|
)
|
|
114
114
|
from rich.prompt import Confirm
|
|
115
115
|
|
|
116
|
+
from litestar_vite import VitePlugin
|
|
116
117
|
from litestar_vite.commands import execute_command, init_vite
|
|
117
|
-
from litestar_vite.plugin import VitePlugin
|
|
118
118
|
|
|
119
119
|
if callable(ctx.obj):
|
|
120
120
|
ctx.obj = ctx.obj()
|
|
@@ -14,7 +14,7 @@ DEFAULT_RESOURCES: set[str] = {"styles.css.j2", "main.ts.j2"}
|
|
|
14
14
|
DEFAULT_DEV_DEPENDENCIES: dict[str, str] = {
|
|
15
15
|
"typescript": "^5.7.2",
|
|
16
16
|
"vite": "^6.0.3",
|
|
17
|
-
"litestar-vite-plugin": "^0.
|
|
17
|
+
"litestar-vite-plugin": "^0.11.0",
|
|
18
18
|
"@types/node": "^22.10.1",
|
|
19
19
|
}
|
|
20
20
|
DEFAULT_DEPENDENCIES: dict[str, str] = {"axios": "^1.7.2"}
|
|
@@ -2,21 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from functools import cached_property
|
|
6
|
-
from inspect import isclass
|
|
7
5
|
from pathlib import Path
|
|
8
|
-
from typing import TYPE_CHECKING, cast
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
from litestar.template import TemplateConfig
|
|
12
|
-
from litestar.template.config import EngineType
|
|
13
|
-
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from collections.abc import Callable
|
|
16
|
-
|
|
17
|
-
from litestar.types import PathType
|
|
18
|
-
|
|
19
|
-
__all__ = ("ViteConfig", "ViteTemplateConfig")
|
|
7
|
+
__all__ = ("ViteConfig",)
|
|
20
8
|
TRUE_VALUES = {"True", "true", "1", "yes", "Y", "T"}
|
|
21
9
|
|
|
22
10
|
|
|
@@ -39,8 +27,6 @@ class ViteConfig:
|
|
|
39
27
|
|
|
40
28
|
In a standalone Vue or React application, this would be equivalent to the ``./src`` directory.
|
|
41
29
|
"""
|
|
42
|
-
template_dir: Path | str | None = field(default="templates")
|
|
43
|
-
"""Location of the Jinja2 template file."""
|
|
44
30
|
public_dir: Path | str = field(default="public")
|
|
45
31
|
"""The optional public directory Vite serves assets from.
|
|
46
32
|
|
|
@@ -113,8 +99,6 @@ class ViteConfig:
|
|
|
113
99
|
self.root_dir = Path(self.root_dir)
|
|
114
100
|
elif self.root_dir is None:
|
|
115
101
|
self.root_dir = Path()
|
|
116
|
-
if self.template_dir is not None and isinstance(self.template_dir, str):
|
|
117
|
-
self.template_dir = Path(self.template_dir)
|
|
118
102
|
if self.public_dir and isinstance(self.public_dir, str):
|
|
119
103
|
self.public_dir = Path(self.public_dir)
|
|
120
104
|
if isinstance(self.resource_dir, str):
|
|
@@ -123,53 +107,3 @@ class ViteConfig:
|
|
|
123
107
|
self.bundle_dir = Path(self.bundle_dir)
|
|
124
108
|
if isinstance(self.ssr_output_dir, str):
|
|
125
109
|
self.ssr_output_dir = Path(self.ssr_output_dir)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
@dataclass
|
|
129
|
-
class ViteTemplateConfig(TemplateConfig[EngineType]):
|
|
130
|
-
"""Configuration for Templating.
|
|
131
|
-
|
|
132
|
-
To enable templating, pass an instance of this class to the
|
|
133
|
-
:class:`Litestar <litestar.app.Litestar>` constructor using the
|
|
134
|
-
'template_config' key.
|
|
135
|
-
"""
|
|
136
|
-
|
|
137
|
-
config: ViteConfig = field(default_factory=lambda: ViteConfig())
|
|
138
|
-
"""A a config for the vite engine`."""
|
|
139
|
-
engine: type[EngineType] | EngineType | None = field(default=None)
|
|
140
|
-
"""A template engine adhering to the :class:`TemplateEngineProtocol <litestar.template.TemplateEngineProtocol>`."""
|
|
141
|
-
directory: PathType | list[PathType] | None = field(default=None)
|
|
142
|
-
"""A directory or list of directories from which to serve templates."""
|
|
143
|
-
engine_callback: Callable[[EngineType], None] | None = field(default=None)
|
|
144
|
-
"""A callback function that allows modifying the instantiated templating
|
|
145
|
-
protocol."""
|
|
146
|
-
|
|
147
|
-
instance: EngineType | None = field(default=None)
|
|
148
|
-
"""An instance of the templating protocol."""
|
|
149
|
-
|
|
150
|
-
def __post_init__(self) -> None:
|
|
151
|
-
"""Ensure that directory is set if engine is a class."""
|
|
152
|
-
if isclass(self.engine) and not self.directory: # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType]
|
|
153
|
-
msg = "directory is a required kwarg when passing a template engine class"
|
|
154
|
-
raise ImproperlyConfiguredException(msg)
|
|
155
|
-
"""Ensure that directory is not set if instance is."""
|
|
156
|
-
if self.instance is not None and self.directory is not None: # pyright: ignore[reportUnknownMemberType]
|
|
157
|
-
msg = "directory cannot be set if instance is"
|
|
158
|
-
raise ImproperlyConfiguredException(msg)
|
|
159
|
-
|
|
160
|
-
def to_engine(self) -> EngineType:
|
|
161
|
-
"""Instantiate the template engine."""
|
|
162
|
-
template_engine = cast(
|
|
163
|
-
"EngineType",
|
|
164
|
-
self.engine(directory=self.directory, config=self.config, engine_instance=None) # pyright: ignore[reportUnknownMemberType,reportCallIssue]
|
|
165
|
-
if isclass(self.engine) # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType]
|
|
166
|
-
else self.engine, # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType]
|
|
167
|
-
)
|
|
168
|
-
if callable(self.engine_callback):
|
|
169
|
-
self.engine_callback(template_engine) # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType]
|
|
170
|
-
return template_engine
|
|
171
|
-
|
|
172
|
-
@cached_property
|
|
173
|
-
def engine_instance(self) -> EngineType:
|
|
174
|
-
"""Return the template engine instance."""
|
|
175
|
-
return self.to_engine() if self.instance is None else self.instance
|
|
@@ -26,8 +26,7 @@ async def redirect_on_asset_version_mismatch(request: Request[UserT, AuthT, Stat
|
|
|
26
26
|
return None
|
|
27
27
|
|
|
28
28
|
vite_plugin = request.app.plugins.get(VitePlugin)
|
|
29
|
-
|
|
30
|
-
if inertia_version == template_engine.asset_loader.version_id:
|
|
29
|
+
if inertia_version == vite_plugin.asset_loader.version_id:
|
|
31
30
|
return None
|
|
32
31
|
return InertiaRedirect(request, redirect_to=str(request.url))
|
|
33
32
|
|
|
@@ -245,7 +245,7 @@ class InertiaResponse(Response[T]):
|
|
|
245
245
|
is_partial_render = cast("bool", getattr(request, "is_partial_render", False))
|
|
246
246
|
partial_keys = cast("set[str]", getattr(request, "partial_keys", {}))
|
|
247
247
|
vite_plugin = request.app.plugins.get(VitePlugin)
|
|
248
|
-
template_engine =
|
|
248
|
+
template_engine = request.app.template_engine # pyright: ignore[reportUnknownVariableType,reportUnknownMemberType]
|
|
249
249
|
headers.update(
|
|
250
250
|
{"Vary": "Accept", **get_headers(InertiaHeaderType(enabled=True))},
|
|
251
251
|
)
|
|
@@ -254,13 +254,13 @@ class InertiaResponse(Response[T]):
|
|
|
254
254
|
page_props = PageProps[T](
|
|
255
255
|
component=request.inertia.route_component, # type: ignore[attr-defined] # pyright: ignore[reportUnknownArgumentType,reportUnknownMemberType,reportAttributeAccessIssue]
|
|
256
256
|
props=shared_props, # pyright: ignore[reportArgumentType]
|
|
257
|
-
version=
|
|
257
|
+
version=vite_plugin.asset_loader.version_id,
|
|
258
258
|
url=request.url.path,
|
|
259
259
|
)
|
|
260
260
|
if is_inertia:
|
|
261
261
|
media_type = get_enum_string_value(self.media_type or media_type or MediaType.JSON)
|
|
262
262
|
body = self.render(page_props, media_type, get_serializer(type_encoders))
|
|
263
|
-
return ASGIResponse(
|
|
263
|
+
return ASGIResponse( # pyright: ignore[reportUnknownMemberType]
|
|
264
264
|
background=self.background or background,
|
|
265
265
|
body=body,
|
|
266
266
|
cookies=cookies,
|
|
@@ -290,17 +290,16 @@ class InertiaResponse(Response[T]):
|
|
|
290
290
|
media_type = MediaType.HTML
|
|
291
291
|
context = self.create_template_context(request, page_props, type_encoders) # pyright: ignore[reportUnknownMemberType]
|
|
292
292
|
if self.template_str is not None:
|
|
293
|
-
body = template_engine.render_string(self.template_str, context).encode(self.encoding)
|
|
293
|
+
body = template_engine.render_string(self.template_str, context).encode(self.encoding) # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
|
|
294
294
|
else:
|
|
295
295
|
inertia_plugin = cast("InertiaPlugin", request.app.plugins.get("InertiaPlugin"))
|
|
296
296
|
template_name = self.template_name or inertia_plugin.config.root_template
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
body = template.render(**context).encode(self.encoding)
|
|
297
|
+
template = template_engine.get_template(template_name) # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
|
|
298
|
+
body = template.render(**context).encode(self.encoding) # pyright: ignore[reportUnknownVariableType,reportUnknownMemberType]
|
|
300
299
|
|
|
301
|
-
return ASGIResponse(
|
|
300
|
+
return ASGIResponse( # pyright: ignore[reportUnknownMemberType]
|
|
302
301
|
background=self.background or background,
|
|
303
|
-
body=body,
|
|
302
|
+
body=body, # pyright: ignore[reportUnknownArgumentType]
|
|
304
303
|
cookies=cookies,
|
|
305
304
|
encoded_headers=encoded_headers,
|
|
306
305
|
encoding=self.encoding,
|
|
@@ -4,11 +4,61 @@ import json
|
|
|
4
4
|
from functools import cached_property
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from textwrap import dedent
|
|
7
|
-
from typing import TYPE_CHECKING, Any, ClassVar
|
|
7
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Mapping, cast
|
|
8
8
|
from urllib.parse import urljoin
|
|
9
9
|
|
|
10
|
+
import markupsafe
|
|
11
|
+
from litestar.exceptions import ImproperlyConfiguredException
|
|
12
|
+
|
|
10
13
|
if TYPE_CHECKING:
|
|
14
|
+
from litestar.connection import Request
|
|
15
|
+
|
|
11
16
|
from litestar_vite.config import ViteConfig
|
|
17
|
+
from litestar_vite.plugin import VitePlugin
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_request_from_context(context: Mapping[str, Any]) -> Request[Any, Any, Any]:
|
|
21
|
+
"""Get the request from the template context.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
context: The template context.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
The request object.
|
|
28
|
+
"""
|
|
29
|
+
return cast("Request[Any, Any, Any]", context["request"])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def render_hmr_client(context: Mapping[str, Any], /) -> markupsafe.Markup:
|
|
33
|
+
"""Render the HMR client.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
context: The template context.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
The HMR client.
|
|
40
|
+
"""
|
|
41
|
+
return cast(
|
|
42
|
+
"VitePlugin", _get_request_from_context(context).app.plugins.get("VitePlugin")
|
|
43
|
+
).asset_loader.render_hmr_client()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def render_asset_tag(
|
|
47
|
+
context: Mapping[str, Any], /, path: str | list[str], scripts_attrs: dict[str, str] | None = None
|
|
48
|
+
) -> markupsafe.Markup:
|
|
49
|
+
"""Render an asset tag.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
context: The template context.
|
|
53
|
+
path: The path to the asset.
|
|
54
|
+
scripts_attrs: The attributes for the script tag.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The asset tag.
|
|
58
|
+
"""
|
|
59
|
+
return cast(
|
|
60
|
+
"VitePlugin", _get_request_from_context(context).app.plugins.get("VitePlugin")
|
|
61
|
+
).asset_loader.render_asset_tag(path, scripts_attrs)
|
|
12
62
|
|
|
13
63
|
|
|
14
64
|
class ViteAssetLoader:
|
|
@@ -39,6 +89,19 @@ class ViteAssetLoader:
|
|
|
39
89
|
return str(hash(self.manifest_content))
|
|
40
90
|
return "1.0"
|
|
41
91
|
|
|
92
|
+
def render_hmr_client(self) -> markupsafe.Markup:
|
|
93
|
+
"""Generate the script tag for the Vite WS client for HMR."""
|
|
94
|
+
return markupsafe.Markup(
|
|
95
|
+
f"{self.generate_react_hmr_tags()}{self.generate_ws_client_tags()}",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def render_asset_tag(self, path: str | list[str], scripts_attrs: dict[str, str] | None = None) -> markupsafe.Markup:
|
|
99
|
+
"""Generate all assets include tags for the file in argument."""
|
|
100
|
+
path = [str(p) for p in path] if isinstance(path, list) else [str(path)]
|
|
101
|
+
return markupsafe.Markup(
|
|
102
|
+
"".join([self.generate_asset_tags(p, scripts_attrs=scripts_attrs) for p in path]),
|
|
103
|
+
)
|
|
104
|
+
|
|
42
105
|
def parse_manifest(self) -> None:
|
|
43
106
|
"""Parse the Vite manifest file.
|
|
44
107
|
|
|
@@ -151,7 +214,7 @@ class ViteAssetLoader:
|
|
|
151
214
|
|
|
152
215
|
if any(p for p in path if p not in self._manifest):
|
|
153
216
|
msg = "Cannot find %s in Vite manifest at %s. Did you forget to build your assets after an update?"
|
|
154
|
-
raise
|
|
217
|
+
raise ImproperlyConfiguredException(
|
|
155
218
|
msg,
|
|
156
219
|
path,
|
|
157
220
|
Path(f"{self._config.bundle_dir}/{self._config.manifest_name}"),
|
|
@@ -159,7 +222,7 @@ class ViteAssetLoader:
|
|
|
159
222
|
|
|
160
223
|
tags: list[str] = []
|
|
161
224
|
manifest_entry: dict[str, Any] = {}
|
|
162
|
-
manifest_entry.update({p: self._manifest[p] for p in path})
|
|
225
|
+
manifest_entry.update({p: self._manifest[p] for p in path if p})
|
|
163
226
|
if not scripts_attrs:
|
|
164
227
|
scripts_attrs = {"type": "module", "async": "", "defer": ""}
|
|
165
228
|
for manifest in manifest_entry.values():
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import signal
|
|
5
|
+
import threading
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, Iterator, cast
|
|
9
|
+
|
|
10
|
+
from litestar.contrib.jinja import JinjaTemplateEngine
|
|
11
|
+
from litestar.exceptions import ImproperlyConfiguredException
|
|
12
|
+
from litestar.plugins import CLIPlugin, InitPluginProtocol
|
|
13
|
+
from litestar.static_files import create_static_files_router # pyright: ignore[reportUnknownVariableType]
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from click import Group
|
|
17
|
+
from litestar import Litestar
|
|
18
|
+
from litestar.config.app import AppConfig
|
|
19
|
+
|
|
20
|
+
from litestar_vite.config import ViteConfig
|
|
21
|
+
from litestar_vite.loader import ViteAssetLoader
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def set_environment(config: ViteConfig) -> None:
|
|
25
|
+
"""Configure environment for easier integration"""
|
|
26
|
+
os.environ.setdefault("ASSET_URL", config.asset_url)
|
|
27
|
+
os.environ.setdefault("VITE_ALLOW_REMOTE", str(True))
|
|
28
|
+
os.environ.setdefault("VITE_PORT", str(config.port))
|
|
29
|
+
os.environ.setdefault("VITE_HOST", config.host)
|
|
30
|
+
os.environ.setdefault("VITE_PROTOCOL", config.protocol)
|
|
31
|
+
os.environ.setdefault("APP_URL", f"http://localhost:{os.environ.get('LITESTAR_PORT', 8000)}")
|
|
32
|
+
if config.dev_mode:
|
|
33
|
+
os.environ.setdefault("VITE_DEV_MODE", str(config.dev_mode))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ViteProcess:
|
|
37
|
+
"""Manages the Vite process."""
|
|
38
|
+
|
|
39
|
+
def __init__(self) -> None:
|
|
40
|
+
self.process: threading.Thread | None = None
|
|
41
|
+
self._lock = threading.Lock()
|
|
42
|
+
|
|
43
|
+
def start(self, command: list[str], cwd: Path | str | None) -> None:
|
|
44
|
+
"""Start the Vite process."""
|
|
45
|
+
from litestar.cli._utils import console
|
|
46
|
+
|
|
47
|
+
from litestar_vite.commands import execute_command
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
with self._lock:
|
|
51
|
+
if self.process and self.process.is_alive():
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
self.process = threading.Thread(
|
|
55
|
+
name="vite",
|
|
56
|
+
target=execute_command,
|
|
57
|
+
args=[],
|
|
58
|
+
kwargs={"command_to_run": command, "cwd": cwd},
|
|
59
|
+
daemon=True, # Make thread daemon so it exits when main thread exits
|
|
60
|
+
)
|
|
61
|
+
console.print(f"Starting Vite process with command: {command}")
|
|
62
|
+
self.process.start()
|
|
63
|
+
except Exception as e:
|
|
64
|
+
console.print(f"[red]Failed to start Vite process: {e!s}[/]")
|
|
65
|
+
raise
|
|
66
|
+
|
|
67
|
+
def stop(self, timeout: float = 5.0) -> None:
|
|
68
|
+
"""Stop the Vite process."""
|
|
69
|
+
from litestar.cli._utils import console
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
with self._lock:
|
|
73
|
+
if self.process and self.process.is_alive():
|
|
74
|
+
# Send SIGTERM to child process
|
|
75
|
+
if hasattr(signal, "SIGTERM") and self.process.ident is not None:
|
|
76
|
+
os.kill(self.process.ident, signal.SIGTERM)
|
|
77
|
+
self.process.join(timeout=timeout)
|
|
78
|
+
|
|
79
|
+
# Force kill if still alive
|
|
80
|
+
if self.process.is_alive():
|
|
81
|
+
if hasattr(signal, "SIGKILL") and self.process.ident is not None:
|
|
82
|
+
os.kill(self.process.ident, signal.SIGKILL)
|
|
83
|
+
self.process.join(timeout=1.0)
|
|
84
|
+
console.print("Stopping Vite process")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
console.print(f"[red]Failed to stop Vite process: {e!s}[/]")
|
|
87
|
+
raise
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class VitePlugin(InitPluginProtocol, CLIPlugin):
|
|
91
|
+
"""Vite plugin."""
|
|
92
|
+
|
|
93
|
+
__slots__ = ("_asset_loader", "_config", "_vite_process")
|
|
94
|
+
|
|
95
|
+
def __init__(self, config: ViteConfig | None = None, asset_loader: ViteAssetLoader | None = None) -> None:
|
|
96
|
+
"""Initialize ``Vite``.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
config: configuration to use for starting Vite. The default configuration will be used if it is not provided.
|
|
100
|
+
asset_loader: an initialized asset loader to use for rendering asset tags.
|
|
101
|
+
"""
|
|
102
|
+
from litestar_vite.config import ViteConfig
|
|
103
|
+
|
|
104
|
+
if config is None:
|
|
105
|
+
config = ViteConfig()
|
|
106
|
+
self._config = config
|
|
107
|
+
self._asset_loader = asset_loader
|
|
108
|
+
self._vite_process = ViteProcess()
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def config(self) -> ViteConfig:
|
|
112
|
+
return self._config
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def asset_loader(self) -> ViteAssetLoader:
|
|
116
|
+
from litestar_vite.loader import ViteAssetLoader
|
|
117
|
+
|
|
118
|
+
if self._asset_loader is None:
|
|
119
|
+
self._asset_loader = ViteAssetLoader.initialize_loader(config=self._config)
|
|
120
|
+
return self._asset_loader
|
|
121
|
+
|
|
122
|
+
def on_cli_init(self, cli: Group) -> None:
|
|
123
|
+
from litestar_vite.cli import vite_group
|
|
124
|
+
|
|
125
|
+
cli.add_command(vite_group)
|
|
126
|
+
|
|
127
|
+
def on_app_init(self, app_config: AppConfig) -> AppConfig:
|
|
128
|
+
"""Configure application for use with Vite.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
app_config: The :class:`AppConfig <litestar.config.app.AppConfig>` instance.
|
|
132
|
+
"""
|
|
133
|
+
from litestar_vite.loader import render_asset_tag, render_hmr_client
|
|
134
|
+
|
|
135
|
+
if app_config.template_config is None: # pyright: ignore[reportUnknownMemberType]
|
|
136
|
+
msg = "A template configuration is required for Vite."
|
|
137
|
+
raise ImproperlyConfiguredException(msg)
|
|
138
|
+
if not isinstance(app_config.template_config.engine_instance, JinjaTemplateEngine): # pyright: ignore[reportUnknownMemberType]
|
|
139
|
+
msg = "Jinja2 template engine is required for Vite."
|
|
140
|
+
raise ImproperlyConfiguredException(msg)
|
|
141
|
+
app_config.template_config.engine_instance.register_template_callable( # pyright: ignore[reportUnknownMemberType]
|
|
142
|
+
key="vite_hmr",
|
|
143
|
+
template_callable=render_hmr_client,
|
|
144
|
+
)
|
|
145
|
+
app_config.template_config.engine_instance.register_template_callable( # pyright: ignore[reportUnknownMemberType]
|
|
146
|
+
key="vite",
|
|
147
|
+
template_callable=render_asset_tag,
|
|
148
|
+
)
|
|
149
|
+
if self._config.set_static_folders:
|
|
150
|
+
static_dirs = [Path(self._config.bundle_dir), Path(self._config.resource_dir)]
|
|
151
|
+
if Path(self._config.public_dir).exists() and self._config.public_dir != self._config.bundle_dir:
|
|
152
|
+
static_dirs.append(Path(self._config.public_dir))
|
|
153
|
+
app_config.route_handlers.append(
|
|
154
|
+
create_static_files_router(
|
|
155
|
+
directories=cast( # type: ignore[arg-type]
|
|
156
|
+
"list[Path]",
|
|
157
|
+
static_dirs if self._config.dev_mode else [Path(self._config.bundle_dir)],
|
|
158
|
+
),
|
|
159
|
+
path=self._config.asset_url,
|
|
160
|
+
name="vite",
|
|
161
|
+
html_mode=False,
|
|
162
|
+
include_in_schema=False,
|
|
163
|
+
opt={"exclude_from_auth": True},
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
return app_config
|
|
167
|
+
|
|
168
|
+
@contextmanager
|
|
169
|
+
def server_lifespan(self, app: Litestar) -> Iterator[None]:
|
|
170
|
+
"""Manage Vite server process lifecycle."""
|
|
171
|
+
from litestar.cli._utils import console
|
|
172
|
+
|
|
173
|
+
if self._config.use_server_lifespan and self._config.dev_mode:
|
|
174
|
+
command_to_run = self._config.run_command if self._config.hot_reload else self._config.build_watch_command
|
|
175
|
+
|
|
176
|
+
if self.config.hot_reload:
|
|
177
|
+
console.rule("[yellow]Starting Vite process with HMR Enabled[/]", align="left")
|
|
178
|
+
else:
|
|
179
|
+
console.rule("[yellow]Starting Vite watch and build process[/]", align="left")
|
|
180
|
+
|
|
181
|
+
if self._config.set_environment:
|
|
182
|
+
set_environment(config=self._config)
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
self._vite_process.start(command_to_run, self._config.root_dir)
|
|
186
|
+
yield
|
|
187
|
+
finally:
|
|
188
|
+
self._vite_process.stop()
|
|
189
|
+
console.print("[yellow]Vite process stopped.[/]")
|
|
190
|
+
else:
|
|
191
|
+
manifest_path = Path(f"{self._config.bundle_dir}/{self._config.manifest_name}")
|
|
192
|
+
if manifest_path.exists():
|
|
193
|
+
console.rule(f"[yellow]Serving assets using manifest at `{manifest_path!s}`.[/]", align="left")
|
|
194
|
+
else:
|
|
195
|
+
console.rule(f"[yellow]Serving assets without manifest at `{manifest_path!s}`.[/]", align="left")
|
|
196
|
+
yield
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Generator
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from litestar.contrib.jinja import JinjaTemplateEngine
|
|
8
|
+
from litestar.template.config import TemplateConfig
|
|
9
|
+
|
|
10
|
+
from litestar_vite.config import ViteConfig
|
|
11
|
+
|
|
12
|
+
pytestmark = pytest.mark.anyio
|
|
13
|
+
here = Path(__file__).parent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def anyio_backend() -> str:
|
|
18
|
+
return "asyncio"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def test_app_path() -> Generator[Path, None, None]:
|
|
23
|
+
yield Path(here / "test_app" / "web")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def template_config(test_app_path: Path) -> TemplateConfig[JinjaTemplateEngine]:
|
|
28
|
+
return TemplateConfig(engine=JinjaTemplateEngine(directory=test_app_path / "templates"))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Define a fixture for ViteConfig
|
|
32
|
+
@pytest.fixture
|
|
33
|
+
def vite_config(test_app_path: Path) -> ViteConfig:
|
|
34
|
+
# Mock the ViteConfig with necessary attributes for testing
|
|
35
|
+
return ViteConfig(
|
|
36
|
+
bundle_dir=test_app_path / "public",
|
|
37
|
+
resource_dir=test_app_path / "resources",
|
|
38
|
+
hot_reload=True,
|
|
39
|
+
)
|
|
@@ -3,17 +3,23 @@ from __future__ import annotations
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from litestar import Litestar
|
|
6
|
+
from litestar.contrib.jinja import JinjaTemplateEngine
|
|
7
|
+
from litestar.template.config import TemplateConfig
|
|
6
8
|
|
|
7
9
|
from litestar_vite import ViteConfig, VitePlugin
|
|
8
10
|
|
|
9
11
|
here = Path(__file__).parent
|
|
10
12
|
|
|
13
|
+
template_config = TemplateConfig(
|
|
14
|
+
engine=JinjaTemplateEngine(
|
|
15
|
+
directory=Path(here / "web" / "templates"),
|
|
16
|
+
)
|
|
17
|
+
)
|
|
11
18
|
vite = VitePlugin(
|
|
12
19
|
config=ViteConfig(
|
|
13
20
|
bundle_dir=Path(here / "web" / "public"),
|
|
14
21
|
resource_dir=Path(here / "web" / "resources"),
|
|
15
|
-
template_dir=Path(here / "web" / "templates"),
|
|
16
22
|
hot_reload=True,
|
|
17
23
|
),
|
|
18
24
|
)
|
|
19
|
-
app = Litestar(plugins=[vite])
|
|
25
|
+
app = Litestar(plugins=[vite], template_config=template_config)
|