litestar-vite-plugin 0.15.0-alpha.6 → 0.15.0-beta.1
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 +1 -1
- package/dist/js/index.d.ts +79 -6
- package/dist/js/index.js +275 -53
- package/dist/js/shared/format-path.d.ts +22 -0
- package/dist/js/shared/format-path.js +24 -0
- package/dist/js/shared/logger.d.ts +43 -0
- package/dist/js/shared/logger.js +59 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -160,7 +160,7 @@ litestar assets generate-types # one-off or CI
|
|
|
160
160
|
|
|
161
161
|
- Prints Python vs Vite config snapshot (asset URLs, bundle/hot paths, ports, modes).
|
|
162
162
|
- Flags missing hot file (dev proxy), missing manifest (prod), type-gen exports, env/config mismatches, and plugin install issues.
|
|
163
|
-
- `--fix` can rewrite simple vite.config values (assetUrl,
|
|
163
|
+
- `--fix` can rewrite simple vite.config values (assetUrl, bundleDir, hotFile, type paths) after creating a backup.
|
|
164
164
|
|
|
165
165
|
## Links
|
|
166
166
|
|
package/dist/js/index.d.ts
CHANGED
|
@@ -35,6 +35,13 @@ export interface TypesConfig {
|
|
|
35
35
|
* @default 'routes.json'
|
|
36
36
|
*/
|
|
37
37
|
routesPath?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Path where Inertia page props metadata is exported by Litestar.
|
|
40
|
+
* The Vite plugin watches this file for page props type generation.
|
|
41
|
+
*
|
|
42
|
+
* @default 'inertia-pages.json'
|
|
43
|
+
*/
|
|
44
|
+
pagePropsPath?: string;
|
|
38
45
|
/**
|
|
39
46
|
* Generate Zod schemas in addition to TypeScript types.
|
|
40
47
|
*
|
|
@@ -47,6 +54,16 @@ export interface TypesConfig {
|
|
|
47
54
|
* @default false
|
|
48
55
|
*/
|
|
49
56
|
generateSdk?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Register route() function globally on window object.
|
|
59
|
+
*
|
|
60
|
+
* When true, the generated routes.ts will include code that registers
|
|
61
|
+
* the type-safe route() function on `window.route`, similar to Laravel's
|
|
62
|
+
* Ziggy library. This allows using route() without imports.
|
|
63
|
+
*
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
globalRoute?: boolean;
|
|
50
67
|
/**
|
|
51
68
|
* Debounce time in milliseconds for type regeneration.
|
|
52
69
|
* Prevents regeneration from running too frequently when
|
|
@@ -72,7 +89,7 @@ export interface PluginConfig {
|
|
|
72
89
|
*
|
|
73
90
|
* @default 'public/dist'
|
|
74
91
|
*/
|
|
75
|
-
|
|
92
|
+
bundleDir?: string;
|
|
76
93
|
/**
|
|
77
94
|
* Vite's public directory for static, unprocessed assets.
|
|
78
95
|
* Mirrors Vite's `publicDir` option.
|
|
@@ -83,13 +100,13 @@ export interface PluginConfig {
|
|
|
83
100
|
/**
|
|
84
101
|
* Litestar's public assets directory. These are the assets that Vite will serve when developing.
|
|
85
102
|
*
|
|
86
|
-
* @default '
|
|
103
|
+
* @default 'src'
|
|
87
104
|
*/
|
|
88
|
-
|
|
105
|
+
resourceDir?: string;
|
|
89
106
|
/**
|
|
90
107
|
* The path to the "hot" file.
|
|
91
108
|
*
|
|
92
|
-
* @default `${
|
|
109
|
+
* @default `${bundleDir}/hot`
|
|
93
110
|
*/
|
|
94
111
|
hotFile?: string;
|
|
95
112
|
/**
|
|
@@ -99,9 +116,9 @@ export interface PluginConfig {
|
|
|
99
116
|
/**
|
|
100
117
|
* The directory where the SSR bundle should be written.
|
|
101
118
|
*
|
|
102
|
-
* @default '${
|
|
119
|
+
* @default '${bundleDir}/bootstrap/ssr'
|
|
103
120
|
*/
|
|
104
|
-
|
|
121
|
+
ssrOutDir?: string;
|
|
105
122
|
/**
|
|
106
123
|
* Configuration for performing full page refresh on python (or other) file changes.
|
|
107
124
|
*
|
|
@@ -121,6 +138,18 @@ export interface PluginConfig {
|
|
|
121
138
|
* @default true
|
|
122
139
|
*/
|
|
123
140
|
autoDetectIndex?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Enable Inertia mode, which disables index.html auto-detection.
|
|
143
|
+
*
|
|
144
|
+
* In Inertia apps, the backend (Litestar) serves all HTML responses.
|
|
145
|
+
* When enabled, direct access to the Vite dev server will show a placeholder
|
|
146
|
+
* page directing users to access the app through the backend.
|
|
147
|
+
*
|
|
148
|
+
* Auto-detected from `.litestar.json` when mode is "inertia".
|
|
149
|
+
*
|
|
150
|
+
* @default false (auto-detected from .litestar.json)
|
|
151
|
+
*/
|
|
152
|
+
inertiaMode?: boolean;
|
|
124
153
|
/**
|
|
125
154
|
* Transform the code while serving.
|
|
126
155
|
*/
|
|
@@ -186,6 +215,50 @@ interface RefreshConfig {
|
|
|
186
215
|
paths: string[];
|
|
187
216
|
config?: FullReloadConfig;
|
|
188
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Bridge schema for `.litestar.json` - the shared configuration contract
|
|
220
|
+
* between Python (Litestar) and TypeScript (Vite plugin).
|
|
221
|
+
*
|
|
222
|
+
* Python writes this file on startup; TypeScript reads it as defaults.
|
|
223
|
+
* Field names use camelCase (JavaScript convention) and match exactly
|
|
224
|
+
* between the JSON file and this TypeScript interface.
|
|
225
|
+
*
|
|
226
|
+
* Precedence: vite.config.ts > .litestar.json > hardcoded defaults
|
|
227
|
+
*/
|
|
228
|
+
export interface BridgeSchema {
|
|
229
|
+
assetUrl: string;
|
|
230
|
+
bundleDir: string;
|
|
231
|
+
resourceDir: string;
|
|
232
|
+
publicDir: string;
|
|
233
|
+
hotFile: string;
|
|
234
|
+
manifest: string;
|
|
235
|
+
mode: "spa" | "inertia" | "ssr" | "hybrid";
|
|
236
|
+
proxyMode: "vite_proxy" | "vite_direct" | "external_proxy";
|
|
237
|
+
host: string;
|
|
238
|
+
port: number;
|
|
239
|
+
protocol: "http" | "https";
|
|
240
|
+
ssrEnabled: boolean;
|
|
241
|
+
ssrOutDir: string | null;
|
|
242
|
+
types: {
|
|
243
|
+
enabled: boolean;
|
|
244
|
+
output: string;
|
|
245
|
+
openapiPath: string;
|
|
246
|
+
routesPath: string;
|
|
247
|
+
pagePropsPath?: string;
|
|
248
|
+
generateZod: boolean;
|
|
249
|
+
generateSdk: boolean;
|
|
250
|
+
globalRoute: boolean;
|
|
251
|
+
} | null;
|
|
252
|
+
executor: "node" | "bun" | "deno" | "yarn" | "pnpm";
|
|
253
|
+
logging: {
|
|
254
|
+
level: "quiet" | "normal" | "verbose";
|
|
255
|
+
showPathsAbsolute: boolean;
|
|
256
|
+
suppressNpmOutput: boolean;
|
|
257
|
+
suppressViteBanner: boolean;
|
|
258
|
+
timestamps: boolean;
|
|
259
|
+
} | null;
|
|
260
|
+
litestarVersion: string;
|
|
261
|
+
}
|
|
189
262
|
type DevServerUrl = `${"http" | "https"}://${string}:${number}`;
|
|
190
263
|
export declare const refreshPaths: string[];
|
|
191
264
|
/**
|
package/dist/js/index.js
CHANGED
|
@@ -10,6 +10,8 @@ import fullReload from "vite-plugin-full-reload";
|
|
|
10
10
|
import { resolveInstallHint, resolvePackageExecutor } from "./install-hint.js";
|
|
11
11
|
import { checkBackendAvailability, loadLitestarMeta } from "./litestar-meta.js";
|
|
12
12
|
import { debounce } from "./shared/debounce.js";
|
|
13
|
+
import { formatPath } from "./shared/format-path.js";
|
|
14
|
+
import { createLogger } from "./shared/logger.js";
|
|
13
15
|
const execAsync = promisify(exec);
|
|
14
16
|
let exitHandlersBound = false;
|
|
15
17
|
let warnedMissingRuntimeConfig = false;
|
|
@@ -23,16 +25,19 @@ function litestar(config) {
|
|
|
23
25
|
return plugins;
|
|
24
26
|
}
|
|
25
27
|
async function findIndexHtmlPath(server, pluginConfig) {
|
|
28
|
+
if (pluginConfig.inertiaMode) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
26
31
|
if (!pluginConfig.autoDetectIndex) {
|
|
27
32
|
return null;
|
|
28
33
|
}
|
|
29
34
|
const root = server.config.root;
|
|
30
35
|
const possiblePaths = [
|
|
31
36
|
path.join(root, "index.html"),
|
|
32
|
-
path.join(root, pluginConfig.
|
|
33
|
-
// Ensure
|
|
37
|
+
path.join(root, pluginConfig.resourceDir.replace(/^\//, ""), "index.html"),
|
|
38
|
+
// Ensure resourceDir path is relative to root
|
|
34
39
|
path.join(root, pluginConfig.publicDir.replace(/^\//, ""), "index.html"),
|
|
35
|
-
path.join(root, pluginConfig.
|
|
40
|
+
path.join(root, pluginConfig.bundleDir.replace(/^\//, ""), "index.html")
|
|
36
41
|
];
|
|
37
42
|
for (const indexPath of possiblePaths) {
|
|
38
43
|
try {
|
|
@@ -63,8 +68,9 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
63
68
|
let shuttingDown = false;
|
|
64
69
|
const pythonDefaults = loadPythonDefaults();
|
|
65
70
|
const proxyMode = pythonDefaults?.proxyMode ?? "vite_proxy";
|
|
71
|
+
const logger = createLogger(pythonDefaults?.logging);
|
|
66
72
|
const defaultAliases = {
|
|
67
|
-
"@": `/${pluginConfig.
|
|
73
|
+
"@": `/${pluginConfig.resourceDir.replace(/^\/+/, "").replace(/\/+$/, "")}/`
|
|
68
74
|
};
|
|
69
75
|
return {
|
|
70
76
|
name: "litestar",
|
|
@@ -195,12 +201,9 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
195
201
|
resolvedConfig.logger.warn(formatMissingConfigWarning());
|
|
196
202
|
}
|
|
197
203
|
}
|
|
198
|
-
const
|
|
199
|
-
if (!fs.existsSync(
|
|
200
|
-
resolvedConfig.logger.warn(
|
|
201
|
-
`${colors.cyan("litestar-vite")} ${colors.yellow("Resource directory not found:")} ${resourceDir}
|
|
202
|
-
Expected directory: ${colors.dim(pluginConfig.resourceDirectory)}`
|
|
203
|
-
);
|
|
204
|
+
const resourceDirPath = path.resolve(resolvedConfig.root, pluginConfig.resourceDir);
|
|
205
|
+
if (!fs.existsSync(resourceDirPath) && typeof resolvedConfig.logger?.warn === "function") {
|
|
206
|
+
resolvedConfig.logger.warn(`${colors.cyan("litestar-vite")} ${colors.yellow("Resource directory not found:")} ${pluginConfig.resourceDir}`);
|
|
204
207
|
}
|
|
205
208
|
const hint = pluginConfig.types !== false ? pluginConfig.types.routesPath : void 0;
|
|
206
209
|
litestarMeta = await loadLitestarMeta(resolvedConfig, hint);
|
|
@@ -232,19 +235,20 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
232
235
|
fs.writeFileSync(pluginConfig.hotFile, viteDevServerUrl);
|
|
233
236
|
}
|
|
234
237
|
setTimeout(async () => {
|
|
235
|
-
|
|
238
|
+
if (logger.config.level === "quiet") return;
|
|
239
|
+
const litestarVersion = litestarMeta.litestarVersion ?? process.env.LITESTAR_VERSION ?? "unknown";
|
|
236
240
|
const backendStatus = await checkBackendAvailability(appUrl);
|
|
237
241
|
resolvedConfig.logger.info(`
|
|
238
|
-
${colors.red(`${colors.bold("LITESTAR")} ${
|
|
242
|
+
${colors.red(`${colors.bold("LITESTAR")} ${litestarVersion}`)}`);
|
|
239
243
|
resolvedConfig.logger.info("");
|
|
240
244
|
if (initialIndexPath) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
245
|
+
const relIndexPath = logger.path(initialIndexPath, server.config.root);
|
|
246
|
+
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`);
|
|
244
249
|
} else {
|
|
245
|
-
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("
|
|
250
|
+
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Mode")}: Litestar`);
|
|
246
251
|
}
|
|
247
|
-
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Dev Server")}: ${colors.cyan(viteDevServerUrl)}`);
|
|
248
252
|
if (backendStatus.available) {
|
|
249
253
|
resolvedConfig.logger.info(
|
|
250
254
|
` ${colors.green("\u279C")} ${colors.bold("App URL")}: ${colors.cyan(appUrl.replace(/:(\d+)/, (_, port) => `:${colors.bold(port)}`))} ${colors.green("\u2713")}`
|
|
@@ -254,12 +258,13 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
254
258
|
` ${colors.yellow("\u279C")} ${colors.bold("App URL")}: ${colors.cyan(appUrl.replace(/:(\d+)/, (_, port) => `:${colors.bold(port)}`))} ${colors.yellow("\u26A0")}`
|
|
255
259
|
);
|
|
256
260
|
}
|
|
257
|
-
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("
|
|
261
|
+
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Dev Server")}: ${colors.cyan(viteDevServerUrl)}`);
|
|
258
262
|
if (pluginConfig.types !== false && pluginConfig.types.enabled) {
|
|
259
263
|
const openapiExists = fs.existsSync(path.resolve(process.cwd(), pluginConfig.types.openapiPath));
|
|
260
264
|
const routesExists = fs.existsSync(path.resolve(process.cwd(), pluginConfig.types.routesPath));
|
|
265
|
+
const relTypesOutput = logger.path(pluginConfig.types.output, process.cwd());
|
|
261
266
|
if (openapiExists || routesExists) {
|
|
262
|
-
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Type Gen")}: ${colors.
|
|
267
|
+
resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Type Gen")}: ${colors.dim(`${relTypesOutput}/`)}`);
|
|
263
268
|
} else {
|
|
264
269
|
resolvedConfig.logger.info(` ${colors.yellow("\u279C")} ${colors.bold("Type Gen")}: ${colors.yellow("waiting")} ${colors.dim("(no schema files yet)")}`);
|
|
265
270
|
}
|
|
@@ -320,12 +325,13 @@ function resolveLitestarPlugin(pluginConfig) {
|
|
|
320
325
|
res.end(transformedHtml);
|
|
321
326
|
return;
|
|
322
327
|
} catch (e) {
|
|
323
|
-
|
|
328
|
+
const relIndexPath = path.relative(server.config.root, indexPath);
|
|
329
|
+
resolvedConfig.logger.error(`Error serving index.html from ${relIndexPath}: ${e instanceof Error ? e.message : e}`);
|
|
324
330
|
next(e);
|
|
325
331
|
return;
|
|
326
332
|
}
|
|
327
333
|
}
|
|
328
|
-
if (!indexPath && req.url === "/index.html") {
|
|
334
|
+
if (!indexPath && (req.url === "/" || req.url === "/index.html")) {
|
|
329
335
|
try {
|
|
330
336
|
const placeholderPath = path.join(dirname(), "dev-server-index.html");
|
|
331
337
|
const placeholderContent = await fs.promises.readFile(placeholderPath, "utf-8");
|
|
@@ -415,7 +421,7 @@ function formatMissingConfigWarning() {
|
|
|
415
421
|
`${y("\u2502")} ${d("litestar({")} ${y("\u2502")}`,
|
|
416
422
|
`${y("\u2502")} ${d(' input: ["src/main.tsx"],')} ${y("\u2502")}`,
|
|
417
423
|
`${y("\u2502")} ${d(' assetUrl: "/static/",')} ${y("\u2502")}`,
|
|
418
|
-
`${y("\u2502")} ${d('
|
|
424
|
+
`${y("\u2502")} ${d(' bundleDir: "public",')} ${y("\u2502")}`,
|
|
419
425
|
`${y("\u2502")} ${d(" types: false,")} ${y("\u2502")}`,
|
|
420
426
|
`${y("\u2502")} ${d("})")} ${y("\u2502")}`,
|
|
421
427
|
`${y("\u2502")} ${y("\u2502")}`,
|
|
@@ -442,16 +448,16 @@ function resolvePluginConfig(config) {
|
|
|
442
448
|
if (typeof resolvedConfig.input === "undefined") {
|
|
443
449
|
throw new Error('litestar-vite-plugin: missing configuration for "input".');
|
|
444
450
|
}
|
|
445
|
-
if (typeof resolvedConfig.
|
|
446
|
-
resolvedConfig.
|
|
447
|
-
if (resolvedConfig.
|
|
448
|
-
throw new Error("litestar-vite-plugin:
|
|
451
|
+
if (typeof resolvedConfig.resourceDir === "string") {
|
|
452
|
+
resolvedConfig.resourceDir = resolvedConfig.resourceDir.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
|
453
|
+
if (resolvedConfig.resourceDir === "") {
|
|
454
|
+
throw new Error("litestar-vite-plugin: resourceDir must be a subdirectory. E.g. 'resources'.");
|
|
449
455
|
}
|
|
450
456
|
}
|
|
451
|
-
if (typeof resolvedConfig.
|
|
452
|
-
resolvedConfig.
|
|
453
|
-
if (resolvedConfig.
|
|
454
|
-
throw new Error("litestar-vite-plugin:
|
|
457
|
+
if (typeof resolvedConfig.bundleDir === "string") {
|
|
458
|
+
resolvedConfig.bundleDir = resolvedConfig.bundleDir.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
|
459
|
+
if (resolvedConfig.bundleDir === "") {
|
|
460
|
+
throw new Error("litestar-vite-plugin: bundleDir must be a subdirectory. E.g. 'public'.");
|
|
455
461
|
}
|
|
456
462
|
}
|
|
457
463
|
if (typeof resolvedConfig.publicDir === "string") {
|
|
@@ -460,8 +466,8 @@ function resolvePluginConfig(config) {
|
|
|
460
466
|
throw new Error("litestar-vite-plugin: publicDir must be a subdirectory. E.g. 'public'.");
|
|
461
467
|
}
|
|
462
468
|
}
|
|
463
|
-
if (typeof resolvedConfig.
|
|
464
|
-
resolvedConfig.
|
|
469
|
+
if (typeof resolvedConfig.ssrOutDir === "string") {
|
|
470
|
+
resolvedConfig.ssrOutDir = resolvedConfig.ssrOutDir.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
|
465
471
|
}
|
|
466
472
|
if (resolvedConfig.refresh === true) {
|
|
467
473
|
resolvedConfig.refresh = [{ paths: refreshPaths }];
|
|
@@ -474,8 +480,10 @@ function resolvePluginConfig(config) {
|
|
|
474
480
|
output: "src/generated/types",
|
|
475
481
|
openapiPath: "src/generated/openapi.json",
|
|
476
482
|
routesPath: "src/generated/routes.json",
|
|
483
|
+
pagePropsPath: "src/generated/inertia-pages.json",
|
|
477
484
|
generateZod: false,
|
|
478
485
|
generateSdk: false,
|
|
486
|
+
globalRoute: false,
|
|
479
487
|
debounce: 300
|
|
480
488
|
};
|
|
481
489
|
} else if (resolvedConfig.types === "auto" || typeof resolvedConfig.types === "undefined") {
|
|
@@ -485,21 +493,26 @@ function resolvePluginConfig(config) {
|
|
|
485
493
|
output: pythonDefaults.types.output,
|
|
486
494
|
openapiPath: pythonDefaults.types.openapiPath,
|
|
487
495
|
routesPath: pythonDefaults.types.routesPath,
|
|
496
|
+
pagePropsPath: pythonDefaults.types.pagePropsPath ?? path.join(pythonDefaults.types.output, "inertia-pages.json"),
|
|
488
497
|
generateZod: pythonDefaults.types.generateZod,
|
|
489
498
|
generateSdk: pythonDefaults.types.generateSdk,
|
|
499
|
+
globalRoute: pythonDefaults.types.globalRoute ?? false,
|
|
490
500
|
debounce: 300
|
|
491
501
|
};
|
|
492
502
|
}
|
|
493
503
|
} else if (typeof resolvedConfig.types === "object" && resolvedConfig.types !== null) {
|
|
494
504
|
const userProvidedOpenapi = Object.hasOwn(resolvedConfig.types, "openapiPath");
|
|
495
505
|
const userProvidedRoutes = Object.hasOwn(resolvedConfig.types, "routesPath");
|
|
506
|
+
const userProvidedPageProps = Object.hasOwn(resolvedConfig.types, "pagePropsPath");
|
|
496
507
|
typesConfig = {
|
|
497
508
|
enabled: resolvedConfig.types.enabled ?? true,
|
|
498
509
|
output: resolvedConfig.types.output ?? "src/generated/types",
|
|
499
510
|
openapiPath: resolvedConfig.types.openapiPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "openapi.json") : "src/generated/openapi.json"),
|
|
500
511
|
routesPath: resolvedConfig.types.routesPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "routes.json") : "src/generated/routes.json"),
|
|
512
|
+
pagePropsPath: resolvedConfig.types.pagePropsPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "inertia-pages.json") : "src/generated/inertia-pages.json"),
|
|
501
513
|
generateZod: resolvedConfig.types.generateZod ?? false,
|
|
502
514
|
generateSdk: resolvedConfig.types.generateSdk ?? false,
|
|
515
|
+
globalRoute: resolvedConfig.types.globalRoute ?? false,
|
|
503
516
|
debounce: resolvedConfig.types.debounce ?? 300
|
|
504
517
|
};
|
|
505
518
|
if (!userProvidedOpenapi && resolvedConfig.types.output) {
|
|
@@ -508,24 +521,59 @@ function resolvePluginConfig(config) {
|
|
|
508
521
|
if (!userProvidedRoutes && resolvedConfig.types.output) {
|
|
509
522
|
typesConfig.routesPath = path.join(typesConfig.output, "routes.json");
|
|
510
523
|
}
|
|
524
|
+
if (!userProvidedPageProps && resolvedConfig.types.output) {
|
|
525
|
+
typesConfig.pagePropsPath = path.join(typesConfig.output, "inertia-pages.json");
|
|
526
|
+
}
|
|
511
527
|
}
|
|
512
|
-
|
|
528
|
+
const inertiaMode = resolvedConfig.inertiaMode ?? pythonDefaults?.mode === "inertia";
|
|
529
|
+
const result = {
|
|
513
530
|
input: resolvedConfig.input,
|
|
514
531
|
assetUrl: normalizeAssetUrl(resolvedConfig.assetUrl ?? pythonDefaults?.assetUrl ?? "/static/"),
|
|
515
|
-
|
|
516
|
-
|
|
532
|
+
resourceDir: resolvedConfig.resourceDir ?? pythonDefaults?.resourceDir ?? "src",
|
|
533
|
+
bundleDir: resolvedConfig.bundleDir ?? pythonDefaults?.bundleDir ?? "public",
|
|
517
534
|
publicDir: resolvedConfig.publicDir ?? pythonDefaults?.publicDir ?? "public",
|
|
518
535
|
ssr: resolvedConfig.ssr ?? resolvedConfig.input,
|
|
519
|
-
|
|
536
|
+
ssrOutDir: resolvedConfig.ssrOutDir ?? pythonDefaults?.ssrOutDir ?? path.join(resolvedConfig.resourceDir ?? pythonDefaults?.resourceDir ?? "src", "bootstrap/ssr"),
|
|
520
537
|
refresh: resolvedConfig.refresh ?? false,
|
|
521
|
-
hotFile: resolvedConfig.hotFile ?? path.join(resolvedConfig.
|
|
538
|
+
hotFile: resolvedConfig.hotFile ?? path.join(resolvedConfig.bundleDir ?? "public", "hot"),
|
|
522
539
|
detectTls: resolvedConfig.detectTls ?? false,
|
|
523
540
|
autoDetectIndex: resolvedConfig.autoDetectIndex ?? true,
|
|
541
|
+
inertiaMode,
|
|
524
542
|
transformOnServe: resolvedConfig.transformOnServe ?? ((code) => code),
|
|
525
543
|
types: typesConfig,
|
|
526
544
|
executor: resolvedConfig.executor ?? pythonDefaults?.executor,
|
|
527
545
|
hasPythonConfig: pythonDefaults !== null
|
|
528
546
|
};
|
|
547
|
+
validateAgainstPythonDefaults(result, pythonDefaults, resolvedConfig);
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
function validateAgainstPythonDefaults(resolved, pythonDefaults, userConfig) {
|
|
551
|
+
if (!pythonDefaults) return;
|
|
552
|
+
const warnings = [];
|
|
553
|
+
const hasPythonValue = (value) => typeof value === "string" && value.length > 0;
|
|
554
|
+
if (userConfig.assetUrl !== void 0 && hasPythonValue(pythonDefaults.assetUrl) && resolved.assetUrl !== pythonDefaults.assetUrl) {
|
|
555
|
+
warnings.push(`assetUrl: vite.config.ts="${resolved.assetUrl}" differs from Python="${pythonDefaults.assetUrl}"`);
|
|
556
|
+
}
|
|
557
|
+
if (userConfig.bundleDir !== void 0 && hasPythonValue(pythonDefaults.bundleDir) && resolved.bundleDir !== pythonDefaults.bundleDir) {
|
|
558
|
+
warnings.push(`bundleDir: vite.config.ts="${resolved.bundleDir}" differs from Python="${pythonDefaults.bundleDir}"`);
|
|
559
|
+
}
|
|
560
|
+
if (userConfig.resourceDir !== void 0 && hasPythonValue(pythonDefaults.resourceDir) && resolved.resourceDir !== pythonDefaults.resourceDir) {
|
|
561
|
+
warnings.push(`resourceDir: vite.config.ts="${resolved.resourceDir}" differs from Python="${pythonDefaults.resourceDir}"`);
|
|
562
|
+
}
|
|
563
|
+
if (userConfig.publicDir !== void 0 && hasPythonValue(pythonDefaults.publicDir) && resolved.publicDir !== pythonDefaults.publicDir) {
|
|
564
|
+
warnings.push(`publicDir: vite.config.ts="${resolved.publicDir}" differs from Python="${pythonDefaults.publicDir}"`);
|
|
565
|
+
}
|
|
566
|
+
if (pythonDefaults.ssrEnabled && userConfig.ssrOutDir !== void 0 && hasPythonValue(pythonDefaults.ssrOutDir) && resolved.ssrOutDir !== pythonDefaults.ssrOutDir) {
|
|
567
|
+
warnings.push(`ssrOutDir: vite.config.ts="${resolved.ssrOutDir}" differs from Python="${pythonDefaults.ssrOutDir}"`);
|
|
568
|
+
}
|
|
569
|
+
if (warnings.length > 0) {
|
|
570
|
+
console.warn(
|
|
571
|
+
colors.yellow("[litestar-vite] Configuration mismatch detected:\n") + warnings.map((w) => ` ${colors.dim("\u2022")} ${w}`).join("\n") + `
|
|
572
|
+
|
|
573
|
+
${colors.dim("Precedence: vite.config.ts > .litestar.json > defaults")}
|
|
574
|
+
` + colors.dim("See: https://docs.litestar.dev/vite/config-precedence\n")
|
|
575
|
+
);
|
|
576
|
+
}
|
|
529
577
|
}
|
|
530
578
|
function resolveBase(_config, assetUrl) {
|
|
531
579
|
if (process.env.NODE_ENV === "development") {
|
|
@@ -541,9 +589,9 @@ function resolveInput(config, ssr) {
|
|
|
541
589
|
}
|
|
542
590
|
function resolveOutDir(config, ssr) {
|
|
543
591
|
if (ssr) {
|
|
544
|
-
return config.
|
|
592
|
+
return config.ssrOutDir.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
545
593
|
}
|
|
546
|
-
return config.
|
|
594
|
+
return config.bundleDir.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
547
595
|
}
|
|
548
596
|
function resolveFullReloadConfig({ refresh: config }) {
|
|
549
597
|
if (typeof config === "boolean") {
|
|
@@ -564,7 +612,155 @@ function resolveFullReloadConfig({ refresh: config }) {
|
|
|
564
612
|
return plugin;
|
|
565
613
|
});
|
|
566
614
|
}
|
|
567
|
-
async function
|
|
615
|
+
async function emitPagePropsTypes(pagesPath, outputDir) {
|
|
616
|
+
const contents = await fs.promises.readFile(pagesPath, "utf-8");
|
|
617
|
+
const json = JSON.parse(contents);
|
|
618
|
+
const outDir = path.resolve(process.cwd(), outputDir);
|
|
619
|
+
await fs.promises.mkdir(outDir, { recursive: true });
|
|
620
|
+
const outFile = path.join(outDir, "page-props.ts");
|
|
621
|
+
const { includeDefaultAuth, includeDefaultFlash } = json.typeGenConfig;
|
|
622
|
+
let userTypes = "";
|
|
623
|
+
let authTypes = "";
|
|
624
|
+
let flashTypes = "";
|
|
625
|
+
if (includeDefaultAuth) {
|
|
626
|
+
userTypes = `/**
|
|
627
|
+
* Default User interface - minimal baseline for common auth patterns.
|
|
628
|
+
* Users extend this via module augmentation with their full user model.
|
|
629
|
+
*
|
|
630
|
+
* @example
|
|
631
|
+
* declare module 'litestar-vite/inertia' {
|
|
632
|
+
* interface User {
|
|
633
|
+
* avatarUrl?: string | null
|
|
634
|
+
* roles: Role[]
|
|
635
|
+
* teams: Team[]
|
|
636
|
+
* }
|
|
637
|
+
* }
|
|
638
|
+
*/
|
|
639
|
+
export interface User {
|
|
640
|
+
id: string
|
|
641
|
+
email: string
|
|
642
|
+
name?: string | null
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
`;
|
|
646
|
+
authTypes = `/**
|
|
647
|
+
* Default AuthData interface - mirrors Laravel Jetstream pattern.
|
|
648
|
+
* isAuthenticated + optional user is the universal pattern.
|
|
649
|
+
*/
|
|
650
|
+
export interface AuthData {
|
|
651
|
+
isAuthenticated: boolean
|
|
652
|
+
user?: User
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
`;
|
|
656
|
+
} else {
|
|
657
|
+
userTypes = `/**
|
|
658
|
+
* User interface - define via module augmentation.
|
|
659
|
+
* Default auth types are disabled.
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* declare module 'litestar-vite/inertia' {
|
|
663
|
+
* interface User {
|
|
664
|
+
* uuid: string
|
|
665
|
+
* username: string
|
|
666
|
+
* }
|
|
667
|
+
* }
|
|
668
|
+
*/
|
|
669
|
+
export interface User {}
|
|
670
|
+
|
|
671
|
+
`;
|
|
672
|
+
authTypes = `/**
|
|
673
|
+
* AuthData interface - define via module augmentation.
|
|
674
|
+
* Default auth types are disabled.
|
|
675
|
+
*/
|
|
676
|
+
export interface AuthData {}
|
|
677
|
+
|
|
678
|
+
`;
|
|
679
|
+
}
|
|
680
|
+
if (includeDefaultFlash) {
|
|
681
|
+
flashTypes = `/**
|
|
682
|
+
* Default FlashMessages interface - category to messages mapping.
|
|
683
|
+
* Standard categories: success, error, info, warning.
|
|
684
|
+
*/
|
|
685
|
+
export interface FlashMessages {
|
|
686
|
+
[category: string]: string[]
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
`;
|
|
690
|
+
} else {
|
|
691
|
+
flashTypes = `/**
|
|
692
|
+
* FlashMessages interface - define via module augmentation.
|
|
693
|
+
* Default flash types are disabled.
|
|
694
|
+
*/
|
|
695
|
+
export interface FlashMessages {}
|
|
696
|
+
|
|
697
|
+
`;
|
|
698
|
+
}
|
|
699
|
+
const sharedPropsContent = includeDefaultAuth || includeDefaultFlash ? ` auth?: AuthData
|
|
700
|
+
flash?: FlashMessages` : "";
|
|
701
|
+
const pageEntries = [];
|
|
702
|
+
for (const [component, data] of Object.entries(json.pages)) {
|
|
703
|
+
const propsType = data.propsType ? data.propsType : "Record<string, unknown>";
|
|
704
|
+
pageEntries.push(` "${component}": ${propsType} & FullSharedProps`);
|
|
705
|
+
}
|
|
706
|
+
const body = `// AUTO-GENERATED by litestar-vite. Do not edit.
|
|
707
|
+
/* eslint-disable */
|
|
708
|
+
|
|
709
|
+
${userTypes}${authTypes}${flashTypes}/**
|
|
710
|
+
* Generated shared props (always present).
|
|
711
|
+
* Includes built-in props + static config props.
|
|
712
|
+
*/
|
|
713
|
+
export interface GeneratedSharedProps {
|
|
714
|
+
errors?: Record<string, string[]>
|
|
715
|
+
csrf_token?: string
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* User-defined shared props for dynamic share() calls in guards/middleware.
|
|
720
|
+
* Extend this interface via module augmentation.
|
|
721
|
+
*
|
|
722
|
+
* @example
|
|
723
|
+
* declare module 'litestar-vite/inertia' {
|
|
724
|
+
* interface User {
|
|
725
|
+
* avatarUrl?: string | null
|
|
726
|
+
* roles: Role[]
|
|
727
|
+
* teams: Team[]
|
|
728
|
+
* }
|
|
729
|
+
* interface SharedProps {
|
|
730
|
+
* locale?: string
|
|
731
|
+
* currentTeam?: CurrentTeam
|
|
732
|
+
* }
|
|
733
|
+
* }
|
|
734
|
+
*/
|
|
735
|
+
export interface SharedProps {
|
|
736
|
+
${sharedPropsContent}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/** Full shared props = generated + user-defined */
|
|
740
|
+
export type FullSharedProps = GeneratedSharedProps & SharedProps
|
|
741
|
+
|
|
742
|
+
/** Page props mapped by component name */
|
|
743
|
+
export interface PageProps {
|
|
744
|
+
${pageEntries.join("\n")}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/** Component name union type */
|
|
748
|
+
export type ComponentName = keyof PageProps
|
|
749
|
+
|
|
750
|
+
/** Type-safe props for a specific component */
|
|
751
|
+
export type InertiaPageProps<C extends ComponentName> = PageProps[C]
|
|
752
|
+
|
|
753
|
+
/** Get props type for a specific page component */
|
|
754
|
+
export type PagePropsFor<C extends ComponentName> = PageProps[C]
|
|
755
|
+
|
|
756
|
+
// Re-export for module augmentation
|
|
757
|
+
declare module "litestar-vite/inertia" {
|
|
758
|
+
export { User, AuthData, FlashMessages, SharedProps, GeneratedSharedProps, FullSharedProps, PageProps, ComponentName, InertiaPageProps, PagePropsFor }
|
|
759
|
+
}
|
|
760
|
+
`;
|
|
761
|
+
await fs.promises.writeFile(outFile, body, "utf-8");
|
|
762
|
+
}
|
|
763
|
+
async function emitRouteTypes(routesPath, outputDir, globalRoute = false) {
|
|
568
764
|
const contents = await fs.promises.readFile(routesPath, "utf-8");
|
|
569
765
|
const json = JSON.parse(contents);
|
|
570
766
|
const outDir = path.resolve(process.cwd(), outputDir);
|
|
@@ -677,18 +873,30 @@ declare global {
|
|
|
677
873
|
*/
|
|
678
874
|
routes?: Record<string, string>
|
|
679
875
|
serverRoutes?: Record<string, string>
|
|
876
|
+
/**
|
|
877
|
+
* Global route helper (available when globalRoute=true in TypeGenConfig).
|
|
878
|
+
* @see route
|
|
879
|
+
*/
|
|
880
|
+
route?: typeof route
|
|
680
881
|
}
|
|
681
882
|
}
|
|
682
883
|
|
|
683
884
|
// Re-export helper functions from litestar-vite-plugin
|
|
684
885
|
// These work with the routes defined above
|
|
685
886
|
export { getCsrfToken, csrfHeaders, csrfFetch } from "litestar-vite-plugin/helpers"
|
|
887
|
+
${globalRoute ? `
|
|
888
|
+
// Register route() globally on window for Laravel/Ziggy-style usage
|
|
889
|
+
if (typeof window !== "undefined") {
|
|
890
|
+
window.route = route
|
|
891
|
+
}
|
|
892
|
+
` : ""}
|
|
686
893
|
`;
|
|
687
894
|
await fs.promises.writeFile(outFile, `${banner}${body}`, "utf-8");
|
|
688
895
|
}
|
|
689
896
|
function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
|
|
690
897
|
let lastTypesHash = null;
|
|
691
898
|
let lastRoutesHash = null;
|
|
899
|
+
let lastPagePropsHash = null;
|
|
692
900
|
let server = null;
|
|
693
901
|
let isGenerating = false;
|
|
694
902
|
let resolvedConfig = null;
|
|
@@ -703,15 +911,17 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
|
|
|
703
911
|
const projectRoot = resolvedConfig?.root ?? process.cwd();
|
|
704
912
|
const openapiPath = path.resolve(projectRoot, typesConfig.openapiPath);
|
|
705
913
|
const routesPath = path.resolve(projectRoot, typesConfig.routesPath);
|
|
914
|
+
const pagePropsPath = path.resolve(projectRoot, typesConfig.pagePropsPath);
|
|
706
915
|
let generated = false;
|
|
707
916
|
const candidates = [path.resolve(projectRoot, "openapi-ts.config.ts"), path.resolve(projectRoot, "hey-api.config.ts"), path.resolve(projectRoot, ".hey-api.config.ts")];
|
|
708
917
|
const configPath = candidates.find((p) => fs.existsSync(p)) || null;
|
|
709
918
|
chosenConfigPath = configPath;
|
|
710
919
|
const shouldRunOpenApiTs = configPath || typesConfig.generateSdk;
|
|
711
920
|
if (fs.existsSync(openapiPath) && shouldRunOpenApiTs) {
|
|
712
|
-
resolvedConfig?.logger.info(`${colors.cyan("
|
|
713
|
-
if (resolvedConfig) {
|
|
714
|
-
|
|
921
|
+
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Generating TypeScript types...`);
|
|
922
|
+
if (resolvedConfig && configPath) {
|
|
923
|
+
const relConfigPath = formatPath(configPath, resolvedConfig.root);
|
|
924
|
+
resolvedConfig.logger.info(`${colors.cyan("\u2022")} openapi-ts config: ${relConfigPath}`);
|
|
715
925
|
}
|
|
716
926
|
const sdkOutput = path.join(typesConfig.output, "api");
|
|
717
927
|
let args;
|
|
@@ -734,7 +944,7 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
|
|
|
734
944
|
try {
|
|
735
945
|
require.resolve("zod", { paths: [process.cwd()] });
|
|
736
946
|
} catch {
|
|
737
|
-
resolvedConfig?.logger.warn(`${colors.
|
|
947
|
+
resolvedConfig?.logger.warn(`${colors.yellow("!")} zod not installed - run: ${resolveInstallHint()} zod`);
|
|
738
948
|
}
|
|
739
949
|
}
|
|
740
950
|
await execAsync(resolvePackageExecutor(args.join(" "), executor), {
|
|
@@ -743,12 +953,16 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
|
|
|
743
953
|
generated = true;
|
|
744
954
|
}
|
|
745
955
|
if (fs.existsSync(routesPath)) {
|
|
746
|
-
await emitRouteTypes(routesPath, typesConfig.output);
|
|
956
|
+
await emitRouteTypes(routesPath, typesConfig.output, typesConfig.globalRoute ?? false);
|
|
957
|
+
generated = true;
|
|
958
|
+
}
|
|
959
|
+
if (fs.existsSync(pagePropsPath)) {
|
|
960
|
+
await emitPagePropsTypes(pagePropsPath, typesConfig.output);
|
|
747
961
|
generated = true;
|
|
748
962
|
}
|
|
749
963
|
if (generated && resolvedConfig) {
|
|
750
964
|
const duration = Date.now() - startTime;
|
|
751
|
-
resolvedConfig.logger.info(`${colors.
|
|
965
|
+
resolvedConfig.logger.info(`${colors.green("\u2713")} TypeScript artifacts updated ${colors.dim(`(${duration}ms)`)}`);
|
|
752
966
|
}
|
|
753
967
|
if (generated && server) {
|
|
754
968
|
server.ws.send({
|
|
@@ -789,11 +1003,12 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
|
|
|
789
1003
|
server = devServer;
|
|
790
1004
|
if (typesConfig.enabled) {
|
|
791
1005
|
const root = resolvedConfig?.root ?? process.cwd();
|
|
792
|
-
const
|
|
793
|
-
const
|
|
794
|
-
resolvedConfig?.logger.info(`${colors.cyan("
|
|
1006
|
+
const openapiRel = path.basename(typesConfig.openapiPath);
|
|
1007
|
+
const routesRel = path.basename(typesConfig.routesPath);
|
|
1008
|
+
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Watching: ${colors.yellow(openapiRel)}, ${colors.yellow(routesRel)}`);
|
|
795
1009
|
if (chosenConfigPath) {
|
|
796
|
-
|
|
1010
|
+
const relConfigPath = formatPath(chosenConfigPath, root);
|
|
1011
|
+
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} openapi-ts config: ${colors.yellow(relConfigPath)}`);
|
|
797
1012
|
}
|
|
798
1013
|
}
|
|
799
1014
|
},
|
|
@@ -831,17 +1046,24 @@ Solutions:
|
|
|
831
1046
|
const relativePath = path.relative(root, file);
|
|
832
1047
|
const openapiPath = typesConfig.openapiPath.replace(/^\.\//, "");
|
|
833
1048
|
const routesPath = typesConfig.routesPath.replace(/^\.\//, "");
|
|
834
|
-
|
|
1049
|
+
const pagePropsPath = typesConfig.pagePropsPath.replace(/^\.\//, "");
|
|
1050
|
+
const isOpenapi = relativePath === openapiPath || file.endsWith(openapiPath);
|
|
1051
|
+
const isRoutes = relativePath === routesPath || file.endsWith(routesPath);
|
|
1052
|
+
const isPageProps = relativePath === pagePropsPath || file.endsWith(pagePropsPath);
|
|
1053
|
+
if (isOpenapi || isRoutes || isPageProps) {
|
|
835
1054
|
if (resolvedConfig) {
|
|
836
1055
|
resolvedConfig.logger.info(`${colors.cyan("litestar-vite")} ${colors.dim("schema changed:")} ${colors.yellow(relativePath)}`);
|
|
837
1056
|
}
|
|
838
1057
|
const newHash = await hashFile(file);
|
|
839
|
-
if (
|
|
1058
|
+
if (isOpenapi) {
|
|
840
1059
|
if (lastTypesHash === newHash) return;
|
|
841
1060
|
lastTypesHash = newHash;
|
|
842
|
-
} else {
|
|
1061
|
+
} else if (isRoutes) {
|
|
843
1062
|
if (lastRoutesHash === newHash) return;
|
|
844
1063
|
lastRoutesHash = newHash;
|
|
1064
|
+
} else if (isPageProps) {
|
|
1065
|
+
if (lastPagePropsHash === newHash) return;
|
|
1066
|
+
lastPagePropsHash = newHash;
|
|
845
1067
|
}
|
|
846
1068
|
debouncedRunTypeGeneration();
|
|
847
1069
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path formatting utilities for consistent logging output.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Format an absolute path as relative to the project root for cleaner logging.
|
|
8
|
+
*
|
|
9
|
+
* @param absolutePath - The absolute path to format
|
|
10
|
+
* @param root - The project root directory (defaults to process.cwd())
|
|
11
|
+
* @returns The path relative to root, or the original path if already relative or on different drive
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatPath(absolutePath: string, root?: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Format multiple paths, joining them with a separator.
|
|
16
|
+
*
|
|
17
|
+
* @param paths - Array of absolute paths to format
|
|
18
|
+
* @param root - The project root directory (defaults to process.cwd())
|
|
19
|
+
* @param separator - Separator between paths (defaults to ", ")
|
|
20
|
+
* @returns Formatted paths joined by separator
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatPaths(paths: string[], root?: string, separator?: string): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
function formatPath(absolutePath, root) {
|
|
3
|
+
if (!absolutePath) return absolutePath;
|
|
4
|
+
const projectRoot = root ?? process.cwd();
|
|
5
|
+
if (!path.isAbsolute(absolutePath)) {
|
|
6
|
+
return absolutePath;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const relativePath = path.relative(projectRoot, absolutePath);
|
|
10
|
+
if (path.isAbsolute(relativePath)) {
|
|
11
|
+
return absolutePath;
|
|
12
|
+
}
|
|
13
|
+
return relativePath;
|
|
14
|
+
} catch {
|
|
15
|
+
return absolutePath;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function formatPaths(paths, root, separator = ", ") {
|
|
19
|
+
return paths.map((p) => formatPath(p, root)).join(separator);
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
formatPath,
|
|
23
|
+
formatPaths
|
|
24
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for litestar-vite with configurable output.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Logging configuration matching the Python LoggingConfig.
|
|
8
|
+
*/
|
|
9
|
+
export interface LoggingConfig {
|
|
10
|
+
level: "quiet" | "normal" | "verbose";
|
|
11
|
+
showPathsAbsolute: boolean;
|
|
12
|
+
suppressNpmOutput: boolean;
|
|
13
|
+
suppressViteBanner: boolean;
|
|
14
|
+
timestamps: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Default logging configuration.
|
|
18
|
+
*/
|
|
19
|
+
export declare const defaultLoggingConfig: LoggingConfig;
|
|
20
|
+
/**
|
|
21
|
+
* Logger instance with configurable behavior.
|
|
22
|
+
*/
|
|
23
|
+
export interface Logger {
|
|
24
|
+
/** Log a message at normal level */
|
|
25
|
+
info: (message: string) => void;
|
|
26
|
+
/** Log a message at verbose level */
|
|
27
|
+
debug: (message: string) => void;
|
|
28
|
+
/** Log a warning (always shown except in quiet mode) */
|
|
29
|
+
warn: (message: string) => void;
|
|
30
|
+
/** Log an error (always shown) */
|
|
31
|
+
error: (message: string) => void;
|
|
32
|
+
/** Format a path according to config (relative or absolute) */
|
|
33
|
+
path: (absolutePath: string, root?: string) => string;
|
|
34
|
+
/** Get the current config */
|
|
35
|
+
config: LoggingConfig;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a logger instance with the given configuration.
|
|
39
|
+
*
|
|
40
|
+
* @param config - Logging configuration (partial, will be merged with defaults)
|
|
41
|
+
* @returns A Logger instance
|
|
42
|
+
*/
|
|
43
|
+
export declare function createLogger(config?: Partial<LoggingConfig> | null): Logger;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { formatPath } from "./format-path.js";
|
|
2
|
+
const defaultLoggingConfig = {
|
|
3
|
+
level: "normal",
|
|
4
|
+
showPathsAbsolute: false,
|
|
5
|
+
suppressNpmOutput: false,
|
|
6
|
+
suppressViteBanner: false,
|
|
7
|
+
timestamps: false
|
|
8
|
+
};
|
|
9
|
+
const LOG_LEVELS = {
|
|
10
|
+
quiet: 0,
|
|
11
|
+
normal: 1,
|
|
12
|
+
verbose: 2
|
|
13
|
+
};
|
|
14
|
+
function createLogger(config) {
|
|
15
|
+
const mergedConfig = {
|
|
16
|
+
...defaultLoggingConfig,
|
|
17
|
+
...config
|
|
18
|
+
};
|
|
19
|
+
const levelNum = LOG_LEVELS[mergedConfig.level];
|
|
20
|
+
const formatMessage = (message) => {
|
|
21
|
+
if (mergedConfig.timestamps) {
|
|
22
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
23
|
+
return `[${now}] ${message}`;
|
|
24
|
+
}
|
|
25
|
+
return message;
|
|
26
|
+
};
|
|
27
|
+
const formatPathValue = (absolutePath, root) => {
|
|
28
|
+
if (mergedConfig.showPathsAbsolute) {
|
|
29
|
+
return absolutePath;
|
|
30
|
+
}
|
|
31
|
+
return formatPath(absolutePath, root);
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
info: (message) => {
|
|
35
|
+
if (levelNum >= LOG_LEVELS.normal) {
|
|
36
|
+
console.log(formatMessage(message));
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
debug: (message) => {
|
|
40
|
+
if (levelNum >= LOG_LEVELS.verbose) {
|
|
41
|
+
console.log(formatMessage(message));
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
warn: (message) => {
|
|
45
|
+
if (levelNum >= LOG_LEVELS.normal) {
|
|
46
|
+
console.warn(formatMessage(message));
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
error: (message) => {
|
|
50
|
+
console.error(formatMessage(message));
|
|
51
|
+
},
|
|
52
|
+
path: formatPathValue,
|
|
53
|
+
config: mergedConfig
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
createLogger,
|
|
58
|
+
defaultLoggingConfig
|
|
59
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litestar-vite-plugin",
|
|
3
|
-
"version": "0.15.0-
|
|
3
|
+
"version": "0.15.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Litestar plugin for Vite.",
|
|
6
6
|
"keywords": [
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"build": "npm run build-plugin && npm run build-helpers && npm run build-inertia-helpers && npm run build-integrations",
|
|
56
56
|
"build-plugin": "rm -rf dist/js && npm run build-plugin-types && npm run build-plugin-esm && cp src/js/src/dev-server-index.html dist/js/",
|
|
57
57
|
"build-plugin-types": "tsc --project src/js/tsconfig.json --emitDeclarationOnly",
|
|
58
|
-
"build-plugin-esm": "esbuild src/js/src/index.ts --platform=node --format=esm --outfile=dist/js/index.js && esbuild src/js/src/install-hint.ts --platform=node --format=esm --outfile=dist/js/install-hint.js && esbuild src/js/src/litestar-meta.ts --platform=node --format=esm --outfile=dist/js/litestar-meta.js && mkdir -p dist/js/shared && esbuild src/js/src/shared/debounce.ts --platform=node --format=esm --
|
|
58
|
+
"build-plugin-esm": "esbuild src/js/src/index.ts --platform=node --format=esm --outfile=dist/js/index.js && esbuild src/js/src/install-hint.ts --platform=node --format=esm --outfile=dist/js/install-hint.js && esbuild src/js/src/litestar-meta.ts --platform=node --format=esm --outfile=dist/js/litestar-meta.js && mkdir -p dist/js/shared && esbuild src/js/src/shared/debounce.ts src/js/src/shared/format-path.ts src/js/src/shared/logger.ts --platform=node --format=esm --outdir=dist/js/shared",
|
|
59
59
|
"build-helpers": "rm -rf dist/js/helpers && tsc --project src/js/tsconfig.helpers.json",
|
|
60
60
|
"build-inertia-helpers": "rm -rf dist/js/inertia-helpers && tsc --project src/js/tsconfig.inertia-helpers.json",
|
|
61
61
|
"build-integrations": "esbuild src/js/src/astro.ts src/js/src/sveltekit.ts src/js/src/nuxt.ts --platform=node --format=esm --outdir=dist/js",
|