path-utils.ts
1 import path from "path"; 2 import os from 'os'; 3 4 /** 5 * Converts WSL or Unix-style Windows paths to Windows format 6 * @param p The path to convert 7 * @returns Converted Windows path 8 */ 9 export function convertToWindowsPath(p: string): string { 10 // Handle WSL paths (/mnt/c/...) 11 if (p.startsWith('/mnt/')) { 12 const driveLetter = p.charAt(5).toUpperCase(); 13 const pathPart = p.slice(6).replace(/\//g, '\\'); 14 return `${driveLetter}:${pathPart}`; 15 } 16 17 // Handle Unix-style Windows paths (/c/...) 18 if (p.match(/^\/[a-zA-Z]\//)) { 19 const driveLetter = p.charAt(1).toUpperCase(); 20 const pathPart = p.slice(2).replace(/\//g, '\\'); 21 return `${driveLetter}:${pathPart}`; 22 } 23 24 // Handle standard Windows paths, ensuring backslashes 25 if (p.match(/^[a-zA-Z]:/)) { 26 return p.replace(/\//g, '\\'); 27 } 28 29 // Leave non-Windows paths unchanged 30 return p; 31 } 32 33 /** 34 * Normalizes path by standardizing format while preserving OS-specific behavior 35 * @param p The path to normalize 36 * @returns Normalized path 37 */ 38 export function normalizePath(p: string): string { 39 // Remove any surrounding quotes and whitespace 40 p = p.trim().replace(/^["']|["']$/g, ''); 41 42 // Check if this is a Unix path (starts with / but not a Windows or WSL path) 43 const isUnixPath = p.startsWith('/') && 44 !p.match(/^\/mnt\/[a-z]\//i) && 45 !p.match(/^\/[a-zA-Z]\//); 46 47 if (isUnixPath) { 48 // For Unix paths, just normalize without converting to Windows format 49 // Replace double slashes with single slashes and remove trailing slashes 50 return p.replace(/\/+/g, '/').replace(/\/+$/, ''); 51 } 52 53 // Convert WSL or Unix-style Windows paths to Windows format 54 p = convertToWindowsPath(p); 55 56 // Handle double backslashes, preserving leading UNC \\ 57 if (p.startsWith('\\\\')) { 58 // For UNC paths, first normalize any excessive leading backslashes to exactly \\ 59 // Then normalize double backslashes in the rest of the path 60 let uncPath = p; 61 // Replace multiple leading backslashes with exactly two 62 uncPath = uncPath.replace(/^\\{2,}/, '\\\\'); 63 // Now normalize any remaining double backslashes in the rest of the path 64 const restOfPath = uncPath.substring(2).replace(/\\\\/g, '\\'); 65 p = '\\\\' + restOfPath; 66 } else { 67 // For non-UNC paths, normalize all double backslashes 68 p = p.replace(/\\\\/g, '\\'); 69 } 70 71 // Use Node's path normalization, which handles . and .. segments 72 let normalized = path.normalize(p); 73 74 // Fix UNC paths after normalization (path.normalize can remove a leading backslash) 75 if (p.startsWith('\\\\') && !normalized.startsWith('\\\\')) { 76 normalized = '\\' + normalized; 77 } 78 79 // Handle Windows paths: convert slashes and ensure drive letter is capitalized 80 if (normalized.match(/^[a-zA-Z]:/)) { 81 let result = normalized.replace(/\//g, '\\'); 82 // Capitalize drive letter if present 83 if (/^[a-z]:/.test(result)) { 84 result = result.charAt(0).toUpperCase() + result.slice(1); 85 } 86 return result; 87 } 88 89 // For all other paths (including relative paths), convert forward slashes to backslashes 90 // This ensures relative paths like "some/relative/path" become "some\\relative\\path" 91 return normalized.replace(/\//g, '\\'); 92 } 93 94 /** 95 * Expands home directory tildes in paths 96 * @param filepath The path to expand 97 * @returns Expanded path 98 */ 99 export function expandHome(filepath: string): string { 100 if (filepath.startsWith('~/') || filepath === '~') { 101 return path.join(os.homedir(), filepath.slice(1)); 102 } 103 return filepath; 104 }