An experimental TypeSpec syntax for Lexicon

fixes

+61 -15
+1 -1
packages/cli/package.json
··· 1 1 { 2 2 "name": "@typelex/cli", 3 - "version": "0.2.10", 3 + "version": "0.2.12", 4 4 "main": "dist/index.js", 5 5 "type": "module", 6 6 "bin": {
+60 -14
packages/cli/src/commands/init.ts
··· 4 4 import { createInterface } from "readline"; 5 5 import pc from "picocolors"; 6 6 7 + // Gradient colors matching website (approximated with ANSI 256 colors) 8 + // Darker variant for better readability on white terminals 9 + function gradientText(text: string): string { 10 + // Approximating: #4a9eff -> #7a8ef7 -> #ff85c1 -> #9b7ef7 11 + // Using darker ANSI 256 color codes for a blue->purple->pink gradient 12 + const colors = [ 13 + '\x1b[38;5;33m', // darker blue 14 + '\x1b[38;5;69m', // blue-purple 15 + '\x1b[38;5;99m', // purple 16 + '\x1b[38;5;133m', // purple-pink 17 + '\x1b[38;5;170m', // pink 18 + '\x1b[38;5;170m', // pink 19 + '\x1b[38;5;133m', // purple-pink 20 + ]; 21 + const reset = '\x1b[0m'; 22 + 23 + return text.split('').map((char, i) => { 24 + const colorIndex = Math.floor((i / text.length) * colors.length); 25 + return colors[colorIndex] + char; 26 + }).join('') + reset; 27 + } 28 + 7 29 function createMainTemplate(namespace: string): string { 8 30 return `import "@typelex/emitter"; 9 31 import "./externals.tsp"; ··· 50 72 return initSetup(); 51 73 } 52 74 53 - console.log(pc.bold("Initializing typelex project...\n")); 54 - console.log("Installing dependencies..."); 75 + console.log(`Adding ${gradientText("typelex")}...\n`); 55 76 56 77 // Detect package manager: walk up from cwd 57 78 let packageManager = "npm"; ··· 87 108 88 109 install.on("close", (code) => { 89 110 if (code === 0) { 90 - console.log("\n✓ Installed @typelex/cli and @typelex/emitter\n"); 111 + console.log(`\n${pc.green("✓")} Installed ${pc.dim("@typelex/cli")} and ${pc.dim("@typelex/emitter")}\n`); 91 112 resolvePromise(); 92 113 } else { 93 114 console.error(pc.red("✗ Failed to install dependencies")); ··· 102 123 }); 103 124 104 125 // Hand off to locally installed version 105 - console.log("Continuing with local installation...\n"); 126 + // Find where node_modules actually is (could be at workspace root with pnpm) 127 + let nodeModulesDir = resolve(cwd, "node_modules"); 128 + let searchDir = cwd; 129 + 130 + // Search upward for node_modules with typelex installed 131 + while (searchDir !== resolve(searchDir, "..")) { 132 + try { 133 + const candidatePath = resolve(searchDir, "node_modules/.bin/typelex"); 134 + await access(candidatePath); 135 + nodeModulesDir = resolve(searchDir, "node_modules"); 136 + break; 137 + } catch { 138 + // Not found, try parent 139 + } 140 + searchDir = resolve(searchDir, ".."); 141 + } 106 142 107 143 return new Promise((resolvePromise, reject) => { 108 - const localCli = resolve(cwd, "node_modules/.bin/typelex"); 144 + const localCli = resolve(nodeModulesDir, ".bin/typelex"); 109 145 const setup = spawn(localCli, ["init", "--setup"], { 110 146 cwd, 111 147 stdio: "inherit", ··· 176 212 } 177 213 } 178 214 215 + // Determine the actual lexicons path for display 216 + const displayLexiconsPath = hasLocalLexicons 217 + ? "./lexicons" 218 + : lexiconsDir || "./lexicons"; 219 + 220 + // Inform about external lexicons 221 + console.log(`\nLexicons other than ${pc.cyan(namespace)} will be considered external.`); 222 + console.log(`Put them into the ${pc.cyan(displayLexiconsPath)} folder as JSON.\n`); 223 + 179 224 // Create typelex directory 180 225 await mkdir(typelexDir, { recursive: true }); 181 226 ··· 185 230 await access(mainTspPath); 186 231 const content = await readFile(mainTspPath, "utf-8"); 187 232 if (content.trim().length > 0) { 188 - console.log(`✓ ${pc.cyan("typelex/main.tsp")} already exists, skipping`); 233 + console.log(`${pc.green("✓")} ${pc.cyan("typelex/main.tsp")} already exists, skipping`); 189 234 shouldCreateMain = false; 190 235 } 191 236 } catch { ··· 194 239 195 240 if (shouldCreateMain) { 196 241 await writeFile(mainTspPath, createMainTemplate(namespacePrefix), "utf-8"); 197 - console.log(`✓ Created ${pc.cyan("typelex/main.tsp")}`); 242 + console.log(`${pc.green("✓")} Created ${pc.cyan("typelex/main.tsp")}`); 198 243 } 199 244 200 245 // Always create/overwrite externals.tsp 201 246 await writeFile(externalsTspPath, EXTERNALS_TSP_TEMPLATE, "utf-8"); 202 - console.log(`✓ Created ${pc.cyan("typelex/externals.tsp")}`); 247 + console.log(`${pc.green("✓")} Created ${pc.cyan("typelex/externals.tsp")}`); 203 248 204 249 // Add build script to package.json 205 250 const packageJsonPath = resolve(cwd, "package.json"); ··· 212 257 const outFlag = lexiconsDir ? ` --out ${lexiconsDir}` : ""; 213 258 packageJson.scripts["build:typelex"] = `typelex compile ${namespace}${outFlag}`; 214 259 await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8"); 215 - console.log(`✓ Added ${pc.cyan("build:typelex")} script to ${pc.cyan("package.json")}`); 260 + console.log(`${pc.green("✓")} Added ${pc.cyan("build:typelex")} script to ${pc.cyan("package.json")}`); 216 261 if (hasLocalLexicons) { 217 262 console.log(pc.dim(` Using existing lexicons directory: ${pc.cyan("./lexicons")}`)); 218 263 } else if (lexiconsDir) { 219 264 console.log(pc.dim(` Using existing lexicons directory: ${pc.cyan(lexiconsDir)}`)); 220 265 } 221 266 } else { 222 - console.log(`✓ ${pc.cyan("build:typelex")} script already exists in ${pc.cyan("package.json")}`); 267 + console.log(`${pc.green("✓")} ${pc.cyan("build:typelex")} script already exists in ${pc.cyan("package.json")}`); 223 268 } 224 269 } catch (err) { 225 270 console.warn(pc.yellow(`⚠ Could not update ${pc.cyan("package.json")}:`), (err as Error).message); 226 271 } 227 272 228 - console.log(pc.bold("\n✓ Typelex initialized successfully!")); 229 - console.log(pc.bold("\nNext steps:")); 230 - console.log(` 1. Edit ${pc.cyan("typelex/main.tsp")} to define your lexicons`); 231 - console.log(` 2. Run: ${pc.cyan("npm run build:typelex")}`); 273 + console.log(`\n${pc.green("✓")} ${pc.bold("All set!")}`); 274 + console.log(`\n${pc.bold("Next steps:")}`); 275 + console.log(` ${pc.dim("1.")} Edit ${pc.cyan("typelex/main.tsp")} to define your lexicons`); 276 + console.log(` ${pc.dim("2.")} Keep putting external lexicons into ${pc.cyan(displayLexiconsPath)}`); 277 + console.log(` ${pc.dim("3.")} Run ${pc.cyan("npm run build:typelex")} to compile to JSON`); 232 278 }