Your music, beautifully tracked. All yours. (coming soon)
teal.fm
teal-fm
atproto
1import { existsSync } from "fs";
2import { join } from "path";
3import { execa } from "execa";
4import pc from "picocolors";
5
6import { findWorkspaceRoot } from "../utils/workspace.js";
7
8interface GenerateOptions {
9 tsOnly?: boolean;
10 rustOnly?: boolean;
11 force?: boolean;
12}
13
14export async function generate(options: GenerateOptions = {}) {
15 const workspaceRoot = findWorkspaceRoot();
16
17 console.log(pc.blue("🔧 Generating lexicon types..."));
18
19 try {
20 if (!options.rustOnly) {
21 await generateTypeScript(workspaceRoot, options.force);
22 }
23
24 if (!options.tsOnly) {
25 await generateRust(workspaceRoot, options.force);
26 }
27
28 console.log(pc.green("✅ Lexicon generation complete!"));
29 } catch (error) {
30 console.error(
31 pc.red("❌ Generation failed:"),
32 error instanceof Error ? error.message : String(error),
33 );
34 process.exit(1);
35 }
36}
37
38async function generateTypeScript(workspaceRoot: string, force?: boolean) {
39 const lexiconsPath = join(workspaceRoot, "lexicons");
40
41 if (!existsSync(lexiconsPath)) {
42 throw new Error("Lexicons directory not found at workspace root");
43 }
44
45 // Check if packages/lexicons exists for TypeScript generation
46 const packagesLexiconsPath = join(workspaceRoot, "packages/lexicons");
47 if (!existsSync(packagesLexiconsPath)) {
48 console.log(
49 pc.yellow(
50 " ⚠️ TypeScript lexicons package not found, skipping TypeScript generation",
51 ),
52 );
53 return;
54 }
55
56 console.log(pc.cyan(" 📦 Generating TypeScript types..."));
57
58 try {
59 await execa("pnpm", ["lex:gen-server"], {
60 cwd: packagesLexiconsPath,
61 stdio: "inherit",
62 });
63 console.log(pc.green(" ✓ TypeScript types generated"));
64 } catch (error) {
65 throw new Error(
66 `TypeScript generation failed: ${error instanceof Error ? error.message : String(error)}`,
67 );
68 }
69}
70
71async function generateRust(workspaceRoot: string, force?: boolean) {
72 const typesPath = join(workspaceRoot, "services/types");
73 const lexiconsPath = join(workspaceRoot, "lexicons");
74
75 if (!existsSync(typesPath)) {
76 throw new Error("Rust types service not found");
77 }
78
79 if (!existsSync(lexiconsPath)) {
80 throw new Error("Lexicons directory not found at workspace root");
81 }
82
83 console.log(pc.cyan(" 🦀 Generating Rust types..."));
84
85 try {
86 // Check if esquema-cli is available
87 try {
88 await execa("esquema-cli", ["--version"], { stdio: "pipe" });
89 } catch {
90 console.log(pc.yellow(" ⚠️ esquema-cli not found. Installing..."));
91 try {
92 await execa(
93 "cargo",
94 [
95 "install",
96 "esquema-cli",
97 "--git",
98 "https://github.com/fatfingers23/esquema.git",
99 ],
100 {
101 stdio: "inherit",
102 },
103 );
104 console.log(pc.green(" ✓ esquema-cli installed successfully"));
105 } catch (installError) {
106 throw new Error(
107 "Failed to install esquema-cli. Please install manually: cargo install esquema-cli --git https://github.com/fatfingers23/esquema.git",
108 );
109 }
110 }
111
112 // create typespath/src if it doesn't exist
113 if (!existsSync(join(typesPath, "src"))) {
114 console.log(pc.yellow(" Creating src directory for Rust types..."));
115 await execa("mkdir", ["-p", join(typesPath, "src")], {
116 stdio: "inherit",
117 });
118 }
119
120 await execa(
121 "esquema-cli",
122 [
123 "generate",
124 "local",
125 "--lexdir",
126 lexiconsPath,
127 "--outdir",
128 join(typesPath, "src"),
129 ],
130 {
131 cwd: typesPath,
132 stdio: "inherit",
133 },
134 );
135
136 console.log(pc.green(" ✓ Rust types generated"));
137 } catch (error) {
138 throw new Error(
139 `Rust generation failed: ${error instanceof Error ? error.message : String(error)}`,
140 );
141 }
142}