path-utils.ts•4.24 kB
import path from "path";
import os from 'os';
/**
 * Converts WSL or Unix-style Windows paths to Windows format
 * @param p The path to convert
 * @returns Converted Windows path
 */
export function convertToWindowsPath(p: string): string {
  // Handle WSL paths (/mnt/c/...)
  // NEVER convert WSL paths - they are valid Linux paths that work with Node.js fs operations in WSL
  // Converting them to Windows format (C:\...) breaks fs operations inside WSL
  if (p.startsWith('/mnt/')) {
    return p; // Leave WSL paths unchanged
  }
  // Handle Unix-style Windows paths (/c/...)
  // Only convert when running on Windows
  if (p.match(/^\/[a-zA-Z]\//) && process.platform === 'win32') {
    const driveLetter = p.charAt(1).toUpperCase();
    const pathPart = p.slice(2).replace(/\//g, '\\');
    return `${driveLetter}:${pathPart}`;
  }
  // Handle standard Windows paths, ensuring backslashes
  if (p.match(/^[a-zA-Z]:/)) {
    return p.replace(/\//g, '\\');
  }
  // Leave non-Windows paths unchanged
  return p;
}
/**
 * Normalizes path by standardizing format while preserving OS-specific behavior
 * @param p The path to normalize
 * @returns Normalized path
 */
export function normalizePath(p: string): string {
  // Remove any surrounding quotes and whitespace
  p = p.trim().replace(/^["']|["']$/g, '');
  // Check if this is a Unix path that should not be converted
  // WSL paths (/mnt/) should ALWAYS be preserved as they work correctly in WSL with Node.js fs
  // Regular Unix paths should also be preserved
  const isUnixPath = p.startsWith('/') && (
    // Always preserve WSL paths (/mnt/c/, /mnt/d/, etc.)
    p.match(/^\/mnt\/[a-z]\//i) ||
    // On non-Windows platforms, treat all absolute paths as Unix paths
    (process.platform !== 'win32') ||
    // On Windows, preserve Unix paths that aren't Unix-style Windows paths (/c/, /d/, etc.)
    (process.platform === 'win32' && !p.match(/^\/[a-zA-Z]\//))
  );
  if (isUnixPath) {
    // For Unix paths, just normalize without converting to Windows format
    // Replace double slashes with single slashes and remove trailing slashes
    return p.replace(/\/+/g, '/').replace(/\/+$/, '');
  }
  // Convert Unix-style Windows paths (/c/, /d/) to Windows format if on Windows
  // This function will now leave /mnt/ paths unchanged
  p = convertToWindowsPath(p);
  // Handle double backslashes, preserving leading UNC \\
  if (p.startsWith('\\\\')) {
    // For UNC paths, first normalize any excessive leading backslashes to exactly \\
    // Then normalize double backslashes in the rest of the path
    let uncPath = p;
    // Replace multiple leading backslashes with exactly two
    uncPath = uncPath.replace(/^\\{2,}/, '\\\\');
    // Now normalize any remaining double backslashes in the rest of the path
    const restOfPath = uncPath.substring(2).replace(/\\\\/g, '\\');
    p = '\\\\' + restOfPath;
  } else {
    // For non-UNC paths, normalize all double backslashes
    p = p.replace(/\\\\/g, '\\');
  }
  // Use Node's path normalization, which handles . and .. segments
  let normalized = path.normalize(p);
  // Fix UNC paths after normalization (path.normalize can remove a leading backslash)
  if (p.startsWith('\\\\') && !normalized.startsWith('\\\\')) {
    normalized = '\\' + normalized;
  }
  // Handle Windows paths: convert slashes and ensure drive letter is capitalized
  if (normalized.match(/^[a-zA-Z]:/)) {
    let result = normalized.replace(/\//g, '\\');
    // Capitalize drive letter if present
    if (/^[a-z]:/.test(result)) {
      result = result.charAt(0).toUpperCase() + result.slice(1);
    }
    return result;
  }
  // On Windows, convert forward slashes to backslashes for relative paths
  // On Linux/Unix, preserve forward slashes
  if (process.platform === 'win32') {
    return normalized.replace(/\//g, '\\');
  }
  // On non-Windows platforms, keep the normalized path as-is
  return normalized;
}
/**
 * Expands home directory tildes in paths
 * @param filepath The path to expand
 * @returns Expanded path
 */
export function expandHome(filepath: string): string {
  if (filepath.startsWith('~/') || filepath === '~') {
    return path.join(os.homedir(), filepath.slice(1));
  }
  return filepath;
}