An experimental TypeSpec syntax for Lexicon

fix

Changed files
+50 -47
packages
cli
src
commands
+50 -47
packages/cli/src/commands/init.ts
··· 1 import { resolve, relative } from "path"; 2 - import { mkdir, writeFile, readFile, access } from "fs/promises"; 3 import { spawn } from "child_process"; 4 import { createInterface } from "readline"; 5 ··· 30 }); 31 32 return new Promise((resolve) => { 33 - rl.question("Namespace (e.g., com.example.*): ", (answer) => { 34 rl.close(); 35 resolve(answer.trim()); 36 }); ··· 47 const externalsTspPath = resolve(typelexDir, "externals.tsp"); 48 49 console.log("Initializing typelex project...\n"); 50 - console.log("Installing dependencies...\n"); 51 52 - // Detect package manager and lexicons directory by walking up directory tree 53 let packageManager = "npm"; 54 - let lexiconsDir: string | null = null; 55 - let currentDir = cwd; 56 - const root = resolve("/"); 57 - 58 - // First, move up one directory (we only want lexicons from parent directories) 59 - const parentDir = resolve(currentDir, ".."); 60 - if (parentDir !== currentDir) { 61 - currentDir = parentDir; 62 - 63 - while (currentDir !== root) { 64 - // Check for package manager lockfiles 65 - if (packageManager === "npm") { 66 - try { 67 - await access(resolve(currentDir, "pnpm-lock.yaml")); 68 - packageManager = "pnpm"; 69 - } catch { 70 - try { 71 - await access(resolve(currentDir, "yarn.lock")); 72 - packageManager = "yarn"; 73 - } catch { 74 - // Continue 75 - } 76 - } 77 - } 78 - 79 - // Check for lexicons directory (only in parent directories) 80 - if (!lexiconsDir) { 81 - try { 82 - const lexiconsPath = resolve(currentDir, "lexicons"); 83 - await access(lexiconsPath); 84 - // Calculate relative path from cwd to this lexicons directory 85 - const relativePath = relative(cwd, lexiconsPath); 86 - lexiconsDir = relativePath; 87 - } catch { 88 - // Continue 89 - } 90 - } 91 - 92 - // Move up one directory 93 - const nextParent = resolve(currentDir, ".."); 94 - if (nextParent === currentDir) break; // Reached root 95 - currentDir = nextParent; 96 } 97 } 98 99 // Install dependencies and only proceed if successful ··· 136 // Remove the .* suffix for use in template 137 const namespacePrefix = namespace.slice(0, -2); 138 139 // Create typelex directory 140 await mkdir(typelexDir, { recursive: true }); 141 ··· 173 packageJson.scripts["build:typelex"] = `typelex compile ${namespace}${outFlag}`; 174 await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8"); 175 console.log("✓ Added build:typelex script to package.json"); 176 - if (lexiconsDir) { 177 console.log(` Using existing lexicons directory: ${lexiconsDir}`); 178 } 179 } else {
··· 1 import { resolve, relative } from "path"; 2 + import { mkdir, writeFile, readFile, access, stat } from "fs/promises"; 3 import { spawn } from "child_process"; 4 import { createInterface } from "readline"; 5 ··· 30 }); 31 32 return new Promise((resolve) => { 33 + rl.question("\x1b[36mYour app's namespace (e.g., com.example.*): \x1b[0m", (answer) => { 34 rl.close(); 35 resolve(answer.trim()); 36 }); ··· 47 const externalsTspPath = resolve(typelexDir, "externals.tsp"); 48 49 console.log("Initializing typelex project...\n"); 50 + console.log("Installing dependencies..."); 51 52 + // Detect package manager: walk up from cwd 53 let packageManager = "npm"; 54 + let dir = cwd; 55 + while (dir !== resolve(dir, "..") && packageManager === "npm") { 56 + try { 57 + await access(resolve(dir, "pnpm-lock.yaml")); 58 + packageManager = "pnpm"; 59 + break; 60 + } catch { 61 + // Not found 62 + } 63 + try { 64 + await access(resolve(dir, "yarn.lock")); 65 + packageManager = "yarn"; 66 + break; 67 + } catch { 68 + // Not found 69 } 70 + dir = resolve(dir, ".."); 71 } 72 73 // Install dependencies and only proceed if successful ··· 110 // Remove the .* suffix for use in template 111 const namespacePrefix = namespace.slice(0, -2); 112 113 + // Detect lexicons directory: check cwd first, then walk up parents 114 + let lexiconsDir: string | null = null; 115 + let hasLocalLexicons = false; 116 + 117 + // Check current directory for lexicons/ (will use default, no --out flag needed) 118 + try { 119 + const localPath = resolve(cwd, "lexicons"); 120 + if ((await stat(localPath)).isDirectory()) { 121 + hasLocalLexicons = true; 122 + } 123 + } catch { 124 + // Not found in current directory, check parent directories 125 + let dir = resolve(cwd, ".."); 126 + while (dir !== resolve(dir, "..")) { 127 + try { 128 + const lexPath = resolve(dir, "lexicons"); 129 + if ((await stat(lexPath)).isDirectory()) { 130 + lexiconsDir = relative(cwd, lexPath); 131 + break; 132 + } 133 + } catch { 134 + // Not found, continue up 135 + } 136 + dir = resolve(dir, ".."); 137 + } 138 + } 139 + 140 // Create typelex directory 141 await mkdir(typelexDir, { recursive: true }); 142 ··· 174 packageJson.scripts["build:typelex"] = `typelex compile ${namespace}${outFlag}`; 175 await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8"); 176 console.log("✓ Added build:typelex script to package.json"); 177 + if (hasLocalLexicons) { 178 + console.log(` Using existing lexicons directory: ./lexicons`); 179 + } else if (lexiconsDir) { 180 console.log(` Using existing lexicons directory: ${lexiconsDir}`); 181 } 182 } else {