litestar-vite-plugin 0.15.0-alpha.3 → 0.15.0-alpha.5
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.
- package/README.md +56 -42
- package/dist/js/astro.d.ts +6 -0
- package/dist/js/astro.js +42 -7
- package/dist/js/helpers/htmx.d.ts +68 -0
- package/dist/js/helpers/htmx.js +494 -0
- package/dist/js/helpers/index.d.ts +13 -5
- package/dist/js/helpers/index.js +14 -6
- package/dist/js/index.d.ts +35 -13
- package/dist/js/index.js +233 -60
- package/dist/js/inertia-helpers/index.d.ts +6 -1
- package/dist/js/inertia-helpers/index.js +7 -4
- package/dist/js/litestar-meta.js +20 -4
- package/dist/js/nuxt.d.ts +6 -0
- package/dist/js/nuxt.js +39 -4
- package/dist/js/sveltekit.d.ts +6 -0
- package/dist/js/sveltekit.js +34 -4
- package/package.json +18 -11
- package/tools/clean.js +1 -1
- package/dist/js/helpers/routes.d.ts +0 -159
- package/dist/js/helpers/routes.js +0 -302
package/README.md
CHANGED
|
@@ -10,66 +10,83 @@ Litestar Vite connects the Litestar backend to a Vite toolchain. It supports SPA
|
|
|
10
10
|
- Type-safe frontends: optional OpenAPI/routes export + `@hey-api/openapi-ts` via the Vite plugin.
|
|
11
11
|
- Inertia support: v2 protocol with session middleware and optional SPA mode.
|
|
12
12
|
|
|
13
|
-
## Quick
|
|
13
|
+
## Quick Start (SPA)
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
pip install litestar-vite
|
|
17
|
-
litestar assets init --template react-tanstack
|
|
18
|
-
litestar assets install # installs frontend deps via configured executor
|
|
19
17
|
```
|
|
20
18
|
|
|
21
19
|
```python
|
|
20
|
+
import os
|
|
21
|
+
from pathlib import Path
|
|
22
22
|
from litestar import Litestar
|
|
23
|
-
from litestar_vite import VitePlugin, ViteConfig
|
|
23
|
+
from litestar_vite import VitePlugin, ViteConfig, PathConfig
|
|
24
|
+
|
|
25
|
+
DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in ("true", "1", "yes")
|
|
24
26
|
|
|
25
|
-
app = Litestar(
|
|
27
|
+
app = Litestar(
|
|
28
|
+
plugins=[VitePlugin(config=ViteConfig(
|
|
29
|
+
dev_mode=DEV_MODE,
|
|
30
|
+
paths=PathConfig(root=Path(__file__).parent),
|
|
31
|
+
))]
|
|
32
|
+
)
|
|
26
33
|
```
|
|
27
34
|
|
|
28
35
|
```bash
|
|
29
|
-
litestar run --reload #
|
|
36
|
+
litestar run --reload # Vite dev server is proxied automatically
|
|
30
37
|
```
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
Scaffold a frontend: `litestar assets init --template vue` (or `react`, `svelte`, `htmx`, `react-inertia`, `vue-inertia`, `angular`, `astro`, `nuxt`, `sveltekit`).
|
|
33
40
|
|
|
34
41
|
## Template / HTMX
|
|
35
42
|
|
|
36
43
|
```python
|
|
44
|
+
from pathlib import Path
|
|
37
45
|
from litestar import Litestar
|
|
38
46
|
from litestar.contrib.jinja import JinjaTemplateEngine
|
|
39
|
-
from litestar.template
|
|
40
|
-
from litestar_vite import VitePlugin, ViteConfig
|
|
47
|
+
from litestar.template import TemplateConfig
|
|
48
|
+
from litestar_vite import VitePlugin, ViteConfig, PathConfig
|
|
49
|
+
|
|
50
|
+
here = Path(__file__).parent
|
|
41
51
|
|
|
42
52
|
app = Litestar(
|
|
43
|
-
template_config=TemplateConfig(
|
|
44
|
-
plugins=[VitePlugin(config=ViteConfig(
|
|
53
|
+
template_config=TemplateConfig(directory=here / "templates", engine=JinjaTemplateEngine),
|
|
54
|
+
plugins=[VitePlugin(config=ViteConfig(
|
|
55
|
+
dev_mode=True,
|
|
56
|
+
paths=PathConfig(root=here),
|
|
57
|
+
))],
|
|
45
58
|
)
|
|
46
59
|
```
|
|
47
60
|
|
|
48
61
|
## Inertia (v2)
|
|
49
62
|
|
|
50
|
-
Requires session middleware.
|
|
63
|
+
Requires session middleware (32-char secret).
|
|
51
64
|
|
|
52
65
|
```python
|
|
66
|
+
import os
|
|
67
|
+
from pathlib import Path
|
|
53
68
|
from litestar import Litestar
|
|
54
69
|
from litestar.middleware.session.client_side import CookieBackendConfig
|
|
55
|
-
from litestar_vite import VitePlugin, ViteConfig
|
|
56
|
-
from litestar_vite.inertia import
|
|
57
|
-
from litestar_vite.inertia.config import InertiaConfig
|
|
70
|
+
from litestar_vite import VitePlugin, ViteConfig, PathConfig
|
|
71
|
+
from litestar_vite.inertia import InertiaConfig
|
|
58
72
|
|
|
59
|
-
|
|
73
|
+
here = Path(__file__).parent
|
|
74
|
+
SECRET_KEY = os.environ.get("SECRET_KEY", "development-only-secret-32-chars")
|
|
75
|
+
session = CookieBackendConfig(secret=SECRET_KEY.encode("utf-8"))
|
|
60
76
|
|
|
61
77
|
app = Litestar(
|
|
62
|
-
middleware=[
|
|
63
|
-
plugins=[
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
middleware=[session.middleware],
|
|
79
|
+
plugins=[VitePlugin(config=ViteConfig(
|
|
80
|
+
dev_mode=True,
|
|
81
|
+
paths=PathConfig(root=here),
|
|
82
|
+
inertia=InertiaConfig(root_template="index.html"),
|
|
83
|
+
))],
|
|
67
84
|
)
|
|
68
85
|
```
|
|
69
86
|
|
|
70
|
-
##
|
|
87
|
+
## Meta-frameworks (Astro, Nuxt, SvelteKit)
|
|
71
88
|
|
|
72
|
-
|
|
89
|
+
Use `proxy_mode="ssr"` to proxy non-API routes to the framework's dev server:
|
|
73
90
|
|
|
74
91
|
```python
|
|
75
92
|
import os
|
|
@@ -77,17 +94,15 @@ from pathlib import Path
|
|
|
77
94
|
from litestar import Litestar
|
|
78
95
|
from litestar_vite import VitePlugin, ViteConfig, PathConfig, RuntimeConfig
|
|
79
96
|
|
|
97
|
+
here = Path(__file__).parent
|
|
80
98
|
DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in ("true", "1", "yes")
|
|
81
99
|
|
|
82
100
|
app = Litestar(
|
|
83
101
|
plugins=[
|
|
84
102
|
VitePlugin(config=ViteConfig(
|
|
85
103
|
dev_mode=DEV_MODE,
|
|
86
|
-
paths=PathConfig(root=
|
|
87
|
-
runtime=RuntimeConfig(
|
|
88
|
-
proxy_mode="ssr" if DEV_MODE else None, # Proxy in dev, static in prod
|
|
89
|
-
spa_handler=not DEV_MODE, # Serve built files in production
|
|
90
|
-
),
|
|
104
|
+
paths=PathConfig(root=here),
|
|
105
|
+
runtime=RuntimeConfig(proxy_mode="ssr"),
|
|
91
106
|
))
|
|
92
107
|
],
|
|
93
108
|
)
|
|
@@ -99,29 +114,28 @@ app = Litestar(
|
|
|
99
114
|
|------|-------|----------|
|
|
100
115
|
| `vite` | - | SPAs - proxies Vite assets only (default) |
|
|
101
116
|
| `direct` | - | Two-port dev - expose Vite port directly |
|
|
102
|
-
| `proxy` | `ssr` |
|
|
117
|
+
| `proxy` | `ssr` | Meta-frameworks - proxies everything except API routes |
|
|
103
118
|
|
|
104
119
|
### Production Deployment
|
|
105
120
|
|
|
106
|
-
**
|
|
121
|
+
**Astro (static):** Astro generates static HTML by default. Build and serve with Litestar:
|
|
107
122
|
|
|
108
123
|
```bash
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# Run in production mode
|
|
124
|
+
litestar --app-dir examples/astro assets install
|
|
125
|
+
litestar --app-dir examples/astro assets build
|
|
113
126
|
VITE_DEV_MODE=false litestar --app-dir examples/astro run
|
|
114
127
|
```
|
|
115
128
|
|
|
116
|
-
|
|
129
|
+
**Nuxt/SvelteKit (SSR):** These run their own Node servers. Deploy as two services:
|
|
117
130
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
| SvelteKit | `build/` | `bundle_dir=Path("build")` |
|
|
131
|
+
```bash
|
|
132
|
+
# Terminal 1: SSR server
|
|
133
|
+
litestar --app-dir examples/nuxt assets build
|
|
134
|
+
litestar --app-dir examples/nuxt assets serve
|
|
123
135
|
|
|
124
|
-
|
|
136
|
+
# Terminal 2: Litestar API
|
|
137
|
+
VITE_DEV_MODE=false litestar --app-dir examples/nuxt run --port 8001
|
|
138
|
+
```
|
|
125
139
|
|
|
126
140
|
## Type generation
|
|
127
141
|
|
|
@@ -151,5 +165,5 @@ litestar assets generate-types # one-off or CI
|
|
|
151
165
|
## Links
|
|
152
166
|
|
|
153
167
|
- Docs: <https://litestar-org.github.io/litestar-vite/>
|
|
154
|
-
- Examples: `examples/` (
|
|
168
|
+
- Examples: `examples/` (react, vue, svelte, react-inertia, vue-inertia, astro, nuxt, sveltekit, htmx)
|
|
155
169
|
- Issues: <https://github.com/litestar-org/litestar-vite/issues/>
|
package/dist/js/astro.d.ts
CHANGED
|
@@ -125,6 +125,12 @@ export interface AstroTypesConfig {
|
|
|
125
125
|
* @default false
|
|
126
126
|
*/
|
|
127
127
|
generateZod?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Generate SDK client functions for API calls.
|
|
130
|
+
*
|
|
131
|
+
* @default true
|
|
132
|
+
*/
|
|
133
|
+
generateSdk?: boolean;
|
|
128
134
|
/**
|
|
129
135
|
* Debounce time in milliseconds for type regeneration.
|
|
130
136
|
*
|
package/dist/js/astro.js
CHANGED
|
@@ -40,6 +40,7 @@ function resolveConfig(config = {}) {
|
|
|
40
40
|
openapiPath: "openapi.json",
|
|
41
41
|
routesPath: "routes.json",
|
|
42
42
|
generateZod: false,
|
|
43
|
+
generateSdk: true,
|
|
43
44
|
debounce: 300
|
|
44
45
|
};
|
|
45
46
|
} else if (typeof config.types === "object" && config.types !== null) {
|
|
@@ -49,6 +50,7 @@ function resolveConfig(config = {}) {
|
|
|
49
50
|
openapiPath: config.types.openapiPath ?? "openapi.json",
|
|
50
51
|
routesPath: config.types.routesPath ?? "routes.json",
|
|
51
52
|
generateZod: config.types.generateZod ?? false,
|
|
53
|
+
generateSdk: config.types.generateSdk ?? true,
|
|
52
54
|
debounce: config.types.debounce ?? 300
|
|
53
55
|
};
|
|
54
56
|
}
|
|
@@ -68,6 +70,9 @@ function createProxyPlugin(config) {
|
|
|
68
70
|
config() {
|
|
69
71
|
return {
|
|
70
72
|
server: {
|
|
73
|
+
// Force IPv4 binding for consistency with Python proxy configuration
|
|
74
|
+
// Without this, Astro might bind to IPv6 localhost which the proxy can't reach
|
|
75
|
+
host: "127.0.0.1",
|
|
71
76
|
// Set the port from Python config/env to ensure Astro uses the expected port
|
|
72
77
|
// strictPort: true prevents Astro from auto-incrementing to a different port
|
|
73
78
|
...config.port !== void 0 ? {
|
|
@@ -227,12 +232,28 @@ function createTypeGenerationPlugin(typesConfig) {
|
|
|
227
232
|
return false;
|
|
228
233
|
}
|
|
229
234
|
console.log(colors.cyan("[litestar-astro]"), colors.dim("Generating TypeScript types..."));
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
235
|
+
const projectRoot = process.cwd();
|
|
236
|
+
const candidates = [path.resolve(projectRoot, "openapi-ts.config.ts"), path.resolve(projectRoot, "hey-api.config.ts"), path.resolve(projectRoot, ".hey-api.config.ts")];
|
|
237
|
+
const configPath = candidates.find((p) => fs.existsSync(p)) || null;
|
|
238
|
+
let args;
|
|
239
|
+
if (configPath) {
|
|
240
|
+
console.log(colors.cyan("[litestar-astro]"), colors.dim("Using config:"), configPath);
|
|
241
|
+
args = ["@hey-api/openapi-ts", "--file", configPath];
|
|
242
|
+
} else {
|
|
243
|
+
args = ["@hey-api/openapi-ts", "-i", typesConfig.openapiPath, "-o", typesConfig.output];
|
|
244
|
+
const plugins = ["@hey-api/typescript", "@hey-api/schemas"];
|
|
245
|
+
if (typesConfig.generateSdk) {
|
|
246
|
+
plugins.push("@hey-api/sdk", "@hey-api/client-fetch");
|
|
247
|
+
}
|
|
248
|
+
if (typesConfig.generateZod) {
|
|
249
|
+
plugins.push("zod");
|
|
250
|
+
}
|
|
251
|
+
if (plugins.length) {
|
|
252
|
+
args.push("--plugins", ...plugins);
|
|
253
|
+
}
|
|
233
254
|
}
|
|
234
255
|
await execAsync(`npx ${args.join(" ")}`, {
|
|
235
|
-
cwd:
|
|
256
|
+
cwd: projectRoot
|
|
236
257
|
});
|
|
237
258
|
const routesPath = path.resolve(process.cwd(), typesConfig.routesPath);
|
|
238
259
|
if (fs.existsSync(routesPath)) {
|
|
@@ -271,6 +292,14 @@ function createTypeGenerationPlugin(typesConfig) {
|
|
|
271
292
|
server = devServer;
|
|
272
293
|
console.log(colors.cyan("[litestar-astro]"), colors.dim("Watching for schema changes:"), colors.yellow(typesConfig.openapiPath));
|
|
273
294
|
},
|
|
295
|
+
async buildStart() {
|
|
296
|
+
if (typesConfig.enabled) {
|
|
297
|
+
const openapiPath = path.resolve(process.cwd(), typesConfig.openapiPath);
|
|
298
|
+
if (fs.existsSync(openapiPath)) {
|
|
299
|
+
await runTypeGeneration();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
},
|
|
274
303
|
handleHotUpdate({ file }) {
|
|
275
304
|
if (!typesConfig.enabled) {
|
|
276
305
|
return;
|
|
@@ -311,12 +340,18 @@ function litestarAstro(userConfig = {}) {
|
|
|
311
340
|
plugins
|
|
312
341
|
}
|
|
313
342
|
};
|
|
314
|
-
if (command === "dev"
|
|
343
|
+
if (command === "dev") {
|
|
315
344
|
configUpdate.server = {
|
|
316
|
-
|
|
345
|
+
// Force IPv4 binding for consistency with Python proxy configuration
|
|
346
|
+
host: "127.0.0.1",
|
|
347
|
+
// Set port from Python config/env if provided
|
|
348
|
+
...config.port !== void 0 ? { port: config.port } : {}
|
|
317
349
|
};
|
|
318
350
|
if (config.verbose) {
|
|
319
|
-
logger.info(
|
|
351
|
+
logger.info("Setting Astro server host to 127.0.0.1");
|
|
352
|
+
if (config.port !== void 0) {
|
|
353
|
+
logger.info(`Setting Astro server port to ${config.port}`);
|
|
354
|
+
}
|
|
320
355
|
}
|
|
321
356
|
}
|
|
322
357
|
updateConfig(configUpdate);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Litestar HTMX Extension
|
|
3
|
+
*
|
|
4
|
+
* Lightweight JSON templating for HTMX with CSRF support.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - `hx-swap="json"` - Client-side JSON templating
|
|
8
|
+
* - Automatic CSRF token injection
|
|
9
|
+
* - Template syntax: `ls-for`, `ls-if`, `:attr`, `@event`, `${expr}`
|
|
10
|
+
*
|
|
11
|
+
* For typed routes, import from your generated routes file:
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { route } from '@/generated/routes'
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```html
|
|
18
|
+
* <div hx-get="/api/books" hx-swap="json" hx-ext="litestar">
|
|
19
|
+
* <template ls-for="book in $data">
|
|
20
|
+
* <article :id="`book-${book.id}`">
|
|
21
|
+
* <h3>${book.title}</h3>
|
|
22
|
+
* <p>${book.author} • ${book.year}</p>
|
|
23
|
+
* </article>
|
|
24
|
+
* </template>
|
|
25
|
+
* </div>
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
/** Type for route function - matches generated routes.ts */
|
|
31
|
+
type RouteFn = (name: string, params?: Record<string, string | number>) => string;
|
|
32
|
+
declare global {
|
|
33
|
+
interface Window {
|
|
34
|
+
htmx?: HtmxApi;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
interface HtmxApi {
|
|
38
|
+
defineExtension: (name: string, ext: HtmxExtension) => void;
|
|
39
|
+
process: (elt: Element) => void;
|
|
40
|
+
}
|
|
41
|
+
interface HtmxExtension {
|
|
42
|
+
init?: () => void;
|
|
43
|
+
onEvent?: (name: string, evt: CustomEvent) => boolean | void;
|
|
44
|
+
transformResponse?: (text: string, xhr: XMLHttpRequest, elt: Element) => string;
|
|
45
|
+
isInlineSwap?: (swapStyle: string) => boolean;
|
|
46
|
+
handleSwap?: (swapStyle: string, target: Element, fragment: DocumentFragment | Element) => Element[];
|
|
47
|
+
}
|
|
48
|
+
/** Template context - inherits from data via prototype for direct access */
|
|
49
|
+
interface Ctx {
|
|
50
|
+
$data: unknown;
|
|
51
|
+
$parent?: Ctx;
|
|
52
|
+
$index?: number;
|
|
53
|
+
$key?: string;
|
|
54
|
+
$event?: Event;
|
|
55
|
+
route?: RouteFn;
|
|
56
|
+
navigate?: (name: string, params?: Record<string, string | number>) => void;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
export declare function registerHtmxExtension(): void;
|
|
60
|
+
export declare function swapJson(el: Element, data: unknown): void;
|
|
61
|
+
type Handler = (ctx: Ctx, el: Element) => Ctx | false | void;
|
|
62
|
+
interface Dir {
|
|
63
|
+
match: (a: Attr) => boolean;
|
|
64
|
+
create: (el: Element, a: Attr) => Handler | null;
|
|
65
|
+
}
|
|
66
|
+
export declare function setDebug(_on: boolean): void;
|
|
67
|
+
export declare function addDirective(dir: Dir): void;
|
|
68
|
+
export {};
|