litestar-vite-plugin 0.15.0-beta.5 → 0.15.0-rc.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/dist/js/nuxt.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import colors from "picocolors";
4
- import { createTypeGenerationPlugin } from "./shared/create-type-gen-plugin.js";
4
+ import { readBridgeConfig } from "./shared/bridge-schema.js";
5
+ import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
5
6
  function normalizeHost(host) {
6
7
  if (host === "::" || host === "::1" || host === "0.0.0.0") {
7
8
  return "localhost";
@@ -12,10 +13,11 @@ function normalizeHost(host) {
12
13
  return host;
13
14
  }
14
15
  function resolveConfig(config = {}) {
15
- const runtimeConfigPath = process.env.LITESTAR_VITE_CONFIG_PATH;
16
16
  let hotFile;
17
17
  let proxyMode = "vite";
18
18
  let devPort;
19
+ let pythonTypesConfig;
20
+ let hasPythonConfig = false;
19
21
  const envPort = process.env.VITE_PORT;
20
22
  if (envPort) {
21
23
  devPort = Number.parseInt(envPort, 10);
@@ -24,42 +26,61 @@ function resolveConfig(config = {}) {
24
26
  }
25
27
  }
26
28
  let pythonExecutor;
27
- if (runtimeConfigPath && fs.existsSync(runtimeConfigPath)) {
28
- try {
29
- const json = JSON.parse(fs.readFileSync(runtimeConfigPath, "utf-8"));
30
- const bundleDir = json.bundleDir ?? "public";
31
- const hot = json.hotFile ?? "hot";
32
- hotFile = path.resolve(process.cwd(), bundleDir, hot);
33
- proxyMode = json.proxyMode ?? "vite";
34
- if (json.port !== void 0) {
35
- devPort = json.port;
36
- }
37
- pythonExecutor = json.executor;
38
- } catch {
39
- hotFile = void 0;
29
+ const runtime = readBridgeConfig();
30
+ if (runtime) {
31
+ hasPythonConfig = true;
32
+ const hot = runtime.hotFile;
33
+ hotFile = path.isAbsolute(hot) ? hot : path.resolve(process.cwd(), runtime.bundleDir, hot);
34
+ proxyMode = runtime.proxyMode;
35
+ devPort = runtime.port;
36
+ pythonExecutor = runtime.executor;
37
+ if (runtime.types) {
38
+ pythonTypesConfig = runtime.types;
40
39
  }
41
40
  }
42
41
  let typesConfig = false;
43
42
  if (config.types === true) {
44
43
  typesConfig = {
45
44
  enabled: true,
46
- output: "types/api",
47
- openapiPath: "openapi.json",
48
- routesPath: "routes.json",
49
- generateZod: false,
50
- generateSdk: true,
45
+ output: pythonTypesConfig?.output ?? "generated",
46
+ openapiPath: pythonTypesConfig?.openapiPath ?? "openapi.json",
47
+ routesPath: pythonTypesConfig?.routesPath ?? "routes.json",
48
+ pagePropsPath: pythonTypesConfig?.pagePropsPath ?? "inertia-pages.json",
49
+ generateZod: pythonTypesConfig?.generateZod ?? false,
50
+ generateSdk: pythonTypesConfig?.generateSdk ?? true,
51
+ generateRoutes: pythonTypesConfig?.generateRoutes ?? true,
52
+ generatePageProps: pythonTypesConfig?.generatePageProps ?? true,
53
+ globalRoute: pythonTypesConfig?.globalRoute ?? false,
51
54
  debounce: 300
52
55
  };
53
56
  } else if (typeof config.types === "object" && config.types !== null) {
54
57
  typesConfig = {
55
58
  enabled: config.types.enabled ?? true,
56
- output: config.types.output ?? "types/api",
57
- openapiPath: config.types.openapiPath ?? "openapi.json",
58
- routesPath: config.types.routesPath ?? "routes.json",
59
- generateZod: config.types.generateZod ?? false,
60
- generateSdk: config.types.generateSdk ?? true,
59
+ output: config.types.output ?? pythonTypesConfig?.output ?? "generated",
60
+ openapiPath: config.types.openapiPath ?? pythonTypesConfig?.openapiPath ?? "openapi.json",
61
+ routesPath: config.types.routesPath ?? pythonTypesConfig?.routesPath ?? "routes.json",
62
+ pagePropsPath: config.types.pagePropsPath ?? pythonTypesConfig?.pagePropsPath ?? "inertia-pages.json",
63
+ generateZod: config.types.generateZod ?? pythonTypesConfig?.generateZod ?? false,
64
+ generateSdk: config.types.generateSdk ?? pythonTypesConfig?.generateSdk ?? true,
65
+ generateRoutes: config.types.generateRoutes ?? pythonTypesConfig?.generateRoutes ?? true,
66
+ generatePageProps: config.types.generatePageProps ?? pythonTypesConfig?.generatePageProps ?? true,
67
+ globalRoute: config.types.globalRoute ?? pythonTypesConfig?.globalRoute ?? false,
61
68
  debounce: config.types.debounce ?? 300
62
69
  };
70
+ } else if (config.types !== false && pythonTypesConfig?.enabled) {
71
+ typesConfig = {
72
+ enabled: true,
73
+ output: pythonTypesConfig.output ?? "generated",
74
+ openapiPath: pythonTypesConfig.openapiPath ?? "openapi.json",
75
+ routesPath: pythonTypesConfig.routesPath ?? "routes.json",
76
+ pagePropsPath: pythonTypesConfig.pagePropsPath ?? "inertia-pages.json",
77
+ generateZod: pythonTypesConfig.generateZod ?? false,
78
+ generateSdk: pythonTypesConfig.generateSdk ?? true,
79
+ generateRoutes: pythonTypesConfig.generateRoutes ?? true,
80
+ generatePageProps: pythonTypesConfig.generatePageProps ?? true,
81
+ globalRoute: pythonTypesConfig.globalRoute ?? false,
82
+ debounce: 300
83
+ };
63
84
  }
64
85
  return {
65
86
  apiProxy: config.apiProxy ?? "http://localhost:8000",
@@ -69,7 +90,8 @@ function resolveConfig(config = {}) {
69
90
  hotFile,
70
91
  proxyMode,
71
92
  devPort,
72
- executor: config.executor ?? pythonExecutor
93
+ executor: config.executor ?? pythonExecutor,
94
+ hasPythonConfig
73
95
  };
74
96
  }
75
97
  async function getPort() {
@@ -147,26 +169,29 @@ function createProxyPlugin(config) {
147
169
  }
148
170
  };
149
171
  }
150
- function litestarPlugins(userConfig = {}) {
151
- const config = resolveConfig(userConfig);
172
+ function litestarPluginsFromResolved(config) {
152
173
  const plugins = [createProxyPlugin(config)];
153
174
  if (config.types !== false && config.types.enabled) {
154
175
  plugins.push(
155
- createTypeGenerationPlugin(config.types, {
156
- frameworkName: "litestar-nuxt",
176
+ createLitestarTypeGenPlugin(config.types, {
157
177
  pluginName: "litestar-nuxt-types",
158
- clientPlugin: "@hey-api/client-nuxt",
159
- executor: config.executor
178
+ frameworkName: "litestar-nuxt",
179
+ sdkClientPlugin: "@hey-api/client-nuxt",
180
+ executor: config.executor,
181
+ hasPythonConfig: config.hasPythonConfig
160
182
  })
161
183
  );
162
184
  }
163
185
  return plugins;
164
186
  }
187
+ function _litestarPlugins(userConfig = {}) {
188
+ return litestarPluginsFromResolved(resolveConfig(userConfig));
189
+ }
165
190
  function litestarNuxtModule(userOptions, nuxt) {
166
191
  const nuxtConfigOptions = nuxt.options.litestar;
167
192
  const mergedOptions = { ...nuxtConfigOptions, ...userOptions };
168
193
  const config = resolveConfig(mergedOptions);
169
- const plugins = litestarPlugins(config);
194
+ const plugins = litestarPluginsFromResolved(config);
170
195
  nuxt.options.vite = nuxt.options.vite || {};
171
196
  nuxt.options.vite.plugins = nuxt.options.vite.plugins || [];
172
197
  nuxt.options.vite.plugins.push(...plugins);
@@ -231,6 +256,5 @@ const litestarModule = litestarNuxtModule;
231
256
  var nuxt_default = litestarModule;
232
257
  export {
233
258
  nuxt_default as default,
234
- litestarModule,
235
- litestarPlugins
259
+ litestarModule
236
260
  };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Canonical bridge schema for `.litestar.json`.
3
+ *
4
+ * This is the single source of truth on the TypeScript side for the config
5
+ * contract emitted by the Python VitePlugin.
6
+ *
7
+ * The project is pre-1.0: no legacy keys, no fallbacks, fail-fast on mismatch.
8
+ *
9
+ * @module
10
+ */
11
+ export type BridgeMode = "spa" | "template" | "htmx" | "hybrid" | "inertia" | "ssr" | "ssg" | "external";
12
+ export type BridgeProxyMode = "vite" | "direct" | "proxy" | null;
13
+ export type BridgeExecutor = "node" | "bun" | "deno" | "yarn" | "pnpm";
14
+ export interface BridgeTypesConfig {
15
+ enabled: boolean;
16
+ output: string;
17
+ openapiPath: string;
18
+ routesPath: string;
19
+ pagePropsPath: string;
20
+ generateZod: boolean;
21
+ generateSdk: boolean;
22
+ generateRoutes: boolean;
23
+ generatePageProps: boolean;
24
+ globalRoute: boolean;
25
+ }
26
+ export interface BridgeSchema {
27
+ assetUrl: string;
28
+ bundleDir: string;
29
+ resourceDir: string;
30
+ staticDir: string;
31
+ hotFile: string;
32
+ manifest: string;
33
+ mode: BridgeMode;
34
+ proxyMode: BridgeProxyMode;
35
+ host: string;
36
+ port: number;
37
+ ssrEnabled: boolean;
38
+ ssrOutDir: string | null;
39
+ types: BridgeTypesConfig | null;
40
+ executor: BridgeExecutor;
41
+ logging: {
42
+ level: "quiet" | "normal" | "verbose";
43
+ showPathsAbsolute: boolean;
44
+ suppressNpmOutput: boolean;
45
+ suppressViteBanner: boolean;
46
+ timestamps: boolean;
47
+ } | null;
48
+ litestarVersion: string;
49
+ }
50
+ export declare function parseBridgeSchema(value: unknown): BridgeSchema;
51
+ export declare function readBridgeConfig(explicitPath?: string): BridgeSchema | null;
@@ -0,0 +1,178 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const allowedTopLevelKeys = /* @__PURE__ */ new Set([
4
+ "assetUrl",
5
+ "bundleDir",
6
+ "resourceDir",
7
+ "staticDir",
8
+ "hotFile",
9
+ "manifest",
10
+ "mode",
11
+ "proxyMode",
12
+ "host",
13
+ "port",
14
+ "ssrEnabled",
15
+ "ssrOutDir",
16
+ "types",
17
+ "executor",
18
+ "logging",
19
+ "litestarVersion"
20
+ ]);
21
+ const allowedModes = /* @__PURE__ */ new Set(["spa", "template", "htmx", "hybrid", "inertia", "ssr", "ssg", "external"]);
22
+ const allowedProxyModes = /* @__PURE__ */ new Set(["vite", "direct", "proxy"]);
23
+ const allowedExecutors = /* @__PURE__ */ new Set(["node", "bun", "deno", "yarn", "pnpm"]);
24
+ const allowedLogLevels = /* @__PURE__ */ new Set(["quiet", "normal", "verbose"]);
25
+ function fail(message) {
26
+ throw new Error(`litestar-vite-plugin: invalid .litestar.json - ${message}`);
27
+ }
28
+ function assertObject(value, label) {
29
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
30
+ fail(`${label} must be an object`);
31
+ }
32
+ return value;
33
+ }
34
+ function assertString(obj, key) {
35
+ const value = obj[key];
36
+ if (typeof value !== "string" || value.length === 0) {
37
+ fail(`"${key}" must be a non-empty string`);
38
+ }
39
+ return value;
40
+ }
41
+ function assertBoolean(obj, key) {
42
+ const value = obj[key];
43
+ if (typeof value !== "boolean") {
44
+ fail(`"${key}" must be a boolean`);
45
+ }
46
+ return value;
47
+ }
48
+ function assertNumber(obj, key) {
49
+ const value = obj[key];
50
+ if (typeof value !== "number" || Number.isNaN(value)) {
51
+ fail(`"${key}" must be a number`);
52
+ }
53
+ return value;
54
+ }
55
+ function assertNullableString(obj, key) {
56
+ const value = obj[key];
57
+ if (value === null) return null;
58
+ if (typeof value !== "string") {
59
+ fail(`"${key}" must be a string or null`);
60
+ }
61
+ return value;
62
+ }
63
+ function assertEnum(value, key, allowed) {
64
+ if (typeof value !== "string" || !allowed.has(value)) {
65
+ fail(`"${key}" must be one of: ${Array.from(allowed).join(", ")}`);
66
+ }
67
+ return value;
68
+ }
69
+ function assertProxyMode(value) {
70
+ if (value === null) return null;
71
+ return assertEnum(value, "proxyMode", allowedProxyModes);
72
+ }
73
+ function parseTypesConfig(value) {
74
+ if (value === null) return null;
75
+ const obj = assertObject(value, "types");
76
+ const enabled = assertBoolean(obj, "enabled");
77
+ const output = assertString(obj, "output");
78
+ const openapiPath = assertString(obj, "openapiPath");
79
+ const routesPath = assertString(obj, "routesPath");
80
+ const pagePropsPath = assertString(obj, "pagePropsPath");
81
+ const generateZod = assertBoolean(obj, "generateZod");
82
+ const generateSdk = assertBoolean(obj, "generateSdk");
83
+ const generateRoutes = assertBoolean(obj, "generateRoutes");
84
+ const generatePageProps = assertBoolean(obj, "generatePageProps");
85
+ const globalRoute = assertBoolean(obj, "globalRoute");
86
+ return {
87
+ enabled,
88
+ output,
89
+ openapiPath,
90
+ routesPath,
91
+ pagePropsPath,
92
+ generateZod,
93
+ generateSdk,
94
+ generateRoutes,
95
+ generatePageProps,
96
+ globalRoute
97
+ };
98
+ }
99
+ function parseLogging(value) {
100
+ if (value === null) return null;
101
+ const obj = assertObject(value, "logging");
102
+ const level = assertEnum(obj.level, "logging.level", allowedLogLevels);
103
+ const showPathsAbsolute = assertBoolean(obj, "showPathsAbsolute");
104
+ const suppressNpmOutput = assertBoolean(obj, "suppressNpmOutput");
105
+ const suppressViteBanner = assertBoolean(obj, "suppressViteBanner");
106
+ const timestamps = assertBoolean(obj, "timestamps");
107
+ return { level, showPathsAbsolute, suppressNpmOutput, suppressViteBanner, timestamps };
108
+ }
109
+ function parseBridgeSchema(value) {
110
+ const obj = assertObject(value, "root");
111
+ for (const key of Object.keys(obj)) {
112
+ if (!allowedTopLevelKeys.has(key)) {
113
+ fail(`unknown top-level key "${key}"`);
114
+ }
115
+ }
116
+ const assetUrl = assertString(obj, "assetUrl");
117
+ const bundleDir = assertString(obj, "bundleDir");
118
+ const resourceDir = assertString(obj, "resourceDir");
119
+ const staticDir = assertString(obj, "staticDir");
120
+ const hotFile = assertString(obj, "hotFile");
121
+ const manifest = assertString(obj, "manifest");
122
+ const mode = assertEnum(obj.mode, "mode", allowedModes);
123
+ const proxyMode = assertProxyMode(obj.proxyMode);
124
+ const host = assertString(obj, "host");
125
+ const port = assertNumber(obj, "port");
126
+ const ssrEnabled = assertBoolean(obj, "ssrEnabled");
127
+ const ssrOutDir = assertNullableString(obj, "ssrOutDir");
128
+ const types = parseTypesConfig(obj.types);
129
+ const executor = assertEnum(obj.executor, "executor", allowedExecutors);
130
+ const logging = parseLogging(obj.logging);
131
+ const litestarVersion = assertString(obj, "litestarVersion");
132
+ return {
133
+ assetUrl,
134
+ bundleDir,
135
+ resourceDir,
136
+ staticDir,
137
+ hotFile,
138
+ manifest,
139
+ mode,
140
+ proxyMode,
141
+ host,
142
+ port,
143
+ ssrEnabled,
144
+ ssrOutDir,
145
+ types,
146
+ executor,
147
+ logging,
148
+ litestarVersion
149
+ };
150
+ }
151
+ function readBridgeConfig(explicitPath) {
152
+ const envPath = explicitPath ?? process.env.LITESTAR_VITE_CONFIG_PATH;
153
+ if (envPath) {
154
+ if (!fs.existsSync(envPath)) {
155
+ return null;
156
+ }
157
+ return readBridgeConfigFile(envPath);
158
+ }
159
+ const defaultPath = path.join(process.cwd(), ".litestar.json");
160
+ if (fs.existsSync(defaultPath)) {
161
+ return readBridgeConfigFile(defaultPath);
162
+ }
163
+ return null;
164
+ }
165
+ function readBridgeConfigFile(filePath) {
166
+ let raw;
167
+ try {
168
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
169
+ } catch (e) {
170
+ const msg = e instanceof Error ? e.message : String(e);
171
+ fail(`failed to parse JSON (${msg})`);
172
+ }
173
+ return parseBridgeSchema(raw);
174
+ }
175
+ export {
176
+ parseBridgeSchema,
177
+ readBridgeConfig
178
+ };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Generate `page-props.ts` from `inertia-pages.json` metadata.
3
+ */
4
+ export declare function emitPagePropsTypes(pagesPath: string, outputDir: string): Promise<void>;
@@ -0,0 +1,268 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ async function emitPagePropsTypes(pagesPath, outputDir) {
4
+ const contents = await fs.promises.readFile(pagesPath, "utf-8");
5
+ const json = JSON.parse(contents);
6
+ const outDir = path.resolve(process.cwd(), outputDir);
7
+ await fs.promises.mkdir(outDir, { recursive: true });
8
+ const outFile = path.join(outDir, "page-props.ts");
9
+ const { includeDefaultAuth, includeDefaultFlash } = json.typeGenConfig;
10
+ const typeImportPaths = json.typeImportPaths ?? {};
11
+ const fallbackType = json.fallbackType ?? "unknown";
12
+ const defaultFallback = fallbackType === "any" ? "Record<string, any>" : "Record<string, unknown>";
13
+ let userTypes = "";
14
+ let authTypes = "";
15
+ let flashTypes = "";
16
+ if (includeDefaultAuth) {
17
+ userTypes = `/**
18
+ * Default User interface - minimal baseline for common auth patterns.
19
+ * Users extend this via module augmentation with their full user model.
20
+ *
21
+ * @example
22
+ * declare module 'litestar-vite-plugin/inertia' {
23
+ * interface User {
24
+ * avatarUrl?: string | null
25
+ * roles: Role[]
26
+ * teams: Team[]
27
+ * }
28
+ * }
29
+ */
30
+ export interface User {
31
+ id: string
32
+ email: string
33
+ name?: string | null
34
+ }
35
+
36
+ `;
37
+ authTypes = `/**
38
+ * Default AuthData interface - mirrors Laravel Jetstream pattern.
39
+ * isAuthenticated + optional user is the universal pattern.
40
+ */
41
+ export interface AuthData {
42
+ isAuthenticated: boolean
43
+ user?: User
44
+ }
45
+
46
+ `;
47
+ } else {
48
+ userTypes = `/**
49
+ * User interface - define via module augmentation.
50
+ * Default auth types are disabled.
51
+ *
52
+ * @example
53
+ * declare module 'litestar-vite-plugin/inertia' {
54
+ * interface User {
55
+ * uuid: string
56
+ * username: string
57
+ * }
58
+ * }
59
+ */
60
+ export interface User {}
61
+
62
+ `;
63
+ authTypes = `/**
64
+ * AuthData interface - define via module augmentation.
65
+ * Default auth types are disabled.
66
+ */
67
+ export interface AuthData {}
68
+
69
+ `;
70
+ }
71
+ if (includeDefaultFlash) {
72
+ flashTypes = `/**
73
+ * Default FlashMessages interface - category to messages mapping.
74
+ * Standard categories: success, error, info, warning.
75
+ */
76
+ export interface FlashMessages {
77
+ [category: string]: string[]
78
+ }
79
+
80
+ `;
81
+ } else {
82
+ flashTypes = `/**
83
+ * FlashMessages interface - define via module augmentation.
84
+ * Default flash types are disabled.
85
+ */
86
+ export interface FlashMessages {}
87
+
88
+ `;
89
+ }
90
+ const defaultGeneratedSharedProps = {
91
+ errors: { type: "Record<string, string[]>", optional: true },
92
+ csrf_token: { type: "string", optional: true },
93
+ ...includeDefaultAuth || includeDefaultFlash ? {
94
+ auth: { type: "AuthData", optional: true },
95
+ flash: { type: "FlashMessages", optional: true }
96
+ } : {}
97
+ };
98
+ const generatedSharedProps = Object.keys(json.sharedProps ?? {}).length > 0 ? json.sharedProps : defaultGeneratedSharedProps;
99
+ const generatedSharedPropLines = Object.entries(generatedSharedProps).sort(([a], [b]) => a.localeCompare(b)).map(([key, def]) => {
100
+ const optional = def.optional ? "?" : "";
101
+ const safeKey = /^[$A-Z_][0-9A-Z_$]*$/i.test(key) ? key : JSON.stringify(key);
102
+ return ` ${safeKey}${optional}: ${def.type}`;
103
+ });
104
+ const allCustomTypes = /* @__PURE__ */ new Set();
105
+ for (const data of Object.values(json.pages)) {
106
+ if (data.tsType) {
107
+ allCustomTypes.add(data.tsType);
108
+ }
109
+ for (const t of data.customTypes ?? []) {
110
+ allCustomTypes.add(t);
111
+ }
112
+ }
113
+ const builtinTypes = /* @__PURE__ */ new Set([
114
+ "any",
115
+ "unknown",
116
+ "never",
117
+ "void",
118
+ "undefined",
119
+ "null",
120
+ "boolean",
121
+ "string",
122
+ "number",
123
+ "bigint",
124
+ "symbol",
125
+ "object",
126
+ "Record",
127
+ "Partial",
128
+ "Required",
129
+ "Readonly",
130
+ "Pick",
131
+ "Omit",
132
+ "Exclude",
133
+ "Extract",
134
+ "NonNullable",
135
+ "Parameters",
136
+ "ReturnType",
137
+ "InstanceType",
138
+ "Uppercase",
139
+ "Lowercase",
140
+ "Capitalize",
141
+ "Uncapitalize",
142
+ "Promise",
143
+ "Array",
144
+ "Map",
145
+ "Set",
146
+ "WeakMap",
147
+ "WeakSet",
148
+ "Date",
149
+ "RegExp",
150
+ "User",
151
+ "AuthData",
152
+ "FlashMessages"
153
+ ]);
154
+ for (const def of Object.values(generatedSharedProps)) {
155
+ for (const match of def.type.matchAll(/\b[A-Za-z_][A-Za-z0-9_]*\b/g)) {
156
+ const name = match[0];
157
+ if (!builtinTypes.has(name)) {
158
+ allCustomTypes.add(name);
159
+ }
160
+ }
161
+ }
162
+ const apiTypesPath = path.join(outDir, "api", "types.gen.ts");
163
+ const availableApiTypes = /* @__PURE__ */ new Set();
164
+ if (fs.existsSync(apiTypesPath)) {
165
+ const content = await fs.promises.readFile(apiTypesPath, "utf-8");
166
+ for (const match of content.matchAll(/export (?:type|interface|enum|class) (\\w+)/g)) {
167
+ if (match[1]) {
168
+ availableApiTypes.add(match[1]);
169
+ }
170
+ }
171
+ }
172
+ const apiImports = [...allCustomTypes].filter((t) => availableApiTypes.has(t)).sort();
173
+ const remainingTypes = [...allCustomTypes].filter((t) => !availableApiTypes.has(t)).sort();
174
+ const importsByPath = /* @__PURE__ */ new Map();
175
+ const unresolvedTypes = [];
176
+ for (const t of remainingTypes) {
177
+ const importPath = typeImportPaths[t];
178
+ if (importPath) {
179
+ const list = importsByPath.get(importPath) ?? [];
180
+ list.push(t);
181
+ importsByPath.set(importPath, list);
182
+ } else {
183
+ unresolvedTypes.push(t);
184
+ }
185
+ }
186
+ if (unresolvedTypes.length > 0) {
187
+ console.warn(`litestar-vite: unresolved Inertia props types: ${unresolvedTypes.join(", ")}. Add them to TypeGenConfig.type_import_paths or include them in OpenAPI.`);
188
+ }
189
+ let importStatement = "";
190
+ if (apiImports.length > 0) {
191
+ importStatement += `import type { ${apiImports.join(", ")} } from "./api/types.gen"
192
+ `;
193
+ }
194
+ const sortedImportPaths = [...importsByPath.keys()].sort();
195
+ for (const p of sortedImportPaths) {
196
+ const names = (importsByPath.get(p) ?? []).sort();
197
+ if (names.length > 0) {
198
+ importStatement += `import type { ${names.join(", ")} } from "${p}"
199
+ `;
200
+ }
201
+ }
202
+ if (importStatement) {
203
+ importStatement += "\n";
204
+ }
205
+ const pageEntries = [];
206
+ for (const [component, data] of Object.entries(json.pages)) {
207
+ const rawType = data.tsType || data.propsType || defaultFallback;
208
+ const propsType = rawType.includes("|") ? `(${rawType})` : rawType;
209
+ pageEntries.push(` "${component}": ${propsType} & FullSharedProps`);
210
+ }
211
+ const body = `// AUTO-GENERATED by litestar-vite. Do not edit.
212
+ /* eslint-disable */
213
+
214
+ ${importStatement}${userTypes}${authTypes}${flashTypes}/**
215
+ * Generated shared props (always present).
216
+ * Includes built-in props + static config props.
217
+ */
218
+ export interface GeneratedSharedProps {
219
+ ${generatedSharedPropLines.join("\n")}
220
+ }
221
+
222
+ /**
223
+ * User-defined shared props for dynamic share() calls in guards/middleware.
224
+ * Extend this interface via module augmentation.
225
+ *
226
+ * @example
227
+ * declare module 'litestar-vite-plugin/inertia' {
228
+ * interface User {
229
+ * avatarUrl?: string | null
230
+ * roles: Role[]
231
+ * teams: Team[]
232
+ * }
233
+ * interface SharedProps {
234
+ * locale?: string
235
+ * currentTeam?: CurrentTeam
236
+ * }
237
+ * }
238
+ */
239
+ export interface SharedProps {
240
+ }
241
+
242
+ /** Full shared props = generated + user-defined */
243
+ export type FullSharedProps = GeneratedSharedProps & SharedProps
244
+
245
+ /** Page props mapped by component name */
246
+ export interface PageProps {
247
+ ${pageEntries.join("\n")}
248
+ }
249
+
250
+ /** Component name union type */
251
+ export type ComponentName = keyof PageProps
252
+
253
+ /** Type-safe props for a specific component */
254
+ export type InertiaPageProps<C extends ComponentName> = PageProps[C]
255
+
256
+ /** Get props type for a specific page component */
257
+ export type PagePropsFor<C extends ComponentName> = PageProps[C]
258
+
259
+ // Re-export for module augmentation
260
+ declare module "litestar-vite-plugin/inertia" {
261
+ export { User, AuthData, FlashMessages, SharedProps, GeneratedSharedProps, FullSharedProps, PageProps, ComponentName, InertiaPageProps, PagePropsFor }
262
+ }
263
+ `;
264
+ await fs.promises.writeFile(outFile, body, "utf-8");
265
+ }
266
+ export {
267
+ emitPagePropsTypes
268
+ };
@@ -11,12 +11,3 @@
11
11
  * @returns The path relative to root, or the original path if already relative or on different drive
12
12
  */
13
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;
@@ -15,10 +15,6 @@ function formatPath(absolutePath, root) {
15
15
  return absolutePath;
16
16
  }
17
17
  }
18
- function formatPaths(paths, root, separator = ", ") {
19
- return paths.map((p) => formatPath(p, root)).join(separator);
20
- }
21
18
  export {
22
- formatPath,
23
- formatPaths
19
+ formatPath
24
20
  };