-
Notifications
You must be signed in to change notification settings - Fork 0
/
build-trpc-types.ts
122 lines (99 loc) · 3.38 KB
/
build-trpc-types.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import fs from "fs";
import path from "path";
import { Project, SourceFile, TypeAliasDeclaration } from "ts-morph";
import { z } from "zod";
import configFile from "./trpc-types-cli.config";
import url from "url";
const ConfigSchema = z.object({
routerFile: z.string(),
outputFile: z.string(),
includePaths: z.array(z.string()).optional(),
excludePaths: z.array(z.string()).optional(),
baseUrl: z.string().optional(),
generateClient: z.boolean().optional(),
});
type Config = z.infer<typeof ConfigSchema>;
class TRPCTypesGenerator {
private project: Project;
private config: Config;
constructor(configPath: string) {
this.config = ConfigSchema.parse(configFile);
// Initialize ts-morph project
this.project = new Project({
tsConfigFilePath: path.resolve(process.cwd(), "tsconfig.json"),
skipAddingFilesFromTsConfig: true,
});
}
private findAppRouter(
sourceFile: SourceFile
): TypeAliasDeclaration | undefined {
return sourceFile
.getTypeAliases()
.find(
(type) =>
type.getName() === "AppRouter" || type.getName()?.includes("Router")
);
}
private generateTypeDefinitions(): string {
const routerFile = this.project.addSourceFileAtPath(
path.resolve(process.cwd(), this.config.routerFile)
);
const appRouter = this.findAppRouter(routerFile);
if (!appRouter) {
throw new Error(
"Could not find AppRouter type in the specified router file"
);
}
let output = "// ✨ Generated by trpc-types-cli\n\n";
output += `import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';\n\n`;
output += `export type AppRouter = ${appRouter
.getType()
.getText(appRouter)};\n\n`;
output += `export type RouterInputs = inferRouterInputs<AppRouter>;\n`;
output += `export type RouterOutputs = inferRouterOutputs<AppRouter>;\n`;
if (this.config.generateClient) {
output += `
export type RouterProcedures = AppRouter['_def']['procedures'];
export type ProcedureNames<T extends RouterProcedures> = {
[K in keyof T]: T[K] extends RouterProcedures ? ProcedureNames<T[K]> : K;
}[keyof T];
`;
}
return output;
}
public async generate(): Promise<void> {
try {
const outputPath = path.resolve(process.cwd(), this.config.outputFile);
const typeDefinitions = this.generateTypeDefinitions();
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(outputPath, typeDefinitions);
console.log(
`✨ Successfully generated types at ${this.config.outputFile}`
);
} catch (error) {
console.error("Error generating types:", error);
process.exit(1);
}
}
}
if (import.meta.url === url.pathToFileURL(process.argv[1]!).href) {
const configPath = process.argv[2] || "./trpc-types-cli.config.ts";
const loadConfig = async () => {
try {
const configModule = await import(
url.pathToFileURL(path.resolve(process.cwd(), configPath)).href
);
const config = configModule.default || configModule;
const generator = new TRPCTypesGenerator(config);
await generator.generate();
} catch (error) {
console.error("Error loading configuration:", error);
process.exit(1);
}
};
loadConfig();
}
export { TRPCTypesGenerator, type Config };