detect-shells
Version:
Detect shells installed on a system
370 lines (369 loc) • 14.8 kB
JavaScript
;
// automatically generated by scripts/fetch-shells.mjs
// do not edit this file directly.
Object.defineProperty(exports, "__esModule", { value: true });
exports.launch = exports.getAvailableShells = exports.parse = exports.Default = exports.Shell = void 0;
const log_1 = require("../log");
const child_process_1 = require("child_process");
const Path = require("path");
const registry_js_1 = require("registry-js");
const fatal_error_1 = require("../fatal-error");
const feature_flag_1 = require("../feature-flag");
const is_git_on_path_1 = require("../is-git-on-path");
const enum_1 = require("../enum");
const path_exists_1 = require("../path-exists");
var Shell;
(function (Shell) {
Shell["Cmd"] = "Command Prompt";
Shell["PowerShell"] = "PowerShell";
Shell["PowerShellCore"] = "PowerShell Core";
Shell["Hyper"] = "Hyper";
Shell["GitBash"] = "Git Bash";
Shell["Cygwin"] = "Cygwin";
Shell["WSL"] = "WSL";
Shell["WindowTerminal"] = "Windows Terminal";
Shell["FluentTerminal"] = "Fluent Terminal";
Shell["Alacritty"] = "Alacritty";
})(Shell = exports.Shell || (exports.Shell = {}));
exports.Default = Shell.Cmd;
function parse(label) {
var _a;
return (_a = (0, enum_1.parseEnumValue)(Shell, label)) !== null && _a !== void 0 ? _a : exports.Default;
}
exports.parse = parse;
async function getAvailableShells() {
const gitPath = await (0, is_git_on_path_1.findGitOnPath)();
const shells = [
{
shell: Shell.Cmd,
path: process.env.comspec || 'C:\\Windows\\System32\\cmd.exe',
extraArgs: gitPath ? ['/K', `"doskey git=^"${gitPath}^" $*"`] : [],
},
];
const powerShellPath = await findPowerShell();
if (powerShellPath != null) {
shells.push({
shell: Shell.PowerShell,
path: powerShellPath,
});
}
const powerShellCorePath = await findPowerShellCore();
if (powerShellCorePath != null) {
shells.push({
shell: Shell.PowerShellCore,
path: powerShellCorePath,
});
}
const hyperPath = await findHyper();
if (hyperPath != null) {
shells.push({
shell: Shell.Hyper,
path: hyperPath,
});
}
const gitBashPath = await findGitBash();
if (gitBashPath != null) {
shells.push({
shell: Shell.GitBash,
path: gitBashPath,
});
}
const cygwinPath = await findCygwin();
if (cygwinPath != null) {
shells.push({
shell: Shell.Cygwin,
path: cygwinPath,
});
}
if ((0, feature_flag_1.enableWSLDetection)()) {
const wslPath = await findWSL();
if (wslPath != null) {
shells.push({
shell: Shell.WSL,
path: wslPath,
});
}
}
const alacrittyPath = await findAlacritty();
if (alacrittyPath != null) {
shells.push({
shell: Shell.Alacritty,
path: alacrittyPath,
});
}
const windowsTerminal = await findWindowsTerminal();
if (windowsTerminal != null) {
shells.push({
shell: Shell.WindowTerminal,
path: windowsTerminal,
});
}
const fluentTerminal = await findFluentTerminal();
if (fluentTerminal != null) {
shells.push({
shell: Shell.FluentTerminal,
path: fluentTerminal,
});
}
return shells;
}
exports.getAvailableShells = getAvailableShells;
async function findPowerShell() {
const powerShell = (0, registry_js_1.enumerateValues)(registry_js_1.HKEY.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\PowerShell.exe');
if (powerShell.length === 0) {
return null;
}
const first = powerShell[0];
// NOTE:
// on Windows 7 these are both REG_SZ, which technically isn't supposed
// to contain unexpanded references to environment variables. But given
// it's also %SystemRoot% and we do the expanding here I think this is
// a fine workaround to do to support the maximum number of setups.
if (first.type === registry_js_1.RegistryValueType.REG_EXPAND_SZ ||
first.type === registry_js_1.RegistryValueType.REG_SZ) {
const path = first.data.replace(/^%SystemRoot%/i, process.env.SystemRoot || 'C:\\Windows');
if (await (0, path_exists_1.pathExists)(path)) {
return path;
}
else {
log_1.default.debug(`[PowerShell] registry entry found but does not exist at '${path}'`);
}
}
return null;
}
async function findPowerShellCore() {
const powerShellCore = (0, registry_js_1.enumerateValues)(registry_js_1.HKEY.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\pwsh.exe');
if (powerShellCore.length === 0) {
return null;
}
const first = powerShellCore[0];
if (first.type === registry_js_1.RegistryValueType.REG_SZ) {
const path = first.data;
if (await (0, path_exists_1.pathExists)(path)) {
return path;
}
else {
log_1.default.debug(`[PowerShellCore] registry entry found but does not exist at '${path}'`);
}
}
return null;
}
async function findHyper() {
const hyper = (0, registry_js_1.enumerateValues)(registry_js_1.HKEY.HKEY_CURRENT_USER, 'Software\\Classes\\Directory\\Background\\shell\\Hyper\\command');
if (hyper.length === 0) {
return null;
}
const first = hyper[0];
if (first.type === registry_js_1.RegistryValueType.REG_SZ) {
// Registry key is structured as "{installationPath}\app-x.x.x\Hyper.exe" "%V"
// This regex is designed to get the path to the version-specific Hyper.
// commandPieces = ['"{installationPath}\app-x.x.x\Hyper.exe"', '"', '{installationPath}\app-x.x.x\Hyper.exe', ...]
const commandPieces = first.data.match(/(["'])(.*?)\1/);
const localAppData = process.env.LocalAppData;
const path = commandPieces
? commandPieces[2]
: localAppData != null
? localAppData.concat('\\hyper\\Hyper.exe')
: null; // fall back to the launcher in install root
if (path == null) {
log_1.default.debug(`[Hyper] LOCALAPPDATA environment variable is unset, aborting fallback behavior`);
}
else if (await (0, path_exists_1.pathExists)(path)) {
return path;
}
else {
log_1.default.debug(`[Hyper] registry entry found but does not exist at '${path}'`);
}
}
return null;
}
async function findGitBash() {
const registryPath = (0, registry_js_1.enumerateValues)(registry_js_1.HKEY.HKEY_LOCAL_MACHINE, 'SOFTWARE\\GitForWindows');
if (registryPath.length === 0) {
return null;
}
const installPathEntry = registryPath.find(e => e.name === 'InstallPath');
if (installPathEntry && installPathEntry.type === registry_js_1.RegistryValueType.REG_SZ) {
const path = Path.join(installPathEntry.data, 'git-bash.exe');
if (await (0, path_exists_1.pathExists)(path)) {
return path;
}
else {
log_1.default.debug(`[Git Bash] registry entry found but does not exist at '${path}'`);
}
}
return null;
}
async function findCygwin() {
const registryPath64 = (0, registry_js_1.enumerateValues)(registry_js_1.HKEY.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Cygwin\\setup');
const registryPath32 = (0, registry_js_1.enumerateValues)(registry_js_1.HKEY.HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\Cygwin\\setup');
if (registryPath64 == null || registryPath32 == null) {
return null;
}
const installPathEntry64 = registryPath64.find(e => e.name === 'rootdir');
const installPathEntry32 = registryPath32.find(e => e.name === 'rootdir');
if (installPathEntry64 &&
installPathEntry64.type === registry_js_1.RegistryValueType.REG_SZ) {
const path = Path.join(installPathEntry64.data, 'bin\\mintty.exe');
if (await (0, path_exists_1.pathExists)(path)) {
return path;
}
else if (installPathEntry32 &&
installPathEntry32.type === registry_js_1.RegistryValueType.REG_SZ) {
const path = Path.join(installPathEntry32.data, 'bin\\mintty.exe');
if (await (0, path_exists_1.pathExists)(path)) {
return path;
}
}
else {
log_1.default.debug(`[Cygwin] registry entry found but does not exist at '${path}'`);
}
}
return null;
}
async function findWSL() {
const system32 = Path.join(process.env.SystemRoot || 'C:\\Windows', 'System32');
const wslPath = Path.join(system32, 'wsl.exe');
const wslConfigPath = Path.join(system32, 'wslconfig.exe');
if (!(await (0, path_exists_1.pathExists)(wslPath))) {
log_1.default.debug(`[WSL] wsl.exe does not exist at '${wslPath}'`);
return null;
}
if (!(await (0, path_exists_1.pathExists)(wslConfigPath))) {
log_1.default.debug(`[WSL] found wsl.exe, but wslconfig.exe does not exist at '${wslConfigPath}'`);
return null;
}
const exitCode = new Promise((resolve, reject) => {
const wslDistros = (0, child_process_1.spawn)(wslConfigPath, ['/list']);
wslDistros.on('error', reject);
wslDistros.on('exit', resolve);
});
try {
const result = await exitCode;
if (result !== 0) {
log_1.default.debug(`[WSL] found wsl.exe and wslconfig.exe, but no distros are installed. Error Code: ${result}`);
return null;
}
return wslPath;
}
catch (err) {
log_1.default.error(`[WSL] unhandled error when invoking 'wsl /list'`, err);
}
return null;
}
async function findAlacritty() {
const registryPath = (0, registry_js_1.enumerateValues)(registry_js_1.HKEY.HKEY_CLASSES_ROOT, 'Directory\\Background\\shell\\Open Alacritty here');
if (registryPath.length === 0) {
return null;
}
const alacritty = registryPath.find(e => e.name === 'Icon');
if (alacritty && alacritty.type === registry_js_1.RegistryValueType.REG_SZ) {
const path = alacritty.data;
if (await (0, path_exists_1.pathExists)(path)) {
return path;
}
else {
log_1.default.debug(`[Alacritty] registry entry found but does not exist at '${path}'`);
}
}
return null;
}
async function findWindowsTerminal() {
// Windows Terminal has a link at
// C:\Users\<User>\AppData\Local\Microsoft\WindowsApps\wt.exe
const localAppData = process.env.LocalAppData;
if (localAppData != null) {
const windowsTerminalpath = Path.join(localAppData, '\\Microsoft\\WindowsApps\\wt.exe');
if (await (0, path_exists_1.pathExists)(windowsTerminalpath)) {
return windowsTerminalpath;
}
else {
log_1.default.debug(`[Windows Terminal] wt.exe doest not exist at '${windowsTerminalpath}'`);
}
}
return null;
}
async function findFluentTerminal() {
// Fluent Terminal has a link at
// C:\Users\<User>\AppData\Local\Microsoft\WindowsApps\flute.exe
const localAppData = process.env.LocalAppData;
if (localAppData != null) {
const fluentTerminalpath = Path.join(localAppData, '\\Microsoft\\WindowsApps\\flute.exe');
if (await (0, path_exists_1.pathExists)(fluentTerminalpath)) {
return fluentTerminalpath;
}
else {
log_1.default.debug(`[Fluent Terminal] flute.exe doest not exist at '${fluentTerminalpath}'`);
}
}
return null;
}
function launch(foundShell, path) {
const shell = foundShell.shell;
switch (shell) {
case Shell.PowerShell:
return (0, child_process_1.spawn)('START', ['"PowerShell"', `"${foundShell.path}"`], {
shell: true,
cwd: path,
});
case Shell.PowerShellCore:
return (0, child_process_1.spawn)('START', [
'"PowerShell Core"',
`"${foundShell.path}"`,
'-WorkingDirectory',
`"${path}"`,
], {
shell: true,
cwd: path,
});
case Shell.Hyper:
const hyperPath = `"${foundShell.path}"`;
log_1.default.info(`launching ${shell} at path: ${hyperPath}`);
return (0, child_process_1.spawn)(hyperPath, [`"${path}"`], {
shell: true,
cwd: path,
});
case Shell.Alacritty:
const alacrittyPath = `"${foundShell.path}"`;
log_1.default.info(`launching ${shell} at path: ${alacrittyPath}`);
return (0, child_process_1.spawn)(alacrittyPath, [`--working-directory "${path}"`], {
shell: true,
cwd: path,
});
case Shell.GitBash:
const gitBashPath = `"${foundShell.path}"`;
log_1.default.info(`launching ${shell} at path: ${gitBashPath}`);
return (0, child_process_1.spawn)(gitBashPath, [`--cd="${path}"`], {
shell: true,
cwd: path,
});
case Shell.Cygwin:
const cygwinPath = `"${foundShell.path}"`;
log_1.default.info(`launching ${shell} at path: ${cygwinPath}`);
return (0, child_process_1.spawn)(cygwinPath, [`/bin/sh -lc 'cd "$(cygpath "${path}")"; exec bash`], {
shell: true,
cwd: path,
});
case Shell.WSL:
return (0, child_process_1.spawn)('START', ['"WSL"', `"${foundShell.path}"`], {
shell: true,
cwd: path,
});
case Shell.Cmd:
return (0, child_process_1.spawn)('START', ['"Command Prompt"', `"${foundShell.path}"`, ...foundShell.extraArgs], {
shell: true,
cwd: path,
});
case Shell.WindowTerminal:
const windowsTerminalPath = `"${foundShell.path}"`;
log_1.default.info(`launching ${shell} at path: ${windowsTerminalPath}`);
return (0, child_process_1.spawn)(windowsTerminalPath, ['-d .'], { shell: true, cwd: path });
case Shell.FluentTerminal:
const fluentTerminalPath = `"${foundShell.path}"`;
log_1.default.info(`launching ${shell} at path: ${fluentTerminalPath}`);
return (0, child_process_1.spawn)(fluentTerminalPath, ['new'], { shell: true, cwd: path });
default:
return (0, fatal_error_1.assertNever)(shell, `Unknown shell: ${shell}`);
}
}
exports.launch = launch;