UNPKG

detect-shells

Version:

Detect shells installed on a system

370 lines (369 loc) 14.8 kB
"use strict"; // 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;