An experimental TypeSpec syntax for Lexicon

nit

+24 -19
+2 -1
packages/cli/package.json
··· 1 1 { 2 2 "name": "@typelex/cli", 3 - "version": "0.2.5", 3 + "version": "0.2.7", 4 4 "main": "dist/index.js", 5 5 "type": "module", 6 6 "bin": { ··· 26 26 "license": "MIT", 27 27 "dependencies": { 28 28 "@typespec/compiler": "^1.4.0", 29 + "picocolors": "^1.1.1", 29 30 "yargs": "^18.0.0" 30 31 }, 31 32 "devDependencies": {
+19 -18
packages/cli/src/commands/init.ts
··· 2 2 import { mkdir, writeFile, readFile, access, stat } from "fs/promises"; 3 3 import { spawn } from "child_process"; 4 4 import { createInterface } from "readline"; 5 + import pc from "picocolors"; 5 6 6 7 function createMainTemplate(namespace: string): string { 7 8 return `import "@typelex/emitter"; ··· 30 31 }); 31 32 32 33 return new Promise((resolve) => { 33 - rl.question("\x1b[36mYour app's namespace (e.g., com.example.*): \x1b[0m", (answer) => { 34 + rl.question(`Enter your app's root namespace (e.g. ${pc.cyan("com.example.*")}): `, (answer) => { 34 35 rl.close(); 35 36 resolve(answer.trim()); 36 37 }); ··· 46 47 const mainTspPath = resolve(typelexDir, "main.tsp"); 47 48 const externalsTspPath = resolve(typelexDir, "externals.tsp"); 48 49 49 - console.log("Initializing typelex project...\n"); 50 + console.log(pc.bold("Initializing typelex project...\n")); 50 51 console.log("Installing dependencies..."); 51 52 52 53 // Detect package manager: walk up from cwd ··· 86 87 console.log("\n✓ Installed @typelex/cli and @typelex/emitter\n"); 87 88 resolvePromise(); 88 89 } else { 89 - console.error("\nFailed to install dependencies"); 90 + console.error(pc.red("✗ Failed to install dependencies")); 90 91 process.exit(code ?? 1); 91 92 } 92 93 }); 93 94 94 95 install.on("error", (err) => { 95 - console.error("Failed to install dependencies:", err); 96 + console.error(pc.red("✗ Failed to install dependencies:"), err); 96 97 reject(err); 97 98 }); 98 99 }); ··· 102 103 103 104 // Validate namespace format 104 105 while (!namespace.endsWith(".*")) { 105 - console.error(`Error: namespace must end with .*`); 106 - console.error(`Got: ${namespace}\n`); 106 + console.error(pc.red(`Error: namespace must end with ${pc.bold(".*")}`)); 107 + console.error(pc.red(`Got: ${pc.bold(namespace)}\n`)); 107 108 namespace = await promptNamespace(); 108 109 } 109 110 ··· 146 147 await access(mainTspPath); 147 148 const content = await readFile(mainTspPath, "utf-8"); 148 149 if (content.trim().length > 0) { 149 - console.log("✓ typelex/main.tsp already exists, skipping"); 150 + console.log(`✓ ${pc.cyan("typelex/main.tsp")} already exists, skipping`); 150 151 shouldCreateMain = false; 151 152 } 152 153 } catch { ··· 155 156 156 157 if (shouldCreateMain) { 157 158 await writeFile(mainTspPath, createMainTemplate(namespacePrefix), "utf-8"); 158 - console.log("✓ Created typelex/main.tsp"); 159 + console.log(`✓ Created ${pc.cyan("typelex/main.tsp")}`); 159 160 } 160 161 161 162 // Always create/overwrite externals.tsp 162 163 await writeFile(externalsTspPath, EXTERNALS_TSP_TEMPLATE, "utf-8"); 163 - console.log("✓ Created typelex/externals.tsp"); 164 + console.log(`✓ Created ${pc.cyan("typelex/externals.tsp")}`); 164 165 165 166 // Add build script to package.json 166 167 const packageJsonPath = resolve(cwd, "package.json"); ··· 173 174 const outFlag = lexiconsDir ? ` --out ${lexiconsDir}` : ""; 174 175 packageJson.scripts["build:typelex"] = `typelex compile ${namespace}${outFlag}`; 175 176 await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8"); 176 - console.log("✓ Added build:typelex script to package.json"); 177 + console.log(`✓ Added ${pc.cyan("build:typelex")} script to ${pc.cyan("package.json")}`); 177 178 if (hasLocalLexicons) { 178 - console.log(` Using existing lexicons directory: ./lexicons`); 179 + console.log(pc.dim(` Using existing lexicons directory: ${pc.cyan("./lexicons")}`)); 179 180 } else if (lexiconsDir) { 180 - console.log(` Using existing lexicons directory: ${lexiconsDir}`); 181 + console.log(pc.dim(` Using existing lexicons directory: ${pc.cyan(lexiconsDir)}`)); 181 182 } 182 183 } else { 183 - console.log("✓ build:typelex script already exists in package.json"); 184 + console.log(`✓ ${pc.cyan("build:typelex")} script already exists in ${pc.cyan("package.json")}`); 184 185 } 185 186 } catch (err) { 186 - console.warn("⚠ Could not update package.json:", (err as Error).message); 187 + console.warn(pc.yellow(`⚠ Could not update ${pc.cyan("package.json")}:`), (err as Error).message); 187 188 } 188 189 189 - console.log("\nTypelex initialized successfully!"); 190 - console.log("\nNext steps:"); 191 - console.log(" 1. Edit typelex/main.tsp to define your lexicons"); 192 - console.log(" 2. Run: npm run build:typelex"); 190 + console.log(pc.bold("\n✓ Typelex initialized successfully!")); 191 + console.log(pc.bold("\nNext steps:")); 192 + console.log(` 1. Edit ${pc.cyan("typelex/main.tsp")} to define your lexicons`); 193 + console.log(` 2. Run: ${pc.cyan("npm run build:typelex")}`); 193 194 }
+3
pnpm-lock.yaml
··· 20 20 '@typespec/compiler': 21 21 specifier: ^1.4.0 22 22 version: 1.4.0(@types/node@20.19.19) 23 + picocolors: 24 + specifier: ^1.1.1 25 + version: 1.1.1 23 26 yargs: 24 27 specifier: ^18.0.0 25 28 version: 18.0.0