detect-shell
Version:
detect shells available on the system
282 lines • 12.9 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
import * as pfs from './pfs';
import os from 'os';
import path from 'path';
import { env } from 'process';
const WindowsPowerShell64BitLabel = 'Windows PowerShell';
const WindowsPowerShell32BitLabel = 'Windows PowerShell (x86)';
// This is required, since parseInt("7-preview") will return 7.
const IntRegex = /^\d+$/;
const PwshMsixRegex = /^Microsoft.PowerShell_.*/;
const PwshPreviewMsixRegex = /^Microsoft.PowerShellPreview_.*/;
// The platform details descriptor for the platform we're on
const isProcess64Bit = process.arch === 'x64';
const isOS64Bit = isProcess64Bit || os.arch() === 'x64';
class PossiblePowerShellExe {
constructor(exePath, displayName, knownToExist) {
this.exePath = exePath;
this.displayName = displayName;
this.knownToExist = knownToExist;
}
exists() {
return __awaiter(this, void 0, void 0, function* () {
if (this.knownToExist === undefined) {
this.knownToExist = yield pfs.fileExists(this.exePath);
}
return this.knownToExist;
});
}
}
function getProgramFilesPath({ useAlternateBitness = false } = {}) {
if (!useAlternateBitness) {
// Just use the native system bitness
return env.ProgramFiles || null;
}
// We might be a 64-bit process looking for 32-bit program files
if (isProcess64Bit) {
return env['ProgramFiles(x86)'] || null;
}
// We might be a 32-bit process looking for 64-bit program files
if (isOS64Bit) {
return env.ProgramW6432 || null;
}
// We're a 32-bit process on 32-bit Windows, there is no other Program Files dir
return null;
}
function getSystem32Path({ useAlternateBitness = false } = {}) {
const windir = env.windir;
if (!useAlternateBitness) {
// Just use the native system bitness
return path.join(windir, 'System32');
}
// We might be a 64-bit process looking for 32-bit system32
if (isProcess64Bit) {
return path.join(windir, 'SysWOW64');
}
// We might be a 32-bit process looking for 64-bit system32
if (isOS64Bit) {
return path.join(windir, 'Sysnative');
}
// We're on a 32-bit Windows, so no alternate bitness
return path.join(windir, 'System32');
}
function findPSCoreWindowsInstallation({ useAlternateBitness = false, findPreview = false } = {}) {
return __awaiter(this, void 0, void 0, function* () {
const programFilesPath = getProgramFilesPath({ useAlternateBitness });
if (!programFilesPath) {
return null;
}
const powerShellInstallBaseDir = path.join(programFilesPath, 'PowerShell');
// Ensure the base directory exists
if (!(yield pfs.dirExists(powerShellInstallBaseDir))) {
return null;
}
let highestSeenVersion = -1;
let pwshExePath = null;
for (const item of yield pfs.readdir(powerShellInstallBaseDir)) {
let currentVersion = -1;
if (findPreview) {
// We are looking for something like "7-preview"
// Preview dirs all have dashes in them
const dashIndex = item.indexOf('-');
if (dashIndex < 0) {
continue;
}
// Verify that the part before the dash is an integer
// and that the part after the dash is "preview"
const intPart = item.substring(0, dashIndex);
if (!IntRegex.test(intPart) || item.substring(dashIndex + 1) !== 'preview') {
continue;
}
currentVersion = parseInt(intPart, 10);
}
else {
// Search for a directory like "6" or "7"
if (!IntRegex.test(item)) {
continue;
}
currentVersion = parseInt(item, 10);
}
// Ensure we haven't already seen a higher version
if (currentVersion <= highestSeenVersion) {
continue;
}
// Now look for the file
const exePath = path.join(powerShellInstallBaseDir, item, 'pwsh.exe');
if (!(yield pfs.fileExists(exePath))) {
continue;
}
pwshExePath = exePath;
highestSeenVersion = currentVersion;
}
if (!pwshExePath) {
return null;
}
const bitness = programFilesPath.includes('x86') ? ' (x86)' : '';
const preview = findPreview ? ' Preview' : '';
return new PossiblePowerShellExe(pwshExePath, `PowerShell${preview}${bitness}`, true);
});
}
function findPSCoreMsix({ findPreview } = {}) {
return __awaiter(this, void 0, void 0, function* () {
// We can't proceed if there's no LOCALAPPDATA path
if (!env.LOCALAPPDATA) {
return null;
}
// Find the base directory for MSIX application exe shortcuts
const msixAppDir = path.join(env.LOCALAPPDATA, 'Microsoft', 'WindowsApps');
if (!(yield pfs.dirExists(msixAppDir))) {
return null;
}
// Define whether we're looking for the preview or the stable
const { pwshMsixDirRegex, pwshMsixName } = findPreview
? { pwshMsixDirRegex: PwshPreviewMsixRegex, pwshMsixName: 'PowerShell Preview (Store)' }
: { pwshMsixDirRegex: PwshMsixRegex, pwshMsixName: 'PowerShell (Store)' };
// We should find only one such application, so return on the first one
for (const subdir of yield pfs.readdir(msixAppDir)) {
if (pwshMsixDirRegex.test(subdir)) {
const pwshMsixPath = path.join(msixAppDir, subdir, 'pwsh.exe');
return new PossiblePowerShellExe(pwshMsixPath, pwshMsixName);
}
}
// If we find nothing, return null
return null;
});
}
function findPSCoreDotnetGlobalTool() {
const dotnetGlobalToolExePath = path.join(os.homedir(), '.dotnet', 'tools', 'pwsh.exe');
return new PossiblePowerShellExe(dotnetGlobalToolExePath, '.NET Core PowerShell Global Tool');
}
function findWinPS({ useAlternateBitness = false } = {}) {
// x86 and ARM only have one WinPS on them
if (!isOS64Bit && useAlternateBitness) {
return null;
}
const systemFolderPath = getSystem32Path({ useAlternateBitness });
const winPSPath = path.join(systemFolderPath, 'WindowsPowerShell', 'v1.0', 'powershell.exe');
let displayName;
if (isProcess64Bit) {
displayName = useAlternateBitness ? WindowsPowerShell32BitLabel : WindowsPowerShell64BitLabel;
}
else if (isOS64Bit) {
displayName = useAlternateBitness ? WindowsPowerShell64BitLabel : WindowsPowerShell32BitLabel;
}
else {
// NOTE: ARM Windows devices also have Windows PowerShell x86 on them. There is no
// "ARM Windows PowerShell".
displayName = WindowsPowerShell32BitLabel;
}
return new PossiblePowerShellExe(winPSPath, displayName, true);
}
/**
* Iterates through all the possible well-known PowerShell installations on a machine.
* Returned values may not exist, but come with an .exists property
* which will check whether the executable exists.
*/
function enumerateDefaultPowerShellInstallations() {
return __asyncGenerator(this, arguments, function* enumerateDefaultPowerShellInstallations_1() {
// Find PSCore stable first
let pwshExe = yield __await(findPSCoreWindowsInstallation());
if (pwshExe) {
yield yield __await(pwshExe);
}
// Windows may have a 32-bit pwsh.exe
pwshExe = yield __await(findPSCoreWindowsInstallation({ useAlternateBitness: true }));
if (pwshExe) {
yield yield __await(pwshExe);
}
// Also look for the MSIX/UWP installation
pwshExe = yield __await(findPSCoreMsix());
if (pwshExe) {
yield yield __await(pwshExe);
}
// Look for the .NET global tool
// Some older versions of PowerShell have a bug in this where startup will fail,
// but this is fixed in newer versions
pwshExe = findPSCoreDotnetGlobalTool();
if (pwshExe) {
yield yield __await(pwshExe);
}
// Look for PSCore preview
pwshExe = yield __await(findPSCoreWindowsInstallation({ findPreview: true }));
if (pwshExe) {
yield yield __await(pwshExe);
}
// Find a preview MSIX
pwshExe = yield __await(findPSCoreMsix({ findPreview: true }));
if (pwshExe) {
yield yield __await(pwshExe);
}
// Look for pwsh-preview with the opposite bitness
pwshExe = yield __await(findPSCoreWindowsInstallation({ useAlternateBitness: true, findPreview: true }));
if (pwshExe) {
yield yield __await(pwshExe);
}
// Finally, get Windows PowerShell
// Get the natural Windows PowerShell for the process bitness
pwshExe = findWinPS();
if (pwshExe) {
yield yield __await(pwshExe);
}
// Get the alternate bitness Windows PowerShell
pwshExe = findWinPS({ useAlternateBitness: true });
if (pwshExe) {
yield yield __await(pwshExe);
}
});
}
/**
* Iterates through PowerShell installations on the machine according
* to configuration passed in through the constructor.
* PowerShell items returned by this object are verified
* to exist on the filesystem.
*/
export function enumeratePowerShellInstallations() {
return __asyncGenerator(this, arguments, function* enumeratePowerShellInstallations_1() {
var e_1, _a;
try {
// Get the default PowerShell installations first
for (var _b = __asyncValues(enumerateDefaultPowerShellInstallations()), _c; _c = yield __await(_b.next()), !_c.done;) {
const defaultPwsh = _c.value;
if (yield __await(defaultPwsh.exists())) {
yield yield __await(defaultPwsh);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) yield __await(_a.call(_b));
}
finally { if (e_1) throw e_1.error; }
}
});
}
//# sourceMappingURL=powershell.js.map