litestar-vite-plugin 0.25.0 → 0.26.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/astro.d.ts +6 -0
- package/dist/js/astro.js +7 -69
- package/dist/js/dev-server-index.html +1 -1
- package/dist/js/helpers/csrf.d.ts +2 -0
- package/dist/js/helpers/csrf.js +28 -1
- package/dist/js/helpers/htmx.js +105 -2
- package/dist/js/index.d.ts +6 -0
- package/dist/js/index.js +55 -112
- package/dist/js/inertia-helpers/index.d.ts +27 -9
- package/dist/js/install-hint.d.ts +17 -0
- package/dist/js/install-hint.js +35 -1
- package/dist/js/nuxt.d.ts +6 -0
- package/dist/js/nuxt.js +7 -69
- package/dist/js/shared/bridge-schema.d.ts +1 -0
- package/dist/js/shared/bridge-schema.js +41 -9
- package/dist/js/shared/constants.d.ts +4 -18
- package/dist/js/shared/constants.js +2 -6
- package/dist/js/shared/emit-page-props-types.d.ts +1 -1
- package/dist/js/shared/emit-page-props-types.js +51 -5
- package/dist/js/shared/emit-schemas-types.d.ts +1 -1
- package/dist/js/shared/emit-schemas-types.js +86 -15
- package/dist/js/shared/emit-static-props-types.d.ts +1 -1
- package/dist/js/shared/emit-static-props-types.js +2 -2
- package/dist/js/shared/typegen-cache.d.ts +10 -11
- package/dist/js/shared/typegen-cache.js +10 -1
- package/dist/js/shared/typegen-core.d.ts +24 -0
- package/dist/js/shared/typegen-core.js +101 -45
- package/dist/js/shared/typegen-plugin.d.ts +26 -0
- package/dist/js/shared/typegen-plugin.js +191 -129
- package/dist/js/sveltekit.d.ts +6 -0
- package/dist/js/sveltekit.js +7 -69
- package/package.json +7 -1
|
@@ -64,12 +64,16 @@ export declare function resolvePageComponent<T>(path: string | string[], pages:
|
|
|
64
64
|
* Offset-based pagination props.
|
|
65
65
|
*
|
|
66
66
|
* Returned when a route returns Litestar's `OffsetPagination` type.
|
|
67
|
-
*
|
|
67
|
+
* Litestar-Vite flattens pagination metadata as top-level sibling props:
|
|
68
|
+
* the route prop contains the item array, and `total`, `limit`, and `offset`
|
|
69
|
+
* are emitted beside it.
|
|
68
70
|
*
|
|
69
71
|
* @example
|
|
70
72
|
* ```ts
|
|
71
73
|
* interface User { id: string; name: string }
|
|
72
|
-
*
|
|
74
|
+
* type UsersPageProps = { users: User[] } & Omit<OffsetPaginationProps<User>, 'items'>
|
|
75
|
+
*
|
|
76
|
+
* const { users, total, limit, offset } = props as UsersPageProps
|
|
73
77
|
* ```
|
|
74
78
|
*/
|
|
75
79
|
export interface OffsetPaginationProps<T> {
|
|
@@ -86,12 +90,16 @@ export interface OffsetPaginationProps<T> {
|
|
|
86
90
|
* Classic page-based pagination props.
|
|
87
91
|
*
|
|
88
92
|
* Returned when a route returns Litestar's `ClassicPagination` type.
|
|
89
|
-
*
|
|
93
|
+
* Litestar-Vite flattens pagination metadata as top-level sibling props:
|
|
94
|
+
* the route prop contains the item array, and `currentPage`, `totalPages`,
|
|
95
|
+
* and `pageSize` are emitted beside it.
|
|
90
96
|
*
|
|
91
97
|
* @example
|
|
92
98
|
* ```ts
|
|
93
99
|
* interface Post { id: string; title: string }
|
|
94
|
-
*
|
|
100
|
+
* type PostsPageProps = { posts: Post[] } & Omit<ClassicPaginationProps<Post>, 'items'>
|
|
101
|
+
*
|
|
102
|
+
* const { posts, currentPage, totalPages, pageSize } = props as PostsPageProps
|
|
95
103
|
* ```
|
|
96
104
|
*/
|
|
97
105
|
export interface ClassicPaginationProps<T> {
|
|
@@ -108,12 +116,16 @@ export interface ClassicPaginationProps<T> {
|
|
|
108
116
|
* Cursor-based pagination props.
|
|
109
117
|
*
|
|
110
118
|
* Used for cursor/keyset pagination, commonly with infinite scroll.
|
|
111
|
-
*
|
|
119
|
+
* Litestar-Vite flattens pagination metadata as top-level sibling props:
|
|
120
|
+
* the route prop contains the item array, and cursor metadata is emitted
|
|
121
|
+
* beside it.
|
|
112
122
|
*
|
|
113
123
|
* @example
|
|
114
124
|
* ```ts
|
|
115
125
|
* interface Message { id: string; content: string }
|
|
116
|
-
*
|
|
126
|
+
* type MessagesPageProps = { messages: Message[] } & Omit<CursorPaginationProps<Message>, 'items'>
|
|
127
|
+
*
|
|
128
|
+
* const { messages, hasMore, nextCursor } = props as MessagesPageProps
|
|
117
129
|
* if (hasMore && nextCursor) {
|
|
118
130
|
* // Fetch more with cursor
|
|
119
131
|
* }
|
|
@@ -139,6 +151,11 @@ export interface CursorPaginationProps<T> {
|
|
|
139
151
|
* Union type for any pagination props.
|
|
140
152
|
*
|
|
141
153
|
* Use when you need to handle multiple pagination styles.
|
|
154
|
+
* Because pagination metadata is flattened as sibling keys, two paginators in
|
|
155
|
+
* one response can collide on keys such as `total`, `limit`, `offset`,
|
|
156
|
+
* `currentPage`, or `pageSize`. Return one flattened paginator per response,
|
|
157
|
+
* or wrap additional paginators in explicit prop containers with distinct
|
|
158
|
+
* metadata.
|
|
142
159
|
*
|
|
143
160
|
* @example
|
|
144
161
|
* ```ts
|
|
@@ -166,11 +183,12 @@ export type PaginationProps<T> = OffsetPaginationProps<T> | ClassicPaginationPro
|
|
|
166
183
|
* import { usePage } from '@inertiajs/vue3'
|
|
167
184
|
*
|
|
168
185
|
* const page = usePage()
|
|
169
|
-
* const scrollProps = page.props.scrollProps as ScrollProps
|
|
186
|
+
* const scrollProps = page.props.scrollProps as Record<string, ScrollProps> | undefined
|
|
187
|
+
* const postsScroll = scrollProps?.posts
|
|
170
188
|
*
|
|
171
189
|
* function loadMore() {
|
|
172
|
-
* if (
|
|
173
|
-
* router.get(url, { [
|
|
190
|
+
* if (postsScroll?.nextPage) {
|
|
191
|
+
* router.get(url, { [postsScroll.pageName]: postsScroll.nextPage })
|
|
174
192
|
* }
|
|
175
193
|
* }
|
|
176
194
|
* ```
|
|
@@ -13,3 +13,20 @@ export declare function resolveInstallHint(pkg?: string): string;
|
|
|
13
13
|
* @returns The full command string (e.g., "npx @hey-api/openapi-ts ..." or "bunx @hey-api/openapi-ts ...")
|
|
14
14
|
*/
|
|
15
15
|
export declare function resolvePackageExecutor(pkg: string, executor?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Resolves the package executor command as argv.
|
|
18
|
+
*
|
|
19
|
+
* This is used for actual process execution so package arguments are never
|
|
20
|
+
* shell-joined. The string-returning ``resolvePackageExecutor`` remains the
|
|
21
|
+
* display/back-compat helper.
|
|
22
|
+
*/
|
|
23
|
+
export interface PackageExecutorArgvOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Package spec to install for executors that support an explicit package
|
|
26
|
+
* separate from the command binary, e.g. npm exec --package.
|
|
27
|
+
*/
|
|
28
|
+
packageSpec?: string;
|
|
29
|
+
/** Binary command exposed by packageSpec. */
|
|
30
|
+
binName?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function resolvePackageExecutorArgv(args: string[], executor?: string, options?: PackageExecutorArgvOptions): string[];
|
package/dist/js/install-hint.js
CHANGED
|
@@ -63,8 +63,42 @@ function resolvePackageExecutor(pkg, executor) {
|
|
|
63
63
|
return `npx ${pkg}`;
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
function getPackageNameFromSpec(packageSpec) {
|
|
67
|
+
if (packageSpec.startsWith("@")) {
|
|
68
|
+
const versionIndex2 = packageSpec.indexOf("@", packageSpec.indexOf("/") + 1);
|
|
69
|
+
return versionIndex2 === -1 ? packageSpec : packageSpec.slice(0, versionIndex2);
|
|
70
|
+
}
|
|
71
|
+
const versionIndex = packageSpec.indexOf("@");
|
|
72
|
+
return versionIndex === -1 ? packageSpec : packageSpec.slice(0, versionIndex);
|
|
73
|
+
}
|
|
74
|
+
function resolveDenoPackageSpec(packageSpec, binName) {
|
|
75
|
+
if (!binName) return packageSpec;
|
|
76
|
+
const packageName = getPackageNameFromSpec(packageSpec);
|
|
77
|
+
const defaultBinName = packageName.split("/").pop();
|
|
78
|
+
return defaultBinName === binName ? packageSpec : `${packageSpec}/${binName}`;
|
|
79
|
+
}
|
|
80
|
+
function resolvePackageExecutorArgv(args, executor, options = {}) {
|
|
81
|
+
const runtime = executor || detectExecutor();
|
|
82
|
+
const { packageSpec, binName } = options;
|
|
83
|
+
switch (runtime) {
|
|
84
|
+
case "bun":
|
|
85
|
+
return ["bunx", ...packageSpec ? [packageSpec, ...args] : args];
|
|
86
|
+
case "deno":
|
|
87
|
+
return ["deno", "run", "-A", ...packageSpec ? [`npm:${resolveDenoPackageSpec(packageSpec, binName)}`, ...args] : args];
|
|
88
|
+
case "pnpm":
|
|
89
|
+
return ["pnpm", "dlx", ...packageSpec ? [packageSpec, ...args] : args];
|
|
90
|
+
case "yarn":
|
|
91
|
+
return ["yarn", "dlx", ...packageSpec ? [packageSpec, ...args] : args];
|
|
92
|
+
default:
|
|
93
|
+
if (packageSpec && binName) {
|
|
94
|
+
return ["npm", "exec", "--yes", "--package", packageSpec, "--", binName, ...args];
|
|
95
|
+
}
|
|
96
|
+
return ["npx", ...args];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
66
99
|
export {
|
|
67
100
|
detectExecutor,
|
|
68
101
|
resolveInstallHint,
|
|
69
|
-
resolvePackageExecutor
|
|
102
|
+
resolvePackageExecutor,
|
|
103
|
+
resolvePackageExecutorArgv
|
|
70
104
|
};
|
package/dist/js/nuxt.d.ts
CHANGED
|
@@ -100,6 +100,12 @@ export interface NuxtTypesConfig {
|
|
|
100
100
|
* @default false
|
|
101
101
|
*/
|
|
102
102
|
globalRoute?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Fail Vite when type generation fails.
|
|
105
|
+
*
|
|
106
|
+
* Defaults to true during build and false during dev.
|
|
107
|
+
*/
|
|
108
|
+
failOnError?: boolean;
|
|
103
109
|
/**
|
|
104
110
|
* Debounce time in milliseconds for type regeneration.
|
|
105
111
|
*
|
package/dist/js/nuxt.js
CHANGED
|
@@ -2,9 +2,8 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import colors from "picocolors";
|
|
4
4
|
import { readBridgeConfig } from "./shared/bridge-schema.js";
|
|
5
|
-
import { DEBOUNCE_MS } from "./shared/constants.js";
|
|
6
5
|
import { normalizeHost, resolveHotFilePath, resolveLitestarPort } from "./shared/network.js";
|
|
7
|
-
import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
|
|
6
|
+
import { createLitestarTypeGenPlugin, resolveTypesConfig } from "./shared/typegen-plugin.js";
|
|
8
7
|
import { hmrServerConfig } from "./shared/vite-compat.js";
|
|
9
8
|
function resolveConfig(config = {}) {
|
|
10
9
|
let hotFile;
|
|
@@ -39,74 +38,13 @@ function resolveConfig(config = {}) {
|
|
|
39
38
|
if (resolvedLitestarPort !== null) {
|
|
40
39
|
litestarPort = resolvedLitestarPort;
|
|
41
40
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
schemasTsPath: path.join(output, "schemas.ts")
|
|
41
|
+
const typesConfig = resolveTypesConfig({
|
|
42
|
+
requested: config.types,
|
|
43
|
+
pythonConfig: pythonTypesConfig ?? void 0,
|
|
44
|
+
defaultOutput: "generated",
|
|
45
|
+
mergePythonWhenTrue: true,
|
|
46
|
+
mergePythonForObject: true
|
|
49
47
|
});
|
|
50
|
-
if (config.types === true) {
|
|
51
|
-
const output = pythonTypesConfig?.output ?? defaultTypesOutput;
|
|
52
|
-
const defaults = buildTypeDefaults(output);
|
|
53
|
-
typesConfig = {
|
|
54
|
-
enabled: true,
|
|
55
|
-
output,
|
|
56
|
-
openapiPath: pythonTypesConfig?.openapiPath ?? defaults.openapiPath,
|
|
57
|
-
routesPath: pythonTypesConfig?.routesPath ?? defaults.routesPath,
|
|
58
|
-
pagePropsPath: pythonTypesConfig?.pagePropsPath ?? defaults.pagePropsPath,
|
|
59
|
-
schemasTsPath: pythonTypesConfig?.schemasTsPath ?? defaults.schemasTsPath,
|
|
60
|
-
generateZod: pythonTypesConfig?.generateZod ?? false,
|
|
61
|
-
generateSdk: pythonTypesConfig?.generateSdk ?? true,
|
|
62
|
-
generateRoutes: pythonTypesConfig?.generateRoutes ?? true,
|
|
63
|
-
generatePageProps: pythonTypesConfig?.generatePageProps ?? true,
|
|
64
|
-
generateSchemas: pythonTypesConfig?.generateSchemas ?? true,
|
|
65
|
-
globalRoute: pythonTypesConfig?.globalRoute ?? false,
|
|
66
|
-
debounce: DEBOUNCE_MS
|
|
67
|
-
};
|
|
68
|
-
} else if (typeof config.types === "object" && config.types !== null) {
|
|
69
|
-
const userProvidedOutput = Object.hasOwn(config.types, "output");
|
|
70
|
-
const output = config.types.output ?? pythonTypesConfig?.output ?? defaultTypesOutput;
|
|
71
|
-
const defaults = buildTypeDefaults(output);
|
|
72
|
-
const openapiFallback = userProvidedOutput ? defaults.openapiPath : pythonTypesConfig?.openapiPath ?? defaults.openapiPath;
|
|
73
|
-
const routesFallback = userProvidedOutput ? defaults.routesPath : pythonTypesConfig?.routesPath ?? defaults.routesPath;
|
|
74
|
-
const pagePropsFallback = userProvidedOutput ? defaults.pagePropsPath : pythonTypesConfig?.pagePropsPath ?? defaults.pagePropsPath;
|
|
75
|
-
const schemasFallback = userProvidedOutput ? defaults.schemasTsPath : pythonTypesConfig?.schemasTsPath ?? defaults.schemasTsPath;
|
|
76
|
-
typesConfig = {
|
|
77
|
-
enabled: config.types.enabled ?? true,
|
|
78
|
-
output,
|
|
79
|
-
openapiPath: config.types.openapiPath ?? openapiFallback,
|
|
80
|
-
routesPath: config.types.routesPath ?? routesFallback,
|
|
81
|
-
pagePropsPath: config.types.pagePropsPath ?? pagePropsFallback,
|
|
82
|
-
schemasTsPath: config.types.schemasTsPath ?? schemasFallback,
|
|
83
|
-
generateZod: config.types.generateZod ?? pythonTypesConfig?.generateZod ?? false,
|
|
84
|
-
generateSdk: config.types.generateSdk ?? pythonTypesConfig?.generateSdk ?? true,
|
|
85
|
-
generateRoutes: config.types.generateRoutes ?? pythonTypesConfig?.generateRoutes ?? true,
|
|
86
|
-
generatePageProps: config.types.generatePageProps ?? pythonTypesConfig?.generatePageProps ?? true,
|
|
87
|
-
generateSchemas: config.types.generateSchemas ?? pythonTypesConfig?.generateSchemas ?? true,
|
|
88
|
-
globalRoute: config.types.globalRoute ?? pythonTypesConfig?.globalRoute ?? false,
|
|
89
|
-
debounce: config.types.debounce ?? DEBOUNCE_MS
|
|
90
|
-
};
|
|
91
|
-
} else if (config.types !== false && pythonTypesConfig?.enabled) {
|
|
92
|
-
const output = pythonTypesConfig.output ?? defaultTypesOutput;
|
|
93
|
-
const defaults = buildTypeDefaults(output);
|
|
94
|
-
typesConfig = {
|
|
95
|
-
enabled: true,
|
|
96
|
-
output,
|
|
97
|
-
openapiPath: pythonTypesConfig.openapiPath ?? defaults.openapiPath,
|
|
98
|
-
routesPath: pythonTypesConfig.routesPath ?? defaults.routesPath,
|
|
99
|
-
pagePropsPath: pythonTypesConfig.pagePropsPath ?? defaults.pagePropsPath,
|
|
100
|
-
schemasTsPath: pythonTypesConfig.schemasTsPath ?? defaults.schemasTsPath,
|
|
101
|
-
generateZod: pythonTypesConfig.generateZod ?? false,
|
|
102
|
-
generateSdk: pythonTypesConfig.generateSdk ?? true,
|
|
103
|
-
generateRoutes: pythonTypesConfig.generateRoutes ?? true,
|
|
104
|
-
generatePageProps: pythonTypesConfig.generatePageProps ?? true,
|
|
105
|
-
generateSchemas: pythonTypesConfig.generateSchemas ?? true,
|
|
106
|
-
globalRoute: pythonTypesConfig.globalRoute ?? false,
|
|
107
|
-
debounce: DEBOUNCE_MS
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
48
|
return {
|
|
111
49
|
apiProxy: config.apiProxy ?? "http://localhost:8000",
|
|
112
50
|
apiPrefix: config.apiPrefix ?? "/api",
|
|
@@ -25,6 +25,7 @@ export interface BridgeTypesConfig {
|
|
|
25
25
|
generatePageProps: boolean;
|
|
26
26
|
generateSchemas: boolean;
|
|
27
27
|
globalRoute: boolean;
|
|
28
|
+
failOnError?: boolean;
|
|
28
29
|
}
|
|
29
30
|
export interface BridgeSpaConfig {
|
|
30
31
|
/** Use script element instead of data-page attribute for Inertia page data */
|
|
@@ -29,6 +29,9 @@ const allowedLogLevels = /* @__PURE__ */ new Set(["quiet", "normal", "verbose"])
|
|
|
29
29
|
function fail(message) {
|
|
30
30
|
throw new Error(`litestar-vite-plugin: invalid .litestar.json - ${message}`);
|
|
31
31
|
}
|
|
32
|
+
function warn(message) {
|
|
33
|
+
console.warn(`litestar-vite-plugin: invalid .litestar.json - ${message}`);
|
|
34
|
+
}
|
|
32
35
|
function assertObject(value, label) {
|
|
33
36
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
34
37
|
fail(`${label} must be an object`);
|
|
@@ -122,6 +125,8 @@ function parseTypesConfig(value) {
|
|
|
122
125
|
const generatePageProps = assertBoolean(obj, "generatePageProps");
|
|
123
126
|
const generateSchemas = assertOptionalBoolean(obj, "generateSchemas", true);
|
|
124
127
|
const globalRoute = assertBoolean(obj, "globalRoute");
|
|
128
|
+
const rawFailOnError = obj.failOnError;
|
|
129
|
+
const failOnError = rawFailOnError === void 0 || rawFailOnError === null ? void 0 : assertBoolean(obj, "failOnError");
|
|
125
130
|
return {
|
|
126
131
|
enabled,
|
|
127
132
|
output,
|
|
@@ -135,7 +140,8 @@ function parseTypesConfig(value) {
|
|
|
135
140
|
generateRoutes,
|
|
136
141
|
generatePageProps,
|
|
137
142
|
generateSchemas,
|
|
138
|
-
globalRoute
|
|
143
|
+
globalRoute,
|
|
144
|
+
failOnError
|
|
139
145
|
};
|
|
140
146
|
}
|
|
141
147
|
function parseLogging(value) {
|
|
@@ -162,7 +168,7 @@ function parseBridgeSchema(value) {
|
|
|
162
168
|
const obj = assertObject(value, "root");
|
|
163
169
|
for (const key of Object.keys(obj)) {
|
|
164
170
|
if (!allowedTopLevelKeys.has(key)) {
|
|
165
|
-
|
|
171
|
+
warn(`unknown top-level key "${key}" ignored`);
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
174
|
const assetUrl = assertString(obj, "assetUrl");
|
|
@@ -211,16 +217,42 @@ function parseBridgeSchema(value) {
|
|
|
211
217
|
function readBridgeConfig(explicitPath) {
|
|
212
218
|
const envPath = explicitPath ?? process.env.LITESTAR_VITE_CONFIG_PATH;
|
|
213
219
|
if (envPath) {
|
|
214
|
-
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
return readBridgeConfigFile(envPath);
|
|
220
|
+
return readBridgeConfigFileCached(envPath);
|
|
218
221
|
}
|
|
219
222
|
const defaultPath = path.join(process.cwd(), ".litestar.json");
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
return readBridgeConfigFileCached(defaultPath);
|
|
224
|
+
}
|
|
225
|
+
const bridgeConfigCache = /* @__PURE__ */ new Map();
|
|
226
|
+
function readBridgeConfigFileCached(filePath) {
|
|
227
|
+
const resolvedPath = path.resolve(filePath);
|
|
228
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
let mtimeMs = null;
|
|
232
|
+
try {
|
|
233
|
+
mtimeMs = fs.statSync(resolvedPath).mtimeMs;
|
|
234
|
+
} catch {
|
|
235
|
+
mtimeMs = null;
|
|
236
|
+
}
|
|
237
|
+
if (mtimeMs !== null) {
|
|
238
|
+
const cached = bridgeConfigCache.get(resolvedPath);
|
|
239
|
+
if (cached?.mtimeMs === mtimeMs) {
|
|
240
|
+
return cached.config;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const config = readBridgeConfigFile(resolvedPath);
|
|
245
|
+
if (mtimeMs !== null) {
|
|
246
|
+
bridgeConfigCache.set(resolvedPath, { mtimeMs, config });
|
|
247
|
+
}
|
|
248
|
+
return config;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
warn(error instanceof Error ? error.message : String(error));
|
|
251
|
+
if (mtimeMs !== null) {
|
|
252
|
+
bridgeConfigCache.set(resolvedPath, { mtimeMs, config: null });
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
222
255
|
}
|
|
223
|
-
return null;
|
|
224
256
|
}
|
|
225
257
|
function readBridgeConfigFile(filePath) {
|
|
226
258
|
let raw;
|
|
@@ -11,23 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const DEBOUNCE_MS = 300;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Pinned fallback package spec for @hey-api/openapi-ts.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Keep this in sync with CURRENT_NPM_VERSION_RANGES in the Python scaffold
|
|
17
|
+
* registry and the optional peer dependency range in package.json.
|
|
18
18
|
*/
|
|
19
|
-
export declare const
|
|
20
|
-
/**
|
|
21
|
-
* Default asset URL path.
|
|
22
|
-
*
|
|
23
|
-
* Used as fallback when no explicit assetUrl is configured.
|
|
24
|
-
* Should match the default in Python ViteConfig.
|
|
25
|
-
*/
|
|
26
|
-
export declare const DEFAULT_ASSET_URL = "/static/";
|
|
27
|
-
/**
|
|
28
|
-
* Default output directory for generated TypeScript types.
|
|
29
|
-
*
|
|
30
|
-
* Used by framework integrations as fallback when no output
|
|
31
|
-
* is specified in Python bridge config or plugin options.
|
|
32
|
-
*/
|
|
33
|
-
export declare const DEFAULT_TYPES_OUTPUT = "src/generated";
|
|
19
|
+
export declare const HEY_API_PINNED_SPEC = "@hey-api/openapi-ts@0.98.2";
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
const DEBOUNCE_MS = 300;
|
|
2
|
-
const
|
|
3
|
-
const DEFAULT_ASSET_URL = "/static/";
|
|
4
|
-
const DEFAULT_TYPES_OUTPUT = "src/generated";
|
|
2
|
+
const HEY_API_PINNED_SPEC = "@hey-api/openapi-ts@0.98.2";
|
|
5
3
|
export {
|
|
6
|
-
BACKEND_CHECK_TIMEOUT_MS,
|
|
7
4
|
DEBOUNCE_MS,
|
|
8
|
-
|
|
9
|
-
DEFAULT_TYPES_OUTPUT
|
|
5
|
+
HEY_API_PINNED_SPEC
|
|
10
6
|
};
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @returns true if file was changed, false if unchanged
|
|
5
5
|
*/
|
|
6
|
-
export declare function emitPagePropsTypes(pagesPath: string, outputDir: string): Promise<boolean>;
|
|
6
|
+
export declare function emitPagePropsTypes(pagesPath: string, outputDir: string, projectRoot?: string): Promise<boolean>;
|
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { writeIfChanged } from "./write-if-changed.js";
|
|
4
|
-
|
|
4
|
+
const semanticAliases = {
|
|
5
|
+
UUID: { description: "UUID v4 string", base: "string" },
|
|
6
|
+
DateTime: { description: "RFC 3339 date-time string", base: "string" },
|
|
7
|
+
DateOnly: { description: "ISO 8601 date string (YYYY-MM-DD)", base: "string" },
|
|
8
|
+
TimeOnly: { description: "ISO 8601 time string", base: "string" },
|
|
9
|
+
Duration: { description: "ISO 8601 duration string", base: "string" },
|
|
10
|
+
Email: { description: "Email address string", base: "string" },
|
|
11
|
+
URI: { description: "URI/URL string", base: "string" },
|
|
12
|
+
IPv4: { description: "IPv4 address string", base: "string" },
|
|
13
|
+
IPv6: { description: "IPv6 address string", base: "string" }
|
|
14
|
+
};
|
|
15
|
+
function collectSemanticAliases(typeExpr) {
|
|
16
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
17
|
+
for (const match of typeExpr.matchAll(/\b[A-Za-z_][A-Za-z0-9_]*\b/g)) {
|
|
18
|
+
const name = match[0];
|
|
19
|
+
if (name in semanticAliases) {
|
|
20
|
+
aliases.add(name);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return [...aliases].toSorted();
|
|
24
|
+
}
|
|
25
|
+
function renderSemanticAliases(aliases) {
|
|
26
|
+
if (aliases.size === 0) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
const lines = ["/** Semantic string aliases derived from OpenAPI `format`. */"];
|
|
30
|
+
for (const alias of [...aliases].toSorted()) {
|
|
31
|
+
const { description, base } = semanticAliases[alias];
|
|
32
|
+
lines.push(`/** ${description} */`, `export type ${alias} = ${base}`, "");
|
|
33
|
+
}
|
|
34
|
+
return `${lines.join("\n").trimEnd()}
|
|
35
|
+
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
async function emitPagePropsTypes(pagesPath, outputDir, projectRoot = process.cwd()) {
|
|
5
39
|
const contents = await fs.promises.readFile(pagesPath, "utf-8");
|
|
6
40
|
const json = JSON.parse(contents);
|
|
7
|
-
const outDir = path.resolve(
|
|
41
|
+
const outDir = path.resolve(projectRoot, outputDir);
|
|
8
42
|
await fs.promises.mkdir(outDir, { recursive: true });
|
|
9
43
|
const outFile = path.join(outDir, "page-props.ts");
|
|
10
44
|
const { includeDefaultAuth, includeDefaultFlash } = json.typeGenConfig;
|
|
@@ -84,8 +118,15 @@ export interface FlashMessages {}
|
|
|
84
118
|
return ` ${safeKey}${optional}: ${def.type}`;
|
|
85
119
|
});
|
|
86
120
|
const allCustomTypes = /* @__PURE__ */ new Set();
|
|
121
|
+
const usedSemanticAliases = /* @__PURE__ */ new Set();
|
|
87
122
|
for (const data of Object.values(json.pages)) {
|
|
88
|
-
|
|
123
|
+
const typeExpr = data.tsType ?? data.propsType;
|
|
124
|
+
if (typeExpr) {
|
|
125
|
+
for (const alias of collectSemanticAliases(typeExpr)) {
|
|
126
|
+
usedSemanticAliases.add(alias);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (data.tsType && /^[A-Za-z_][A-Za-z0-9_]*$/.test(data.tsType)) {
|
|
89
130
|
allCustomTypes.add(data.tsType);
|
|
90
131
|
}
|
|
91
132
|
for (const t of data.customTypes ?? []) {
|
|
@@ -131,9 +172,13 @@ export interface FlashMessages {}
|
|
|
131
172
|
"RegExp",
|
|
132
173
|
"User",
|
|
133
174
|
"AuthData",
|
|
134
|
-
"FlashMessages"
|
|
175
|
+
"FlashMessages",
|
|
176
|
+
...Object.keys(semanticAliases)
|
|
135
177
|
]);
|
|
136
178
|
for (const def of Object.values(generatedSharedProps)) {
|
|
179
|
+
for (const alias of collectSemanticAliases(def.type)) {
|
|
180
|
+
usedSemanticAliases.add(alias);
|
|
181
|
+
}
|
|
137
182
|
for (const match of def.type.matchAll(/\b[A-Za-z_][A-Za-z0-9_]*\b/g)) {
|
|
138
183
|
const name = match[0];
|
|
139
184
|
if (!builtinTypes.has(name)) {
|
|
@@ -190,6 +235,7 @@ export interface FlashMessages {}
|
|
|
190
235
|
if (importStatement) {
|
|
191
236
|
importStatement += "\n";
|
|
192
237
|
}
|
|
238
|
+
const semanticAliasTypes = renderSemanticAliases(usedSemanticAliases);
|
|
193
239
|
const pageEntries = [];
|
|
194
240
|
for (const [component, data] of Object.entries(json.pages)) {
|
|
195
241
|
const rawType = data.tsType || data.propsType || defaultFallback;
|
|
@@ -206,7 +252,7 @@ export interface FlashMessages {}
|
|
|
206
252
|
// Import user-defined type extensions (edit page-props.user.ts to customize)
|
|
207
253
|
import type { UserExtensions, SharedPropsExtensions } from "./page-props.user"
|
|
208
254
|
|
|
209
|
-
${importStatement}${userTypes}${authTypes}${flashTypes}/**
|
|
255
|
+
${importStatement}${semanticAliasTypes}${userTypes}${authTypes}${flashTypes}/**
|
|
210
256
|
* Generated shared props (always present).
|
|
211
257
|
* Includes built-in props + static config props.
|
|
212
258
|
*/
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* @param outputDir - Output directory (contains api/types.gen.ts)
|
|
6
6
|
* @returns true if file was changed, false if unchanged
|
|
7
7
|
*/
|
|
8
|
-
export declare function emitSchemasTypes(routesJsonPath: string, outputDir: string, schemasOutputPath?: string): Promise<boolean>;
|
|
8
|
+
export declare function emitSchemasTypes(routesJsonPath: string, outputDir: string, schemasOutputPath?: string, projectRoot?: string): Promise<boolean>;
|
|
@@ -10,21 +10,12 @@ function toImportPath(fromDir, targetFile) {
|
|
|
10
10
|
return `./${withoutExtension}`;
|
|
11
11
|
}
|
|
12
12
|
function parseHeyApiTypes(content) {
|
|
13
|
-
const dataTypes = /* @__PURE__ */ new Map();
|
|
14
13
|
const responsesTypes = /* @__PURE__ */ new Set();
|
|
15
14
|
const errorsTypes = /* @__PURE__ */ new Set();
|
|
16
15
|
const urlToDataType = /* @__PURE__ */ new Map();
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const typeName = match[1];
|
|
20
|
-
const typeBody = match[2];
|
|
21
|
-
const urlMatch = typeBody.match(/url:\s*['"]([^'"]+)['"]/);
|
|
16
|
+
for (const { typeName, body } of findExportedDataTypeBlocks(content)) {
|
|
17
|
+
const urlMatch = body.match(/url:\s*['"]([^'"]+)['"]/);
|
|
22
18
|
const url = urlMatch ? urlMatch[1] : null;
|
|
23
|
-
const hasBody = /\bbody\s*:/.test(typeBody) && !/\bbody\s*\?\s*:\s*never/.test(typeBody);
|
|
24
|
-
const hasPath = /\bpath\s*:/.test(typeBody) && !/\bpath\s*\?\s*:\s*never/.test(typeBody);
|
|
25
|
-
const hasQuery = /\bquery\s*:/.test(typeBody) && !/\bquery\s*\?\s*:\s*never/.test(typeBody);
|
|
26
|
-
const info = { typeName, url, hasBody, hasPath, hasQuery };
|
|
27
|
-
dataTypes.set(typeName, info);
|
|
28
19
|
if (url) {
|
|
29
20
|
urlToDataType.set(url, typeName);
|
|
30
21
|
}
|
|
@@ -37,7 +28,87 @@ function parseHeyApiTypes(content) {
|
|
|
37
28
|
for (const match of content.matchAll(errorsRegex)) {
|
|
38
29
|
errorsTypes.add(match[1]);
|
|
39
30
|
}
|
|
40
|
-
return {
|
|
31
|
+
return { responsesTypes, errorsTypes, urlToDataType };
|
|
32
|
+
}
|
|
33
|
+
function findExportedDataTypeBlocks(content) {
|
|
34
|
+
const blocks = [];
|
|
35
|
+
const declarationRegex = /export\s+type\s+(\w+Data)\s*=\s*/g;
|
|
36
|
+
for (const match of content.matchAll(declarationRegex)) {
|
|
37
|
+
const typeName = match[1];
|
|
38
|
+
const bodyStart = match.index + match[0].length;
|
|
39
|
+
const openIndex = content.indexOf("{", bodyStart);
|
|
40
|
+
const nextToken = content.slice(bodyStart, openIndex === -1 ? content.length : openIndex).trim();
|
|
41
|
+
if (openIndex === -1 || nextToken.length > 0) {
|
|
42
|
+
console.warn(`litestar-vite: Unable to parse hey-api Data type ${typeName}; expected object type`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const closeIndex = findMatchingBrace(content, openIndex);
|
|
46
|
+
if (closeIndex === -1) {
|
|
47
|
+
console.warn(`litestar-vite: Unable to parse hey-api Data type ${typeName}; unbalanced braces`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
blocks.push({ typeName, body: content.slice(openIndex + 1, closeIndex) });
|
|
51
|
+
}
|
|
52
|
+
return blocks;
|
|
53
|
+
}
|
|
54
|
+
function findMatchingBrace(content, openIndex) {
|
|
55
|
+
let depth = 0;
|
|
56
|
+
let quote = null;
|
|
57
|
+
let escaped = false;
|
|
58
|
+
let lineComment = false;
|
|
59
|
+
let blockComment = false;
|
|
60
|
+
for (let i = openIndex; i < content.length; i += 1) {
|
|
61
|
+
const char = content[i];
|
|
62
|
+
const next = content[i + 1];
|
|
63
|
+
if (lineComment) {
|
|
64
|
+
if (char === "\n") {
|
|
65
|
+
lineComment = false;
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (blockComment) {
|
|
70
|
+
if (char === "*" && next === "/") {
|
|
71
|
+
blockComment = false;
|
|
72
|
+
i += 1;
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (quote) {
|
|
77
|
+
if (escaped) {
|
|
78
|
+
escaped = false;
|
|
79
|
+
} else if (char === "\\") {
|
|
80
|
+
escaped = true;
|
|
81
|
+
} else if (char === quote) {
|
|
82
|
+
quote = null;
|
|
83
|
+
}
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (char === "/" && next === "/") {
|
|
87
|
+
lineComment = true;
|
|
88
|
+
i += 1;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (char === "/" && next === "*") {
|
|
92
|
+
blockComment = true;
|
|
93
|
+
i += 1;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
97
|
+
quote = char;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (char === "{") {
|
|
101
|
+
depth += 1;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (char === "}") {
|
|
105
|
+
depth -= 1;
|
|
106
|
+
if (depth === 0) {
|
|
107
|
+
return i;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return -1;
|
|
41
112
|
}
|
|
42
113
|
function normalizePath(routePath) {
|
|
43
114
|
return routePath.replace(/\{([^:}]+):[^}]+\}/g, "{$1}");
|
|
@@ -312,9 +383,9 @@ export type HasPathParams<T extends OperationName> = PathParams<T> extends never
|
|
|
312
383
|
export type HasQueryParams<T extends OperationName> = QueryParams<T> extends never ? false : true
|
|
313
384
|
`;
|
|
314
385
|
}
|
|
315
|
-
async function emitSchemasTypes(routesJsonPath, outputDir, schemasOutputPath) {
|
|
316
|
-
const outDir = path.resolve(
|
|
317
|
-
const outFile = schemasOutputPath ? path.resolve(
|
|
386
|
+
async function emitSchemasTypes(routesJsonPath, outputDir, schemasOutputPath, projectRoot = process.cwd()) {
|
|
387
|
+
const outDir = path.resolve(projectRoot, outputDir);
|
|
388
|
+
const outFile = schemasOutputPath ? path.resolve(projectRoot, schemasOutputPath) : path.join(outDir, "schemas.ts");
|
|
318
389
|
const schemasDir = path.dirname(outFile);
|
|
319
390
|
const apiTypesPath = path.join(outDir, "api", "types.gen.ts");
|
|
320
391
|
const apiTypesImportPath = toImportPath(schemasDir, apiTypesPath);
|
|
@@ -53,10 +53,10 @@ function toInterfaceName(key) {
|
|
|
53
53
|
const pascal = key.replace(/[^a-zA-Z0-9_$]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").replace(/(^|_)([a-z])/g, (_, __, c) => c.toUpperCase());
|
|
54
54
|
return pascal || "StaticProp";
|
|
55
55
|
}
|
|
56
|
-
async function emitStaticPropsTypes(outputDir) {
|
|
56
|
+
async function emitStaticPropsTypes(outputDir, projectRoot = process.cwd()) {
|
|
57
57
|
const bridgeConfig = readBridgeConfig();
|
|
58
58
|
const staticProps = bridgeConfig?.staticProps ?? {};
|
|
59
|
-
const outDir = path.resolve(
|
|
59
|
+
const outDir = path.resolve(projectRoot, outputDir);
|
|
60
60
|
await fs.promises.mkdir(outDir, { recursive: true });
|
|
61
61
|
const outFile = path.join(outDir, "static-props.ts");
|
|
62
62
|
if (Object.keys(staticProps).length === 0) {
|