litestar-vite-plugin 0.15.0-alpha.2 → 0.15.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,12 +34,26 @@ export interface RoutesMap {
34
34
  * Convenience alias for route names when using injected metadata.
35
35
  */
36
36
  export type RouteName = keyof RoutesMap["routes"];
37
+ /**
38
+ * Litestar helpers namespace for clean global access.
39
+ */
40
+ export interface LitestarHelpers {
41
+ route: typeof route;
42
+ toRoute: typeof toRoute;
43
+ currentRoute: typeof currentRoute;
44
+ isRoute: typeof isRoute;
45
+ isCurrentRoute: typeof isCurrentRoute;
46
+ getRelativeUrlPath: typeof getRelativeUrlPath;
47
+ routes: Record<string, string>;
48
+ }
37
49
  declare global {
38
50
  interface Window {
39
51
  __LITESTAR_ROUTES__?: RoutesMap;
52
+ __LITESTAR__?: LitestarHelpers;
40
53
  routes?: Record<string, string>;
41
54
  serverRoutes?: Record<string, string>;
42
55
  }
56
+ var __LITESTAR__: LitestarHelpers | undefined;
43
57
  var routes: Record<string, string>;
44
58
  var serverRoutes: Record<string, string>;
45
59
  }
@@ -137,4 +151,9 @@ export declare function isRoute(url: string, routeName: string): boolean;
137
151
  * ```
138
152
  */
139
153
  export declare function isCurrentRoute(routeName: string): boolean;
154
+ /**
155
+ * Litestar helpers namespace object.
156
+ * Access via window.__LITESTAR__ or import functions directly from this module.
157
+ */
158
+ export declare const LITESTAR: LitestarHelpers;
140
159
  export {};
@@ -13,6 +13,40 @@
13
13
  *
14
14
  * @module
15
15
  */
16
+ /**
17
+ * Cache for compiled route patterns to avoid repeated regex compilation.
18
+ */
19
+ const routePatternCache = new Map();
20
+ /**
21
+ * Compile a route pattern to a regex for URL matching.
22
+ * Results are cached for performance.
23
+ *
24
+ * @param routePattern - Route pattern with optional {param:type} placeholders
25
+ * @returns Compiled regex pattern
26
+ */
27
+ function compileRoutePattern(routePattern) {
28
+ const cached = routePatternCache.get(routePattern);
29
+ if (cached) {
30
+ return cached;
31
+ }
32
+ const regexPattern = routePattern.replace(/\//g, "\\/").replace(/\{([^}]+)\}/g, (_, paramSpec) => {
33
+ // Handle {param:type} syntax
34
+ const paramType = paramSpec.includes(":") ? paramSpec.split(":")[1] : "str";
35
+ switch (paramType) {
36
+ case "uuid":
37
+ return "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
38
+ case "path":
39
+ return ".*";
40
+ case "int":
41
+ return "\\d+";
42
+ default:
43
+ return "[^/]+";
44
+ }
45
+ });
46
+ const compiled = new RegExp(`^${regexPattern}$`);
47
+ routePatternCache.set(routePattern, compiled);
48
+ return compiled;
49
+ }
16
50
  /**
17
51
  * Get the routes object from the page.
18
52
  *
@@ -154,21 +188,7 @@ export function toRoute(url) {
154
188
  const processedUrl = getRelativeUrlPath(url);
155
189
  const normalizedUrl = processedUrl === "/" ? processedUrl : processedUrl.replace(/\/$/, "");
156
190
  for (const [routeName, routePattern] of Object.entries(routes)) {
157
- const regexPattern = routePattern.replace(/\//g, "\\/").replace(/\{([^}]+)\}/g, (_, paramSpec) => {
158
- // Handle {param:type} syntax
159
- const paramType = paramSpec.includes(":") ? paramSpec.split(":")[1] : "str";
160
- switch (paramType) {
161
- case "uuid":
162
- return "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
163
- case "path":
164
- return ".*";
165
- case "int":
166
- return "\\d+";
167
- default:
168
- return "[^/]+";
169
- }
170
- });
171
- const regex = new RegExp(`^${regexPattern}$`);
191
+ const regex = compileRoutePattern(routePattern);
172
192
  if (regex.test(normalizedUrl)) {
173
193
  return routeName;
174
194
  }
@@ -214,20 +234,7 @@ export function isRoute(url, routeName) {
214
234
  const matchingRouteNames = Object.keys(routes).filter((name) => routeNameRegex.test(name));
215
235
  for (const name of matchingRouteNames) {
216
236
  const routePattern = routes[name];
217
- const regexPattern = routePattern.replace(/\//g, "\\/").replace(/\{([^}]+)\}/g, (_, paramSpec) => {
218
- const paramType = paramSpec.includes(":") ? paramSpec.split(":")[1] : "str";
219
- switch (paramType) {
220
- case "uuid":
221
- return "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
222
- case "path":
223
- return "(.*)";
224
- case "int":
225
- return "(\\d+)";
226
- default:
227
- return "([^/]+)";
228
- }
229
- });
230
- const regex = new RegExp(`^${regexPattern}$`);
237
+ const regex = compileRoutePattern(routePattern);
231
238
  if (regex.test(normalizedUrl)) {
232
239
  return true;
233
240
  }
@@ -255,17 +262,27 @@ export function isCurrentRoute(routeName) {
255
262
  const routeNameRegex = new RegExp(`^${routeName.replace(/\*/g, ".*")}$`);
256
263
  return routeNameRegex.test(current);
257
264
  }
258
- // Set up global functions for backward compatibility
265
+ /**
266
+ * Litestar helpers namespace object.
267
+ * Access via window.__LITESTAR__ or import functions directly from this module.
268
+ */
269
+ export const LITESTAR = {
270
+ route,
271
+ toRoute,
272
+ currentRoute,
273
+ isRoute,
274
+ isCurrentRoute,
275
+ getRelativeUrlPath,
276
+ routes: {},
277
+ };
278
+ // Set up namespaced global access
259
279
  if (typeof globalThis !== "undefined") {
260
- globalThis.routes = globalThis.routes || {};
280
+ globalThis.__LITESTAR__ = LITESTAR;
281
+ // Also set up routes for internal use
282
+ globalThis.routes = globalThis.routes || LITESTAR.routes;
261
283
  globalThis.serverRoutes = globalThis.serverRoutes || globalThis.routes;
262
- globalThis.route = route;
263
- globalThis.toRoute = toRoute;
264
- globalThis.currentRoute = currentRoute;
265
- globalThis.isRoute = isRoute;
266
- globalThis.isCurrentRoute = isCurrentRoute;
267
284
  }
268
- // Keep serverRoutes fresh during Vite HMR when the plugin regenerates metadata/types
285
+ // Keep routes fresh during Vite HMR when the plugin regenerates metadata/types
269
286
  if (import.meta.hot) {
270
287
  import.meta.hot.on("litestar:types-updated", () => {
271
288
  if (typeof window === "undefined") {
@@ -273,8 +290,13 @@ if (import.meta.hot) {
273
290
  }
274
291
  const updated = getRoutes();
275
292
  if (updated) {
293
+ // Update all references
294
+ LITESTAR.routes = updated;
276
295
  window.serverRoutes = updated;
277
296
  window.routes = updated;
297
+ if (window.__LITESTAR__) {
298
+ window.__LITESTAR__.routes = updated;
299
+ }
278
300
  }
279
301
  });
280
302
  }
@@ -1,4 +1,3 @@
1
- import { type ConfigEnv, type Plugin, type UserConfig } from "vite";
2
1
  import { type Config as FullReloadConfig } from "vite-plugin-full-reload";
3
2
  /**
4
3
  * Configuration for TypeScript type generation.
@@ -147,20 +146,28 @@ export interface PluginConfig {
147
146
  * @default false
148
147
  */
149
148
  types?: boolean | TypesConfig;
149
+ /**
150
+ * JavaScript runtime executor for package commands.
151
+ * Used when running tools like @hey-api/openapi-ts.
152
+ *
153
+ * This is typically auto-detected from Python config via LITESTAR_VITE_RUNTIME env var,
154
+ * but can be overridden here for JS-only projects or specific needs.
155
+ *
156
+ * @default undefined (uses LITESTAR_VITE_RUNTIME env or 'node')
157
+ */
158
+ executor?: "node" | "bun" | "deno" | "yarn" | "pnpm";
150
159
  }
151
160
  interface RefreshConfig {
152
161
  paths: string[];
153
162
  config?: FullReloadConfig;
154
163
  }
155
- interface LitestarPlugin extends Plugin {
156
- config: (config: UserConfig, env: ConfigEnv) => UserConfig;
157
- }
158
164
  type DevServerUrl = `${"http" | "https"}://${string}:${number}`;
159
165
  export declare const refreshPaths: string[];
160
166
  /**
161
167
  * Litestar plugin for Vite.
162
168
  *
163
169
  * @param config - A config object or relative path(s) of the scripts to be compiled.
170
+ * @returns An array of Vite plugins. Return type is `any[]` to avoid cross-version type conflicts.
164
171
  */
165
- export default function litestar(config: string | string[] | PluginConfig): [LitestarPlugin, ...Plugin[]];
172
+ export default function litestar(config: string | string[] | PluginConfig): any[];
166
173
  export {};
package/dist/js/index.js CHANGED
@@ -7,8 +7,9 @@ import { promisify } from "node:util";
7
7
  import colors from "picocolors";
8
8
  import { loadEnv } from "vite";
9
9
  import fullReload from "vite-plugin-full-reload";
10
- import { resolveInstallHint } from "./install-hint.js";
10
+ import { resolveInstallHint, resolvePackageExecutor } from "./install-hint.js";
11
11
  import { checkBackendAvailability, loadLitestarMeta } from "./litestar-meta.js";
12
+ import { debounce } from "./shared/debounce.js";
12
13
  const execAsync = promisify(exec);
13
14
  let exitHandlersBound = false;
14
15
  const refreshPaths = ["src/**", "resources/**", "assets/**"].filter((path2) => fs.existsSync(path2.replace(/\*\*$/, "")));
@@ -16,13 +17,12 @@ function litestar(config) {
16
17
  const pluginConfig = resolvePluginConfig(config);
17
18
  const plugins = [resolveLitestarPlugin(pluginConfig), ...resolveFullReloadConfig(pluginConfig)];
18
19
  if (pluginConfig.types !== false && pluginConfig.types.enabled) {
19
- plugins.push(resolveTypeGenerationPlugin(pluginConfig.types));
20
+ plugins.push(resolveTypeGenerationPlugin(pluginConfig.types, pluginConfig.executor));
20
21
  }
21
22
  return plugins;
22
23
  }
23
24
  async function findIndexHtmlPath(server, pluginConfig) {
24
25
  if (!pluginConfig.autoDetectIndex) {
25
- console.log("Auto-detection disabled.");
26
26
  return null;
27
27
  }
28
28
  const root = server.config.root;
@@ -42,7 +42,7 @@ async function findIndexHtmlPath(server, pluginConfig) {
42
42
  }
43
43
  return null;
44
44
  }
45
- function normalizeAppUrl(appUrl, fallbackPort) {
45
+ function normalizeAppUrl(appUrl, _fallbackPort) {
46
46
  if (!appUrl || appUrl === "__litestar_app_url_missing__") {
47
47
  return { url: null, note: "APP_URL missing" };
48
48
  }
@@ -59,6 +59,8 @@ function resolveLitestarPlugin(pluginConfig) {
59
59
  let resolvedConfig;
60
60
  let userConfig;
61
61
  let litestarMeta = {};
62
+ const pythonDefaults = loadPythonDefaults();
63
+ const proxyMode = pythonDefaults?.proxyMode ?? "vite_proxy";
62
64
  const defaultAliases = {
63
65
  "@": `/${pluginConfig.resourceDirectory.replace(/^\/+/, "").replace(/\/+$/, "")}/`
64
66
  };
@@ -109,10 +111,19 @@ function resolveLitestarPlugin(pluginConfig) {
109
111
  changeOrigin: true
110
112
  }
111
113
  } : void 0),
114
+ // Always respect VITE_PORT when set by Python (regardless of VITE_ALLOW_REMOTE)
115
+ ...process.env.VITE_PORT ? {
116
+ port: userConfig.server?.port ?? Number.parseInt(process.env.VITE_PORT),
117
+ strictPort: userConfig.server?.strictPort ?? true
118
+ } : void 0,
119
+ // VITE_ALLOW_REMOTE controls host binding (0.0.0.0 for remote access)
120
+ // Also sets port/strictPort for backwards compatibility when VITE_PORT not set
112
121
  ...process.env.VITE_ALLOW_REMOTE ? {
113
122
  host: userConfig.server?.host ?? "0.0.0.0",
114
- port: userConfig.server?.port ?? (env.VITE_PORT ? Number.parseInt(env.VITE_PORT) : 5173),
115
- strictPort: userConfig.server?.strictPort ?? true
123
+ ...process.env.VITE_PORT ? {} : {
124
+ port: userConfig.server?.port ?? 5173,
125
+ strictPort: userConfig.server?.strictPort ?? true
126
+ }
116
127
  } : void 0,
117
128
  ...serverConfig ? {
118
129
  host: userConfig.server?.host ?? serverConfig.host,
@@ -149,7 +160,7 @@ function resolveLitestarPlugin(pluginConfig) {
149
160
  const hint = pluginConfig.types !== false ? pluginConfig.types.routesPath : void 0;
150
161
  litestarMeta = await loadLitestarMeta(resolvedConfig, hint);
151
162
  },
152
- transform(code, id) {
163
+ transform(code, _id) {
153
164
  if (resolvedConfig.command === "serve" && code.includes("__litestar_vite_placeholder__")) {
154
165
  const transformedCode = code.replace(/__litestar_vite_placeholder__/g, viteDevServerUrl);
155
166
  return pluginConfig.transformOnServe(transformedCode, viteDevServerUrl);
@@ -171,8 +182,10 @@ function resolveLitestarPlugin(pluginConfig) {
171
182
  const isAddressInfo = (x) => typeof x === "object";
172
183
  if (isAddressInfo(address)) {
173
184
  viteDevServerUrl = userConfig.server?.origin ? userConfig.server.origin : resolveDevServerUrl(address, server.config, userConfig);
174
- fs.mkdirSync(path.dirname(pluginConfig.hotFile), { recursive: true });
175
- fs.writeFileSync(pluginConfig.hotFile, viteDevServerUrl);
185
+ if (proxyMode !== "external_proxy") {
186
+ fs.mkdirSync(path.dirname(pluginConfig.hotFile), { recursive: true });
187
+ fs.writeFileSync(pluginConfig.hotFile, viteDevServerUrl);
188
+ }
176
189
  setTimeout(async () => {
177
190
  const version = litestarMeta.litestarVersion ?? process.env.LITESTAR_VERSION ?? "unknown";
178
191
  const backendStatus = await checkBackendAvailability(appUrl);
@@ -229,7 +242,7 @@ function resolveLitestarPlugin(pluginConfig) {
229
242
  }, 100);
230
243
  }
231
244
  });
232
- if (!exitHandlersBound) {
245
+ if (!exitHandlersBound && proxyMode !== "external_proxy") {
233
246
  const clean = () => {
234
247
  if (pluginConfig.hotFile && fs.existsSync(pluginConfig.hotFile)) {
235
248
  fs.rmSync(pluginConfig.hotFile);
@@ -291,7 +304,7 @@ function ensureCommandShouldRunInEnvironment(command, env) {
291
304
  );
292
305
  }
293
306
  }
294
- function pluginVersion() {
307
+ function _pluginVersion() {
295
308
  try {
296
309
  return JSON.parse(fs.readFileSync(path.join(dirname(), "../package.json")).toString())?.version;
297
310
  } catch {
@@ -362,15 +375,23 @@ function resolvePluginConfig(config) {
362
375
  debounce: 300
363
376
  };
364
377
  } else if (typeof resolvedConfig.types === "object" && resolvedConfig.types !== null) {
378
+ const userProvidedOpenapi = Object.hasOwn(resolvedConfig.types, "openapiPath");
379
+ const userProvidedRoutes = Object.hasOwn(resolvedConfig.types, "routesPath");
365
380
  typesConfig = {
366
381
  enabled: resolvedConfig.types.enabled ?? true,
367
382
  output: resolvedConfig.types.output ?? "src/generated/types",
368
- openapiPath: resolvedConfig.types.openapiPath ?? "src/generated/openapi.json",
369
- routesPath: resolvedConfig.types.routesPath ?? "src/generated/routes.json",
383
+ openapiPath: resolvedConfig.types.openapiPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "openapi.json") : "src/generated/openapi.json"),
384
+ routesPath: resolvedConfig.types.routesPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "routes.json") : "src/generated/routes.json"),
370
385
  generateZod: resolvedConfig.types.generateZod ?? false,
371
386
  generateSdk: resolvedConfig.types.generateSdk ?? false,
372
387
  debounce: resolvedConfig.types.debounce ?? 300
373
388
  };
389
+ if (!userProvidedOpenapi && resolvedConfig.types.output) {
390
+ typesConfig.openapiPath = path.join(typesConfig.output, "openapi.json");
391
+ }
392
+ if (!userProvidedRoutes && resolvedConfig.types.output) {
393
+ typesConfig.routesPath = path.join(typesConfig.output, "routes.json");
394
+ }
374
395
  }
375
396
  return {
376
397
  input: resolvedConfig.input,
@@ -384,10 +405,11 @@ function resolvePluginConfig(config) {
384
405
  detectTls: resolvedConfig.detectTls ?? false,
385
406
  autoDetectIndex: resolvedConfig.autoDetectIndex ?? true,
386
407
  transformOnServe: resolvedConfig.transformOnServe ?? ((code) => code),
387
- types: typesConfig
408
+ types: typesConfig,
409
+ executor: resolvedConfig.executor ?? pythonDefaults?.executor
388
410
  };
389
411
  }
390
- function resolveBase(config, assetUrl) {
412
+ function resolveBase(_config, assetUrl) {
391
413
  if (process.env.NODE_ENV === "development") {
392
414
  return assetUrl;
393
415
  }
@@ -424,15 +446,6 @@ function resolveFullReloadConfig({ refresh: config }) {
424
446
  return plugin;
425
447
  });
426
448
  }
427
- function debounce(func, wait) {
428
- let timeout = null;
429
- return (...args) => {
430
- if (timeout) {
431
- clearTimeout(timeout);
432
- }
433
- timeout = setTimeout(() => func(...args), wait);
434
- };
435
- }
436
449
  async function emitRouteTypes(routesPath, outputDir) {
437
450
  const contents = await fs.promises.readFile(routesPath, "utf-8");
438
451
  const json = JSON.parse(contents);
@@ -558,7 +571,7 @@ export { getCsrfToken, csrfHeaders, csrfFetch } from "litestar-vite-plugin/helpe
558
571
  `;
559
572
  await fs.promises.writeFile(outFile, `${banner}${body}`, "utf-8");
560
573
  }
561
- function resolveTypeGenerationPlugin(typesConfig) {
574
+ function resolveTypeGenerationPlugin(typesConfig, executor) {
562
575
  let lastTypesHash = null;
563
576
  let lastRoutesHash = null;
564
577
  let server = null;
@@ -580,12 +593,12 @@ function resolveTypeGenerationPlugin(typesConfig) {
580
593
  }
581
594
  const args = ["@hey-api/openapi-ts", "-i", typesConfig.openapiPath, "-o", typesConfig.output];
582
595
  if (typesConfig.generateZod) {
583
- args.push("--plugins", "@hey-api/schemas", "@hey-api/types");
596
+ args.push("--plugins", "zod", "@hey-api/typescript");
584
597
  }
585
598
  if (typesConfig.generateSdk) {
586
599
  args.push("--client", "fetch");
587
600
  }
588
- await execAsync(`npx ${args.join(" ")}`, {
601
+ await execAsync(resolvePackageExecutor(args.join(" "), executor), {
589
602
  cwd: process.cwd()
590
603
  });
591
604
  generated = true;
@@ -6,11 +6,22 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- export { getCsrfToken, csrfHeaders, csrfFetch, route, getRoutes, toRoute, currentRoute, isRoute, isCurrentRoute, getRelativeUrlPath, type RouteDefinition, type RoutesMap, } from "../helpers/index.js";
9
+ export { csrfFetch, csrfHeaders, currentRoute, getCsrfToken, getRelativeUrlPath, getRoutes, isCurrentRoute, isRoute, type RouteDefinition, type RoutesMap, route, toRoute, } from "litestar-vite-plugin/helpers";
10
+ /**
11
+ * Unwrap page props that may have content nested under "content" key.
12
+ *
13
+ * Litestar wraps route return values under `content`. This utility
14
+ * spreads the content at the top level for ergonomic prop access.
15
+ *
16
+ * @param props - The raw page props from Inertia
17
+ * @returns Props with content unwrapped if applicable
18
+ */
19
+ export declare function unwrapPageProps<T extends Record<string, unknown>>(props: T): T;
10
20
  /**
11
21
  * Resolve a page component from a glob import.
12
22
  *
13
23
  * Used with Inertia.js to dynamically import page components.
24
+ * Automatically unwraps Litestar's `content` prop for ergonomic access.
14
25
  *
15
26
  * @param path - Component path or array of paths to try
16
27
  * @param pages - Glob import result (e.g., import.meta.glob('./pages/**\/*.vue'))
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Inertia.js helpers for Litestar applications.
3
+ *
4
+ * This module re-exports common helpers from litestar-vite-plugin/helpers
5
+ * and adds Inertia-specific utilities.
6
+ *
7
+ * @module
8
+ */
9
+ // Re-export all helpers from the main helpers module
10
+ // Note: Using package path instead of relative import to ensure proper build output structure
11
+ export { csrfFetch, csrfHeaders, currentRoute,
12
+ // CSRF utilities
13
+ getCsrfToken, getRelativeUrlPath, getRoutes, isCurrentRoute, isRoute,
14
+ // Route utilities
15
+ route, toRoute, } from "litestar-vite-plugin/helpers";
16
+ /**
17
+ * Unwrap page props that may have content nested under "content" key.
18
+ *
19
+ * Litestar wraps route return values under `content`. This utility
20
+ * spreads the content at the top level for ergonomic prop access.
21
+ *
22
+ * @param props - The raw page props from Inertia
23
+ * @returns Props with content unwrapped if applicable
24
+ */
25
+ export function unwrapPageProps(props) {
26
+ if (props.content !== undefined && props.content !== null && typeof props.content === "object" && !Array.isArray(props.content)) {
27
+ const { content, ...rest } = props;
28
+ return { ...rest, ...content };
29
+ }
30
+ return props;
31
+ }
32
+ /**
33
+ * Wrap a component to automatically unwrap Litestar's content prop.
34
+ *
35
+ * @param component - The original component (function or object with default)
36
+ * @returns Wrapped component that transforms props
37
+ */
38
+ function wrapComponent(module) {
39
+ // Handle ES module with default export
40
+ const mod = module;
41
+ if (mod.default && typeof mod.default === "function") {
42
+ const Original = mod.default;
43
+ const Wrapped = (props) => Original(unwrapPageProps(props));
44
+ // Copy static properties (displayName, layout, etc.)
45
+ Object.assign(Wrapped, Original);
46
+ return { ...mod, default: Wrapped };
47
+ }
48
+ // Handle direct function export
49
+ if (typeof module === "function") {
50
+ const Original = module;
51
+ const Wrapped = (props) => Original(unwrapPageProps(props));
52
+ Object.assign(Wrapped, Original);
53
+ return Wrapped;
54
+ }
55
+ return module;
56
+ }
57
+ /**
58
+ * Resolve a page component from a glob import.
59
+ *
60
+ * Used with Inertia.js to dynamically import page components.
61
+ * Automatically unwraps Litestar's `content` prop for ergonomic access.
62
+ *
63
+ * @param path - Component path or array of paths to try
64
+ * @param pages - Glob import result (e.g., import.meta.glob('./pages/**\/*.vue'))
65
+ * @returns Promise resolving to the component
66
+ * @throws Error if no matching component is found
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { resolvePageComponent } from 'litestar-vite-plugin/inertia-helpers'
71
+ *
72
+ * createInertiaApp({
73
+ * resolve: (name) => resolvePageComponent(
74
+ * `./pages/${name}.vue`,
75
+ * import.meta.glob('./pages/**\/*.vue')
76
+ * ),
77
+ * // ...
78
+ * })
79
+ * ```
80
+ */
81
+ export async function resolvePageComponent(path, pages) {
82
+ for (const p of Array.isArray(path) ? path : [path]) {
83
+ const page = pages[p];
84
+ if (typeof page === "undefined") {
85
+ continue;
86
+ }
87
+ const resolved = typeof page === "function" ? await page() : await page;
88
+ return wrapComponent(resolved);
89
+ }
90
+ throw new Error(`Page not found: ${path}`);
91
+ }
@@ -1 +1,10 @@
1
1
  export declare function resolveInstallHint(pkg?: string): string;
2
+ /**
3
+ * Resolves the package executor command based on runtime.
4
+ * Priority: explicit executor > LITESTAR_VITE_RUNTIME env > 'node' default
5
+ *
6
+ * @param pkg - The package command to execute (e.g., "@hey-api/openapi-ts -i schema.json -o src/types")
7
+ * @param executor - Optional explicit executor override
8
+ * @returns The full command string (e.g., "npx @hey-api/openapi-ts ..." or "bunx @hey-api/openapi-ts ...")
9
+ */
10
+ export declare function resolvePackageExecutor(pkg: string, executor?: string): string;
@@ -16,6 +16,22 @@ function resolveInstallHint(pkg = "@hey-api/openapi-ts") {
16
16
  }
17
17
  return `npm install -D ${pkg}`;
18
18
  }
19
+ function resolvePackageExecutor(pkg, executor) {
20
+ const runtime = (executor ?? process.env.LITESTAR_VITE_RUNTIME ?? "").toLowerCase();
21
+ switch (runtime) {
22
+ case "bun":
23
+ return `bunx ${pkg}`;
24
+ case "deno":
25
+ return `deno run -A npm:${pkg}`;
26
+ case "pnpm":
27
+ return `pnpm dlx ${pkg}`;
28
+ case "yarn":
29
+ return `yarn dlx ${pkg}`;
30
+ default:
31
+ return `npx ${pkg}`;
32
+ }
33
+ }
19
34
  export {
20
- resolveInstallHint
35
+ resolveInstallHint,
36
+ resolvePackageExecutor
21
37
  };
package/dist/js/nuxt.d.ts CHANGED
@@ -102,6 +102,13 @@ export interface LitestarNuxtConfig {
102
102
  * @default false
103
103
  */
104
104
  verbose?: boolean;
105
+ /**
106
+ * JavaScript runtime executor for package commands.
107
+ * Used when running tools like @hey-api/openapi-ts.
108
+ *
109
+ * @default undefined (uses LITESTAR_VITE_RUNTIME env or 'node')
110
+ */
111
+ executor?: "node" | "bun" | "deno" | "yarn" | "pnpm";
105
112
  }
106
113
  /**
107
114
  * Litestar Vite plugins for Nuxt.
@@ -133,8 +140,7 @@ export declare function litestarPlugins(userConfig?: LitestarNuxtConfig): Plugin
133
140
  /**
134
141
  * Nuxt module definition for Litestar integration.
135
142
  *
136
- * This is a minimal module interface that can be used with Nuxt's module system.
137
- * For full type safety, install @nuxt/kit as a dev dependency.
143
+ * This is a function-based module that works with Nuxt's module system.
138
144
  *
139
145
  * @example
140
146
  * ```typescript
@@ -164,30 +170,31 @@ export declare function litestarPlugins(userConfig?: LitestarNuxtConfig): Plugin
164
170
  * }
165
171
  * ```
166
172
  */
167
- export declare const litestarModule: {
168
- meta: {
173
+ interface NuxtModuleFunction {
174
+ (userOptions: LitestarNuxtConfig, nuxt: NuxtContext): void | Promise<void>;
175
+ meta?: {
169
176
  name: string;
170
177
  configKey: string;
171
- compatibility: {
178
+ compatibility?: {
172
179
  nuxt: string;
173
180
  };
174
181
  };
175
- defaults: {
176
- apiProxy: string;
177
- apiPrefix: string;
178
- types: false;
179
- verbose: false;
180
- };
181
- /**
182
- * Setup function for the Nuxt module.
183
- * This is called by Nuxt when the module is loaded.
184
- */
185
- setup(userOptions: LitestarNuxtConfig, nuxt: {
186
- options: {
187
- vite: {
188
- plugins?: Plugin[];
189
- };
182
+ getOptions?: () => LitestarNuxtConfig;
183
+ }
184
+ interface NuxtContext {
185
+ options: {
186
+ vite: {
187
+ plugins?: Plugin[];
188
+ };
189
+ runtimeConfig?: {
190
+ public?: Record<string, unknown>;
191
+ };
192
+ nitro?: {
193
+ devProxy?: Record<string, unknown>;
190
194
  };
191
- }): void;
192
- };
195
+ litestar?: LitestarNuxtConfig;
196
+ };
197
+ hook?: (name: string, fn: (...args: unknown[]) => void | Promise<void>) => void;
198
+ }
199
+ export declare const litestarModule: NuxtModuleFunction;
193
200
  export default litestarModule;