/ src / utils / path-utils.ts
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  }