litestar-vite-plugin 0.24.1 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/js/astro.d.ts +6 -0
- package/dist/js/astro.js +14 -77
- package/dist/js/dev-server-index.html +2 -2
- 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 +75 -128
- 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 +12 -72
- 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/shared/vite-compat.d.ts +23 -15
- package/dist/js/shared/vite-compat.js +12 -6
- package/dist/js/sveltekit.d.ts +6 -0
- package/dist/js/sveltekit.js +14 -77
- package/package.json +14 -8
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
|
-
import { resolveInstallHint,
|
|
6
|
+
import { resolveInstallHint, resolvePackageExecutorArgv } from "../install-hint.js";
|
|
7
|
+
import { HEY_API_PINNED_SPEC } from "./constants.js";
|
|
7
8
|
import { emitPagePropsTypes } from "./emit-page-props-types.js";
|
|
8
9
|
import { emitSchemasTypes } from "./emit-schemas-types.js";
|
|
9
|
-
|
|
10
|
+
import { emitStaticPropsTypes } from "./emit-static-props-types.js";
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
10
12
|
const nodeRequire = createRequire(import.meta.url);
|
|
11
13
|
function resolveDefaultSdkClientPlugin(options) {
|
|
12
14
|
void options;
|
|
13
15
|
return "@hey-api/client-fetch";
|
|
14
16
|
}
|
|
15
17
|
function findOpenApiTsConfig(projectRoot) {
|
|
16
|
-
const
|
|
18
|
+
const bases = ["openapi-ts.config", "hey-api.config", ".hey-api.config"];
|
|
19
|
+
const extensions = [".ts", ".mjs", ".cjs", ".js"];
|
|
20
|
+
const candidates = bases.flatMap((base) => extensions.map((extension) => path.resolve(projectRoot, `${base}${extension}`)));
|
|
17
21
|
return candidates.find((p) => fs.existsSync(p)) || null;
|
|
18
22
|
}
|
|
19
23
|
function buildHeyApiPlugins(config) {
|
|
@@ -26,22 +30,28 @@ function buildHeyApiPlugins(config) {
|
|
|
26
30
|
}
|
|
27
31
|
return plugins;
|
|
28
32
|
}
|
|
33
|
+
function resolveHeyApiBin(projectRoot) {
|
|
34
|
+
try {
|
|
35
|
+
const packageJsonPath = nodeRequire.resolve("@hey-api/openapi-ts/package.json", { paths: [projectRoot] });
|
|
36
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
37
|
+
const bin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.["openapi-ts"];
|
|
38
|
+
if (!bin) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return { binPath: path.resolve(path.dirname(packageJsonPath), bin) };
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
29
46
|
async function runHeyApiGeneration(config, configPath, plugins, logger) {
|
|
30
47
|
const { projectRoot, openapiPath, output, executor, generateZod } = config;
|
|
31
48
|
const sdkOutput = path.join(output, "api");
|
|
32
|
-
if (process.env.VITEST || process.env.VITE_TEST || process.env.NODE_ENV === "test" || process.env.CI) {
|
|
33
|
-
try {
|
|
34
|
-
nodeRequire.resolve("@hey-api/openapi-ts/package.json", { paths: [projectRoot] });
|
|
35
|
-
} catch {
|
|
36
|
-
throw new Error("@hey-api/openapi-ts not installed");
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
49
|
let args;
|
|
40
50
|
if (configPath) {
|
|
41
|
-
args = ["
|
|
51
|
+
args = ["--file", configPath];
|
|
42
52
|
} else {
|
|
43
53
|
const relativeOpenapiPath = path.relative(projectRoot, path.resolve(projectRoot, openapiPath));
|
|
44
|
-
args = ["
|
|
54
|
+
args = ["-i", relativeOpenapiPath, "-o", sdkOutput];
|
|
45
55
|
if (plugins.length) {
|
|
46
56
|
args.push("--plugins", ...plugins);
|
|
47
57
|
}
|
|
@@ -53,12 +63,23 @@ async function runHeyApiGeneration(config, configPath, plugins, logger) {
|
|
|
53
63
|
logger?.warn(`zod not installed - run: ${resolveInstallHint("zod")}`);
|
|
54
64
|
}
|
|
55
65
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
66
|
+
const local = resolveHeyApiBin(projectRoot);
|
|
67
|
+
if (local) {
|
|
68
|
+
await execFileAsync(process.execPath, [local.binPath, ...args], { cwd: projectRoot });
|
|
69
|
+
return sdkOutput;
|
|
70
|
+
}
|
|
71
|
+
const fallback = resolvePackageExecutorArgv(args, executor, {
|
|
72
|
+
packageSpec: HEY_API_PINNED_SPEC,
|
|
73
|
+
binName: "openapi-ts"
|
|
74
|
+
});
|
|
75
|
+
if (!fallback.length) {
|
|
76
|
+
throw new Error("@hey-api/openapi-ts not installed");
|
|
77
|
+
}
|
|
78
|
+
await execFileAsync(fallback[0], fallback.slice(1), { cwd: projectRoot });
|
|
58
79
|
return sdkOutput;
|
|
59
80
|
}
|
|
60
81
|
async function runTypeGeneration(config, options = {}) {
|
|
61
|
-
const { logger } = options;
|
|
82
|
+
const { cache, logger } = options;
|
|
62
83
|
const startTime = Date.now();
|
|
63
84
|
const result = {
|
|
64
85
|
generated: false,
|
|
@@ -76,40 +97,60 @@ async function runTypeGeneration(config, options = {}) {
|
|
|
76
97
|
const shouldGenerateSdk = configPath || generateSdk;
|
|
77
98
|
if (fs.existsSync(absoluteOpenapiPath) && shouldGenerateSdk) {
|
|
78
99
|
const plugins = buildHeyApiPlugins(config);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (isPackageNotInstalled) {
|
|
93
|
-
const zodHint = config.generateZod ? " zod" : "";
|
|
94
|
-
const warning = `@hey-api/openapi-ts not installed - run: ${resolveInstallHint(`@hey-api/openapi-ts${zodHint}`)}`;
|
|
95
|
-
result.warnings.push(warning);
|
|
96
|
-
logger?.warn(warning);
|
|
97
|
-
} else if (isRuntimeEnoent) {
|
|
98
|
-
result.errors.push(`File not found during type generation: ${message}`);
|
|
99
|
-
logger?.error(`Type generation failed (file not found): ${message}`);
|
|
100
|
-
} else {
|
|
101
|
-
result.errors.push(message);
|
|
102
|
-
logger?.error(`Type generation failed: ${message}`);
|
|
100
|
+
const sdkOutput = path.join(output, "api");
|
|
101
|
+
const cacheOptions = {
|
|
102
|
+
generateSdk: config.generateSdk,
|
|
103
|
+
generateZod: config.generateZod,
|
|
104
|
+
plugins,
|
|
105
|
+
outputPaths: [path.join(projectRoot, sdkOutput, "types.gen.ts")]
|
|
106
|
+
};
|
|
107
|
+
const shouldRun = cache ? await cache.shouldRunOpenApiTs(absoluteOpenapiPath, configPath, cacheOptions) : true;
|
|
108
|
+
if (shouldRun) {
|
|
109
|
+
logger?.info("Generating TypeScript types...");
|
|
110
|
+
if (configPath) {
|
|
111
|
+
const relConfigPath = path.relative(projectRoot, configPath);
|
|
112
|
+
logger?.info(`openapi-ts config: ${relConfigPath}`);
|
|
103
113
|
}
|
|
114
|
+
try {
|
|
115
|
+
const outputPath = await runHeyApiGeneration(config, configPath, plugins, logger);
|
|
116
|
+
await cache?.updateOpenApiTsCache(absoluteOpenapiPath, configPath, cacheOptions);
|
|
117
|
+
result.generatedFiles.push(outputPath);
|
|
118
|
+
result.generated = true;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
+
const isPackageNotInstalled = message.includes("not installed");
|
|
122
|
+
const isRuntimeEnoent = !isPackageNotInstalled && (message.includes("ENOENT") || error instanceof Error && "code" in error && error.code === "ENOENT");
|
|
123
|
+
if (isPackageNotInstalled) {
|
|
124
|
+
const zodHint = config.generateZod ? " zod" : "";
|
|
125
|
+
const errorMessage = `@hey-api/openapi-ts not installed - run: ${resolveInstallHint(`@hey-api/openapi-ts${zodHint}`)}`;
|
|
126
|
+
result.errors.push(errorMessage);
|
|
127
|
+
logger?.error(errorMessage);
|
|
128
|
+
} else if (isRuntimeEnoent) {
|
|
129
|
+
result.errors.push(`File not found during type generation: ${message}`);
|
|
130
|
+
logger?.error(`Type generation failed (file not found): ${message}`);
|
|
131
|
+
} else {
|
|
132
|
+
result.errors.push(message);
|
|
133
|
+
logger?.error(`Type generation failed: ${message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
result.skippedFiles.push(sdkOutput);
|
|
104
138
|
}
|
|
105
139
|
}
|
|
106
140
|
if (generatePageProps && fs.existsSync(absolutePagePropsPath)) {
|
|
107
141
|
try {
|
|
108
|
-
const changed = await emitPagePropsTypes(absolutePagePropsPath, output);
|
|
109
142
|
const pagePropsOutput = path.join(output, "page-props.ts");
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
143
|
+
const absolutePagePropsOutput = path.resolve(projectRoot, pagePropsOutput);
|
|
144
|
+
const shouldRun = cache ? await cache.shouldRegeneratePageProps(absolutePagePropsPath, absolutePagePropsOutput) : true;
|
|
145
|
+
if (shouldRun) {
|
|
146
|
+
const changed = await emitPagePropsTypes(absolutePagePropsPath, output, projectRoot);
|
|
147
|
+
await cache?.updatePagePropsCache(absolutePagePropsPath);
|
|
148
|
+
if (changed) {
|
|
149
|
+
result.generatedFiles.push(pagePropsOutput);
|
|
150
|
+
result.generated = true;
|
|
151
|
+
} else {
|
|
152
|
+
result.skippedFiles.push(pagePropsOutput);
|
|
153
|
+
}
|
|
113
154
|
} else {
|
|
114
155
|
result.skippedFiles.push(pagePropsOutput);
|
|
115
156
|
}
|
|
@@ -124,7 +165,7 @@ async function runTypeGeneration(config, options = {}) {
|
|
|
124
165
|
const absoluteRoutesPath = path.resolve(projectRoot, routesPath);
|
|
125
166
|
if (fs.existsSync(absoluteRoutesPath)) {
|
|
126
167
|
try {
|
|
127
|
-
const changed = await emitSchemasTypes(absoluteRoutesPath, output, schemasTsPath);
|
|
168
|
+
const changed = await emitSchemasTypes(absoluteRoutesPath, output, schemasTsPath, projectRoot);
|
|
128
169
|
const schemasOutput = schemasTsPath ?? path.join(output, "schemas.ts");
|
|
129
170
|
if (changed) {
|
|
130
171
|
result.generatedFiles.push(schemasOutput);
|
|
@@ -139,6 +180,20 @@ async function runTypeGeneration(config, options = {}) {
|
|
|
139
180
|
}
|
|
140
181
|
}
|
|
141
182
|
}
|
|
183
|
+
try {
|
|
184
|
+
const changed = await emitStaticPropsTypes(output, projectRoot);
|
|
185
|
+
const staticPropsOutput = path.join(output, "static-props.ts");
|
|
186
|
+
if (changed) {
|
|
187
|
+
result.generatedFiles.push(staticPropsOutput);
|
|
188
|
+
result.generated = true;
|
|
189
|
+
} else {
|
|
190
|
+
result.skippedFiles.push(staticPropsOutput);
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
194
|
+
result.errors.push(`Static props generation failed: ${message}`);
|
|
195
|
+
logger?.error(`Static props generation failed: ${message}`);
|
|
196
|
+
}
|
|
142
197
|
} finally {
|
|
143
198
|
result.durationMs = Date.now() - startTime;
|
|
144
199
|
}
|
|
@@ -148,6 +203,7 @@ export {
|
|
|
148
203
|
buildHeyApiPlugins,
|
|
149
204
|
findOpenApiTsConfig,
|
|
150
205
|
resolveDefaultSdkClientPlugin,
|
|
206
|
+
resolveHeyApiBin,
|
|
151
207
|
runHeyApiGeneration,
|
|
152
208
|
runTypeGeneration
|
|
153
209
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
import type { BridgeTypesConfig } from "./bridge-schema.js";
|
|
2
3
|
export interface RequiredTypeGenConfig {
|
|
3
4
|
enabled: boolean;
|
|
4
5
|
output: string;
|
|
@@ -12,6 +13,7 @@ export interface RequiredTypeGenConfig {
|
|
|
12
13
|
generatePageProps: boolean;
|
|
13
14
|
generateSchemas: boolean;
|
|
14
15
|
globalRoute: boolean;
|
|
16
|
+
failOnError?: boolean;
|
|
15
17
|
debounce: number;
|
|
16
18
|
}
|
|
17
19
|
export interface TypeGenPluginOptions {
|
|
@@ -26,6 +28,30 @@ export interface TypeGenPluginOptions {
|
|
|
26
28
|
/** Whether .litestar.json was present (used for buildStart warnings) */
|
|
27
29
|
hasPythonConfig?: boolean;
|
|
28
30
|
}
|
|
31
|
+
export interface TypesConfigShape {
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
output?: string;
|
|
34
|
+
openapiPath?: string;
|
|
35
|
+
routesPath?: string;
|
|
36
|
+
pagePropsPath?: string;
|
|
37
|
+
schemasTsPath?: string;
|
|
38
|
+
generateZod?: boolean;
|
|
39
|
+
generateSdk?: boolean;
|
|
40
|
+
generateRoutes?: boolean;
|
|
41
|
+
generatePageProps?: boolean;
|
|
42
|
+
generateSchemas?: boolean;
|
|
43
|
+
globalRoute?: boolean;
|
|
44
|
+
failOnError?: boolean;
|
|
45
|
+
debounce?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ResolveTypesConfigOptions {
|
|
48
|
+
requested: boolean | "auto" | TypesConfigShape | undefined;
|
|
49
|
+
pythonConfig?: BridgeTypesConfig;
|
|
50
|
+
defaultOutput: string;
|
|
51
|
+
mergePythonWhenTrue?: boolean;
|
|
52
|
+
mergePythonForObject?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export declare function resolveTypesConfig(options: ResolveTypesConfigOptions): RequiredTypeGenConfig | false;
|
|
29
55
|
/**
|
|
30
56
|
* Unified Litestar type generation Vite plugin.
|
|
31
57
|
*
|
|
@@ -1,13 +1,97 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import colors from "picocolors";
|
|
4
|
+
import { DEBOUNCE_MS } from "./constants.js";
|
|
4
5
|
import { debounce } from "./debounce.js";
|
|
5
|
-
import { emitPagePropsTypes } from "./emit-page-props-types.js";
|
|
6
|
-
import { emitSchemasTypes } from "./emit-schemas-types.js";
|
|
7
|
-
import { emitStaticPropsTypes } from "./emit-static-props-types.js";
|
|
8
|
-
import { formatPath } from "./format-path.js";
|
|
9
6
|
import { shouldRegeneratePageProps, shouldRunOpenApiTs, updateOpenApiTsCache, updatePagePropsCache } from "./typegen-cache.js";
|
|
10
|
-
import {
|
|
7
|
+
import { runTypeGeneration } from "./typegen-core.js";
|
|
8
|
+
function buildTypeDefaults(output) {
|
|
9
|
+
return {
|
|
10
|
+
openapiPath: path.join(output, "openapi.json"),
|
|
11
|
+
routesPath: path.join(output, "routes.json"),
|
|
12
|
+
pagePropsPath: path.join(output, "inertia-pages.json"),
|
|
13
|
+
schemasTsPath: path.join(output, "schemas.ts")
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function resolveFromPython(pythonConfig, defaultOutput) {
|
|
17
|
+
const output = pythonConfig.output ?? defaultOutput;
|
|
18
|
+
const defaults = buildTypeDefaults(output);
|
|
19
|
+
return {
|
|
20
|
+
enabled: true,
|
|
21
|
+
output,
|
|
22
|
+
openapiPath: pythonConfig.openapiPath ?? defaults.openapiPath,
|
|
23
|
+
routesPath: pythonConfig.routesPath ?? defaults.routesPath,
|
|
24
|
+
pagePropsPath: pythonConfig.pagePropsPath ?? defaults.pagePropsPath,
|
|
25
|
+
schemasTsPath: pythonConfig.schemasTsPath ?? defaults.schemasTsPath,
|
|
26
|
+
generateZod: pythonConfig.generateZod ?? false,
|
|
27
|
+
generateSdk: pythonConfig.generateSdk ?? true,
|
|
28
|
+
generateRoutes: pythonConfig.generateRoutes ?? true,
|
|
29
|
+
generatePageProps: pythonConfig.generatePageProps ?? true,
|
|
30
|
+
generateSchemas: pythonConfig.generateSchemas ?? true,
|
|
31
|
+
globalRoute: pythonConfig.globalRoute ?? false,
|
|
32
|
+
failOnError: pythonConfig.failOnError,
|
|
33
|
+
debounce: DEBOUNCE_MS
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function resolveDefaultTypesConfig(defaultOutput) {
|
|
37
|
+
const defaults = buildTypeDefaults(defaultOutput);
|
|
38
|
+
return {
|
|
39
|
+
enabled: true,
|
|
40
|
+
output: defaultOutput,
|
|
41
|
+
openapiPath: defaults.openapiPath,
|
|
42
|
+
routesPath: defaults.routesPath,
|
|
43
|
+
pagePropsPath: defaults.pagePropsPath,
|
|
44
|
+
schemasTsPath: defaults.schemasTsPath,
|
|
45
|
+
generateZod: false,
|
|
46
|
+
generateSdk: true,
|
|
47
|
+
generateRoutes: true,
|
|
48
|
+
generatePageProps: true,
|
|
49
|
+
generateSchemas: true,
|
|
50
|
+
globalRoute: false,
|
|
51
|
+
failOnError: void 0,
|
|
52
|
+
debounce: DEBOUNCE_MS
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function resolveTypesConfig(options) {
|
|
56
|
+
const { requested, pythonConfig, defaultOutput, mergePythonWhenTrue = false, mergePythonForObject = false } = options;
|
|
57
|
+
if (requested === false) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (requested === true) {
|
|
61
|
+
if (mergePythonWhenTrue && pythonConfig) {
|
|
62
|
+
return resolveFromPython(pythonConfig, defaultOutput);
|
|
63
|
+
}
|
|
64
|
+
return resolveDefaultTypesConfig(defaultOutput);
|
|
65
|
+
}
|
|
66
|
+
if (requested === "auto" || typeof requested === "undefined") {
|
|
67
|
+
return pythonConfig?.enabled ? resolveFromPython(pythonConfig, defaultOutput) : false;
|
|
68
|
+
}
|
|
69
|
+
const userProvidedOutput = Object.hasOwn(requested, "output");
|
|
70
|
+
const output = requested.output ?? (mergePythonForObject ? pythonConfig?.output : void 0) ?? defaultOutput;
|
|
71
|
+
const defaults = buildTypeDefaults(output);
|
|
72
|
+
const pathFallback = (key) => {
|
|
73
|
+
if (!mergePythonForObject || userProvidedOutput) {
|
|
74
|
+
return defaults[key];
|
|
75
|
+
}
|
|
76
|
+
return pythonConfig?.[key] ?? defaults[key];
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
enabled: requested.enabled ?? true,
|
|
80
|
+
output,
|
|
81
|
+
openapiPath: requested.openapiPath ?? pathFallback("openapiPath"),
|
|
82
|
+
routesPath: requested.routesPath ?? pathFallback("routesPath"),
|
|
83
|
+
pagePropsPath: requested.pagePropsPath ?? pathFallback("pagePropsPath"),
|
|
84
|
+
schemasTsPath: requested.schemasTsPath ?? pathFallback("schemasTsPath"),
|
|
85
|
+
generateZod: requested.generateZod ?? (mergePythonForObject ? pythonConfig?.generateZod : void 0) ?? false,
|
|
86
|
+
generateSdk: requested.generateSdk ?? (mergePythonForObject ? pythonConfig?.generateSdk : void 0) ?? true,
|
|
87
|
+
generateRoutes: requested.generateRoutes ?? (mergePythonForObject ? pythonConfig?.generateRoutes : void 0) ?? true,
|
|
88
|
+
generatePageProps: requested.generatePageProps ?? (mergePythonForObject ? pythonConfig?.generatePageProps : void 0) ?? true,
|
|
89
|
+
generateSchemas: requested.generateSchemas ?? (mergePythonForObject ? pythonConfig?.generateSchemas : void 0) ?? true,
|
|
90
|
+
globalRoute: requested.globalRoute ?? (mergePythonForObject ? pythonConfig?.globalRoute : void 0) ?? false,
|
|
91
|
+
failOnError: requested.failOnError ?? pythonConfig?.failOnError,
|
|
92
|
+
debounce: requested.debounce ?? DEBOUNCE_MS
|
|
93
|
+
};
|
|
94
|
+
}
|
|
11
95
|
async function getFileMtime(filePath) {
|
|
12
96
|
const stat = await fs.promises.stat(filePath);
|
|
13
97
|
return stat.mtimeMs.toString();
|
|
@@ -16,8 +100,10 @@ function createLitestarTypeGenPlugin(typesConfig, options) {
|
|
|
16
100
|
const { pluginName, frameworkName, sdkClientPlugin, executor, hasPythonConfig } = options;
|
|
17
101
|
let lastTypesHash = null;
|
|
18
102
|
let lastPagePropsHash = null;
|
|
103
|
+
let lastRoutesHash = null;
|
|
19
104
|
let server = null;
|
|
20
|
-
let
|
|
105
|
+
let generationPromise = null;
|
|
106
|
+
let rerunRequested = false;
|
|
21
107
|
let resolvedConfig = null;
|
|
22
108
|
function createViteLogger() {
|
|
23
109
|
return {
|
|
@@ -27,37 +113,31 @@ function createLitestarTypeGenPlugin(typesConfig, options) {
|
|
|
27
113
|
};
|
|
28
114
|
}
|
|
29
115
|
async function runTypeGenerationWithCache() {
|
|
30
|
-
if (
|
|
31
|
-
|
|
116
|
+
if (generationPromise) {
|
|
117
|
+
rerunRequested = true;
|
|
118
|
+
return generationPromise;
|
|
32
119
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
120
|
+
generationPromise = (async () => {
|
|
121
|
+
const combined = {
|
|
122
|
+
generated: false,
|
|
123
|
+
generatedFiles: [],
|
|
124
|
+
skippedFiles: [],
|
|
125
|
+
durationMs: 0,
|
|
126
|
+
warnings: [],
|
|
127
|
+
errors: []
|
|
128
|
+
};
|
|
36
129
|
const projectRoot = resolvedConfig?.root ?? process.cwd();
|
|
37
|
-
const absoluteOpenapiPath = path.resolve(projectRoot, typesConfig.openapiPath);
|
|
38
|
-
const absolutePagePropsPath = path.resolve(projectRoot, typesConfig.pagePropsPath);
|
|
39
|
-
let generated = false;
|
|
40
130
|
const logger = createViteLogger();
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
generateZod: typesConfig.generateZod,
|
|
52
|
-
plugins
|
|
53
|
-
};
|
|
54
|
-
const shouldRun = await shouldRunOpenApiTs(absoluteOpenapiPath, configPath, cacheOptions);
|
|
55
|
-
if (shouldRun) {
|
|
56
|
-
logger.info("Generating TypeScript types...");
|
|
57
|
-
if (configPath && resolvedConfig) {
|
|
58
|
-
const relConfigPath = formatPath(configPath, resolvedConfig.root);
|
|
59
|
-
logger.info(`openapi-ts config: ${colors.yellow(relConfigPath)}`);
|
|
60
|
-
}
|
|
131
|
+
const cache = {
|
|
132
|
+
shouldRunOpenApiTs,
|
|
133
|
+
updateOpenApiTsCache,
|
|
134
|
+
shouldRegeneratePageProps,
|
|
135
|
+
updatePagePropsCache
|
|
136
|
+
};
|
|
137
|
+
try {
|
|
138
|
+
do {
|
|
139
|
+
rerunRequested = false;
|
|
140
|
+
const startTime = Date.now();
|
|
61
141
|
const coreConfig = {
|
|
62
142
|
projectRoot,
|
|
63
143
|
openapiPath: typesConfig.openapiPath,
|
|
@@ -66,96 +146,76 @@ function createLitestarTypeGenPlugin(typesConfig, options) {
|
|
|
66
146
|
pagePropsPath: typesConfig.pagePropsPath,
|
|
67
147
|
generateSdk: typesConfig.generateSdk,
|
|
68
148
|
generateZod: typesConfig.generateZod,
|
|
69
|
-
generatePageProps:
|
|
70
|
-
// Handle separately below
|
|
149
|
+
generatePageProps: typesConfig.generatePageProps,
|
|
71
150
|
generateSchemas: typesConfig.generateSchemas,
|
|
151
|
+
schemasTsPath: typesConfig.schemasTsPath,
|
|
72
152
|
sdkClientPlugin,
|
|
73
153
|
executor
|
|
74
154
|
};
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
155
|
+
const result = await runTypeGeneration(coreConfig, { logger, cache });
|
|
156
|
+
combined.generated ||= result.generated;
|
|
157
|
+
combined.generatedFiles.push(...result.generatedFiles);
|
|
158
|
+
combined.skippedFiles.push(...result.skippedFiles);
|
|
159
|
+
combined.warnings.push(...result.warnings);
|
|
160
|
+
combined.errors.push(...result.errors);
|
|
161
|
+
combined.durationMs += result.durationMs || Date.now() - startTime;
|
|
162
|
+
for (const file of result.skippedFiles) {
|
|
163
|
+
const label = file.endsWith("page-props.ts") ? "Page props types" : file.endsWith("schemas.ts") ? "Schema types" : file.includes("api") ? "TypeScript types" : file;
|
|
164
|
+
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} ${label} ${colors.dim("(unchanged)")}`);
|
|
86
165
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
166
|
+
} while (rerunRequested);
|
|
167
|
+
if (combined.generated && resolvedConfig) {
|
|
168
|
+
resolvedConfig.logger.info(`${colors.green("\u2713")} TypeScript artifacts updated ${colors.dim(`(${combined.durationMs}ms)`)}`);
|
|
89
169
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (changed) {
|
|
98
|
-
generated = true;
|
|
99
|
-
} else {
|
|
100
|
-
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Page props types ${colors.dim("(unchanged)")}`);
|
|
170
|
+
if (combined.generated && server) {
|
|
171
|
+
server.ws.send({
|
|
172
|
+
type: "custom",
|
|
173
|
+
event: "litestar:types-updated",
|
|
174
|
+
data: {
|
|
175
|
+
output: typesConfig.output,
|
|
176
|
+
timestamp: Date.now()
|
|
101
177
|
}
|
|
102
|
-
}
|
|
103
|
-
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Page props types ${colors.dim("(unchanged)")}`);
|
|
104
|
-
}
|
|
105
|
-
} catch (error) {
|
|
106
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
107
|
-
logger.error(`Page props generation failed: ${message}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const absoluteRoutesPath = path.resolve(projectRoot, typesConfig.routesPath);
|
|
111
|
-
if (typesConfig.generateSchemas && fs.existsSync(absoluteRoutesPath)) {
|
|
112
|
-
try {
|
|
113
|
-
const changed = await emitSchemasTypes(absoluteRoutesPath, typesConfig.output, typesConfig.schemasTsPath);
|
|
114
|
-
if (changed) {
|
|
115
|
-
generated = true;
|
|
116
|
-
} else {
|
|
117
|
-
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Schema types ${colors.dim("(unchanged)")}`);
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
-
logger.error(`Schema types generation failed: ${message}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
try {
|
|
125
|
-
const changed = await emitStaticPropsTypes(typesConfig.output);
|
|
126
|
-
if (changed) {
|
|
127
|
-
generated = true;
|
|
128
|
-
} else {
|
|
129
|
-
resolvedConfig?.logger.info(`${colors.cyan("\u2022")} Static props types ${colors.dim("(unchanged)")}`);
|
|
178
|
+
});
|
|
130
179
|
}
|
|
180
|
+
return combined;
|
|
131
181
|
} catch (error) {
|
|
132
182
|
const message = error instanceof Error ? error.message : String(error);
|
|
133
|
-
logger.error(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
183
|
+
resolvedConfig?.logger.error(`${colors.cyan("litestar-vite")} ${colors.red("type generation failed:")} ${message}`);
|
|
184
|
+
combined.errors.push(message);
|
|
185
|
+
return combined;
|
|
186
|
+
} finally {
|
|
187
|
+
generationPromise = null;
|
|
138
188
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
189
|
+
})();
|
|
190
|
+
return generationPromise;
|
|
191
|
+
}
|
|
192
|
+
const debouncedRunTypeGeneration = debounce(async (file, cacheKey) => {
|
|
193
|
+
const newHash = await getFileMtime(file);
|
|
194
|
+
if (cacheKey === "openapi" && lastTypesHash === newHash) return;
|
|
195
|
+
if (cacheKey === "pageProps" && lastPagePropsHash === newHash) return;
|
|
196
|
+
if (cacheKey === "routes" && lastRoutesHash === newHash) return;
|
|
197
|
+
const result = await runTypeGenerationWithCache();
|
|
198
|
+
if (result.errors.length === 0) {
|
|
199
|
+
if (cacheKey === "openapi") lastTypesHash = newHash;
|
|
200
|
+
if (cacheKey === "pageProps") lastPagePropsHash = newHash;
|
|
201
|
+
if (cacheKey === "routes") lastRoutesHash = newHash;
|
|
202
|
+
}
|
|
203
|
+
}, typesConfig.debounce);
|
|
204
|
+
function shouldFailOnTypegenError() {
|
|
205
|
+
return typesConfig.failOnError ?? resolvedConfig?.command === "build";
|
|
206
|
+
}
|
|
207
|
+
function reportTypegenErrors(result, context) {
|
|
208
|
+
if (!result.errors.length) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const message = `Litestar type generation failed:
|
|
212
|
+
${result.errors.join("\n")}`;
|
|
213
|
+
if (shouldFailOnTypegenError()) {
|
|
214
|
+
context.error(message);
|
|
215
|
+
} else {
|
|
216
|
+
context.warn(message);
|
|
156
217
|
}
|
|
157
218
|
}
|
|
158
|
-
const debouncedRunTypeGeneration = debounce(runTypeGenerationWithCache, typesConfig.debounce);
|
|
159
219
|
return {
|
|
160
220
|
name: pluginName,
|
|
161
221
|
enforce: "pre",
|
|
@@ -170,6 +230,9 @@ function createLitestarTypeGenPlugin(typesConfig, options) {
|
|
|
170
230
|
}
|
|
171
231
|
},
|
|
172
232
|
async buildStart() {
|
|
233
|
+
if (resolvedConfig?.command === "build" && process.env.LITESTAR_VITE_SKIP_BUILD_TYPEGEN === "1") {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
173
236
|
if (typesConfig.enabled && hasPythonConfig === false) {
|
|
174
237
|
const projectRoot = resolvedConfig?.root ?? process.cwd();
|
|
175
238
|
const openapiPath = path.resolve(projectRoot, typesConfig.openapiPath);
|
|
@@ -190,10 +253,13 @@ Solutions:
|
|
|
190
253
|
const projectRoot = resolvedConfig?.root ?? process.cwd();
|
|
191
254
|
const openapiPath = path.resolve(projectRoot, typesConfig.openapiPath);
|
|
192
255
|
const pagePropsPath = path.resolve(projectRoot, typesConfig.pagePropsPath);
|
|
256
|
+
const routesPath = path.resolve(projectRoot, typesConfig.routesPath);
|
|
193
257
|
const hasOpenapi = fs.existsSync(openapiPath);
|
|
194
258
|
const hasPageProps = typesConfig.generatePageProps && fs.existsSync(pagePropsPath);
|
|
195
|
-
|
|
196
|
-
|
|
259
|
+
const hasRoutes = typesConfig.generateSchemas && fs.existsSync(routesPath);
|
|
260
|
+
if (hasOpenapi || hasPageProps || hasRoutes) {
|
|
261
|
+
const result = await runTypeGenerationWithCache();
|
|
262
|
+
reportTypegenErrors(result, this);
|
|
197
263
|
}
|
|
198
264
|
}
|
|
199
265
|
},
|
|
@@ -202,26 +268,22 @@ Solutions:
|
|
|
202
268
|
return;
|
|
203
269
|
}
|
|
204
270
|
const root = resolvedConfig?.root ?? process.cwd();
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
271
|
+
const absoluteFile = path.resolve(file);
|
|
272
|
+
const relativePath = path.relative(root, absoluteFile);
|
|
273
|
+
const openapiPath = path.resolve(root, typesConfig.openapiPath);
|
|
274
|
+
const pagePropsPath = path.resolve(root, typesConfig.pagePropsPath);
|
|
275
|
+
const routesPath = path.resolve(root, typesConfig.routesPath);
|
|
276
|
+
const isOpenapi = absoluteFile === openapiPath;
|
|
277
|
+
const isPageProps = typesConfig.generatePageProps && absoluteFile === pagePropsPath;
|
|
278
|
+
const isRoutes = typesConfig.generateSchemas && absoluteFile === routesPath;
|
|
279
|
+
if (isOpenapi || isPageProps || isRoutes) {
|
|
211
280
|
resolvedConfig?.logger.info(`${colors.cyan(frameworkName)} ${colors.dim("schema changed:")} ${colors.yellow(relativePath)}`);
|
|
212
|
-
|
|
213
|
-
if (isOpenapi) {
|
|
214
|
-
if (lastTypesHash === newHash) return;
|
|
215
|
-
lastTypesHash = newHash;
|
|
216
|
-
} else if (isPageProps) {
|
|
217
|
-
if (lastPagePropsHash === newHash) return;
|
|
218
|
-
lastPagePropsHash = newHash;
|
|
219
|
-
}
|
|
220
|
-
debouncedRunTypeGeneration();
|
|
281
|
+
debouncedRunTypeGeneration(absoluteFile, isOpenapi ? "openapi" : isPageProps ? "pageProps" : "routes");
|
|
221
282
|
}
|
|
222
283
|
}
|
|
223
284
|
};
|
|
224
285
|
}
|
|
225
286
|
export {
|
|
226
|
-
createLitestarTypeGenPlugin
|
|
287
|
+
createLitestarTypeGenPlugin,
|
|
288
|
+
resolveTypesConfig
|
|
227
289
|
};
|