litestar-vite-plugin 0.15.0-alpha.2 → 0.15.0-alpha.4
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 +91 -20
- package/dist/js/astro.d.ts +85 -23
- package/dist/js/astro.js +300 -9
- package/dist/js/helpers/index.d.ts +2 -2
- package/dist/js/helpers/index.js +2 -2
- package/dist/js/helpers/routes.d.ts +19 -0
- package/dist/js/helpers/routes.js +59 -37
- package/dist/js/index.d.ts +12 -5
- package/dist/js/index.js +40 -27
- package/dist/js/inertia-helpers/{inertia-helpers/index.d.ts → index.d.ts} +12 -1
- package/dist/js/inertia-helpers/index.js +91 -0
- package/dist/js/install-hint.d.ts +9 -0
- package/dist/js/install-hint.js +17 -1
- package/dist/js/nuxt.d.ts +29 -22
- package/dist/js/nuxt.js +269 -45
- package/dist/js/shared/debounce.d.ts +27 -0
- package/dist/js/shared/debounce.js +15 -0
- package/dist/js/sveltekit.d.ts +7 -0
- package/dist/js/sveltekit.js +214 -23
- package/package.json +14 -6
- package/tools/clean.js +1 -1
- package/dist/js/inertia-helpers/helpers/csrf.d.ts +0 -76
- package/dist/js/inertia-helpers/helpers/csrf.js +0 -114
- package/dist/js/inertia-helpers/helpers/index.d.ts +0 -24
- package/dist/js/inertia-helpers/helpers/index.js +0 -26
- package/dist/js/inertia-helpers/helpers/routes.d.ts +0 -140
- package/dist/js/inertia-helpers/helpers/routes.js +0 -280
- package/dist/js/inertia-helpers/inertia-helpers/index.js +0 -47
package/README.md
CHANGED
|
@@ -5,67 +5,138 @@ Litestar Vite connects the Litestar backend to a Vite toolchain. It supports SPA
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- One-port dev: proxies Vite HTTP + WS/HMR through Litestar by default; switch to two-port with `VITE_PROXY_MODE=direct`.
|
|
8
|
+
- SSR framework support: use `proxy_mode="ssr"` for Astro, Nuxt, SvelteKit - proxies everything except your API routes.
|
|
8
9
|
- Production assets: reads Vite manifest from `public/manifest.json` (configurable) and serves under `asset_url`.
|
|
9
10
|
- Type-safe frontends: optional OpenAPI/routes export + `@hey-api/openapi-ts` via the Vite plugin.
|
|
10
11
|
- Inertia support: v2 protocol with session middleware and optional SPA mode.
|
|
11
12
|
|
|
12
|
-
## Quick
|
|
13
|
+
## Quick Start (SPA)
|
|
13
14
|
|
|
14
15
|
```bash
|
|
15
16
|
pip install litestar-vite
|
|
16
|
-
litestar assets init --template react-tanstack
|
|
17
|
-
litestar assets install # installs frontend deps via configured executor
|
|
18
17
|
```
|
|
19
18
|
|
|
20
19
|
```python
|
|
20
|
+
import os
|
|
21
|
+
from pathlib import Path
|
|
21
22
|
from litestar import Litestar
|
|
22
|
-
from litestar_vite import VitePlugin, ViteConfig
|
|
23
|
+
from litestar_vite import VitePlugin, ViteConfig, PathConfig
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in ("true", "1", "yes")
|
|
26
|
+
|
|
27
|
+
app = Litestar(
|
|
28
|
+
plugins=[VitePlugin(config=ViteConfig(
|
|
29
|
+
dev_mode=DEV_MODE,
|
|
30
|
+
paths=PathConfig(root=Path(__file__).parent),
|
|
31
|
+
))]
|
|
32
|
+
)
|
|
25
33
|
```
|
|
26
34
|
|
|
27
35
|
```bash
|
|
28
|
-
litestar run --reload #
|
|
36
|
+
litestar run --reload # Vite dev server is proxied automatically
|
|
29
37
|
```
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
Scaffold a frontend: `litestar assets init --template vue` (or `react`, `svelte`, `htmx`, `react-inertia`, `vue-inertia`, `angular`, `astro`, `nuxt`, `sveltekit`).
|
|
32
40
|
|
|
33
41
|
## Template / HTMX
|
|
34
42
|
|
|
35
43
|
```python
|
|
44
|
+
from pathlib import Path
|
|
36
45
|
from litestar import Litestar
|
|
37
46
|
from litestar.contrib.jinja import JinjaTemplateEngine
|
|
38
|
-
from litestar.template
|
|
39
|
-
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
|
|
40
51
|
|
|
41
52
|
app = Litestar(
|
|
42
|
-
template_config=TemplateConfig(
|
|
43
|
-
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
|
+
))],
|
|
44
58
|
)
|
|
45
59
|
```
|
|
46
60
|
|
|
47
61
|
## Inertia (v2)
|
|
48
62
|
|
|
49
|
-
Requires session middleware.
|
|
63
|
+
Requires session middleware (32-char secret).
|
|
50
64
|
|
|
51
65
|
```python
|
|
66
|
+
import os
|
|
67
|
+
from pathlib import Path
|
|
52
68
|
from litestar import Litestar
|
|
53
69
|
from litestar.middleware.session.client_side import CookieBackendConfig
|
|
54
|
-
from litestar_vite import VitePlugin, ViteConfig
|
|
55
|
-
from litestar_vite.inertia import
|
|
56
|
-
|
|
70
|
+
from litestar_vite import VitePlugin, ViteConfig, PathConfig
|
|
71
|
+
from litestar_vite.inertia import InertiaConfig
|
|
72
|
+
|
|
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"))
|
|
76
|
+
|
|
77
|
+
app = Litestar(
|
|
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
|
+
))],
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Meta-frameworks (Astro, Nuxt, SvelteKit)
|
|
88
|
+
|
|
89
|
+
Use `proxy_mode="ssr"` to proxy non-API routes to the framework's dev server:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import os
|
|
93
|
+
from pathlib import Path
|
|
94
|
+
from litestar import Litestar
|
|
95
|
+
from litestar_vite import VitePlugin, ViteConfig, PathConfig, RuntimeConfig
|
|
57
96
|
|
|
58
|
-
|
|
97
|
+
here = Path(__file__).parent
|
|
98
|
+
DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in ("true", "1", "yes")
|
|
59
99
|
|
|
60
100
|
app = Litestar(
|
|
61
|
-
middleware=[session_backend.middleware],
|
|
62
101
|
plugins=[
|
|
63
|
-
VitePlugin(config=ViteConfig(
|
|
64
|
-
|
|
102
|
+
VitePlugin(config=ViteConfig(
|
|
103
|
+
dev_mode=DEV_MODE,
|
|
104
|
+
paths=PathConfig(root=here),
|
|
105
|
+
runtime=RuntimeConfig(proxy_mode="ssr"),
|
|
106
|
+
))
|
|
65
107
|
],
|
|
66
108
|
)
|
|
67
109
|
```
|
|
68
110
|
|
|
111
|
+
### Proxy Modes
|
|
112
|
+
|
|
113
|
+
| Mode | Alias | Use Case |
|
|
114
|
+
|------|-------|----------|
|
|
115
|
+
| `vite` | - | SPAs - proxies Vite assets only (default) |
|
|
116
|
+
| `direct` | - | Two-port dev - expose Vite port directly |
|
|
117
|
+
| `proxy` | `ssr` | Meta-frameworks - proxies everything except API routes |
|
|
118
|
+
|
|
119
|
+
### Production Deployment
|
|
120
|
+
|
|
121
|
+
**Astro (static):** Astro generates static HTML by default. Build and serve with Litestar:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
litestar --app-dir examples/astro assets install
|
|
125
|
+
litestar --app-dir examples/astro assets build
|
|
126
|
+
VITE_DEV_MODE=false litestar --app-dir examples/astro run
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Nuxt/SvelteKit (SSR):** These run their own Node servers. Deploy as two services:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Terminal 1: SSR server
|
|
133
|
+
litestar --app-dir examples/nuxt assets build
|
|
134
|
+
litestar --app-dir examples/nuxt assets serve
|
|
135
|
+
|
|
136
|
+
# Terminal 2: Litestar API
|
|
137
|
+
VITE_DEV_MODE=false litestar --app-dir examples/nuxt run --port 8001
|
|
138
|
+
```
|
|
139
|
+
|
|
69
140
|
## Type generation
|
|
70
141
|
|
|
71
142
|
```python
|
|
@@ -94,5 +165,5 @@ litestar assets generate-types # one-off or CI
|
|
|
94
165
|
## Links
|
|
95
166
|
|
|
96
167
|
- Docs: <https://litestar-org.github.io/litestar-vite/>
|
|
97
|
-
- Examples: `examples/` (
|
|
168
|
+
- Examples: `examples/` (react, vue, svelte, react-inertia, vue-inertia, astro, nuxt, sveltekit, htmx)
|
|
98
169
|
- Issues: <https://github.com/litestar-org/litestar-vite/issues/>
|
package/dist/js/astro.d.ts
CHANGED
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
* integrations: [
|
|
18
18
|
* litestar({
|
|
19
19
|
* apiProxy: 'http://localhost:8000',
|
|
20
|
-
*
|
|
20
|
+
* types: {
|
|
21
|
+
* enabled: true,
|
|
22
|
+
* output: 'src/generated/api',
|
|
23
|
+
* },
|
|
21
24
|
* }),
|
|
22
25
|
* ],
|
|
23
26
|
* });
|
|
@@ -31,6 +34,31 @@ import type { Plugin, ViteDevServer } from "vite";
|
|
|
31
34
|
* This is a minimal type definition to avoid requiring astro as a dependency.
|
|
32
35
|
* When using this integration, Astro will be available in the project.
|
|
33
36
|
*/
|
|
37
|
+
/**
|
|
38
|
+
* Astro config for updateConfig - partial types we support.
|
|
39
|
+
*/
|
|
40
|
+
interface AstroConfigPartial {
|
|
41
|
+
server?: {
|
|
42
|
+
port?: number;
|
|
43
|
+
host?: string | boolean;
|
|
44
|
+
};
|
|
45
|
+
vite?: {
|
|
46
|
+
plugins?: Plugin[];
|
|
47
|
+
server?: {
|
|
48
|
+
port?: number;
|
|
49
|
+
strictPort?: boolean;
|
|
50
|
+
proxy?: Record<string, unknown>;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* AddressInfo from Node.js net module.
|
|
56
|
+
*/
|
|
57
|
+
interface AddressInfo {
|
|
58
|
+
address: string;
|
|
59
|
+
family: string;
|
|
60
|
+
port: number;
|
|
61
|
+
}
|
|
34
62
|
interface AstroIntegration {
|
|
35
63
|
name: string;
|
|
36
64
|
hooks: {
|
|
@@ -38,17 +66,17 @@ interface AstroIntegration {
|
|
|
38
66
|
config: unknown;
|
|
39
67
|
command: "dev" | "build" | "preview" | "sync";
|
|
40
68
|
isRestart: boolean;
|
|
41
|
-
updateConfig: (newConfig:
|
|
42
|
-
vite?: {
|
|
43
|
-
plugins?: Plugin[];
|
|
44
|
-
};
|
|
45
|
-
}) => unknown;
|
|
69
|
+
updateConfig: (newConfig: AstroConfigPartial) => unknown;
|
|
46
70
|
logger: AstroIntegrationLogger;
|
|
47
71
|
}) => void | Promise<void>;
|
|
48
72
|
"astro:server:setup"?: (options: {
|
|
49
73
|
server: ViteDevServer;
|
|
50
74
|
logger: AstroIntegrationLogger;
|
|
51
75
|
}) => void | Promise<void>;
|
|
76
|
+
"astro:server:start"?: (options: {
|
|
77
|
+
address: AddressInfo;
|
|
78
|
+
logger: AstroIntegrationLogger;
|
|
79
|
+
}) => void | Promise<void>;
|
|
52
80
|
"astro:build:start"?: (options: {
|
|
53
81
|
logger: AstroIntegrationLogger;
|
|
54
82
|
}) => void | Promise<void>;
|
|
@@ -62,6 +90,48 @@ interface AstroIntegrationLogger {
|
|
|
62
90
|
warn: (message: string) => void;
|
|
63
91
|
error: (message: string) => void;
|
|
64
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Configuration for TypeScript type generation in Astro.
|
|
95
|
+
*/
|
|
96
|
+
export interface AstroTypesConfig {
|
|
97
|
+
/**
|
|
98
|
+
* Enable type generation.
|
|
99
|
+
*
|
|
100
|
+
* @default false
|
|
101
|
+
*/
|
|
102
|
+
enabled?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Path to output generated TypeScript types.
|
|
105
|
+
* Relative to the Astro project root.
|
|
106
|
+
*
|
|
107
|
+
* @default 'src/types/api'
|
|
108
|
+
*/
|
|
109
|
+
output?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Path where the OpenAPI schema is exported by Litestar.
|
|
112
|
+
*
|
|
113
|
+
* @default 'openapi.json'
|
|
114
|
+
*/
|
|
115
|
+
openapiPath?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Path where route metadata is exported by Litestar.
|
|
118
|
+
*
|
|
119
|
+
* @default 'routes.json'
|
|
120
|
+
*/
|
|
121
|
+
routesPath?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Generate Zod schemas in addition to TypeScript types.
|
|
124
|
+
*
|
|
125
|
+
* @default false
|
|
126
|
+
*/
|
|
127
|
+
generateZod?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Debounce time in milliseconds for type regeneration.
|
|
130
|
+
*
|
|
131
|
+
* @default 300
|
|
132
|
+
*/
|
|
133
|
+
debounce?: number;
|
|
134
|
+
}
|
|
65
135
|
/**
|
|
66
136
|
* Configuration options for the Litestar Astro integration.
|
|
67
137
|
*/
|
|
@@ -82,25 +152,14 @@ export interface LitestarAstroConfig {
|
|
|
82
152
|
*/
|
|
83
153
|
apiPrefix?: string;
|
|
84
154
|
/**
|
|
85
|
-
*
|
|
86
|
-
* This should match the output path configured in your Litestar ViteConfig.
|
|
155
|
+
* Enable and configure TypeScript type generation.
|
|
87
156
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*/
|
|
91
|
-
typesPath?: string;
|
|
92
|
-
/**
|
|
93
|
-
* Path to the OpenAPI schema file exported by Litestar.
|
|
157
|
+
* When set to `true`, enables type generation with default settings.
|
|
158
|
+
* When set to an AstroTypesConfig object, enables type generation with custom settings.
|
|
94
159
|
*
|
|
95
|
-
* @default
|
|
96
|
-
*/
|
|
97
|
-
openapiPath?: string;
|
|
98
|
-
/**
|
|
99
|
-
* Path to the routes metadata file exported by Litestar.
|
|
100
|
-
*
|
|
101
|
-
* @default 'routes.json'
|
|
160
|
+
* @default false
|
|
102
161
|
*/
|
|
103
|
-
|
|
162
|
+
types?: boolean | AstroTypesConfig;
|
|
104
163
|
/**
|
|
105
164
|
* Enable verbose logging for debugging.
|
|
106
165
|
*
|
|
@@ -128,7 +187,10 @@ export interface LitestarAstroConfig {
|
|
|
128
187
|
* litestar({
|
|
129
188
|
* apiProxy: 'http://localhost:8000',
|
|
130
189
|
* apiPrefix: '/api',
|
|
131
|
-
*
|
|
190
|
+
* types: {
|
|
191
|
+
* enabled: true,
|
|
192
|
+
* output: 'src/generated/api',
|
|
193
|
+
* },
|
|
132
194
|
* }),
|
|
133
195
|
* ],
|
|
134
196
|
* });
|
package/dist/js/astro.js
CHANGED
|
@@ -1,11 +1,65 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import colors from "picocolors";
|
|
6
|
+
import { resolveInstallHint } from "./install-hint.js";
|
|
7
|
+
import { debounce } from "./shared/debounce.js";
|
|
8
|
+
const execAsync = promisify(exec);
|
|
1
9
|
function resolveConfig(config = {}) {
|
|
10
|
+
const runtimeConfigPath = process.env.LITESTAR_VITE_CONFIG_PATH;
|
|
11
|
+
let hotFile;
|
|
12
|
+
let proxyMode = "vite";
|
|
13
|
+
let port;
|
|
14
|
+
const envPort = process.env.VITE_PORT;
|
|
15
|
+
if (envPort) {
|
|
16
|
+
port = Number.parseInt(envPort, 10);
|
|
17
|
+
if (Number.isNaN(port)) {
|
|
18
|
+
port = void 0;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (runtimeConfigPath && fs.existsSync(runtimeConfigPath)) {
|
|
22
|
+
try {
|
|
23
|
+
const json = JSON.parse(fs.readFileSync(runtimeConfigPath, "utf-8"));
|
|
24
|
+
const bundleDir = json.bundleDir ?? "public";
|
|
25
|
+
const hot = json.hotFile ?? "hot";
|
|
26
|
+
hotFile = path.resolve(process.cwd(), bundleDir, hot);
|
|
27
|
+
proxyMode = json.proxyMode ?? "vite";
|
|
28
|
+
if (json.port !== void 0) {
|
|
29
|
+
port = json.port;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
hotFile = void 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
let typesConfig = false;
|
|
36
|
+
if (config.types === true) {
|
|
37
|
+
typesConfig = {
|
|
38
|
+
enabled: true,
|
|
39
|
+
output: "src/types/api",
|
|
40
|
+
openapiPath: "openapi.json",
|
|
41
|
+
routesPath: "routes.json",
|
|
42
|
+
generateZod: false,
|
|
43
|
+
debounce: 300
|
|
44
|
+
};
|
|
45
|
+
} else if (typeof config.types === "object" && config.types !== null) {
|
|
46
|
+
typesConfig = {
|
|
47
|
+
enabled: config.types.enabled ?? true,
|
|
48
|
+
output: config.types.output ?? "src/types/api",
|
|
49
|
+
openapiPath: config.types.openapiPath ?? "openapi.json",
|
|
50
|
+
routesPath: config.types.routesPath ?? "routes.json",
|
|
51
|
+
generateZod: config.types.generateZod ?? false,
|
|
52
|
+
debounce: config.types.debounce ?? 300
|
|
53
|
+
};
|
|
54
|
+
}
|
|
2
55
|
return {
|
|
3
56
|
apiProxy: config.apiProxy ?? "http://localhost:8000",
|
|
4
57
|
apiPrefix: config.apiPrefix ?? "/api",
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
58
|
+
types: typesConfig,
|
|
59
|
+
verbose: config.verbose ?? false,
|
|
60
|
+
hotFile,
|
|
61
|
+
proxyMode,
|
|
62
|
+
port
|
|
9
63
|
};
|
|
10
64
|
}
|
|
11
65
|
function createProxyPlugin(config) {
|
|
@@ -14,6 +68,12 @@ function createProxyPlugin(config) {
|
|
|
14
68
|
config() {
|
|
15
69
|
return {
|
|
16
70
|
server: {
|
|
71
|
+
// Set the port from Python config/env to ensure Astro uses the expected port
|
|
72
|
+
// strictPort: true prevents Astro from auto-incrementing to a different port
|
|
73
|
+
...config.port !== void 0 ? {
|
|
74
|
+
port: config.port,
|
|
75
|
+
strictPort: true
|
|
76
|
+
} : {},
|
|
17
77
|
proxy: {
|
|
18
78
|
[config.apiPrefix]: {
|
|
19
79
|
target: config.apiProxy,
|
|
@@ -26,23 +86,240 @@ function createProxyPlugin(config) {
|
|
|
26
86
|
}
|
|
27
87
|
};
|
|
28
88
|
}
|
|
89
|
+
async function emitRouteTypes(routesPath, outputDir) {
|
|
90
|
+
const contents = await fs.promises.readFile(routesPath, "utf-8");
|
|
91
|
+
const json = JSON.parse(contents);
|
|
92
|
+
const outDir = path.resolve(process.cwd(), outputDir);
|
|
93
|
+
await fs.promises.mkdir(outDir, { recursive: true });
|
|
94
|
+
const outFile = path.join(outDir, "routes.ts");
|
|
95
|
+
const banner = `// AUTO-GENERATED by litestar-vite. Do not edit.
|
|
96
|
+
/* eslint-disable */
|
|
97
|
+
|
|
98
|
+
`;
|
|
99
|
+
const routesData = json.routes || json;
|
|
100
|
+
const routeNames = Object.keys(routesData);
|
|
101
|
+
const routeNameType = routeNames.length > 0 ? routeNames.map((n) => `"${n}"`).join(" | ") : "never";
|
|
102
|
+
const routeParamTypes = [];
|
|
103
|
+
for (const [name, data] of Object.entries(routesData)) {
|
|
104
|
+
const routeData = data;
|
|
105
|
+
if (routeData.parameters && routeData.parameters.length > 0) {
|
|
106
|
+
const params = routeData.parameters.map((p) => `${p}: string | number`).join("; ");
|
|
107
|
+
routeParamTypes.push(` "${name}": { ${params} }`);
|
|
108
|
+
} else {
|
|
109
|
+
routeParamTypes.push(` "${name}": Record<string, never>`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const body = `/**
|
|
113
|
+
* AUTO-GENERATED by litestar-vite.
|
|
114
|
+
*
|
|
115
|
+
* Exports:
|
|
116
|
+
* - routesMeta: full route metadata
|
|
117
|
+
* - routes: name -> uri map
|
|
118
|
+
* - serverRoutes: alias of routes for clarity in apps
|
|
119
|
+
* - route(): type-safe URL generator
|
|
120
|
+
* - hasRoute(): type guard
|
|
121
|
+
* - csrf helpers re-exported from litestar-vite-plugin/helpers
|
|
122
|
+
*
|
|
123
|
+
* @see https://litestar-vite.litestar.dev/
|
|
124
|
+
*/
|
|
125
|
+
export const routesMeta = ${JSON.stringify(json, null, 2)} as const
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Route name to URI mapping.
|
|
129
|
+
*/
|
|
130
|
+
export const routes = ${JSON.stringify(Object.fromEntries(Object.entries(routesData).map(([name, data]) => [name, data.uri])), null, 2)} as const
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Alias for server-injected route map (more descriptive for consumers).
|
|
134
|
+
*/
|
|
135
|
+
export const serverRoutes = routes
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* All available route names.
|
|
139
|
+
*/
|
|
140
|
+
export type RouteName = ${routeNameType}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Parameter types for each route.
|
|
144
|
+
*/
|
|
145
|
+
export interface RouteParams {
|
|
146
|
+
${routeParamTypes.join("\n")}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Generate a URL for a named route with type-safe parameters.
|
|
151
|
+
*
|
|
152
|
+
* @param name - The route name
|
|
153
|
+
* @param params - Route parameters (required if route has path parameters)
|
|
154
|
+
* @returns The generated URL
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* \`\`\`ts
|
|
158
|
+
* import { route } from '@/generated/routes'
|
|
159
|
+
*
|
|
160
|
+
* // Route without parameters
|
|
161
|
+
* route('home') // "/"
|
|
162
|
+
*
|
|
163
|
+
* // Route with parameters
|
|
164
|
+
* route('user:detail', { user_id: 123 }) // "/users/123"
|
|
165
|
+
* \`\`\`
|
|
166
|
+
*/
|
|
167
|
+
export function route<T extends RouteName>(
|
|
168
|
+
name: T,
|
|
169
|
+
...args: RouteParams[T] extends Record<string, never> ? [] : [params: RouteParams[T]]
|
|
170
|
+
): string {
|
|
171
|
+
let uri = routes[name] as string
|
|
172
|
+
const params = args[0] as Record<string, string | number> | undefined
|
|
173
|
+
|
|
174
|
+
if (params) {
|
|
175
|
+
for (const [key, value] of Object.entries(params)) {
|
|
176
|
+
// Handle both {param} and {param:type} syntax
|
|
177
|
+
uri = uri.replace(new RegExp(\`\\\\{\${key}(?::[^}]+)?\\\\}\`, "g"), String(value))
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return uri
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if a route name exists.
|
|
186
|
+
*/
|
|
187
|
+
export function hasRoute(name: string): name is RouteName {
|
|
188
|
+
return name in routes
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
declare global {
|
|
192
|
+
interface Window {
|
|
193
|
+
/**
|
|
194
|
+
* Fully-typed route metadata injected by Litestar.
|
|
195
|
+
*/
|
|
196
|
+
__LITESTAR_ROUTES__?: typeof routesMeta
|
|
197
|
+
/**
|
|
198
|
+
* Simple route map (name -> uri) for legacy consumers.
|
|
199
|
+
*/
|
|
200
|
+
routes?: typeof routes
|
|
201
|
+
serverRoutes?: typeof serverRoutes
|
|
202
|
+
}
|
|
203
|
+
// eslint-disable-next-line no-var
|
|
204
|
+
var routes: typeof routes | undefined
|
|
205
|
+
var serverRoutes: typeof serverRoutes | undefined
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Re-export helper functions from litestar-vite-plugin
|
|
209
|
+
// These work with the routes defined above
|
|
210
|
+
export { getCsrfToken, csrfHeaders, csrfFetch } from "litestar-vite-plugin/helpers"
|
|
211
|
+
`;
|
|
212
|
+
await fs.promises.writeFile(outFile, `${banner}${body}`, "utf-8");
|
|
213
|
+
}
|
|
214
|
+
function createTypeGenerationPlugin(typesConfig) {
|
|
215
|
+
let server = null;
|
|
216
|
+
let isGenerating = false;
|
|
217
|
+
async function runTypeGeneration() {
|
|
218
|
+
if (isGenerating) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
isGenerating = true;
|
|
222
|
+
const startTime = Date.now();
|
|
223
|
+
try {
|
|
224
|
+
const openapiPath = path.resolve(process.cwd(), typesConfig.openapiPath);
|
|
225
|
+
if (!fs.existsSync(openapiPath)) {
|
|
226
|
+
console.log(colors.cyan("[litestar-astro]"), colors.yellow("OpenAPI schema not found:"), typesConfig.openapiPath);
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
console.log(colors.cyan("[litestar-astro]"), colors.dim("Generating TypeScript types..."));
|
|
230
|
+
const args = ["@hey-api/openapi-ts", "-i", typesConfig.openapiPath, "-o", typesConfig.output];
|
|
231
|
+
if (typesConfig.generateZod) {
|
|
232
|
+
args.push("--plugins", "zod", "@hey-api/typescript");
|
|
233
|
+
}
|
|
234
|
+
await execAsync(`npx ${args.join(" ")}`, {
|
|
235
|
+
cwd: process.cwd()
|
|
236
|
+
});
|
|
237
|
+
const routesPath = path.resolve(process.cwd(), typesConfig.routesPath);
|
|
238
|
+
if (fs.existsSync(routesPath)) {
|
|
239
|
+
await emitRouteTypes(routesPath, typesConfig.output);
|
|
240
|
+
}
|
|
241
|
+
const duration = Date.now() - startTime;
|
|
242
|
+
console.log(colors.cyan("[litestar-astro]"), colors.green("Types generated"), colors.dim(`in ${duration}ms`));
|
|
243
|
+
if (server) {
|
|
244
|
+
server.ws.send({
|
|
245
|
+
type: "custom",
|
|
246
|
+
event: "litestar:types-updated",
|
|
247
|
+
data: {
|
|
248
|
+
output: typesConfig.output,
|
|
249
|
+
timestamp: Date.now()
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return true;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
256
|
+
if (message.includes("not found") || message.includes("ENOENT")) {
|
|
257
|
+
console.log(colors.cyan("[litestar-astro]"), colors.yellow("@hey-api/openapi-ts not installed"), "- run:", resolveInstallHint());
|
|
258
|
+
} else {
|
|
259
|
+
console.error(colors.cyan("[litestar-astro]"), colors.red("Type generation failed:"), message);
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
} finally {
|
|
263
|
+
isGenerating = false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const debouncedRunTypeGeneration = debounce(runTypeGeneration, typesConfig.debounce);
|
|
267
|
+
return {
|
|
268
|
+
name: "litestar-astro-types",
|
|
269
|
+
enforce: "pre",
|
|
270
|
+
configureServer(devServer) {
|
|
271
|
+
server = devServer;
|
|
272
|
+
console.log(colors.cyan("[litestar-astro]"), colors.dim("Watching for schema changes:"), colors.yellow(typesConfig.openapiPath));
|
|
273
|
+
},
|
|
274
|
+
handleHotUpdate({ file }) {
|
|
275
|
+
if (!typesConfig.enabled) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
279
|
+
const openapiPath = typesConfig.openapiPath.replace(/^\.\//, "");
|
|
280
|
+
const routesPath = typesConfig.routesPath.replace(/^\.\//, "");
|
|
281
|
+
if (relativePath === openapiPath || relativePath === routesPath || file.endsWith(openapiPath) || file.endsWith(routesPath)) {
|
|
282
|
+
console.log(colors.cyan("[litestar-astro]"), colors.dim("Schema changed:"), colors.yellow(relativePath));
|
|
283
|
+
debouncedRunTypeGeneration();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
29
288
|
function litestarAstro(userConfig = {}) {
|
|
30
289
|
const config = resolveConfig(userConfig);
|
|
31
290
|
return {
|
|
32
291
|
name: "litestar-vite",
|
|
33
292
|
hooks: {
|
|
34
|
-
"astro:config:setup": ({ updateConfig, logger }) => {
|
|
293
|
+
"astro:config:setup": ({ updateConfig, logger, command }) => {
|
|
35
294
|
if (config.verbose) {
|
|
36
295
|
logger.info("Configuring Litestar integration");
|
|
37
296
|
logger.info(` API Proxy: ${config.apiProxy}`);
|
|
38
297
|
logger.info(` API Prefix: ${config.apiPrefix}`);
|
|
39
|
-
|
|
298
|
+
if (config.types !== false) {
|
|
299
|
+
logger.info(` Types Output: ${config.types.output}`);
|
|
300
|
+
}
|
|
301
|
+
if (config.port !== void 0) {
|
|
302
|
+
logger.info(` Port: ${config.port}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const plugins = [createProxyPlugin(config)];
|
|
306
|
+
if (config.types !== false && config.types.enabled) {
|
|
307
|
+
plugins.push(createTypeGenerationPlugin(config.types));
|
|
40
308
|
}
|
|
41
|
-
|
|
309
|
+
const configUpdate = {
|
|
42
310
|
vite: {
|
|
43
|
-
plugins
|
|
311
|
+
plugins
|
|
44
312
|
}
|
|
45
|
-
}
|
|
313
|
+
};
|
|
314
|
+
if (command === "dev" && config.port !== void 0) {
|
|
315
|
+
configUpdate.server = {
|
|
316
|
+
port: config.port
|
|
317
|
+
};
|
|
318
|
+
if (config.verbose) {
|
|
319
|
+
logger.info(`Setting Astro server port to ${config.port}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
updateConfig(configUpdate);
|
|
46
323
|
logger.info(`Litestar integration configured - proxying ${config.apiPrefix}/* to ${config.apiProxy}`);
|
|
47
324
|
},
|
|
48
325
|
"astro:server:setup": ({ server, logger }) => {
|
|
@@ -58,6 +335,20 @@ function litestarAstro(userConfig = {}) {
|
|
|
58
335
|
});
|
|
59
336
|
}
|
|
60
337
|
},
|
|
338
|
+
// Write hotfile AFTER server starts listening (astro:server:start fires after listen())
|
|
339
|
+
// Always write hotfile - proxy mode needs it for dynamic target discovery
|
|
340
|
+
"astro:server:start": ({ address, logger }) => {
|
|
341
|
+
if (config.hotFile) {
|
|
342
|
+
const rawAddr = address.address;
|
|
343
|
+
const host = rawAddr === "::" || rawAddr === "::1" || rawAddr === "0.0.0.0" || rawAddr === "127.0.0.1" ? "localhost" : rawAddr;
|
|
344
|
+
const url = `http://${host}:${address.port}`;
|
|
345
|
+
fs.mkdirSync(path.dirname(config.hotFile), { recursive: true });
|
|
346
|
+
fs.writeFileSync(config.hotFile, url);
|
|
347
|
+
if (config.verbose) {
|
|
348
|
+
logger.info(`Hotfile written: ${config.hotFile} -> ${url}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
61
352
|
"astro:build:start": ({ logger }) => {
|
|
62
353
|
logger.info("Building with Litestar integration");
|
|
63
354
|
logger.info(` Make sure your Litestar backend is accessible at: ${config.apiProxy}`);
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
*
|
|
21
21
|
* @module
|
|
22
22
|
*/
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
23
|
+
export { csrfFetch, csrfHeaders, getCsrfToken } from "./csrf.js";
|
|
24
|
+
export { currentRoute, getRelativeUrlPath, getRoutes, isCurrentRoute, isRoute, LITESTAR, type LitestarHelpers, type RouteDefinition, type RoutesMap, route, toRoute, } from "./routes.js";
|
package/dist/js/helpers/index.js
CHANGED
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
* @module
|
|
22
22
|
*/
|
|
23
23
|
// CSRF utilities
|
|
24
|
-
export {
|
|
24
|
+
export { csrfFetch, csrfHeaders, getCsrfToken } from "./csrf.js";
|
|
25
25
|
// Route utilities
|
|
26
|
-
export {
|
|
26
|
+
export { currentRoute, getRelativeUrlPath, getRoutes, isCurrentRoute, isRoute, LITESTAR, route, toRoute, } from "./routes.js";
|