litestar-vite-plugin 0.15.0-rc.2 → 0.15.0-rc.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 +4 -4
- package/dist/js/helpers/routes.d.ts +1 -0
- package/dist/js/index.d.ts +10 -2
- package/dist/js/index.js +82 -22
- package/dist/js/inertia-types.d.ts +37 -0
- package/dist/js/inertia-types.js +0 -0
- package/dist/js/install-hint.d.ts +6 -1
- package/dist/js/install-hint.js +35 -2
- package/dist/js/shared/bridge-schema.d.ts +7 -2
- package/dist/js/shared/bridge-schema.js +13 -4
- package/dist/js/shared/emit-page-props-types.d.ts +3 -1
- package/dist/js/shared/emit-page-props-types.js +70 -48
- package/dist/js/shared/typegen-cache.d.ts +45 -0
- package/dist/js/shared/typegen-cache.js +83 -0
- package/dist/js/shared/typegen-core.d.ts +90 -0
- package/dist/js/shared/typegen-core.js +122 -0
- package/dist/js/shared/typegen-plugin.js +71 -57
- package/dist/js/shared/write-if-changed.d.ts +16 -0
- package/dist/js/shared/write-if-changed.js +19 -0
- package/dist/js/typegen-cli.js +66 -0
- package/package.json +33 -4
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ 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 `
|
|
8
|
+
- SSR framework support: use `mode="ssr"` for Astro, Nuxt, SvelteKit - proxies everything except your API routes.
|
|
9
9
|
- Production assets: reads Vite manifest from `public/manifest.json` (configurable) and serves under `asset_url`.
|
|
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.
|
|
@@ -103,13 +103,13 @@ app = Litestar(
|
|
|
103
103
|
|
|
104
104
|
## Meta-frameworks (Astro, Nuxt, SvelteKit)
|
|
105
105
|
|
|
106
|
-
Use `
|
|
106
|
+
Use `mode="ssr"` (or `mode="framework"`) to proxy non-API routes to the framework's dev server:
|
|
107
107
|
|
|
108
108
|
```python
|
|
109
109
|
import os
|
|
110
110
|
from pathlib import Path
|
|
111
111
|
from litestar import Litestar
|
|
112
|
-
from litestar_vite import VitePlugin, ViteConfig, PathConfig
|
|
112
|
+
from litestar_vite import VitePlugin, ViteConfig, PathConfig
|
|
113
113
|
|
|
114
114
|
here = Path(__file__).parent
|
|
115
115
|
DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in ("true", "1", "yes")
|
|
@@ -117,9 +117,9 @@ DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in ("true", "1", "yes")
|
|
|
117
117
|
app = Litestar(
|
|
118
118
|
plugins=[
|
|
119
119
|
VitePlugin(config=ViteConfig(
|
|
120
|
+
mode="ssr",
|
|
120
121
|
dev_mode=DEV_MODE,
|
|
121
122
|
paths=PathConfig(root=here),
|
|
122
|
-
runtime=RuntimeConfig(proxy_mode="ssr"),
|
|
123
123
|
))
|
|
124
124
|
],
|
|
125
125
|
)
|
package/dist/js/index.d.ts
CHANGED
|
@@ -100,6 +100,13 @@ export interface PluginConfig {
|
|
|
100
100
|
* @default '/static/'
|
|
101
101
|
*/
|
|
102
102
|
assetUrl?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Optional asset URL to use only during production builds.
|
|
105
|
+
*
|
|
106
|
+
* This is typically derived from Python DeployConfig.asset_url and written into `.litestar.json`
|
|
107
|
+
* as `deployAssetUrl`. It is only used when `command === "build"`.
|
|
108
|
+
*/
|
|
109
|
+
deployAssetUrl?: string;
|
|
103
110
|
/**
|
|
104
111
|
* The public directory where all compiled/bundled assets should be written.
|
|
105
112
|
*
|
|
@@ -160,11 +167,12 @@ export interface PluginConfig {
|
|
|
160
167
|
*/
|
|
161
168
|
autoDetectIndex?: boolean;
|
|
162
169
|
/**
|
|
163
|
-
* Enable Inertia mode
|
|
170
|
+
* Enable Inertia mode.
|
|
164
171
|
*
|
|
165
172
|
* In Inertia apps, the backend (Litestar) serves all HTML responses.
|
|
166
173
|
* When enabled, direct access to the Vite dev server will show a placeholder
|
|
167
|
-
* page directing users to access the app through the backend
|
|
174
|
+
* page directing users to access the app through the backend (even if an
|
|
175
|
+
* index.html exists for the backend to render).
|
|
168
176
|
*
|
|
169
177
|
* Auto-detected from `.litestar.json` when mode is "inertia".
|
|
170
178
|
*
|
package/dist/js/index.js
CHANGED
|
@@ -28,9 +28,6 @@ function litestar(config) {
|
|
|
28
28
|
return plugins;
|
|
29
29
|
}
|
|
30
30
|
async function findIndexHtmlPath(server, pluginConfig) {
|
|
31
|
-
if (pluginConfig.inertiaMode) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
31
|
if (!pluginConfig.autoDetectIndex) {
|
|
35
32
|
return null;
|
|
36
33
|
}
|
|
@@ -81,7 +78,8 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
81
78
|
userConfig = config;
|
|
82
79
|
const ssr = !!userConfig.build?.ssr;
|
|
83
80
|
const env = loadEnv(mode, userConfig.envDir || process.cwd(), "");
|
|
84
|
-
const
|
|
81
|
+
const runtimeAssetUrl = normalizeAssetUrl(env.ASSET_URL || pluginConfig.assetUrl);
|
|
82
|
+
const buildAssetUrl = pluginConfig.deployAssetUrl ?? runtimeAssetUrl;
|
|
85
83
|
const serverConfig = command === "serve" ? resolveDevelopmentEnvironmentServerConfig(pluginConfig.detectTls) ?? resolveEnvironmentServerConfig(env) : void 0;
|
|
86
84
|
const withProxyErrorSilencer = (proxyConfig) => {
|
|
87
85
|
if (!proxyConfig) return void 0;
|
|
@@ -114,7 +112,7 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
114
112
|
const devBase = pluginConfig.assetUrl.startsWith("/") ? pluginConfig.assetUrl : pluginConfig.assetUrl.replace(/\/+$/, "");
|
|
115
113
|
ensureCommandShouldRunInEnvironment(command, env, mode);
|
|
116
114
|
return {
|
|
117
|
-
base: userConfig.base ?? (command === "build" ? resolveBase(pluginConfig,
|
|
115
|
+
base: userConfig.base ?? (command === "build" ? resolveBase(pluginConfig, buildAssetUrl) : devBase),
|
|
118
116
|
publicDir: userConfig.publicDir ?? pluginConfig.staticDir ?? false,
|
|
119
117
|
clearScreen: false,
|
|
120
118
|
build: {
|
|
@@ -237,15 +235,21 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
237
235
|
setTimeout(async () => {
|
|
238
236
|
if (logger.config.level === "quiet") return;
|
|
239
237
|
const litestarVersion = litestarMeta.litestarVersion ?? process.env.LITESTAR_VERSION ?? "unknown";
|
|
240
|
-
|
|
238
|
+
let backendStatus = await checkBackendAvailability(appUrl);
|
|
239
|
+
if (!backendStatus.available) {
|
|
240
|
+
for (let i = 0; i < 3 && !backendStatus.available; i++) {
|
|
241
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
242
|
+
backendStatus = await checkBackendAvailability(appUrl);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
241
245
|
resolvedConfig.logger.info(`
|
|
242
246
|
${colors.red(`${colors.bold("LITESTAR")} ${litestarVersion}`)}`);
|
|
243
247
|
resolvedConfig.logger.info("");
|
|
244
|
-
if (
|
|
248
|
+
if (pluginConfig.inertiaMode) {
|
|
249
|
+
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Mode")}: Inertia`);
|
|
250
|
+
} else if (initialIndexPath) {
|
|
245
251
|
const relIndexPath = logger.path(initialIndexPath, server.config.root);
|
|
246
252
|
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Mode")}: SPA (${colors.cyan(relIndexPath)})`);
|
|
247
|
-
} else if (pluginConfig.inertiaMode) {
|
|
248
|
-
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Mode")}: Inertia`);
|
|
249
253
|
} else {
|
|
250
254
|
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Mode")}: Litestar`);
|
|
251
255
|
}
|
|
@@ -314,24 +318,51 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
314
318
|
exitHandlersBound = true;
|
|
315
319
|
}
|
|
316
320
|
server.middlewares.use(async (req, res, next) => {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
321
|
+
const requestUrl = req.originalUrl ?? req.url ?? "/";
|
|
322
|
+
const requestPath = requestUrl.split("?")[0];
|
|
323
|
+
const isRootRequest = requestPath === "/" || requestPath === "/index.html";
|
|
324
|
+
if (requestPath === "/__litestar__/transform-index") {
|
|
325
|
+
if (req.method !== "POST") {
|
|
326
|
+
res.statusCode = 405;
|
|
327
|
+
res.setHeader("Content-Type", "text/plain");
|
|
328
|
+
res.end("Method Not Allowed");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const readBody = async () => new Promise((resolve, reject) => {
|
|
332
|
+
let data = "";
|
|
333
|
+
req.on("data", (chunk) => {
|
|
334
|
+
data += chunk;
|
|
335
|
+
});
|
|
336
|
+
req.on("end", () => resolve(data));
|
|
337
|
+
req.on("error", (err) => reject(err));
|
|
338
|
+
});
|
|
320
339
|
try {
|
|
321
|
-
const
|
|
322
|
-
const
|
|
340
|
+
const body = await readBody();
|
|
341
|
+
const payload = JSON.parse(body);
|
|
342
|
+
if (!payload.html || typeof payload.html !== "string") {
|
|
343
|
+
res.statusCode = 400;
|
|
344
|
+
res.setHeader("Content-Type", "text/plain");
|
|
345
|
+
res.end("Invalid payload");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const url = typeof payload.url === "string" && payload.url ? payload.url : "/";
|
|
349
|
+
const transformedHtml = await server.transformIndexHtml(url, payload.html, url);
|
|
323
350
|
res.statusCode = 200;
|
|
324
351
|
res.setHeader("Content-Type", "text/html");
|
|
325
352
|
res.end(transformedHtml);
|
|
326
|
-
return;
|
|
327
353
|
} catch (e) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
354
|
+
resolvedConfig.logger.error(`Error transforming index.html: ${e instanceof Error ? e.message : e}`);
|
|
355
|
+
res.statusCode = 500;
|
|
356
|
+
res.setHeader("Content-Type", "text/plain");
|
|
357
|
+
res.end("Error transforming HTML");
|
|
332
358
|
}
|
|
359
|
+
return;
|
|
333
360
|
}
|
|
334
|
-
if (!
|
|
361
|
+
if (!isRootRequest) {
|
|
362
|
+
next();
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (pluginConfig.inertiaMode) {
|
|
335
366
|
try {
|
|
336
367
|
const placeholderPath = path.join(dirname(), "dev-server-index.html");
|
|
337
368
|
const placeholderContent = await fs.promises.readFile(placeholderPath, "utf-8");
|
|
@@ -345,7 +376,33 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
345
376
|
}
|
|
346
377
|
return;
|
|
347
378
|
}
|
|
348
|
-
|
|
379
|
+
const indexPath = await findIndexHtmlPath(server, pluginConfig);
|
|
380
|
+
if (indexPath) {
|
|
381
|
+
try {
|
|
382
|
+
const htmlContent = await fs.promises.readFile(indexPath, "utf-8");
|
|
383
|
+
const transformedHtml = await server.transformIndexHtml(requestUrl, htmlContent, requestUrl);
|
|
384
|
+
res.statusCode = 200;
|
|
385
|
+
res.setHeader("Content-Type", "text/html");
|
|
386
|
+
res.end(transformedHtml);
|
|
387
|
+
return;
|
|
388
|
+
} catch (e) {
|
|
389
|
+
const relIndexPath = path.relative(server.config.root, indexPath);
|
|
390
|
+
resolvedConfig.logger.error(`Error serving index.html from ${relIndexPath}: ${e instanceof Error ? e.message : e}`);
|
|
391
|
+
next(e);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
const placeholderPath = path.join(dirname(), "dev-server-index.html");
|
|
397
|
+
const placeholderContent = await fs.promises.readFile(placeholderPath, "utf-8");
|
|
398
|
+
res.statusCode = 200;
|
|
399
|
+
res.setHeader("Content-Type", "text/html");
|
|
400
|
+
res.end(placeholderContent.replace(/{{ APP_URL }}/g, appUrl));
|
|
401
|
+
} catch (e) {
|
|
402
|
+
resolvedConfig.logger.error(`Error serving placeholder index.html: ${e instanceof Error ? e.message : e}`);
|
|
403
|
+
res.statusCode = 404;
|
|
404
|
+
res.end("Not Found (Error loading placeholder)");
|
|
405
|
+
}
|
|
349
406
|
});
|
|
350
407
|
}
|
|
351
408
|
};
|
|
@@ -512,9 +569,11 @@ function resolvePluginConfig(config) {
|
|
|
512
569
|
}
|
|
513
570
|
const inertiaMode = resolvedConfig.inertiaMode ?? (pythonDefaults?.mode === "hybrid" || pythonDefaults?.mode === "inertia");
|
|
514
571
|
const effectiveResourceDir = resolvedConfig.resourceDir ?? pythonDefaults?.resourceDir ?? "src";
|
|
572
|
+
const deployAssetUrlRaw = resolvedConfig.deployAssetUrl ?? pythonDefaults?.deployAssetUrl ?? void 0;
|
|
515
573
|
const result = {
|
|
516
574
|
input: resolvedConfig.input,
|
|
517
575
|
assetUrl: normalizeAssetUrl(resolvedConfig.assetUrl ?? pythonDefaults?.assetUrl ?? "/static/"),
|
|
576
|
+
deployAssetUrl: typeof deployAssetUrlRaw === "string" ? normalizeAssetUrl(deployAssetUrlRaw) : void 0,
|
|
518
577
|
resourceDir: effectiveResourceDir,
|
|
519
578
|
bundleDir: resolvedConfig.bundleDir ?? pythonDefaults?.bundleDir ?? "public",
|
|
520
579
|
staticDir: resolvedConfig.staticDir ?? pythonDefaults?.staticDir ?? path.join(effectiveResourceDir, "public"),
|
|
@@ -549,7 +608,8 @@ function validateAgainstPythonDefaults(resolved, pythonDefaults, userConfig) {
|
|
|
549
608
|
if (userConfig.staticDir !== void 0 && hasPythonValue(pythonDefaults.staticDir) && resolved.staticDir !== pythonDefaults.staticDir) {
|
|
550
609
|
warnings.push(`staticDir: vite.config.ts="${resolved.staticDir}" differs from Python="${pythonDefaults.staticDir}"`);
|
|
551
610
|
}
|
|
552
|
-
|
|
611
|
+
const frameworkMode = pythonDefaults.mode === "framework" || pythonDefaults.mode === "ssr" || pythonDefaults.mode === "ssg";
|
|
612
|
+
if (frameworkMode && userConfig.ssrOutDir !== void 0 && hasPythonValue(pythonDefaults.ssrOutDir) && resolved.ssrOutDir !== pythonDefaults.ssrOutDir) {
|
|
553
613
|
warnings.push(`ssrOutDir: vite.config.ts="${resolved.ssrOutDir}" differs from Python="${pythonDefaults.ssrOutDir}"`);
|
|
554
614
|
}
|
|
555
615
|
if (warnings.length > 0) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base types for Inertia.js type generation.
|
|
3
|
+
*
|
|
4
|
+
* These types are generated by `litestar assets generate-types` in your
|
|
5
|
+
* project's `generated/page-props.ts` file. This module provides fallback
|
|
6
|
+
* types for when the generated file hasn't been created yet.
|
|
7
|
+
*
|
|
8
|
+
* To customize types, edit your project's `generated/page-props.user.ts` file.
|
|
9
|
+
*/
|
|
10
|
+
/** User interface - customize in page-props.user.ts */
|
|
11
|
+
export interface User {
|
|
12
|
+
}
|
|
13
|
+
/** Authentication data interface */
|
|
14
|
+
export interface AuthData {
|
|
15
|
+
}
|
|
16
|
+
/** Flash messages interface */
|
|
17
|
+
export interface FlashMessages {
|
|
18
|
+
}
|
|
19
|
+
/** User-defined shared props - customize in page-props.user.ts */
|
|
20
|
+
export interface SharedProps {
|
|
21
|
+
}
|
|
22
|
+
/** Generated shared props (populated by page-props.ts) */
|
|
23
|
+
export interface GeneratedSharedProps {
|
|
24
|
+
}
|
|
25
|
+
/** Full shared props = generated + user-defined */
|
|
26
|
+
export type FullSharedProps = GeneratedSharedProps & SharedProps & {
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
/** Page props mapped by component name (populated by page-props.ts) */
|
|
30
|
+
export interface PageProps {
|
|
31
|
+
}
|
|
32
|
+
/** Component name union type */
|
|
33
|
+
export type ComponentName = keyof PageProps;
|
|
34
|
+
/** Type-safe props for a specific component */
|
|
35
|
+
export type InertiaPageProps<C extends ComponentName> = PageProps[C];
|
|
36
|
+
/** Get props type for a specific page component */
|
|
37
|
+
export type PagePropsFor<C extends ComponentName> = PageProps[C];
|
|
File without changes
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the executor from .litestar.json or environment.
|
|
3
|
+
* Priority: LITESTAR_VITE_RUNTIME env > .litestar.json executor > lockfile detection > 'node'
|
|
4
|
+
*/
|
|
5
|
+
export declare function detectExecutor(): string;
|
|
1
6
|
export declare function resolveInstallHint(pkg?: string): string;
|
|
2
7
|
/**
|
|
3
8
|
* Resolves the package executor command based on runtime.
|
|
4
|
-
* Priority: explicit executor > LITESTAR_VITE_RUNTIME env > '
|
|
9
|
+
* Priority: explicit executor > .litestar.json > LITESTAR_VITE_RUNTIME env > lockfile detection > 'npx'
|
|
5
10
|
*
|
|
6
11
|
* @param pkg - The package command to execute (e.g., "@hey-api/openapi-ts -i schema.json -o src/types")
|
|
7
12
|
* @param executor - Optional explicit executor override
|
package/dist/js/install-hint.js
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
function detectExecutor() {
|
|
4
|
+
const envRuntime = (process.env.LITESTAR_VITE_RUNTIME ?? "").toLowerCase();
|
|
5
|
+
if (envRuntime) return envRuntime;
|
|
6
|
+
const configPath = process.env.LITESTAR_VITE_CONFIG_PATH ?? path.resolve(process.cwd(), ".litestar.json");
|
|
7
|
+
if (fs.existsSync(configPath)) {
|
|
8
|
+
try {
|
|
9
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
10
|
+
const data = JSON.parse(raw);
|
|
11
|
+
const executor = data?.executor;
|
|
12
|
+
if (typeof executor === "string" && executor.trim()) {
|
|
13
|
+
return executor.trim().toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) {
|
|
20
|
+
return "bun";
|
|
21
|
+
}
|
|
22
|
+
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
|
|
23
|
+
return "pnpm";
|
|
24
|
+
}
|
|
25
|
+
if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
|
|
26
|
+
return "yarn";
|
|
27
|
+
}
|
|
28
|
+
if (fs.existsSync(path.join(cwd, "deno.lock"))) {
|
|
29
|
+
return "deno";
|
|
30
|
+
}
|
|
31
|
+
return "node";
|
|
32
|
+
}
|
|
1
33
|
function resolveInstallHint(pkg = "@hey-api/openapi-ts") {
|
|
2
|
-
const runtime = (
|
|
34
|
+
const runtime = detectExecutor();
|
|
3
35
|
switch (runtime) {
|
|
4
36
|
case "bun":
|
|
5
37
|
return `bun add -d ${pkg}`;
|
|
@@ -17,7 +49,7 @@ function resolveInstallHint(pkg = "@hey-api/openapi-ts") {
|
|
|
17
49
|
return `npm install -D ${pkg}`;
|
|
18
50
|
}
|
|
19
51
|
function resolvePackageExecutor(pkg, executor) {
|
|
20
|
-
const runtime =
|
|
52
|
+
const runtime = executor || detectExecutor();
|
|
21
53
|
switch (runtime) {
|
|
22
54
|
case "bun":
|
|
23
55
|
return `bunx ${pkg}`;
|
|
@@ -32,6 +64,7 @@ function resolvePackageExecutor(pkg, executor) {
|
|
|
32
64
|
}
|
|
33
65
|
}
|
|
34
66
|
export {
|
|
67
|
+
detectExecutor,
|
|
35
68
|
resolveInstallHint,
|
|
36
69
|
resolvePackageExecutor
|
|
37
70
|
};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
-
export type BridgeMode = "spa" | "template" | "htmx" | "hybrid" | "inertia" | "ssr" | "ssg" | "external";
|
|
11
|
+
export type BridgeMode = "spa" | "template" | "htmx" | "hybrid" | "inertia" | "framework" | "ssr" | "ssg" | "external";
|
|
12
12
|
export type BridgeProxyMode = "vite" | "direct" | "proxy" | null;
|
|
13
13
|
export type BridgeExecutor = "node" | "bun" | "deno" | "yarn" | "pnpm";
|
|
14
14
|
export interface BridgeTypesConfig {
|
|
@@ -23,8 +23,13 @@ export interface BridgeTypesConfig {
|
|
|
23
23
|
generatePageProps: boolean;
|
|
24
24
|
globalRoute: boolean;
|
|
25
25
|
}
|
|
26
|
+
export interface BridgeSpaConfig {
|
|
27
|
+
/** Use script element instead of data-page attribute for Inertia page data */
|
|
28
|
+
useScriptElement: boolean;
|
|
29
|
+
}
|
|
26
30
|
export interface BridgeSchema {
|
|
27
31
|
assetUrl: string;
|
|
32
|
+
deployAssetUrl: string | null;
|
|
28
33
|
bundleDir: string;
|
|
29
34
|
resourceDir: string;
|
|
30
35
|
staticDir: string;
|
|
@@ -34,9 +39,9 @@ export interface BridgeSchema {
|
|
|
34
39
|
proxyMode: BridgeProxyMode;
|
|
35
40
|
host: string;
|
|
36
41
|
port: number;
|
|
37
|
-
ssrEnabled: boolean;
|
|
38
42
|
ssrOutDir: string | null;
|
|
39
43
|
types: BridgeTypesConfig | null;
|
|
44
|
+
spa: BridgeSpaConfig | null;
|
|
40
45
|
executor: BridgeExecutor;
|
|
41
46
|
logging: {
|
|
42
47
|
level: "quiet" | "normal" | "verbose";
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
const allowedTopLevelKeys = /* @__PURE__ */ new Set([
|
|
4
4
|
"assetUrl",
|
|
5
|
+
"deployAssetUrl",
|
|
5
6
|
"bundleDir",
|
|
6
7
|
"resourceDir",
|
|
7
8
|
"staticDir",
|
|
@@ -11,14 +12,14 @@ const allowedTopLevelKeys = /* @__PURE__ */ new Set([
|
|
|
11
12
|
"proxyMode",
|
|
12
13
|
"host",
|
|
13
14
|
"port",
|
|
14
|
-
"ssrEnabled",
|
|
15
15
|
"ssrOutDir",
|
|
16
16
|
"types",
|
|
17
|
+
"spa",
|
|
17
18
|
"executor",
|
|
18
19
|
"logging",
|
|
19
20
|
"litestarVersion"
|
|
20
21
|
]);
|
|
21
|
-
const allowedModes = /* @__PURE__ */ new Set(["spa", "template", "htmx", "hybrid", "inertia", "ssr", "ssg", "external"]);
|
|
22
|
+
const allowedModes = /* @__PURE__ */ new Set(["spa", "template", "htmx", "hybrid", "inertia", "framework", "ssr", "ssg", "external"]);
|
|
22
23
|
const allowedProxyModes = /* @__PURE__ */ new Set(["vite", "direct", "proxy"]);
|
|
23
24
|
const allowedExecutors = /* @__PURE__ */ new Set(["node", "bun", "deno", "yarn", "pnpm"]);
|
|
24
25
|
const allowedLogLevels = /* @__PURE__ */ new Set(["quiet", "normal", "verbose"]);
|
|
@@ -106,6 +107,12 @@ function parseLogging(value) {
|
|
|
106
107
|
const timestamps = assertBoolean(obj, "timestamps");
|
|
107
108
|
return { level, showPathsAbsolute, suppressNpmOutput, suppressViteBanner, timestamps };
|
|
108
109
|
}
|
|
110
|
+
function parseSpaConfig(value) {
|
|
111
|
+
if (value === null || value === void 0) return null;
|
|
112
|
+
const obj = assertObject(value, "spa");
|
|
113
|
+
const useScriptElement = assertBoolean(obj, "useScriptElement");
|
|
114
|
+
return { useScriptElement };
|
|
115
|
+
}
|
|
109
116
|
function parseBridgeSchema(value) {
|
|
110
117
|
const obj = assertObject(value, "root");
|
|
111
118
|
for (const key of Object.keys(obj)) {
|
|
@@ -114,6 +121,7 @@ function parseBridgeSchema(value) {
|
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
const assetUrl = assertString(obj, "assetUrl");
|
|
124
|
+
const deployAssetUrl = assertNullableString(obj, "deployAssetUrl");
|
|
117
125
|
const bundleDir = assertString(obj, "bundleDir");
|
|
118
126
|
const resourceDir = assertString(obj, "resourceDir");
|
|
119
127
|
const staticDir = assertString(obj, "staticDir");
|
|
@@ -123,14 +131,15 @@ function parseBridgeSchema(value) {
|
|
|
123
131
|
const proxyMode = assertProxyMode(obj.proxyMode);
|
|
124
132
|
const host = assertString(obj, "host");
|
|
125
133
|
const port = assertNumber(obj, "port");
|
|
126
|
-
const ssrEnabled = assertBoolean(obj, "ssrEnabled");
|
|
127
134
|
const ssrOutDir = assertNullableString(obj, "ssrOutDir");
|
|
128
135
|
const types = parseTypesConfig(obj.types);
|
|
136
|
+
const spa = parseSpaConfig(obj.spa);
|
|
129
137
|
const executor = assertEnum(obj.executor, "executor", allowedExecutors);
|
|
130
138
|
const logging = parseLogging(obj.logging);
|
|
131
139
|
const litestarVersion = assertString(obj, "litestarVersion");
|
|
132
140
|
return {
|
|
133
141
|
assetUrl,
|
|
142
|
+
deployAssetUrl,
|
|
134
143
|
bundleDir,
|
|
135
144
|
resourceDir,
|
|
136
145
|
staticDir,
|
|
@@ -140,9 +149,9 @@ function parseBridgeSchema(value) {
|
|
|
140
149
|
proxyMode,
|
|
141
150
|
host,
|
|
142
151
|
port,
|
|
143
|
-
ssrEnabled,
|
|
144
152
|
ssrOutDir,
|
|
145
153
|
types,
|
|
154
|
+
spa,
|
|
146
155
|
executor,
|
|
147
156
|
logging,
|
|
148
157
|
litestarVersion
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generate `page-props.ts` from `inertia-pages.json` metadata.
|
|
3
|
+
*
|
|
4
|
+
* @returns true if file was changed, false if unchanged
|
|
3
5
|
*/
|
|
4
|
-
export declare function emitPagePropsTypes(pagesPath: string, outputDir: string): Promise<
|
|
6
|
+
export declare function emitPagePropsTypes(pagesPath: string, outputDir: string): Promise<boolean>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { writeIfChanged } from "./write-if-changed.js";
|
|
3
4
|
async function emitPagePropsTypes(pagesPath, outputDir) {
|
|
4
5
|
const contents = await fs.promises.readFile(pagesPath, "utf-8");
|
|
5
6
|
const json = JSON.parse(contents);
|
|
@@ -16,18 +17,9 @@ async function emitPagePropsTypes(pagesPath, outputDir) {
|
|
|
16
17
|
if (includeDefaultAuth) {
|
|
17
18
|
userTypes = `/**
|
|
18
19
|
* Default User interface - minimal baseline for common auth patterns.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* declare module 'litestar-vite-plugin/inertia' {
|
|
23
|
-
* interface User {
|
|
24
|
-
* avatarUrl?: string | null
|
|
25
|
-
* roles: Role[]
|
|
26
|
-
* teams: Team[]
|
|
27
|
-
* }
|
|
28
|
-
* }
|
|
20
|
+
* Extend by adding properties to UserExtensions in page-props.user.ts
|
|
29
21
|
*/
|
|
30
|
-
export interface User {
|
|
22
|
+
export interface User extends UserExtensions {
|
|
31
23
|
id: string
|
|
32
24
|
email: string
|
|
33
25
|
name?: string | null
|
|
@@ -46,23 +38,13 @@ export interface AuthData {
|
|
|
46
38
|
`;
|
|
47
39
|
} else {
|
|
48
40
|
userTypes = `/**
|
|
49
|
-
* User interface -
|
|
50
|
-
* Default auth types are disabled.
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* declare module 'litestar-vite-plugin/inertia' {
|
|
54
|
-
* interface User {
|
|
55
|
-
* uuid: string
|
|
56
|
-
* username: string
|
|
57
|
-
* }
|
|
58
|
-
* }
|
|
41
|
+
* User interface - add properties to UserExtensions in page-props.user.ts
|
|
59
42
|
*/
|
|
60
|
-
export interface User {}
|
|
43
|
+
export interface User extends UserExtensions {}
|
|
61
44
|
|
|
62
45
|
`;
|
|
63
46
|
authTypes = `/**
|
|
64
|
-
* AuthData interface - define
|
|
65
|
-
* Default auth types are disabled.
|
|
47
|
+
* AuthData interface - define your auth structure here or in page-props.user.ts
|
|
66
48
|
*/
|
|
67
49
|
export interface AuthData {}
|
|
68
50
|
|
|
@@ -163,7 +145,7 @@ export interface FlashMessages {}
|
|
|
163
145
|
const availableApiTypes = /* @__PURE__ */ new Set();
|
|
164
146
|
if (fs.existsSync(apiTypesPath)) {
|
|
165
147
|
const content = await fs.promises.readFile(apiTypesPath, "utf-8");
|
|
166
|
-
for (const match of content.matchAll(/export (?:type|interface|enum|class) (
|
|
148
|
+
for (const match of content.matchAll(/export (?:type|interface|enum|class) (\w+)/g)) {
|
|
167
149
|
if (match[1]) {
|
|
168
150
|
availableApiTypes.add(match[1]);
|
|
169
151
|
}
|
|
@@ -184,7 +166,13 @@ export interface FlashMessages {}
|
|
|
184
166
|
}
|
|
185
167
|
}
|
|
186
168
|
if (unresolvedTypes.length > 0) {
|
|
187
|
-
console.warn(
|
|
169
|
+
console.warn(
|
|
170
|
+
`litestar-vite: Unresolved Inertia props types: ${unresolvedTypes.join(", ")}.
|
|
171
|
+
To fix:
|
|
172
|
+
1. Add to OpenAPI by including in route return types
|
|
173
|
+
2. Or configure TypeGenConfig.type_import_paths:
|
|
174
|
+
types=TypeGenConfig(type_import_paths={"${unresolvedTypes[0]}": "@/types/custom"})`
|
|
175
|
+
);
|
|
188
176
|
}
|
|
189
177
|
let importStatement = "";
|
|
190
178
|
if (apiImports.length > 0) {
|
|
@@ -211,6 +199,9 @@ export interface FlashMessages {}
|
|
|
211
199
|
const body = `// AUTO-GENERATED by litestar-vite. Do not edit.
|
|
212
200
|
/* eslint-disable */
|
|
213
201
|
|
|
202
|
+
// Import user-defined type extensions (edit page-props.user.ts to customize)
|
|
203
|
+
import type { UserExtensions, SharedPropsExtensions } from "./page-props.user"
|
|
204
|
+
|
|
214
205
|
${importStatement}${userTypes}${authTypes}${flashTypes}/**
|
|
215
206
|
* Generated shared props (always present).
|
|
216
207
|
* Includes built-in props + static config props.
|
|
@@ -221,26 +212,13 @@ ${generatedSharedPropLines.join("\n")}
|
|
|
221
212
|
|
|
222
213
|
/**
|
|
223
214
|
* User-defined shared props for dynamic share() calls in guards/middleware.
|
|
224
|
-
* Extend
|
|
225
|
-
*
|
|
226
|
-
* @example
|
|
227
|
-
* declare module 'litestar-vite-plugin/inertia' {
|
|
228
|
-
* interface User {
|
|
229
|
-
* avatarUrl?: string | null
|
|
230
|
-
* roles: Role[]
|
|
231
|
-
* teams: Team[]
|
|
232
|
-
* }
|
|
233
|
-
* interface SharedProps {
|
|
234
|
-
* locale?: string
|
|
235
|
-
* currentTeam?: CurrentTeam
|
|
236
|
-
* }
|
|
237
|
-
* }
|
|
215
|
+
* Extend by adding properties to SharedPropsExtensions in page-props.user.ts
|
|
238
216
|
*/
|
|
239
|
-
export
|
|
240
|
-
}
|
|
217
|
+
export type SharedProps = SharedPropsExtensions
|
|
241
218
|
|
|
242
|
-
/** Full shared props = generated + user-defined
|
|
243
|
-
|
|
219
|
+
/** Full shared props = generated + user-defined.
|
|
220
|
+
* Includes index signature for compatibility with Inertia's usePage<T>(). */
|
|
221
|
+
export type FullSharedProps = GeneratedSharedProps & SharedProps & { [key: string]: unknown }
|
|
244
222
|
|
|
245
223
|
/** Page props mapped by component name */
|
|
246
224
|
export interface PageProps {
|
|
@@ -255,13 +233,57 @@ export type InertiaPageProps<C extends ComponentName> = PageProps[C]
|
|
|
255
233
|
|
|
256
234
|
/** Get props type for a specific page component */
|
|
257
235
|
export type PagePropsFor<C extends ComponentName> = PageProps[C]
|
|
236
|
+
`;
|
|
237
|
+
const result = await writeIfChanged(outFile, body, { encoding: "utf-8" });
|
|
238
|
+
const userStubFile = path.join(outDir, "page-props.user.ts");
|
|
239
|
+
if (!fs.existsSync(userStubFile)) {
|
|
240
|
+
const userStub = `/**
|
|
241
|
+
* User-defined type extensions for Inertia page props.
|
|
242
|
+
* This file is generated ONCE and never overwritten - edit freely!
|
|
243
|
+
*
|
|
244
|
+
* Add properties to these interfaces to extend the generated types.
|
|
245
|
+
* The main page-props.ts file imports and uses these extensions.
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Extend the User interface with additional properties.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* export interface UserExtensions {
|
|
253
|
+
* avatarUrl?: string | null
|
|
254
|
+
* roles: string[]
|
|
255
|
+
* teams: Team[]
|
|
256
|
+
* }
|
|
257
|
+
*/
|
|
258
|
+
export interface UserExtensions {
|
|
259
|
+
// Add your custom User properties here
|
|
260
|
+
}
|
|
258
261
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Extend SharedProps with session-based or dynamic properties.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* export interface SharedPropsExtensions {
|
|
267
|
+
* locale?: string
|
|
268
|
+
* currentTeam?: {
|
|
269
|
+
* teamId: string
|
|
270
|
+
* teamName: string
|
|
271
|
+
* }
|
|
272
|
+
* }
|
|
273
|
+
*/
|
|
274
|
+
export interface SharedPropsExtensions {
|
|
275
|
+
// Add your custom shared props here
|
|
262
276
|
}
|
|
277
|
+
|
|
278
|
+
// Export custom types that can be used in page props
|
|
279
|
+
// export interface CurrentTeam {
|
|
280
|
+
// teamId: string
|
|
281
|
+
// teamName: string
|
|
282
|
+
// }
|
|
263
283
|
`;
|
|
264
|
-
|
|
284
|
+
await fs.promises.writeFile(userStubFile, userStub, "utf-8");
|
|
285
|
+
}
|
|
286
|
+
return result.changed;
|
|
265
287
|
}
|
|
266
288
|
export {
|
|
267
289
|
emitPagePropsTypes
|