127 lines
4.1 KiB
JavaScript
127 lines
4.1 KiB
JavaScript
import path from 'node:path';
|
|||
|
|
import {promisify} from 'node:util';
|
||
|
|
import childProcess from 'node:child_process';
|
||
|
|
import fs, {constants as fsConstants} from 'node:fs/promises';
|
||
|
|
import isWsl from 'is-wsl';
|
||
|
|
import {powerShellPath as windowsPowerShellPath, executePowerShell} from 'powershell-utils';
|
||
|
|
import {parseMountPointFromConfig} from './utilities.js';
|
||
|
|
|
||
|
|
const execFile = promisify(childProcess.execFile);
|
||
|
|
|
||
|
|
export const wslDrivesMountPoint = (() => {
|
||
|
|
// Default value for "root" param
|
||
|
|
// according to https://docs.microsoft.com/en-us/windows/wsl/wsl-config
|
||
|
|
const defaultMountPoint = '/mnt/';
|
||
|
|
|
||
|
|
let mountPoint;
|
||
|
|
|
||
|
|
return async function () {
|
||
|
|
if (mountPoint) {
|
||
|
|
// Return memoized mount point value
|
||
|
|
return mountPoint;
|
||
|
|
}
|
||
|
|
|
||
|
|
const configFilePath = '/etc/wsl.conf';
|
||
|
|
|
||
|
|
let isConfigFileExists = false;
|
||
|
|
try {
|
||
|
|
await fs.access(configFilePath, fsConstants.F_OK);
|
||
|
|
isConfigFileExists = true;
|
||
|
|
} catch {}
|
||
|
|
|
||
|
|
if (!isConfigFileExists) {
|
||
|
|
return defaultMountPoint;
|
||
|
|
}
|
||
|
|
|
||
|
|
const configContent = await fs.readFile(configFilePath, {encoding: 'utf8'});
|
||
|
|
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
||
|
|
|
||
|
|
if (parsedMountPoint === undefined) {
|
||
|
|
return defaultMountPoint;
|
||
|
|
}
|
||
|
|
|
||
|
|
mountPoint = parsedMountPoint;
|
||
|
|
mountPoint = mountPoint.endsWith('/') ? mountPoint : `${mountPoint}/`;
|
||
|
|
|
||
|
|
return mountPoint;
|
||
|
|
};
|
||
|
|
})();
|
||
|
|
|
||
|
|
export const powerShellPathFromWsl = async () => {
|
||
|
|
const mountPoint = await wslDrivesMountPoint();
|
||
|
|
return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const powerShellPath = isWsl ? powerShellPathFromWsl : windowsPowerShellPath;
|
||
|
|
|
||
|
|
// Cache for PowerShell accessibility check
|
||
|
|
let canAccessPowerShellPromise;
|
||
|
|
|
||
|
|
export const canAccessPowerShell = async () => {
|
||
|
|
canAccessPowerShellPromise ??= (async () => {
|
||
|
|
try {
|
||
|
|
const psPath = await powerShellPath();
|
||
|
|
await fs.access(psPath, fsConstants.X_OK);
|
||
|
|
return true;
|
||
|
|
} catch {
|
||
|
|
// PowerShell is not accessible (either doesn't exist, no execute permission, or other error)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
})();
|
||
|
|
|
||
|
|
return canAccessPowerShellPromise;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const wslDefaultBrowser = async () => {
|
||
|
|
const psPath = await powerShellPath();
|
||
|
|
const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
|
||
|
|
|
||
|
|
// 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.
|
||
|
|
const {stdout} = await executePowerShell(command, {
|
||
|
|
powerShellPath: psPath,
|
||
|
|
cwd: path.dirname(psPath),
|
||
|
|
});
|
||
|
|
|
||
|
|
return stdout.trim();
|
||
|
|
};
|
||
|
|
|
||
|
|
const isUrl = path => /^[a-z]+:\/\//i.test(path);
|
||
|
|
|
||
|
|
// `wslpath` only accepts a single path, so each path needs its own call.
|
||
|
|
const convertPath = async (flag, path) => {
|
||
|
|
// `--` stops `wslpath` from reading a leading `-` as an option. Only pass it when needed, as it is only confirmed for WSL 2.5 and later.
|
||
|
|
const wslpathArguments = path.startsWith('-') ? [flag, '--', path] : [flag, path];
|
||
|
|
|
||
|
|
try {
|
||
|
|
const {stdout} = await execFile('wslpath', wslpathArguments, {encoding: 'utf8'});
|
||
|
|
return stdout.replace(/\r?\n$/, '') || path;
|
||
|
|
} catch {
|
||
|
|
// If wslpath fails, keep original path
|
||
|
|
return path;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
export const convertWslPathToWindows = async paths => {
|
||
|
|
const isBatch = Array.isArray(paths);
|
||
|
|
const pathArray = isBatch ? paths : [paths];
|
||
|
|
// URLs stay as-is
|
||
|
|
const results = await Promise.all(pathArray.map(path => isUrl(path) ? path : convertPath('-aw', path)));
|
||
|
|
return isBatch ? results : results[0];
|
||
|
|
};
|
||
|
|
|
||
|
|
export const isUncPath = path => /^\\\\/u.test(path);
|
||
|
|
|
||
|
|
export const isPathOnWindowsFilesystem = async path => {
|
||
|
|
const windowsPath = await convertWslPathToWindows(path);
|
||
|
|
return !isUncPath(windowsPath);
|
||
|
|
};
|
||
|
|
|
||
|
|
export const convertWindowsPathToWsl = async paths => {
|
||
|
|
const isBatch = Array.isArray(paths);
|
||
|
|
const pathArray = isBatch ? paths : [paths];
|
||
|
|
const results = await Promise.all(pathArray.map(path => convertPath('-u', path)));
|
||
|
|
return isBatch ? results : results[0];
|
||
|
|
};
|
||
|
|
|
||
|
|
export {default as isWsl} from 'is-wsl';
|