litestar-vite-plugin 0.15.0-alpha.7 → 0.15.0-beta.2

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/dist/js/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { exec } from "node:child_process";
2
- import { createHash } from "node:crypto";
3
2
  import fs from "node:fs";
4
3
  import path from "node:path";
5
4
  import { fileURLToPath } from "node:url";
@@ -10,6 +9,9 @@ import fullReload from "vite-plugin-full-reload";
10
9
  import { resolveInstallHint, resolvePackageExecutor } from "./install-hint.js";
11
10
  import { checkBackendAvailability, loadLitestarMeta } from "./litestar-meta.js";
12
11
  import { debounce } from "./shared/debounce.js";
12
+ import { emitRouteTypes } from "./shared/emit-route-types.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.resourceDirectory.replace(/^\//, ""), "index.html"),
33
- // Ensure resourceDirectory path is relative to root
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.bundleDirectory.replace(/^\//, ""), "index.html")
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.resourceDirectory.replace(/^\/+/, "").replace(/\/+$/, "")}/`
73
+ "@": `/${pluginConfig.resourceDir.replace(/^\/+/, "").replace(/\/+$/, "")}/`
68
74
  };
69
75
  return {
70
76
  name: "litestar",
@@ -195,9 +201,9 @@ function resolveLitestarPlugin(pluginConfig) {
195
201
  resolvedConfig.logger.warn(formatMissingConfigWarning());
196
202
  }
197
203
  }
198
- const resourceDir = path.resolve(resolvedConfig.root, pluginConfig.resourceDirectory);
199
- if (!fs.existsSync(resourceDir) && typeof resolvedConfig.logger?.warn === "function") {
200
- resolvedConfig.logger.warn(`${colors.cyan("litestar-vite")} ${colors.yellow("Resource directory not found:")} ${pluginConfig.resourceDirectory}`);
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}`);
201
207
  }
202
208
  const hint = pluginConfig.types !== false ? pluginConfig.types.routesPath : void 0;
203
209
  litestarMeta = await loadLitestarMeta(resolvedConfig, hint);
@@ -229,19 +235,20 @@ function resolveLitestarPlugin(pluginConfig) {
229
235
  fs.writeFileSync(pluginConfig.hotFile, viteDevServerUrl);
230
236
  }
231
237
  setTimeout(async () => {
232
- const version = litestarMeta.litestarVersion ?? process.env.LITESTAR_VERSION ?? "unknown";
238
+ if (logger.config.level === "quiet") return;
239
+ const litestarVersion = litestarMeta.litestarVersion ?? process.env.LITESTAR_VERSION ?? "unknown";
233
240
  const backendStatus = await checkBackendAvailability(appUrl);
234
241
  resolvedConfig.logger.info(`
235
- ${colors.red(`${colors.bold("LITESTAR")} ${version}`)}`);
242
+ ${colors.red(`${colors.bold("LITESTAR")} ${litestarVersion}`)}`);
236
243
  resolvedConfig.logger.info("");
237
244
  if (initialIndexPath) {
238
- resolvedConfig.logger.info(
239
- ` ${colors.green("\u279C")} ${colors.bold("Index Mode")}: SPA (Serving ${colors.cyan(path.relative(server.config.root, initialIndexPath))} from root)`
240
- );
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`);
241
249
  } else {
242
- resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Index Mode")}: Litestar (Plugin will serve placeholder for /index.html)`);
250
+ resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Mode")}: Litestar`);
243
251
  }
244
- resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Dev Server")}: ${colors.cyan(viteDevServerUrl)}`);
245
252
  if (backendStatus.available) {
246
253
  resolvedConfig.logger.info(
247
254
  ` ${colors.green("\u279C")} ${colors.bold("App URL")}: ${colors.cyan(appUrl.replace(/:(\d+)/, (_, port) => `:${colors.bold(port)}`))} ${colors.green("\u2713")}`
@@ -251,12 +258,13 @@ function resolveLitestarPlugin(pluginConfig) {
251
258
  ` ${colors.yellow("\u279C")} ${colors.bold("App URL")}: ${colors.cyan(appUrl.replace(/:(\d+)/, (_, port) => `:${colors.bold(port)}`))} ${colors.yellow("\u26A0")}`
252
259
  );
253
260
  }
254
- resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Assets Base")}: ${colors.cyan(resolvedConfig.base)}`);
261
+ resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Dev Server")}: ${colors.cyan(viteDevServerUrl)}`);
255
262
  if (pluginConfig.types !== false && pluginConfig.types.enabled) {
256
263
  const openapiExists = fs.existsSync(path.resolve(process.cwd(), pluginConfig.types.openapiPath));
257
264
  const routesExists = fs.existsSync(path.resolve(process.cwd(), pluginConfig.types.routesPath));
265
+ const relTypesOutput = logger.path(pluginConfig.types.output, process.cwd());
258
266
  if (openapiExists || routesExists) {
259
- resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Type Gen")}: ${colors.green("enabled")} ${colors.dim(`\u2192 ${pluginConfig.types.output}`)}`);
267
+ resolvedConfig.logger.info(` ${colors.green("\u279C")} ${colors.bold("Type Gen")}: ${colors.dim(`${relTypesOutput}/`)}`);
260
268
  } else {
261
269
  resolvedConfig.logger.info(` ${colors.yellow("\u279C")} ${colors.bold("Type Gen")}: ${colors.yellow("waiting")} ${colors.dim("(no schema files yet)")}`);
262
270
  }
@@ -359,13 +367,6 @@ function ensureCommandShouldRunInEnvironment(command, env, mode) {
359
367
  );
360
368
  }
361
369
  }
362
- function _pluginVersion() {
363
- try {
364
- return JSON.parse(fs.readFileSync(path.join(dirname(), "../package.json")).toString())?.version;
365
- } catch {
366
- return "";
367
- }
368
- }
369
370
  function loadPythonDefaults() {
370
371
  const isTestEnv = Boolean(process.env.VITEST || process.env.VITE_TEST || process.env.NODE_ENV === "test");
371
372
  let configPath = process.env.LITESTAR_VITE_CONFIG_PATH;
@@ -413,7 +414,7 @@ function formatMissingConfigWarning() {
413
414
  `${y("\u2502")} ${d("litestar({")} ${y("\u2502")}`,
414
415
  `${y("\u2502")} ${d(' input: ["src/main.tsx"],')} ${y("\u2502")}`,
415
416
  `${y("\u2502")} ${d(' assetUrl: "/static/",')} ${y("\u2502")}`,
416
- `${y("\u2502")} ${d(' bundleDirectory: "public",')} ${y("\u2502")}`,
417
+ `${y("\u2502")} ${d(' bundleDir: "public",')} ${y("\u2502")}`,
417
418
  `${y("\u2502")} ${d(" types: false,")} ${y("\u2502")}`,
418
419
  `${y("\u2502")} ${d("})")} ${y("\u2502")}`,
419
420
  `${y("\u2502")} ${y("\u2502")}`,
@@ -440,16 +441,16 @@ function resolvePluginConfig(config) {
440
441
  if (typeof resolvedConfig.input === "undefined") {
441
442
  throw new Error('litestar-vite-plugin: missing configuration for "input".');
442
443
  }
443
- if (typeof resolvedConfig.resourceDirectory === "string") {
444
- resolvedConfig.resourceDirectory = resolvedConfig.resourceDirectory.trim().replace(/^\/+/, "").replace(/\/+$/, "");
445
- if (resolvedConfig.resourceDirectory === "") {
446
- throw new Error("litestar-vite-plugin: resourceDirectory must be a subdirectory. E.g. 'resources'.");
444
+ if (typeof resolvedConfig.resourceDir === "string") {
445
+ resolvedConfig.resourceDir = resolvedConfig.resourceDir.trim().replace(/^\/+/, "").replace(/\/+$/, "");
446
+ if (resolvedConfig.resourceDir === "") {
447
+ throw new Error("litestar-vite-plugin: resourceDir must be a subdirectory. E.g. 'resources'.");
447
448
  }
448
449
  }
449
- if (typeof resolvedConfig.bundleDirectory === "string") {
450
- resolvedConfig.bundleDirectory = resolvedConfig.bundleDirectory.trim().replace(/^\/+/, "").replace(/\/+$/, "");
451
- if (resolvedConfig.bundleDirectory === "") {
452
- throw new Error("litestar-vite-plugin: bundleDirectory must be a subdirectory. E.g. 'public'.");
450
+ if (typeof resolvedConfig.bundleDir === "string") {
451
+ resolvedConfig.bundleDir = resolvedConfig.bundleDir.trim().replace(/^\/+/, "").replace(/\/+$/, "");
452
+ if (resolvedConfig.bundleDir === "") {
453
+ throw new Error("litestar-vite-plugin: bundleDir must be a subdirectory. E.g. 'public'.");
453
454
  }
454
455
  }
455
456
  if (typeof resolvedConfig.publicDir === "string") {
@@ -458,8 +459,8 @@ function resolvePluginConfig(config) {
458
459
  throw new Error("litestar-vite-plugin: publicDir must be a subdirectory. E.g. 'public'.");
459
460
  }
460
461
  }
461
- if (typeof resolvedConfig.ssrOutputDirectory === "string") {
462
- resolvedConfig.ssrOutputDirectory = resolvedConfig.ssrOutputDirectory.trim().replace(/^\/+/, "").replace(/\/+$/, "");
462
+ if (typeof resolvedConfig.ssrOutDir === "string") {
463
+ resolvedConfig.ssrOutDir = resolvedConfig.ssrOutDir.trim().replace(/^\/+/, "").replace(/\/+$/, "");
463
464
  }
464
465
  if (resolvedConfig.refresh === true) {
465
466
  resolvedConfig.refresh = [{ paths: refreshPaths }];
@@ -475,6 +476,7 @@ function resolvePluginConfig(config) {
475
476
  pagePropsPath: "src/generated/inertia-pages.json",
476
477
  generateZod: false,
477
478
  generateSdk: false,
479
+ globalRoute: false,
478
480
  debounce: 300
479
481
  };
480
482
  } else if (resolvedConfig.types === "auto" || typeof resolvedConfig.types === "undefined") {
@@ -487,6 +489,7 @@ function resolvePluginConfig(config) {
487
489
  pagePropsPath: pythonDefaults.types.pagePropsPath ?? path.join(pythonDefaults.types.output, "inertia-pages.json"),
488
490
  generateZod: pythonDefaults.types.generateZod,
489
491
  generateSdk: pythonDefaults.types.generateSdk,
492
+ globalRoute: pythonDefaults.types.globalRoute ?? false,
490
493
  debounce: 300
491
494
  };
492
495
  }
@@ -502,6 +505,7 @@ function resolvePluginConfig(config) {
502
505
  pagePropsPath: resolvedConfig.types.pagePropsPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "inertia-pages.json") : "src/generated/inertia-pages.json"),
503
506
  generateZod: resolvedConfig.types.generateZod ?? false,
504
507
  generateSdk: resolvedConfig.types.generateSdk ?? false,
508
+ globalRoute: resolvedConfig.types.globalRoute ?? false,
505
509
  debounce: resolvedConfig.types.debounce ?? 300
506
510
  };
507
511
  if (!userProvidedOpenapi && resolvedConfig.types.output) {
@@ -514,23 +518,55 @@ function resolvePluginConfig(config) {
514
518
  typesConfig.pagePropsPath = path.join(typesConfig.output, "inertia-pages.json");
515
519
  }
516
520
  }
517
- return {
521
+ const inertiaMode = resolvedConfig.inertiaMode ?? pythonDefaults?.mode === "inertia";
522
+ const result = {
518
523
  input: resolvedConfig.input,
519
524
  assetUrl: normalizeAssetUrl(resolvedConfig.assetUrl ?? pythonDefaults?.assetUrl ?? "/static/"),
520
- resourceDirectory: resolvedConfig.resourceDirectory ?? pythonDefaults?.resourceDir ?? "resources",
521
- bundleDirectory: resolvedConfig.bundleDirectory ?? pythonDefaults?.bundleDir ?? "public",
525
+ resourceDir: resolvedConfig.resourceDir ?? pythonDefaults?.resourceDir ?? "src",
526
+ bundleDir: resolvedConfig.bundleDir ?? pythonDefaults?.bundleDir ?? "public",
522
527
  publicDir: resolvedConfig.publicDir ?? pythonDefaults?.publicDir ?? "public",
523
528
  ssr: resolvedConfig.ssr ?? resolvedConfig.input,
524
- ssrOutputDirectory: resolvedConfig.ssrOutputDirectory ?? pythonDefaults?.ssrOutDir ?? path.join(resolvedConfig.resourceDirectory ?? pythonDefaults?.resourceDir ?? "resources", "bootstrap/ssr"),
529
+ ssrOutDir: resolvedConfig.ssrOutDir ?? pythonDefaults?.ssrOutDir ?? path.join(resolvedConfig.resourceDir ?? pythonDefaults?.resourceDir ?? "src", "bootstrap/ssr"),
525
530
  refresh: resolvedConfig.refresh ?? false,
526
- hotFile: resolvedConfig.hotFile ?? path.join(resolvedConfig.bundleDirectory ?? "public", "hot"),
531
+ hotFile: resolvedConfig.hotFile ?? path.join(resolvedConfig.bundleDir ?? "public", "hot"),
527
532
  detectTls: resolvedConfig.detectTls ?? false,
528
533
  autoDetectIndex: resolvedConfig.autoDetectIndex ?? true,
534
+ inertiaMode,
529
535
  transformOnServe: resolvedConfig.transformOnServe ?? ((code) => code),
530
536
  types: typesConfig,
531
537
  executor: resolvedConfig.executor ?? pythonDefaults?.executor,
532
538
  hasPythonConfig: pythonDefaults !== null
533
539
  };
540
+ validateAgainstPythonDefaults(result, pythonDefaults, resolvedConfig);
541
+ return result;
542
+ }
543
+ function validateAgainstPythonDefaults(resolved, pythonDefaults, userConfig) {
544
+ if (!pythonDefaults) return;
545
+ const warnings = [];
546
+ const hasPythonValue = (value) => typeof value === "string" && value.length > 0;
547
+ if (userConfig.assetUrl !== void 0 && hasPythonValue(pythonDefaults.assetUrl) && resolved.assetUrl !== pythonDefaults.assetUrl) {
548
+ warnings.push(`assetUrl: vite.config.ts="${resolved.assetUrl}" differs from Python="${pythonDefaults.assetUrl}"`);
549
+ }
550
+ if (userConfig.bundleDir !== void 0 && hasPythonValue(pythonDefaults.bundleDir) && resolved.bundleDir !== pythonDefaults.bundleDir) {
551
+ warnings.push(`bundleDir: vite.config.ts="${resolved.bundleDir}" differs from Python="${pythonDefaults.bundleDir}"`);
552
+ }
553
+ if (userConfig.resourceDir !== void 0 && hasPythonValue(pythonDefaults.resourceDir) && resolved.resourceDir !== pythonDefaults.resourceDir) {
554
+ warnings.push(`resourceDir: vite.config.ts="${resolved.resourceDir}" differs from Python="${pythonDefaults.resourceDir}"`);
555
+ }
556
+ if (userConfig.publicDir !== void 0 && hasPythonValue(pythonDefaults.publicDir) && resolved.publicDir !== pythonDefaults.publicDir) {
557
+ warnings.push(`publicDir: vite.config.ts="${resolved.publicDir}" differs from Python="${pythonDefaults.publicDir}"`);
558
+ }
559
+ if (pythonDefaults.ssrEnabled && userConfig.ssrOutDir !== void 0 && hasPythonValue(pythonDefaults.ssrOutDir) && resolved.ssrOutDir !== pythonDefaults.ssrOutDir) {
560
+ warnings.push(`ssrOutDir: vite.config.ts="${resolved.ssrOutDir}" differs from Python="${pythonDefaults.ssrOutDir}"`);
561
+ }
562
+ if (warnings.length > 0) {
563
+ console.warn(
564
+ colors.yellow("[litestar-vite] Configuration mismatch detected:\n") + warnings.map((w) => ` ${colors.dim("\u2022")} ${w}`).join("\n") + `
565
+
566
+ ${colors.dim("Precedence: vite.config.ts > .litestar.json > defaults")}
567
+ ` + colors.dim("See: https://docs.litestar.dev/vite/config-precedence\n")
568
+ );
569
+ }
534
570
  }
535
571
  function resolveBase(_config, assetUrl) {
536
572
  if (process.env.NODE_ENV === "development") {
@@ -544,11 +580,15 @@ function resolveInput(config, ssr) {
544
580
  }
545
581
  return config.input;
546
582
  }
583
+ function isAbsolutePath(path2) {
584
+ return path2.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(path2);
585
+ }
547
586
  function resolveOutDir(config, ssr) {
548
- if (ssr) {
549
- return config.ssrOutputDirectory.replace(/^\/+/, "").replace(/\/+$/, "");
587
+ const dir = ssr ? config.ssrOutDir : config.bundleDir;
588
+ if (isAbsolutePath(dir)) {
589
+ return dir.replace(/[\\/]+$/, "");
550
590
  }
551
- return config.bundleDirectory.replace(/^\/+/, "").replace(/\/+$/, "");
591
+ return dir.replace(/^\/+/, "").replace(/\/+$/, "");
552
592
  }
553
593
  function resolveFullReloadConfig({ refresh: config }) {
554
594
  if (typeof config === "boolean") {
@@ -717,128 +757,6 @@ declare module "litestar-vite/inertia" {
717
757
  `;
718
758
  await fs.promises.writeFile(outFile, body, "utf-8");
719
759
  }
720
- async function emitRouteTypes(routesPath, outputDir) {
721
- const contents = await fs.promises.readFile(routesPath, "utf-8");
722
- const json = JSON.parse(contents);
723
- const outDir = path.resolve(process.cwd(), outputDir);
724
- await fs.promises.mkdir(outDir, { recursive: true });
725
- const outFile = path.join(outDir, "routes.ts");
726
- const banner = `// AUTO-GENERATED by litestar-vite. Do not edit.
727
- /* eslint-disable */
728
-
729
- `;
730
- const routesData = json.routes || json;
731
- const routeNames = Object.keys(routesData);
732
- const routeNameType = routeNames.length > 0 ? routeNames.map((n) => `"${n}"`).join(" | ") : "never";
733
- const routeParamTypes = [];
734
- for (const [name, data] of Object.entries(routesData)) {
735
- const routeData = data;
736
- if (routeData.parameters && routeData.parameters.length > 0) {
737
- const params = routeData.parameters.map((p) => `${p}: string | number`).join("; ");
738
- routeParamTypes.push(` "${name}": { ${params} }`);
739
- } else {
740
- routeParamTypes.push(` "${name}": Record<string, never>`);
741
- }
742
- }
743
- const body = `/**
744
- * AUTO-GENERATED by litestar-vite.
745
- *
746
- * Exports:
747
- * - routesMeta: full route metadata
748
- * - routes: name -> uri map
749
- * - serverRoutes: alias of routes for clarity in apps
750
- * - route(): type-safe URL generator
751
- * - hasRoute(): type guard
752
- * - csrf helpers re-exported from litestar-vite-plugin/helpers
753
- *
754
- * @see https://litestar-vite.litestar.dev/
755
- */
756
- export const routesMeta = ${JSON.stringify(json, null, 2)} as const
757
-
758
- /**
759
- * Route name to URI mapping.
760
- */
761
- export const routes = ${JSON.stringify(Object.fromEntries(Object.entries(routesData).map(([name, data]) => [name, data.uri])), null, 2)} as const
762
-
763
- /**
764
- * Alias for server-injected route map (more descriptive for consumers).
765
- */
766
- export const serverRoutes = routes
767
-
768
- /**
769
- * All available route names.
770
- */
771
- export type RouteName = ${routeNameType}
772
-
773
- /**
774
- * Parameter types for each route.
775
- */
776
- export interface RouteParams {
777
- ${routeParamTypes.join("\n")}
778
- }
779
-
780
- /**
781
- * Generate a URL for a named route with type-safe parameters.
782
- *
783
- * @param name - The route name
784
- * @param params - Route parameters (required if route has path parameters)
785
- * @returns The generated URL
786
- *
787
- * @example
788
- * \`\`\`ts
789
- * import { route } from '@/generated/routes'
790
- *
791
- * // Route without parameters
792
- * route('home') // "/"
793
- *
794
- * // Route with parameters
795
- * route('user:detail', { user_id: 123 }) // "/users/123"
796
- * \`\`\`
797
- */
798
- export function route<T extends RouteName>(
799
- name: T,
800
- ...args: RouteParams[T] extends Record<string, never> ? [] : [params: RouteParams[T]]
801
- ): string {
802
- let uri = routes[name] as string
803
- const params = args[0] as Record<string, string | number> | undefined
804
-
805
- if (params) {
806
- for (const [key, value] of Object.entries(params)) {
807
- // Handle both {param} and {param:type} syntax
808
- uri = uri.replace(new RegExp(\`\\\\{\${key}(?::[^}]+)?\\\\}\`, "g"), String(value))
809
- }
810
- }
811
-
812
- return uri
813
- }
814
-
815
- /**
816
- * Check if a route name exists.
817
- */
818
- export function hasRoute(name: string): name is RouteName {
819
- return name in routes
820
- }
821
-
822
- declare global {
823
- interface Window {
824
- /**
825
- * Fully-typed route metadata injected by Litestar.
826
- */
827
- __LITESTAR_ROUTES__?: typeof routesMeta
828
- /**
829
- * Simple route map (name -> uri) for legacy consumers.
830
- */
831
- routes?: Record<string, string>
832
- serverRoutes?: Record<string, string>
833
- }
834
- }
835
-
836
- // Re-export helper functions from litestar-vite-plugin
837
- // These work with the routes defined above
838
- export { getCsrfToken, csrfHeaders, csrfFetch } from "litestar-vite-plugin/helpers"
839
- `;
840
- await fs.promises.writeFile(outFile, `${banner}${body}`, "utf-8");
841
- }
842
760
  function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
843
761
  let lastTypesHash = null;
844
762
  let lastRoutesHash = null;
@@ -864,10 +782,10 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
864
782
  chosenConfigPath = configPath;
865
783
  const shouldRunOpenApiTs = configPath || typesConfig.generateSdk;
866
784
  if (fs.existsSync(openapiPath) && shouldRunOpenApiTs) {
867
- resolvedConfig?.logger.info(`${colors.cyan("litestar-vite")} ${colors.dim("generating TypeScript types...")}`);
868
- if (resolvedConfig) {
869
- const relConfigPath = configPath ? path.relative(resolvedConfig.root, configPath) : null;
870
- resolvedConfig.logger.info(`${colors.cyan("litestar-vite")} ${colors.dim("openapi-ts config: ")}${relConfigPath ?? "<built-in defaults>"}`);
785
+ resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Generating TypeScript types...`);
786
+ if (resolvedConfig && configPath) {
787
+ const relConfigPath = formatPath(configPath, resolvedConfig.root);
788
+ resolvedConfig.logger.info(`${colors.cyan("\u2022")} openapi-ts config: ${relConfigPath}`);
871
789
  }
872
790
  const sdkOutput = path.join(typesConfig.output, "api");
873
791
  let args;
@@ -890,7 +808,7 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
890
808
  try {
891
809
  require.resolve("zod", { paths: [process.cwd()] });
892
810
  } catch {
893
- resolvedConfig?.logger.warn(`${colors.cyan("litestar-vite")} ${colors.yellow("zod not installed")} - run: ${resolveInstallHint()} zod`);
811
+ resolvedConfig?.logger.warn(`${colors.yellow("!")} zod not installed - run: ${resolveInstallHint()} zod`);
894
812
  }
895
813
  }
896
814
  await execAsync(resolvePackageExecutor(args.join(" "), executor), {
@@ -899,7 +817,7 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
899
817
  generated = true;
900
818
  }
901
819
  if (fs.existsSync(routesPath)) {
902
- await emitRouteTypes(routesPath, typesConfig.output);
820
+ await emitRouteTypes(routesPath, typesConfig.output, { globalRoute: typesConfig.globalRoute ?? false });
903
821
  generated = true;
904
822
  }
905
823
  if (fs.existsSync(pagePropsPath)) {
@@ -908,7 +826,7 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
908
826
  }
909
827
  if (generated && resolvedConfig) {
910
828
  const duration = Date.now() - startTime;
911
- resolvedConfig.logger.info(`${colors.cyan("litestar-vite")} ${colors.green("TypeScript artifacts updated")} ${colors.dim(`in ${duration}ms`)}`);
829
+ resolvedConfig.logger.info(`${colors.green("\u2713")} TypeScript artifacts updated ${colors.dim(`(${duration}ms)`)}`);
912
830
  }
913
831
  if (generated && server) {
914
832
  server.ws.send({
@@ -949,12 +867,12 @@ function resolveTypeGenerationPlugin(typesConfig, executor, hasPythonConfig) {
949
867
  server = devServer;
950
868
  if (typesConfig.enabled) {
951
869
  const root = resolvedConfig?.root ?? process.cwd();
952
- const openapiRel = typesConfig.openapiPath;
953
- const routesRel = typesConfig.routesPath;
954
- resolvedConfig?.logger.info(`${colors.cyan("litestar-vite")} ${colors.dim("watching schema/routes:")} ${colors.yellow(openapiRel)}, ${colors.yellow(routesRel)}`);
870
+ const openapiRel = path.basename(typesConfig.openapiPath);
871
+ const routesRel = path.basename(typesConfig.routesPath);
872
+ resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Watching: ${colors.yellow(openapiRel)}, ${colors.yellow(routesRel)}`);
955
873
  if (chosenConfigPath) {
956
- const relConfigPath = path.relative(root, chosenConfigPath);
957
- resolvedConfig?.logger.info(`${colors.cyan("litestar-vite")} ${colors.dim("openapi-ts config:")} ${colors.yellow(relConfigPath)}`);
874
+ const relConfigPath = formatPath(chosenConfigPath, root);
875
+ resolvedConfig?.logger.info(`${colors.cyan("\u2022")} openapi-ts config: ${colors.yellow(relConfigPath)}`);
958
876
  }
959
877
  }
960
878
  },
@@ -1000,7 +918,7 @@ Solutions:
1000
918
  if (resolvedConfig) {
1001
919
  resolvedConfig.logger.info(`${colors.cyan("litestar-vite")} ${colors.dim("schema changed:")} ${colors.yellow(relativePath)}`);
1002
920
  }
1003
- const newHash = await hashFile(file);
921
+ const newHash = await getFileMtime(file);
1004
922
  if (isOpenapi) {
1005
923
  if (lastTypesHash === newHash) return;
1006
924
  lastTypesHash = newHash;
@@ -1016,9 +934,9 @@ Solutions:
1016
934
  }
1017
935
  };
1018
936
  }
1019
- async function hashFile(filePath) {
1020
- const content = await fs.promises.readFile(filePath);
1021
- return createHash("sha1").update(content).digest("hex");
937
+ async function getFileMtime(filePath) {
938
+ const stat = await fs.promises.stat(filePath);
939
+ return stat.mtimeMs.toString();
1022
940
  }
1023
941
  function resolveDevServerUrl(address, config, userConfig) {
1024
942
  const configHmrProtocol = typeof config.server.hmr === "object" ? config.server.hmr.protocol : null;
@@ -1146,6 +1064,5 @@ function normalizeAssetUrl(url) {
1146
1064
  return withTrailing;
1147
1065
  }
1148
1066
  export {
1149
- litestar as default,
1150
- refreshPaths
1067
+ litestar as default
1151
1068
  };