litestar-vite 0.1.1__py3-none-any.whl → 0.15.0__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.
- litestar_vite/__init__.py +54 -4
- litestar_vite/__metadata__.py +12 -7
- litestar_vite/cli.py +1048 -10
- litestar_vite/codegen/__init__.py +48 -0
- litestar_vite/codegen/_export.py +229 -0
- litestar_vite/codegen/_inertia.py +619 -0
- litestar_vite/codegen/_openapi.py +280 -0
- litestar_vite/codegen/_routes.py +720 -0
- litestar_vite/codegen/_ts.py +235 -0
- litestar_vite/codegen/_utils.py +141 -0
- litestar_vite/commands.py +73 -0
- litestar_vite/config/__init__.py +997 -0
- litestar_vite/config/_constants.py +97 -0
- litestar_vite/config/_deploy.py +70 -0
- litestar_vite/config/_inertia.py +241 -0
- litestar_vite/config/_paths.py +63 -0
- litestar_vite/config/_runtime.py +235 -0
- litestar_vite/config/_spa.py +93 -0
- litestar_vite/config/_types.py +94 -0
- litestar_vite/deploy.py +366 -0
- litestar_vite/doctor.py +1181 -0
- litestar_vite/exceptions.py +78 -0
- litestar_vite/executor.py +360 -0
- litestar_vite/handler/__init__.py +9 -0
- litestar_vite/handler/_app.py +612 -0
- litestar_vite/handler/_routing.py +130 -0
- litestar_vite/html_transform.py +569 -0
- litestar_vite/inertia/__init__.py +77 -0
- litestar_vite/inertia/_utils.py +119 -0
- litestar_vite/inertia/exception_handler.py +178 -0
- litestar_vite/inertia/helpers.py +1571 -0
- litestar_vite/inertia/middleware.py +54 -0
- litestar_vite/inertia/plugin.py +199 -0
- litestar_vite/inertia/precognition.py +274 -0
- litestar_vite/inertia/request.py +334 -0
- litestar_vite/inertia/response.py +802 -0
- litestar_vite/inertia/types.py +335 -0
- litestar_vite/loader.py +464 -123
- litestar_vite/plugin/__init__.py +687 -0
- litestar_vite/plugin/_process.py +185 -0
- litestar_vite/plugin/_proxy.py +689 -0
- litestar_vite/plugin/_proxy_headers.py +244 -0
- litestar_vite/plugin/_static.py +37 -0
- litestar_vite/plugin/_utils.py +489 -0
- litestar_vite/py.typed +0 -0
- litestar_vite/scaffolding/__init__.py +20 -0
- litestar_vite/scaffolding/generator.py +270 -0
- litestar_vite/scaffolding/templates.py +437 -0
- litestar_vite/templates/__init__.py +0 -0
- litestar_vite/templates/addons/tailwindcss/tailwind.css.j2 +1 -0
- litestar_vite/templates/angular/index.html.j2 +12 -0
- litestar_vite/templates/angular/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular/package.json.j2 +36 -0
- litestar_vite/templates/angular/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular/src/main.ts.j2 +9 -0
- litestar_vite/templates/angular/src/styles.css.j2 +9 -0
- litestar_vite/templates/angular/tsconfig.app.json.j2 +34 -0
- litestar_vite/templates/angular/tsconfig.json.j2 +20 -0
- litestar_vite/templates/angular/vite.config.ts.j2 +21 -0
- litestar_vite/templates/angular-cli/.postcssrc.json.j2 +5 -0
- litestar_vite/templates/angular-cli/angular.json.j2 +36 -0
- litestar_vite/templates/angular-cli/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular-cli/package.json.j2 +28 -0
- litestar_vite/templates/angular-cli/proxy.conf.json.j2 +18 -0
- litestar_vite/templates/angular-cli/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular-cli/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular-cli/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular-cli/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular-cli/src/index.html.j2 +13 -0
- litestar_vite/templates/angular-cli/src/main.ts.j2 +6 -0
- litestar_vite/templates/angular-cli/src/styles.css.j2 +10 -0
- litestar_vite/templates/angular-cli/tailwind.config.js.j2 +4 -0
- litestar_vite/templates/angular-cli/tsconfig.app.json.j2 +16 -0
- litestar_vite/templates/angular-cli/tsconfig.json.j2 +26 -0
- litestar_vite/templates/angular-cli/tsconfig.spec.json.j2 +9 -0
- litestar_vite/templates/astro/astro.config.mjs.j2 +28 -0
- litestar_vite/templates/astro/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/astro/src/layouts/Layout.astro.j2 +63 -0
- litestar_vite/templates/astro/src/pages/index.astro.j2 +36 -0
- litestar_vite/templates/astro/src/styles/global.css.j2 +1 -0
- litestar_vite/templates/base/.gitignore.j2 +42 -0
- litestar_vite/templates/base/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/base/package.json.j2 +39 -0
- litestar_vite/templates/base/resources/vite-env.d.ts.j2 +1 -0
- litestar_vite/templates/base/tsconfig.json.j2 +37 -0
- litestar_vite/templates/htmx/src/main.js.j2 +8 -0
- litestar_vite/templates/htmx/templates/base.html.j2.j2 +56 -0
- litestar_vite/templates/htmx/templates/index.html.j2.j2 +13 -0
- litestar_vite/templates/htmx/vite.config.ts.j2 +40 -0
- litestar_vite/templates/nuxt/app.vue.j2 +29 -0
- litestar_vite/templates/nuxt/composables/useApi.ts.j2 +33 -0
- litestar_vite/templates/nuxt/nuxt.config.ts.j2 +31 -0
- litestar_vite/templates/nuxt/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/nuxt/pages/index.vue.j2 +54 -0
- litestar_vite/templates/react/index.html.j2 +13 -0
- litestar_vite/templates/react/src/App.css.j2 +56 -0
- litestar_vite/templates/react/src/App.tsx.j2 +19 -0
- litestar_vite/templates/react/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-inertia/index.html.j2 +14 -0
- litestar_vite/templates/react-inertia/package.json.j2 +47 -0
- litestar_vite/templates/react-inertia/resources/App.css.j2 +68 -0
- litestar_vite/templates/react-inertia/resources/main.tsx.j2 +17 -0
- litestar_vite/templates/react-inertia/resources/pages/Home.tsx.j2 +18 -0
- litestar_vite/templates/react-inertia/resources/ssr.tsx.j2 +19 -0
- litestar_vite/templates/react-inertia/vite.config.ts.j2 +59 -0
- litestar_vite/templates/react-router/index.html.j2 +12 -0
- litestar_vite/templates/react-router/src/App.css.j2 +17 -0
- litestar_vite/templates/react-router/src/App.tsx.j2 +7 -0
- litestar_vite/templates/react-router/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react-router/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-tanstack/index.html.j2 +12 -0
- litestar_vite/templates/react-tanstack/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/react-tanstack/src/App.css.j2 +17 -0
- litestar_vite/templates/react-tanstack/src/main.tsx.j2 +21 -0
- litestar_vite/templates/react-tanstack/src/routeTree.gen.ts.j2 +7 -0
- litestar_vite/templates/react-tanstack/src/routes/__root.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/books.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/index.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte/index.html.j2 +13 -0
- litestar_vite/templates/svelte/src/App.svelte.j2 +30 -0
- litestar_vite/templates/svelte/src/app.css.j2 +45 -0
- litestar_vite/templates/svelte/src/main.ts.j2 +8 -0
- litestar_vite/templates/svelte/src/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte-inertia/index.html.j2 +14 -0
- litestar_vite/templates/svelte-inertia/resources/app.css.j2 +21 -0
- litestar_vite/templates/svelte-inertia/resources/main.ts.j2 +11 -0
- litestar_vite/templates/svelte-inertia/resources/pages/Home.svelte.j2 +43 -0
- litestar_vite/templates/svelte-inertia/resources/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte-inertia/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte-inertia/vite.config.ts.j2 +37 -0
- litestar_vite/templates/sveltekit/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/sveltekit/src/app.css.j2 +40 -0
- litestar_vite/templates/sveltekit/src/app.html.j2 +12 -0
- litestar_vite/templates/sveltekit/src/hooks.server.ts.j2 +55 -0
- litestar_vite/templates/sveltekit/src/routes/+layout.svelte.j2 +12 -0
- litestar_vite/templates/sveltekit/src/routes/+page.svelte.j2 +34 -0
- litestar_vite/templates/sveltekit/svelte.config.js.j2 +12 -0
- litestar_vite/templates/sveltekit/tsconfig.json.j2 +14 -0
- litestar_vite/templates/sveltekit/vite.config.ts.j2 +31 -0
- litestar_vite/templates/vue/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue/index.html.j2 +13 -0
- litestar_vite/templates/vue/src/App.vue.j2 +28 -0
- litestar_vite/templates/vue/src/main.ts.j2 +5 -0
- litestar_vite/templates/vue/src/style.css.j2 +45 -0
- litestar_vite/templates/vue/vite.config.ts.j2 +39 -0
- litestar_vite/templates/vue-inertia/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue-inertia/index.html.j2 +14 -0
- litestar_vite/templates/vue-inertia/package.json.j2 +50 -0
- litestar_vite/templates/vue-inertia/resources/main.ts.j2 +18 -0
- litestar_vite/templates/vue-inertia/resources/pages/Home.vue.j2 +22 -0
- litestar_vite/templates/vue-inertia/resources/ssr.ts.j2 +21 -0
- litestar_vite/templates/vue-inertia/resources/style.css.j2 +21 -0
- litestar_vite/templates/vue-inertia/vite.config.ts.j2 +59 -0
- litestar_vite-0.15.0.dist-info/METADATA +230 -0
- litestar_vite-0.15.0.dist-info/RECORD +164 -0
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0.dist-info}/WHEEL +1 -1
- litestar_vite/config.py +0 -100
- litestar_vite/plugin.py +0 -45
- litestar_vite/template_engine.py +0 -103
- litestar_vite-0.1.1.dist-info/METADATA +0 -68
- litestar_vite-0.1.1.dist-info/RECORD +0 -11
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Litestar-Vite exception classes."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"AssetNotFoundError",
|
|
5
|
+
"LitestarViteError",
|
|
6
|
+
"ManifestNotFoundError",
|
|
7
|
+
"MissingDependencyError",
|
|
8
|
+
"ViteExecutableNotFoundError",
|
|
9
|
+
"ViteExecutionError",
|
|
10
|
+
"ViteProcessError",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LitestarViteError(Exception):
|
|
15
|
+
"""Base exception for Litestar-Vite related errors."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MissingDependencyError(LitestarViteError, ImportError):
|
|
19
|
+
"""Raised when a package is not installed but required."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, package: str, install_package: "str | None" = None) -> None:
|
|
22
|
+
"""Initialize the exception.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
package: The name of the missing package.
|
|
26
|
+
install_package: Optional alternative package name for installation.
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(
|
|
29
|
+
f"Package {package!r} is not installed but required. You can install it by running "
|
|
30
|
+
f"'pip install litestar-vite[{install_package or package}]' to install litestar-vite with the required extra "
|
|
31
|
+
f"or 'pip install {install_package or package}' to install the package separately"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ViteExecutableNotFoundError(LitestarViteError):
|
|
36
|
+
"""Raised when the vite executable is not found."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, executable: str) -> None:
|
|
39
|
+
super().__init__(f"Executable {executable!r} not found.")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ViteExecutionError(LitestarViteError):
|
|
43
|
+
"""Raised when the vite execution fails."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, command: list[str], return_code: int, stderr: str) -> None:
|
|
46
|
+
super().__init__(f"Command {command!r} failed with return code {return_code}.\nStderr: {stderr}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ManifestNotFoundError(LitestarViteError):
|
|
50
|
+
"""Raised when the manifest file is not found."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, manifest_path: str) -> None:
|
|
53
|
+
super().__init__(f"Vite manifest file not found at {manifest_path!r}. Did you forget to build your assets?")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ViteProcessError(LitestarViteError):
|
|
57
|
+
"""Raised when the Vite process fails to start or stop."""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
message: str,
|
|
62
|
+
command: list[str] | None = None,
|
|
63
|
+
exit_code: int | None = None,
|
|
64
|
+
stderr: str | None = None,
|
|
65
|
+
stdout: str | None = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
super().__init__(message)
|
|
68
|
+
self.command = command
|
|
69
|
+
self.exit_code = exit_code
|
|
70
|
+
self.stderr = stderr
|
|
71
|
+
self.stdout = stdout
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class AssetNotFoundError(LitestarViteError):
|
|
75
|
+
"""Raised when an asset is not found in the manifest."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, file_path: str, manifest_path: str) -> None:
|
|
78
|
+
super().__init__(f"Asset {file_path!r} not found in manifest at {manifest_path!r}.")
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""JavaScript runtime executors for Vite commands.
|
|
2
|
+
|
|
3
|
+
This module provides executor classes for different JavaScript runtimes
|
|
4
|
+
(Node.js/npm, Bun, Deno, Yarn, pnpm) to run Vite commands.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from importlib.util import find_spec
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, ClassVar, Protocol, runtime_checkable
|
|
16
|
+
|
|
17
|
+
from litestar.cli._utils import console
|
|
18
|
+
|
|
19
|
+
from litestar_vite.exceptions import ViteExecutableNotFoundError, ViteExecutionError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _windows_create_new_process_group_flag() -> int:
|
|
23
|
+
"""Return the Windows-only process creation flag for new process groups.
|
|
24
|
+
|
|
25
|
+
When available, ``subprocess.CREATE_NEW_PROCESS_GROUP`` is used as ``creationflags`` for long-lived dev servers.
|
|
26
|
+
This improves process lifecycle management on Windows (notably console signal / Ctrl+C behavior). On non-Windows
|
|
27
|
+
platforms the constant is not defined, so this returns ``0``.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The ``creationflags`` value to start a new process group on Windows, otherwise ``0``.
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
return subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore[attr-defined]
|
|
34
|
+
except AttributeError:
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
_create_new_process_group = _windows_create_new_process_group_flag()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _popen_server_kwargs(cwd: Path) -> dict[str, Any]:
|
|
42
|
+
"""Return Popen kwargs that keep server processes alive and grouped.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Keyword arguments for ``subprocess.Popen`` suitable for long-lived dev servers.
|
|
46
|
+
"""
|
|
47
|
+
kwargs: dict[str, Any] = {"cwd": cwd, "stdin": subprocess.PIPE, "stdout": None, "stderr": None}
|
|
48
|
+
if platform.system() == "Windows":
|
|
49
|
+
kwargs["shell"] = True
|
|
50
|
+
kwargs["creationflags"] = _create_new_process_group
|
|
51
|
+
else:
|
|
52
|
+
kwargs["shell"] = False
|
|
53
|
+
kwargs["start_new_session"] = True
|
|
54
|
+
return kwargs
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class JSExecutor(ABC):
|
|
58
|
+
"""Abstract base class for Javascript executors.
|
|
59
|
+
|
|
60
|
+
The default ``silent_flag`` matches npm-style CLIs (``--silent``). Executors that do not support a silent flag
|
|
61
|
+
(e.g., Deno) override it with an empty string.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
bin_name: ClassVar[str]
|
|
65
|
+
silent_flag: ClassVar[str] = "--silent"
|
|
66
|
+
|
|
67
|
+
def __init__(self, executable_path: "Path | str | None" = None, *, silent: bool = False) -> None:
|
|
68
|
+
self.executable_path = executable_path
|
|
69
|
+
self.silent = silent
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def install(self, cwd: Path) -> None:
|
|
73
|
+
"""Install dependencies."""
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def update(self, cwd: Path, *, latest: bool = False) -> None:
|
|
77
|
+
"""Update dependencies.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
cwd: The working directory.
|
|
81
|
+
latest: If True, update to latest versions (ignoring semver constraints where supported).
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def run(self, args: list[str], cwd: Path) -> "subprocess.Popen[Any]":
|
|
86
|
+
"""Run a command.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
The result.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
def execute(self, args: list[str], cwd: Path) -> None:
|
|
94
|
+
"""Execute a command and wait for it to finish."""
|
|
95
|
+
|
|
96
|
+
def _resolve_executable(self) -> str:
|
|
97
|
+
"""Return the executable path or raise if not found.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Path to the resolved executable.
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
ViteExecutableNotFoundError: If the binary cannot be located.
|
|
104
|
+
"""
|
|
105
|
+
if self.executable_path:
|
|
106
|
+
return str(self.executable_path)
|
|
107
|
+
path = shutil.which(self.bin_name)
|
|
108
|
+
if path is None:
|
|
109
|
+
raise ViteExecutableNotFoundError(self.bin_name)
|
|
110
|
+
return path
|
|
111
|
+
|
|
112
|
+
def _apply_silent_flag(self, args: list[str]) -> list[str]:
|
|
113
|
+
"""Apply silent flag to command args if silent mode is enabled.
|
|
114
|
+
|
|
115
|
+
The silent flag is inserted after 'run' in npm-style commands
|
|
116
|
+
(e.g., ['npm', 'run', 'dev'] -> ['npm', 'run', '--silent', 'dev']).
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
args: The command arguments.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Modified args with silent flag inserted if applicable.
|
|
123
|
+
"""
|
|
124
|
+
if not self.silent or not self.silent_flag:
|
|
125
|
+
return args
|
|
126
|
+
|
|
127
|
+
if args and args[0] == "run" and len(args) >= 2:
|
|
128
|
+
return [args[0], self.silent_flag, *args[1:]]
|
|
129
|
+
|
|
130
|
+
if "run" in args:
|
|
131
|
+
run_idx = args.index("run")
|
|
132
|
+
return [*args[: run_idx + 1], self.silent_flag, *args[run_idx + 1 :]]
|
|
133
|
+
|
|
134
|
+
return [*args, self.silent_flag]
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def start_command(self) -> list[str]:
|
|
138
|
+
"""Get the default command to start the dev server (e.g., npm run start).
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
The argv list used to start the dev server.
|
|
142
|
+
"""
|
|
143
|
+
return [self.bin_name, "run", "start"]
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def build_command(self) -> list[str]:
|
|
147
|
+
"""Get the default command to build for production (e.g., npm run build).
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
The argv list used to build production assets.
|
|
151
|
+
"""
|
|
152
|
+
return [self.bin_name, "run", "build"]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class CommandExecutor(JSExecutor):
|
|
156
|
+
"""Generic command executor."""
|
|
157
|
+
|
|
158
|
+
# Subclasses override to customize update behavior
|
|
159
|
+
update_command: ClassVar[str] = "update"
|
|
160
|
+
update_latest_flag: ClassVar[str] = "--latest"
|
|
161
|
+
|
|
162
|
+
def install(self, cwd: Path) -> None:
|
|
163
|
+
executable = self._resolve_executable()
|
|
164
|
+
command = [executable, "install"]
|
|
165
|
+
process = subprocess.run(command, cwd=cwd, shell=platform.system() == "Windows", check=False)
|
|
166
|
+
if process.returncode != 0:
|
|
167
|
+
raise ViteExecutionError(command, process.returncode, "package install failed")
|
|
168
|
+
|
|
169
|
+
def update(self, cwd: Path, *, latest: bool = False) -> None:
|
|
170
|
+
executable = self._resolve_executable()
|
|
171
|
+
command = [executable, self.update_command]
|
|
172
|
+
if latest and self.update_latest_flag:
|
|
173
|
+
command.append(self.update_latest_flag)
|
|
174
|
+
process = subprocess.run(command, cwd=cwd, shell=platform.system() == "Windows", check=False)
|
|
175
|
+
if process.returncode != 0:
|
|
176
|
+
raise ViteExecutionError(command, process.returncode, "package update failed")
|
|
177
|
+
|
|
178
|
+
def run(self, args: list[str], cwd: Path) -> "subprocess.Popen[Any]":
|
|
179
|
+
executable = self._resolve_executable()
|
|
180
|
+
args = self._apply_silent_flag(args)
|
|
181
|
+
command = args if args and Path(args[0]).name == Path(executable).name else [executable, *args]
|
|
182
|
+
return subprocess.Popen(command, **_popen_server_kwargs(cwd))
|
|
183
|
+
|
|
184
|
+
def execute(self, args: list[str], cwd: Path) -> None:
|
|
185
|
+
executable = self._resolve_executable()
|
|
186
|
+
args = self._apply_silent_flag(args)
|
|
187
|
+
command = args if args and Path(args[0]).name == Path(executable).name else [executable, *args]
|
|
188
|
+
process = subprocess.run(
|
|
189
|
+
command,
|
|
190
|
+
cwd=cwd,
|
|
191
|
+
shell=platform.system() == "Windows",
|
|
192
|
+
check=False,
|
|
193
|
+
stdin=subprocess.PIPE,
|
|
194
|
+
stdout=None,
|
|
195
|
+
stderr=subprocess.PIPE,
|
|
196
|
+
)
|
|
197
|
+
if process.returncode != 0:
|
|
198
|
+
stderr = process.stderr.decode() if process.stderr else ""
|
|
199
|
+
raise ViteExecutionError(command, process.returncode, stderr)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class NodeExecutor(CommandExecutor):
|
|
203
|
+
"""Node.js executor."""
|
|
204
|
+
|
|
205
|
+
bin_name = "npm"
|
|
206
|
+
# npm doesn't have --latest; use --save to update package.json
|
|
207
|
+
update_latest_flag: ClassVar[str] = "--save"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class BunExecutor(CommandExecutor):
|
|
211
|
+
"""Bun executor."""
|
|
212
|
+
|
|
213
|
+
bin_name = "bun"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class DenoExecutor(CommandExecutor):
|
|
217
|
+
"""Deno executor."""
|
|
218
|
+
|
|
219
|
+
bin_name = "deno"
|
|
220
|
+
silent_flag: ClassVar[str] = ""
|
|
221
|
+
update_latest_flag: ClassVar[str] = ""
|
|
222
|
+
|
|
223
|
+
def install(self, cwd: Path) -> None:
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
def update(self, cwd: Path, *, latest: bool = False) -> None:
|
|
227
|
+
"""Deno doesn't have traditional package management."""
|
|
228
|
+
del cwd, latest # unused
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class YarnExecutor(CommandExecutor):
|
|
232
|
+
"""Yarn executor."""
|
|
233
|
+
|
|
234
|
+
bin_name = "yarn"
|
|
235
|
+
# yarn uses "upgrade" command (not "update")
|
|
236
|
+
update_command: ClassVar[str] = "upgrade"
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class PnpmExecutor(CommandExecutor):
|
|
240
|
+
"""PNPM executor."""
|
|
241
|
+
|
|
242
|
+
bin_name = "pnpm"
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class NodeenvExecutor(JSExecutor):
|
|
246
|
+
"""Nodeenv executor.
|
|
247
|
+
|
|
248
|
+
This executor detects and uses nodeenv in a Python virtual environment.
|
|
249
|
+
It installs nodeenv if not present and uses the npm from within the virtualenv.
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
bin_name = "nodeenv"
|
|
253
|
+
|
|
254
|
+
@runtime_checkable
|
|
255
|
+
class _SupportsDetectNodeenv(Protocol):
|
|
256
|
+
detect_nodeenv: bool
|
|
257
|
+
|
|
258
|
+
def __init__(self, config: Any = None, *, silent: bool = False) -> None:
|
|
259
|
+
"""Initialize NodeenvExecutor.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
config: Optional ViteConfig for detecting nodeenv. Can be the new
|
|
263
|
+
ViteConfig or legacy config. Only used to check detect_nodeenv.
|
|
264
|
+
silent: Whether to suppress npm output with --silent flag.
|
|
265
|
+
"""
|
|
266
|
+
super().__init__(None, silent=silent)
|
|
267
|
+
self.config = config
|
|
268
|
+
self._detect_nodeenv = bool(config.detect_nodeenv) if isinstance(config, self._SupportsDetectNodeenv) else False
|
|
269
|
+
|
|
270
|
+
def _get_nodeenv_command(self) -> str:
|
|
271
|
+
"""Return the nodeenv executable to run.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Absolute path to the nodeenv binary if present next to the Python
|
|
275
|
+
interpreter, otherwise the string ``"nodeenv"`` for PATH lookup.
|
|
276
|
+
"""
|
|
277
|
+
candidate = Path(sys.executable).with_name("nodeenv")
|
|
278
|
+
if candidate.exists():
|
|
279
|
+
return str(candidate)
|
|
280
|
+
return "nodeenv"
|
|
281
|
+
|
|
282
|
+
def install_nodeenv(self, cwd: Path) -> None:
|
|
283
|
+
"""Install nodeenv when available in the environment."""
|
|
284
|
+
if find_spec("nodeenv") is None:
|
|
285
|
+
console.print("[yellow]Nodeenv not found. Skipping installation.[/]")
|
|
286
|
+
return
|
|
287
|
+
|
|
288
|
+
install_dir = os.environ.get("VIRTUAL_ENV", sys.prefix)
|
|
289
|
+
console.rule("[yellow]Starting Nodeenv installation process[/]", align="left")
|
|
290
|
+
|
|
291
|
+
command = [self._get_nodeenv_command(), install_dir, "--force", "--quiet"]
|
|
292
|
+
subprocess.run(command, cwd=cwd, check=False)
|
|
293
|
+
|
|
294
|
+
def install(self, cwd: Path) -> None:
|
|
295
|
+
if self._detect_nodeenv:
|
|
296
|
+
self.install_nodeenv(cwd)
|
|
297
|
+
|
|
298
|
+
npm_path = self._find_npm_in_venv()
|
|
299
|
+
command = [npm_path, "install"]
|
|
300
|
+
subprocess.run(command, cwd=cwd, check=True)
|
|
301
|
+
|
|
302
|
+
def update(self, cwd: Path, *, latest: bool = False) -> None:
|
|
303
|
+
npm_path = self._find_npm_in_venv()
|
|
304
|
+
command = [npm_path, "update"]
|
|
305
|
+
if latest:
|
|
306
|
+
command.append("--save")
|
|
307
|
+
process = subprocess.run(command, cwd=cwd, shell=platform.system() == "Windows", check=False)
|
|
308
|
+
if process.returncode != 0:
|
|
309
|
+
raise ViteExecutionError(command, process.returncode, "package update failed")
|
|
310
|
+
|
|
311
|
+
def run(self, args: list[str], cwd: Path) -> "subprocess.Popen[Any]":
|
|
312
|
+
npm_path = self._find_npm_in_venv()
|
|
313
|
+
args = self._apply_silent_flag(args)
|
|
314
|
+
command = [npm_path, *args]
|
|
315
|
+
return subprocess.Popen(command, **_popen_server_kwargs(cwd))
|
|
316
|
+
|
|
317
|
+
def execute(self, args: list[str], cwd: Path) -> None:
|
|
318
|
+
npm_path = self._find_npm_in_venv()
|
|
319
|
+
args = self._apply_silent_flag(args)
|
|
320
|
+
command = [npm_path, *args]
|
|
321
|
+
process = subprocess.run(
|
|
322
|
+
command, cwd=cwd, shell=platform.system() == "Windows", check=False, capture_output=True
|
|
323
|
+
)
|
|
324
|
+
if process.returncode != 0:
|
|
325
|
+
raise ViteExecutionError(command, process.returncode, process.stderr.decode())
|
|
326
|
+
|
|
327
|
+
def _find_npm_in_venv(self) -> str:
|
|
328
|
+
"""Locate npm within the active virtual environment or fall back to PATH.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
Path to ``npm`` inside the virtual environment when available, or
|
|
332
|
+
``"npm"`` to defer to PATH resolution.
|
|
333
|
+
"""
|
|
334
|
+
venv_path = os.environ.get("VIRTUAL_ENV", sys.prefix)
|
|
335
|
+
bin_dir = "Scripts" if platform.system() == "Windows" else "bin"
|
|
336
|
+
npm_path = Path(venv_path) / bin_dir / "npm"
|
|
337
|
+
if platform.system() == "Windows":
|
|
338
|
+
npm_path = npm_path.with_suffix(".cmd")
|
|
339
|
+
|
|
340
|
+
if npm_path.exists():
|
|
341
|
+
return str(npm_path)
|
|
342
|
+
return "npm"
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def start_command(self) -> list[str]:
|
|
346
|
+
"""Get the default command to start the dev server using nodeenv npm.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
The argv list used to start the dev server.
|
|
350
|
+
"""
|
|
351
|
+
return [self._find_npm_in_venv(), "run", "start"]
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
def build_command(self) -> list[str]:
|
|
355
|
+
"""Get the default command to build for production using nodeenv npm.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
The argv list used to build production assets.
|
|
359
|
+
"""
|
|
360
|
+
return [self._find_npm_in_venv(), "run", "build"]
|