An experimental TypeSpec syntax for Lexicon

nits

+51 -36
+17 -1
packages/cli/src/cli.ts
··· 19 }); 20 }, 21 async (argv) => { 22 - await initCommand(argv.setup); 23 } 24 ) 25 .command(
··· 19 }); 20 }, 21 async (argv) => { 22 + // Extract any unknown flags to pass through to package manager 23 + const flags: string[] = []; 24 + const knownFlags = new Set(["setup", "_", "$0"]); 25 + 26 + for (const [key, value] of Object.entries(argv)) { 27 + if (!knownFlags.has(key)) { 28 + // Single letter = short flag, multiple letters = long flag 29 + const prefix = key.length === 1 ? "-" : "--"; 30 + if (typeof value === "boolean" && value) { 31 + flags.push(`${prefix}${key}`); 32 + } else if (value !== false && value !== undefined) { 33 + flags.push(`${prefix}${key}`, String(value)); 34 + } 35 + } 36 + } 37 + 38 + await initCommand(argv.setup, flags); 39 } 40 ) 41 .command(
+34 -35
packages/cli/src/commands/init.ts
··· 4 import { createInterface } from "readline"; 5 import pc from "picocolors"; 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 ··· 60 }); 61 } 62 63 - /** 64 - * Initialize command - installs packages and hands off to local version 65 - * This is what gets called by npx 66 - */ 67 - export async function initCommand(isSetup: boolean = false): Promise<void> { 68 - const cwd = process.cwd(); 69 70 - // If this is the second pass (after handoff), run setup 71 if (isSetup) { 72 return initSetup(); 73 } 74 75 console.log(`Adding ${gradientText("typelex")}...\n`); 76 77 - // Detect package manager: walk up from cwd 78 let packageManager = "npm"; 79 - let dir = cwd; 80 while (dir !== resolve(dir, "..") && packageManager === "npm") { 81 try { 82 await access(resolve(dir, "pnpm-lock.yaml")); ··· 95 dir = resolve(dir, ".."); 96 } 97 98 - // Install dependencies (always use latest) 99 await new Promise<void>((resolvePromise, reject) => { 100 const args = packageManager === "npm" 101 ? ["install", "--save-dev", "@typelex/cli@latest", "@typelex/emitter@latest"] 102 : ["add", "-D", "@typelex/cli@latest", "@typelex/emitter@latest"]; 103 104 const install = spawn(packageManager, args, { 105 - cwd, 106 stdio: "inherit", 107 }); 108 ··· 122 }); 123 }); 124 125 - // Hand off to locally installed version 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"); ··· 135 nodeModulesDir = resolve(searchDir, "node_modules"); 136 break; 137 } catch { 138 - // Not found, try parent 139 } 140 - searchDir = resolve(searchDir, ".."); 141 } 142 143 return new Promise((resolvePromise, reject) => { 144 const localCli = resolve(nodeModulesDir, ".bin/typelex"); 145 const setup = spawn(localCli, ["init", "--setup"], { 146 - cwd, 147 stdio: "inherit", 148 }); 149 ··· 162 }); 163 } 164 165 - /** 166 - * Setup function - called after packages are installed 167 - * This runs from the locally installed version 168 - */ 169 export async function initSetup(): Promise<void> { 170 const cwd = process.cwd(); 171 const typelexDir = resolve(cwd, "typelex");
··· 4 import { createInterface } from "readline"; 5 import pc from "picocolors"; 6 7 function gradientText(text: string): string { 8 const colors = [ 9 + '\x1b[38;5;33m', 10 + '\x1b[38;5;69m', 11 + '\x1b[38;5;99m', 12 + '\x1b[38;5;133m', 13 + '\x1b[38;5;170m', 14 + '\x1b[38;5;170m', 15 + '\x1b[38;5;133m', 16 ]; 17 const reset = '\x1b[0m'; 18 ··· 56 }); 57 } 58 59 + export async function initCommand(isSetup: boolean = false, flags: string[] = []): Promise<void> { 60 + const originalCwd = process.cwd(); 61 + 62 + // Find nearest package.json upward 63 + let projectRoot = originalCwd; 64 + let dir = originalCwd; 65 + while (dir !== resolve(dir, "..")) { 66 + try { 67 + await access(resolve(dir, "package.json")); 68 + projectRoot = dir; 69 + break; 70 + } catch { 71 + dir = resolve(dir, ".."); 72 + } 73 + } 74 75 if (isSetup) { 76 return initSetup(); 77 } 78 79 console.log(`Adding ${gradientText("typelex")}...\n`); 80 81 + // Detect package manager 82 let packageManager = "npm"; 83 + dir = projectRoot; 84 while (dir !== resolve(dir, "..") && packageManager === "npm") { 85 try { 86 await access(resolve(dir, "pnpm-lock.yaml")); ··· 99 dir = resolve(dir, ".."); 100 } 101 102 + // Install dependencies 103 await new Promise<void>((resolvePromise, reject) => { 104 const args = packageManager === "npm" 105 ? ["install", "--save-dev", "@typelex/cli@latest", "@typelex/emitter@latest"] 106 : ["add", "-D", "@typelex/cli@latest", "@typelex/emitter@latest"]; 107 108 + // Add any additional flags 109 + args.push(...flags); 110 + 111 const install = spawn(packageManager, args, { 112 + cwd: projectRoot, 113 stdio: "inherit", 114 }); 115 ··· 129 }); 130 }); 131 132 + // Find node_modules 133 + let nodeModulesDir = resolve(projectRoot, "node_modules"); 134 + let searchDir = projectRoot; 135 while (searchDir !== resolve(searchDir, "..")) { 136 try { 137 const candidatePath = resolve(searchDir, "node_modules/.bin/typelex"); ··· 139 nodeModulesDir = resolve(searchDir, "node_modules"); 140 break; 141 } catch { 142 + searchDir = resolve(searchDir, ".."); 143 } 144 } 145 146 return new Promise((resolvePromise, reject) => { 147 const localCli = resolve(nodeModulesDir, ".bin/typelex"); 148 const setup = spawn(localCli, ["init", "--setup"], { 149 + cwd: projectRoot, 150 stdio: "inherit", 151 }); 152 ··· 165 }); 166 } 167 168 export async function initSetup(): Promise<void> { 169 const cwd = process.cwd(); 170 const typelexDir = resolve(cwd, "typelex");