index.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import path from 'node:path';
  2. import {promisify} from 'node:util';
  3. import childProcess from 'node:child_process';
  4. import fs, {constants as fsConstants} from 'node:fs/promises';
  5. import isWsl from 'is-wsl';
  6. import {powerShellPath as windowsPowerShellPath, executePowerShell} from 'powershell-utils';
  7. import {parseMountPointFromConfig} from './utilities.js';
  8. const execFile = promisify(childProcess.execFile);
  9. export const wslDrivesMountPoint = (() => {
  10. // Default value for "root" param
  11. // according to https://docs.microsoft.com/en-us/windows/wsl/wsl-config
  12. const defaultMountPoint = '/mnt/';
  13. let mountPoint;
  14. return async function () {
  15. if (mountPoint) {
  16. // Return memoized mount point value
  17. return mountPoint;
  18. }
  19. const configFilePath = '/etc/wsl.conf';
  20. let isConfigFileExists = false;
  21. try {
  22. await fs.access(configFilePath, fsConstants.F_OK);
  23. isConfigFileExists = true;
  24. } catch {}
  25. if (!isConfigFileExists) {
  26. return defaultMountPoint;
  27. }
  28. const configContent = await fs.readFile(configFilePath, {encoding: 'utf8'});
  29. const parsedMountPoint = parseMountPointFromConfig(configContent);
  30. if (parsedMountPoint === undefined) {
  31. return defaultMountPoint;
  32. }
  33. mountPoint = parsedMountPoint;
  34. mountPoint = mountPoint.endsWith('/') ? mountPoint : `${mountPoint}/`;
  35. return mountPoint;
  36. };
  37. })();
  38. export const powerShellPathFromWsl = async () => {
  39. const mountPoint = await wslDrivesMountPoint();
  40. return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
  41. };
  42. export const powerShellPath = isWsl ? powerShellPathFromWsl : windowsPowerShellPath;
  43. // Cache for PowerShell accessibility check
  44. let canAccessPowerShellPromise;
  45. export const canAccessPowerShell = async () => {
  46. canAccessPowerShellPromise ??= (async () => {
  47. try {
  48. const psPath = await powerShellPath();
  49. await fs.access(psPath, fsConstants.X_OK);
  50. return true;
  51. } catch {
  52. // PowerShell is not accessible (either doesn't exist, no execute permission, or other error)
  53. return false;
  54. }
  55. })();
  56. return canAccessPowerShellPromise;
  57. };
  58. export const wslDefaultBrowser = async () => {
  59. const psPath = await powerShellPath();
  60. const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
  61. // The spawned Windows process inherits the Linux working directory, which WSL exposes to Windows as a `\\wsl.localhost\…` UNC path served by the distro's default user, so a directory that user cannot traverse makes the launch fail. PowerShell's own directory is on the Windows drive, so it always resolves to a plain `C:\…` path.
  62. const {stdout} = await executePowerShell(command, {
  63. powerShellPath: psPath,
  64. cwd: path.dirname(psPath),
  65. });
  66. return stdout.trim();
  67. };
  68. const isUrl = path => /^[a-z]+:\/\//i.test(path);
  69. export const convertWslPathToWindows = async paths => {
  70. const isBatch = Array.isArray(paths);
  71. const pathArray = isBatch ? paths : [paths];
  72. // Find indices of non-URL paths that need conversion
  73. const indicesToConvert = [];
  74. const pathsToConvert = [];
  75. for (const [index, path] of pathArray.entries()) {
  76. if (!isUrl(path)) {
  77. indicesToConvert.push(index);
  78. pathsToConvert.push(path);
  79. }
  80. }
  81. // Start with original paths (URLs stay as-is)
  82. const results = [...pathArray];
  83. if (pathsToConvert.length > 0) {
  84. try {
  85. const {stdout} = await execFile('wslpath', ['-aw', ...pathsToConvert], {encoding: 'utf8'});
  86. const convertedPaths = stdout.split(/\r?\n/).filter(Boolean);
  87. for (const [index, originalIndex] of indicesToConvert.entries()) {
  88. results[originalIndex] = convertedPaths[index] ?? pathArray[originalIndex];
  89. }
  90. } catch {
  91. // If wslpath fails, keep original paths
  92. }
  93. }
  94. return isBatch ? results : results[0];
  95. };
  96. export const isUncPath = path => /^\\\\/u.test(path);
  97. export const isPathOnWindowsFilesystem = async path => {
  98. const windowsPath = await convertWslPathToWindows(path);
  99. return !isUncPath(windowsPath);
  100. };
  101. export const convertWindowsPathToWsl = async paths => {
  102. const isBatch = Array.isArray(paths);
  103. const pathArray = isBatch ? paths : [paths];
  104. try {
  105. const {stdout} = await execFile('wslpath', ['-u', ...pathArray], {encoding: 'utf8'});
  106. const convertedPaths = stdout.split(/\r?\n/).filter(Boolean);
  107. const results = pathArray.map((original, index) => convertedPaths[index] ?? original);
  108. return isBatch ? results : results[0];
  109. } catch {
  110. return isBatch ? pathArray : pathArray[0];
  111. }
  112. };
  113. export {default as isWsl} from 'is-wsl';