litestar-vite 0.13.0__py3-none-any.whl → 0.13.1__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  from litestar_vite import inertia
4
2
  from litestar_vite.config import ViteConfig
5
3
  from litestar_vite.loader import ViteAssetLoader
@@ -1,7 +1,5 @@
1
1
  """Metadata for the Project."""
2
2
 
3
- from __future__ import annotations
4
-
5
3
  from importlib.metadata import PackageNotFoundError, metadata, version
6
4
 
7
5
  __all__ = ("__project__", "__version__")
litestar_vite/cli.py CHANGED
@@ -1,14 +1,9 @@
1
- from __future__ import annotations
2
-
3
1
  from pathlib import Path
4
- from typing import TYPE_CHECKING
2
+ from typing import TYPE_CHECKING, Optional
5
3
 
6
4
  from click import Context, group, option
7
5
  from click import Path as ClickPath
8
- from litestar.cli._utils import (
9
- LitestarEnv,
10
- LitestarGroup,
11
- )
6
+ from litestar.cli._utils import LitestarEnv, LitestarGroup # pyright: ignore[reportPrivateImportUsage]
12
7
 
13
8
  if TYPE_CHECKING:
14
9
  from litestar import Litestar
@@ -89,18 +84,18 @@ def vite_group() -> None:
89
84
  is_flag=True,
90
85
  )
91
86
  def vite_init(
92
- ctx: Context,
93
- vite_port: int | None,
94
- enable_ssr: bool | None,
95
- asset_url: str | None,
96
- root_path: Path | None,
97
- bundle_path: Path | None,
98
- resource_path: Path | None,
99
- public_path: Path | None,
100
- overwrite: bool,
101
- verbose: bool,
102
- no_prompt: bool,
103
- no_install: bool,
87
+ ctx: "Context",
88
+ vite_port: "Optional[int]",
89
+ enable_ssr: "Optional[bool]",
90
+ asset_url: "Optional[str]",
91
+ root_path: "Optional[Path]",
92
+ bundle_path: "Optional[Path]",
93
+ resource_path: "Optional[Path]",
94
+ public_path: "Optional[Path]",
95
+ overwrite: "bool",
96
+ verbose: "bool",
97
+ no_prompt: "bool",
98
+ no_install: "bool",
104
99
  ) -> None: # sourcery skip: low-code-quality
105
100
  """Run vite build."""
106
101
  import os
@@ -108,9 +103,7 @@ def vite_init(
108
103
  from importlib.util import find_spec
109
104
  from pathlib import Path
110
105
 
111
- from litestar.cli._utils import (
112
- console,
113
- )
106
+ from litestar.cli._utils import console # pyright: ignore[reportPrivateImportUsage]
114
107
  from rich.prompt import Confirm
115
108
 
116
109
  from litestar_vite import VitePlugin
@@ -188,14 +181,14 @@ def vite_init(
188
181
  help="Install frontend packages.",
189
182
  )
190
183
  @option("--verbose", type=bool, help="Enable verbose output.", default=False, is_flag=True)
191
- def vite_install(app: Litestar, verbose: bool) -> None:
184
+ def vite_install(app: "Litestar", verbose: "bool") -> None:
192
185
  """Run vite build."""
193
186
  import os
194
187
  import sys
195
188
  from importlib.util import find_spec
196
189
  from pathlib import Path
197
190
 
198
- from litestar.cli._utils import console
191
+ from litestar.cli._utils import console # pyright: ignore[reportPrivateImportUsage]
199
192
 
200
193
  from litestar_vite.commands import execute_command
201
194
  from litestar_vite.plugin import VitePlugin
@@ -226,11 +219,9 @@ def vite_install(app: Litestar, verbose: bool) -> None:
226
219
  help="Building frontend assets with Vite.",
227
220
  )
228
221
  @option("--verbose", type=bool, help="Enable verbose output.", default=False, is_flag=True)
229
- def vite_build(app: Litestar, verbose: bool) -> None:
222
+ def vite_build(app: "Litestar", verbose: "bool") -> None:
230
223
  """Run vite build."""
231
- from litestar.cli._utils import (
232
- console,
233
- )
224
+ from litestar.cli._utils import console # pyright: ignore[reportPrivateImportUsage]
234
225
 
235
226
  from litestar_vite.commands import execute_command
236
227
  from litestar_vite.plugin import VitePlugin, set_environment
@@ -253,11 +244,9 @@ def vite_build(app: Litestar, verbose: bool) -> None:
253
244
  help="Serving frontend assets with Vite.",
254
245
  )
255
246
  @option("--verbose", type=bool, help="Enable verbose output.", default=False, is_flag=True)
256
- def vite_serve(app: Litestar, verbose: bool) -> None:
247
+ def vite_serve(app: "Litestar", verbose: "bool") -> None:
257
248
  """Run vite serve."""
258
- from litestar.cli._utils import (
259
- console,
260
- )
249
+ from litestar.cli._utils import console # pyright: ignore[reportPrivateImportUsage]
261
250
 
262
251
  from litestar_vite.commands import execute_command
263
252
  from litestar_vite.plugin import VitePlugin, set_environment
@@ -289,13 +278,19 @@ def vite_serve(app: Litestar, verbose: bool) -> None:
289
278
  show_default=True,
290
279
  )
291
280
  @option("--verbose", type=bool, help="Enable verbose output.", default=False, is_flag=True)
292
- def generate_js_routes(app: Litestar, output: Path, verbose: bool) -> None:
293
- """Run vite serve."""
281
+ def generate_js_routes(app: "Litestar", output: "Path", verbose: "bool") -> None:
282
+ """Run vite serve.
283
+
284
+ Args:
285
+ app: The Litestar application instance.
286
+ output: The path to the output file.
287
+ verbose: Whether to enable verbose output.
288
+
289
+ Raises:
290
+ LitestarCLIException: If the output file cannot be written.
291
+ """
294
292
  import msgspec
295
- from litestar.cli._utils import (
296
- LitestarCLIException,
297
- console,
298
- )
293
+ from litestar.cli._utils import LitestarCLIException, console # pyright: ignore[reportPrivateImportUsage]
299
294
  from litestar.serialization import encode_json, get_serializer
300
295
 
301
296
  from litestar_vite.plugin import VitePlugin, set_environment
litestar_vite/commands.py CHANGED
@@ -1,26 +1,27 @@
1
- from __future__ import annotations
2
-
3
1
  import platform
4
2
  import subprocess
3
+ from collections.abc import MutableMapping
5
4
  from pathlib import Path
6
- from typing import TYPE_CHECKING, Any, MutableMapping
5
+ from typing import TYPE_CHECKING, Any, Optional, Union
7
6
 
8
7
  if TYPE_CHECKING:
8
+ from collections.abc import MutableMapping
9
+
9
10
  from jinja2 import Environment, Template
10
11
  from litestar import Litestar
11
12
 
12
- VITE_INIT_TEMPLATES: set[str] = {"package.json.j2", "tsconfig.json.j2", "vite.config.ts.j2"}
13
- DEFAULT_RESOURCES: set[str] = {"styles.css.j2", "main.ts.j2"}
14
- DEFAULT_DEV_DEPENDENCIES: dict[str, str] = {
13
+ VITE_INIT_TEMPLATES: "set[str]" = {"package.json.j2", "tsconfig.json.j2", "vite.config.ts.j2"}
14
+ DEFAULT_RESOURCES: "set[str]" = {"styles.css.j2", "main.ts.j2"}
15
+ DEFAULT_DEV_DEPENDENCIES: "dict[str, str]" = {
15
16
  "typescript": "^5.7.2",
16
17
  "vite": "^6.0.6",
17
- "litestar-vite-plugin": "^0.13.0",
18
+ "litestar-vite-plugin": "^0.13.1",
18
19
  "@types/node": "^22.10.2",
19
20
  }
20
- DEFAULT_DEPENDENCIES: dict[str, str] = {"axios": "^1.7.9"}
21
+ DEFAULT_DEPENDENCIES: "dict[str, str]" = {"axios": "^1.7.9"}
21
22
 
22
23
 
23
- def to_json(value: Any) -> str:
24
+ def to_json(value: "Any") -> str:
24
25
  """Serialize JSON field values.
25
26
 
26
27
  Args:
@@ -35,15 +36,15 @@ def to_json(value: Any) -> str:
35
36
 
36
37
 
37
38
  def init_vite(
38
- app: Litestar,
39
- root_path: Path,
40
- resource_path: Path,
41
- asset_url: str,
42
- public_path: Path,
43
- bundle_path: Path,
44
- enable_ssr: bool,
39
+ app: "Litestar",
40
+ root_path: "Path",
41
+ resource_path: "Path",
42
+ asset_url: "str",
43
+ public_path: "Path",
44
+ bundle_path: "Path",
45
+ enable_ssr: "bool",
45
46
  vite_port: int,
46
- hot_file: Path,
47
+ hot_file: "Path",
47
48
  litestar_port: int,
48
49
  ) -> None:
49
50
  """Initialize a new Vite project.
@@ -61,7 +62,7 @@ def init_vite(
61
62
  litestar_port: Port for Litestar server.
62
63
  """
63
64
  from jinja2 import Environment, FileSystemLoader, select_autoescape
64
- from litestar.cli._utils import console
65
+ from litestar.cli._utils import console # pyright: ignore[reportPrivateImportUsage]
65
66
  from litestar.utils import module_loader
66
67
 
67
68
  template_path = module_loader.module_to_os_path("litestar_vite.templates")
@@ -70,11 +71,11 @@ def init_vite(
70
71
  autoescape=select_autoescape(),
71
72
  )
72
73
 
73
- enabled_templates: set[str] = VITE_INIT_TEMPLATES
74
- enabled_resources: set[str] = DEFAULT_RESOURCES
75
- dependencies: dict[str, str] = DEFAULT_DEPENDENCIES
76
- dev_dependencies: dict[str, str] = DEFAULT_DEV_DEPENDENCIES
77
- templates: dict[str, Template] = {
74
+ enabled_templates: "set[str]" = VITE_INIT_TEMPLATES
75
+ enabled_resources: "set[str]" = DEFAULT_RESOURCES
76
+ dependencies: "dict[str, str]" = DEFAULT_DEPENDENCIES
77
+ dev_dependencies: "dict[str, str]" = DEFAULT_DEV_DEPENDENCIES
78
+ templates: "dict[str, Template]" = {
78
79
  template_name: get_template(environment=vite_template_env, name=template_name)
79
80
  for template_name in enabled_templates
80
81
  }
@@ -82,7 +83,7 @@ def init_vite(
82
83
  # Prepare root_path
83
84
  root_path.mkdir(parents=True, exist_ok=True)
84
85
  for template_name, template in templates.items():
85
- target_file_name = template_name[:-3] if template_name.endswith(".j2") else template_name
86
+ target_file_name = template_name.removesuffix(".j2")
86
87
  target_file_path = root_path / target_file_name
87
88
  with target_file_path.open(mode="w") as file:
88
89
  console.print(f" * Writing {target_file_name} to {target_file_path!s}")
@@ -90,8 +91,7 @@ def init_vite(
90
91
  file.write(
91
92
  template.render(
92
93
  entry_point=[
93
- f"{resource_path!s}/{resource_name[:-3] if resource_name.endswith('.j2') else resource_name}"
94
- for resource_name in enabled_resources
94
+ f"{resource_path!s}/{resource_name.removesuffix('.j2')}" for resource_name in enabled_resources
95
95
  ],
96
96
  enable_ssr=enable_ssr,
97
97
  asset_url=asset_url,
@@ -112,27 +112,37 @@ def init_vite(
112
112
  (root_path / resource_path).mkdir(parents=True, exist_ok=True)
113
113
  for resource_name in enabled_resources:
114
114
  template = get_template(environment=vite_template_env, name=resource_name)
115
- target_file_name = f"{resource_name[:-3] if resource_name.endswith('.j2') else resource_name}"
115
+ target_file_name = f"{resource_name.removesuffix('.j2')}"
116
116
  target_file_path = root_path / resource_path / target_file_name
117
117
  with target_file_path.open(mode="w") as file:
118
118
  console.print(
119
- f" * Writing {resource_name[:-3] if resource_name.endswith('.j2') else resource_name} to {target_file_path!s}",
119
+ f" * Writing {resource_name.removesuffix('.j2')} to {target_file_path!s}",
120
120
  )
121
121
  file.write(template.render())
122
122
  console.print("[yellow]Vite initialization completed.[/]")
123
123
 
124
124
 
125
125
  def get_template(
126
- environment: Environment,
127
- name: str | Template,
128
- parent: str | None = None,
129
- globals: MutableMapping[str, Any] | None = None, # noqa: A002
130
- ) -> Template:
126
+ environment: "Environment",
127
+ name: "Union[str, Template]",
128
+ parent: "Optional[str]" = None,
129
+ globals: "Optional[MutableMapping[str, Any]]" = None, # noqa: A002
130
+ ) -> "Template":
131
131
  return environment.get_template(name=name, parent=parent, globals=globals)
132
132
 
133
133
 
134
- def execute_command(command_to_run: list[str], cwd: str | Path | None = None) -> subprocess.CompletedProcess[bytes]:
135
- """Run Vite in a subprocess."""
134
+ def execute_command(
135
+ command_to_run: "list[str]", cwd: "Optional[Union[str, Path]]" = None
136
+ ) -> "subprocess.CompletedProcess[bytes]":
137
+ """Run Vite in a subprocess.
138
+
139
+ Args:
140
+ command_to_run: The command to run.
141
+ cwd: The current working directory.
142
+
143
+ Returns:
144
+ The completed process.
145
+ """
136
146
  kwargs = {}
137
147
  if cwd is not None:
138
148
  kwargs["cwd"] = Path(cwd)
litestar_vite/config.py CHANGED
@@ -1,8 +1,7 @@
1
- from __future__ import annotations
2
-
3
1
  import os
4
2
  from dataclasses import dataclass, field
5
3
  from pathlib import Path
4
+ from typing import Union
6
5
 
7
6
  __all__ = ("ViteConfig",)
8
7
  TRUE_VALUES = {"True", "true", "1", "yes", "Y", "T"}
@@ -17,17 +16,17 @@ class ViteConfig:
17
16
  'plugins' key.
18
17
  """
19
18
 
20
- bundle_dir: Path | str = field(default="public")
19
+ bundle_dir: "Union[Path, str]" = field(default="public")
21
20
  """Location of the compiled assets from Vite.
22
21
 
23
22
  The manifest file will also be found here.
24
23
  """
25
- resource_dir: Path | str = field(default="resources")
24
+ resource_dir: "Union[Path, str]" = field(default="resources")
26
25
  """The directory where all typescript/javascript source are written.
27
26
 
28
27
  In a standalone Vue or React application, this would be equivalent to the ``./src`` directory.
29
28
  """
30
- public_dir: Path | str = field(default="public")
29
+ public_dir: "Union[Path, str]" = field(default="public")
31
30
  """The optional public directory Vite serves assets from.
32
31
 
33
32
  In a standalone Vue or React application, this would be equivalent to the ``./public`` directory.
@@ -45,9 +44,9 @@ class ViteConfig:
45
44
  """Enable HMR for Vite development server."""
46
45
  ssr_enabled: bool = False
47
46
  """Enable SSR."""
48
- ssr_output_dir: Path | str | None = None
47
+ ssr_output_dir: "Union[Path, str, None]" = None
49
48
  """SSR Output path"""
50
- root_dir: Path | str | None = None
49
+ root_dir: "Union[Path, str, None]" = None
51
50
  """The is the base path to your application.
52
51
 
53
52
  In a standalone Vue or React application, this would be equivalent to the top-level project folder containing the ``./src`` directory.
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  from enum import Enum
4
2
  from typing import TYPE_CHECKING, Any, Callable
5
3
 
@@ -18,43 +16,81 @@ class InertiaHeaders(str, Enum):
18
16
  REFERER = "Referer"
19
17
 
20
18
 
21
- def get_enabled_header(enabled: bool = True) -> dict[str, Any]:
22
- """True if inertia is enabled."""
19
+ def get_enabled_header(enabled: bool = True) -> "dict[str, Any]":
20
+ """True if inertia is enabled.
21
+
22
+ Args:
23
+ enabled: Whether inertia is enabled.
24
+
25
+ Returns:
26
+ The headers for inertia.
27
+ """
23
28
 
24
29
  return {InertiaHeaders.ENABLED.value: "true" if enabled else "false"}
25
30
 
26
31
 
27
- def get_version_header(version: str) -> dict[str, Any]:
28
- """Return headers for change swap method response."""
32
+ def get_version_header(version: str) -> "dict[str, Any]":
33
+ """Return headers for change swap method response.
34
+
35
+ Args:
36
+ version: The version of the inertia.
37
+
38
+ Returns:
39
+ The headers for inertia.
40
+ """
29
41
  return {InertiaHeaders.VERSION.value: version}
30
42
 
31
43
 
32
- def get_partial_data_header(partial: str) -> dict[str, Any]:
33
- """Return headers for a partial data response."""
44
+ def get_partial_data_header(partial: str) -> "dict[str, Any]":
45
+ """Return headers for a partial data response.
46
+
47
+ Args:
48
+ partial: The partial data.
49
+
50
+ Returns:
51
+ The headers for inertia.
52
+ """
34
53
  return {InertiaHeaders.PARTIAL_DATA.value: partial}
35
54
 
36
55
 
37
- def get_partial_component_header(partial: str) -> dict[str, Any]:
38
- """Return headers for a partial data response."""
56
+ def get_partial_component_header(partial: str) -> "dict[str, Any]":
57
+ """Return headers for a partial data response.
58
+
59
+ Args:
60
+ partial: The partial data.
61
+
62
+ Returns:
63
+ The headers for inertia.
64
+ """
39
65
  return {InertiaHeaders.PARTIAL_COMPONENT.value: partial}
40
66
 
41
67
 
42
- def get_headers(inertia_headers: InertiaHeaderType) -> dict[str, Any]:
43
- """Return headers for Inertia responses."""
68
+ def get_headers(inertia_headers: "InertiaHeaderType") -> "dict[str, Any]":
69
+ """Return headers for Inertia responses.
70
+
71
+ Args:
72
+ inertia_headers: The inertia headers.
73
+
74
+ Raises:
75
+ ValueError: If the inertia headers are None.
76
+
77
+ Returns:
78
+ The headers for inertia.
79
+ """
44
80
  if not inertia_headers:
45
81
  msg = "Value for inertia_headers cannot be None."
46
82
  raise ValueError(msg)
47
- inertia_headers_dict: dict[str, Callable[..., dict[str, Any]]] = {
83
+ inertia_headers_dict: "dict[str, Callable[..., dict[str, Any]]]" = {
48
84
  "enabled": get_enabled_header,
49
85
  "partial_data": get_partial_data_header,
50
86
  "partial_component": get_partial_component_header,
51
87
  "version": get_version_header,
52
88
  }
53
89
 
54
- header: dict[str, Any] = {}
55
- response: dict[str, Any]
56
- key: str
57
- value: Any
90
+ header: "dict[str, Any]" = {}
91
+ response: "dict[str, Any]"
92
+ key: "str"
93
+ value: "Any"
58
94
 
59
95
  for key, value in inertia_headers.items():
60
96
  if value is not None:
@@ -1,7 +1,5 @@
1
- from __future__ import annotations
2
-
3
1
  from dataclasses import dataclass, field
4
- from typing import Any
2
+ from typing import Any, Optional
5
3
 
6
4
  __all__ = ("InertiaConfig",)
7
5
 
@@ -19,11 +17,11 @@ class InertiaConfig:
19
17
  """An identifier to use on routes to get the inertia component to render."""
20
18
  exclude_from_js_routes_key: str = "exclude_from_routes"
21
19
  """An identifier to use on routes to exclude a route from the generated routes typescript file."""
22
- redirect_unauthorized_to: str | None = None
20
+ redirect_unauthorized_to: "Optional[str]" = None
23
21
  """Optionally supply a path where unauthorized requests should redirect."""
24
- redirect_404: str | None = None
22
+ redirect_404: "Optional[str]" = None
25
23
  """Optionally supply a path where 404 requests should redirect."""
26
- extra_static_page_props: dict[str, Any] = field(default_factory=dict)
24
+ extra_static_page_props: "dict[str, Any]" = field(default_factory=dict)
27
25
  """A dictionary of values to automatically add in to page props on every response."""
28
- extra_session_page_props: set[str] = field(default_factory=set)
26
+ extra_session_page_props: "set[str]" = field(default_factory=set)
29
27
  """A set of session keys for which the value automatically be added (if it exists) to the response."""
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  import re
4
2
  from typing import TYPE_CHECKING, Any, cast
5
3
 
@@ -54,8 +52,16 @@ class _HTTPConflictException(HTTPException):
54
52
  status_code: int = HTTP_409_CONFLICT
55
53
 
56
54
 
57
- def exception_to_http_response(request: Request[UserT, AuthT, StateT], exc: Exception) -> Response[Any]:
58
- """Handler for all exceptions subclassed from HTTPException."""
55
+ def exception_to_http_response(request: "Request[UserT, AuthT, StateT]", exc: "Exception") -> "Response[Any]":
56
+ """Handler for all exceptions subclassed from HTTPException.
57
+
58
+ Args:
59
+ request: The request object.
60
+ exc: The exception to handle.
61
+
62
+ Returns:
63
+ The response object.
64
+ """
59
65
  inertia_enabled = getattr(request, "inertia_enabled", False) or getattr(request, "is_inertia", False)
60
66
 
61
67
  if not inertia_enabled:
@@ -65,14 +71,22 @@ def exception_to_http_response(request: Request[UserT, AuthT, StateT], exc: Exce
65
71
  http_exc = _HTTPConflictException # type: ignore[assignment]
66
72
  else:
67
73
  http_exc = InternalServerException # type: ignore[assignment]
68
- if request.app.debug and http_exc not in (PermissionDeniedException, NotFoundError):
74
+ if request.app.debug and http_exc not in {PermissionDeniedException, NotFoundError}:
69
75
  return cast("Response[Any]", create_debug_response(request, exc))
70
76
  return cast("Response[Any]", create_exception_response(request, http_exc(detail=str(exc.__cause__)))) # pyright: ignore[reportUnknownArgumentType]
71
77
  return create_inertia_exception_response(request, exc)
72
78
 
73
79
 
74
- def create_inertia_exception_response(request: Request[UserT, AuthT, StateT], exc: Exception) -> Response[Any]:
75
- """Create the inertia exception response"""
80
+ def create_inertia_exception_response(request: "Request[UserT, AuthT, StateT]", exc: "Exception") -> "Response[Any]":
81
+ """Create the inertia exception response.
82
+
83
+ Args:
84
+ request: The request object.
85
+ exc: The exception to handle.
86
+
87
+ Returns:
88
+ The response object.
89
+ """
76
90
  is_inertia = getattr(request, "is_inertia", False)
77
91
  status_code = getattr(exc, "status_code", HTTP_500_INTERNAL_SERVER_ERROR)
78
92
  preferred_type = MediaType.HTML if not is_inertia else MediaType.JSON
@@ -94,7 +108,7 @@ def create_inertia_exception_response(request: Request[UserT, AuthT, StateT], ex
94
108
  match = FIELD_ERR_RE.search(error_detail)
95
109
  field = match.group(1) if match else default_field
96
110
  if isinstance(message, dict):
97
- error(request, field, error_detail if error_detail else detail)
111
+ error(request, field, error_detail or detail)
98
112
  if status_code in {HTTP_422_UNPROCESSABLE_ENTITY, HTTP_400_BAD_REQUEST}:
99
113
  return InertiaBack(request)
100
114
  if isinstance(exc, PermissionDeniedException):