import * as os from 'os' ; import * as paths from 'path' ; import * as fs from 'fs' ; import { SymlinkSupport } from './symlink' ; const IntRegex = /^\d+$/ ; const PwshMsixRegex = /^Microsoft.PowerShell_.*/ ; const PwshPreviewMsixRegex = /^Microsoft.PowerShellPreview_.*/ ; const enum Arch { x64 , x86 , ARM } let processArch ; switch ( process . arch ) { case 'ia32' : case 'x32' : processArch = Arch . x86 ; break ; case 'arm' : case 'arm64' : processArch = Arch . ARM ; break ; default : processArch = Arch . x64 ; break ; } let osArch ; if ( process . env [ 'PROCESSOR_ARCHITEW6432' ] ) { osArch = process . env [ 'PROCESSOR_ARCHITEW6432' ] === 'ARM64' ? Arch . ARM : Arch . x64 ; } else if ( process . env [ 'PROCESSOR_ARCHITECTURE' ] === 'ARM64' ) { osArch = Arch . ARM ; } else if ( process . env [ 'PROCESSOR_ARCHITECTURE' ] === 'X86' ) { osArch = Arch . x86 ; } else { osArch = Arch . x64 ; } export interface IPowerShellExeDetails { readonly displayName : string ; readonly exePath : string ; } export interface IPossiblePowerShellExe extends IPowerShellExeDetails { exists ( ) : Promise < boolean > ; } class PossiblePowerShellExe implements IPossiblePowerShellExe { constructor ( public readonly exePath , public readonly displayName , private knownToExist ? ) { } public async exists ( ) { if ( this . knownToExist === undefined ) { this . knownToExist = await SymlinkSupport . existsFile ( this . exePath ) ; } return this . knownToExist ; } } function getProgramFilesPath ( { useAlternateBitness = false } = { } ) { if ( ! useAlternateBitness ) { return process . env . ProgramFiles || null ; } if ( processArch === Arch . x64 ) { return process . env [ 'ProgramFiles(x86)' ] || null ; } if ( osArch === Arch . x64 ) { return process . env . ProgramW6432 || null ; } return null ; } async function findPSCoreWindowsInstallation ( { useAlternateBitness = false , findPreview = false } = { } ) { const programFilesPath = getProgramFilesPath ( { useAlternateBitness } ) ; if ( ! programFilesPath ) { return null ; } const powerShellInstallBaseDir = paths . join ( programFilesPath , 'PowerShell' ) ; if ( ! await SymlinkSupport . existsDirectory ( powerShellInstallBaseDir ) ) { return null ; } let highestSeenVersion = - 1 ; let pwshExePath = null ; for ( const item of await fs . promises . readdir ( powerShellInstallBaseDir ) ) { let currentVersion = - 1 ; if ( findPreview ) { const dashIndex = item . indexOf ( '-' ) ; if ( dashIndex < 0 ) { continue ; } const intPart = item . substring ( 0 , dashIndex ) ; if ( ! IntRegex . test ( intPart ) || item . substring ( dashIndex + 1 ) !== 'preview' ) { continue ; } currentVersion = parseInt ( intPart , 10 ) ; } else { if ( ! IntRegex . test ( item ) ) { continue ; } currentVersion = parseInt ( item , 10 ) ; } if ( currentVersion <= highestSeenVersion ) { continue ; } const exePath = paths . join ( powerShellInstallBaseDir , item , 'pwsh.exe' ) ; if ( ! await SymlinkSupport . existsFile ( 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 ) ; } async function findPSCoreMsix ( { findPreview } = { } ) { if ( ! process . env . LOCALAPPDATA ) { return null ; } const msixAppDir = paths . join ( process . env . LOCALAPPDATA , 'Microsoft' , 'WindowsApps' ) ; if ( ! await SymlinkSupport . existsDirectory ( msixAppDir ) ) { return null ; } const { pwshMsixDirRegex , pwshMsixName } = findPreview ? { pwshMsixDirRegex : PwshPreviewMsixRegex , pwshMsixName : 'PowerShell Preview (Store)' } : { pwshMsixDirRegex : PwshMsixRegex , pwshMsixName : 'PowerShell (Store)' } ; for ( const subdir of await fs . promises . readdir ( msixAppDir ) ) { if ( pwshMsixDirRegex . test ( subdir ) ) { const pwshMsixPath = paths . join ( msixAppDir , subdir , 'pwsh.exe' ) ; return new PossiblePowerShellExe ( pwshMsixPath , pwshMsixName ) ; } } return null ; } function findPSCoreDotnetGlobalTool ( ) { const dotnetGlobalToolExePath = paths . join ( os . homedir ( ) , '.dotnet' , 'tools' , 'pwsh.exe' ) ; return new PossiblePowerShellExe ( dotnetGlobalToolExePath , '.NET Core PowerShell Global Tool' ) ; } function findWinPS ( ) { const winPSPath = paths . join ( process . env . windir ! , processArch === Arch . x86 && osArch !== Arch . x86 ? 'SysNative' : 'System32' , 'WindowsPowerShell' , 'v1.0' , 'powershell.exe' ) ; return new PossiblePowerShellExe ( winPSPath , 'Windows PowerShell' , 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.
 */ async function * enumerateDefaultPowerShellInstallations ( ) { let pwshExe = await findPSCoreWindowsInstallation ( ) ; if ( pwshExe ) { yield pwshExe ; } pwshExe = await findPSCoreWindowsInstallation ( { useAlternateBitness : true } ) ; if ( pwshExe ) { yield pwshExe ; } pwshExe = await findPSCoreMsix ( ) ; if ( pwshExe ) { yield pwshExe ; } pwshExe = findPSCoreDotnetGlobalTool ( ) ; if ( pwshExe ) { yield pwshExe ; } pwshExe = await findPSCoreWindowsInstallation ( { findPreview : true } ) ; if ( pwshExe ) { yield pwshExe ; } pwshExe = await findPSCoreMsix ( { findPreview : true } ) ; if ( pwshExe ) { yield pwshExe ; } pwshExe = await findPSCoreWindowsInstallation ( { useAlternateBitness : true , findPreview : true } ) ; if ( pwshExe ) { yield pwshExe ; } pwshExe = findWinPS ( ) ; if ( pwshExe ) { yield 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 async function * enumeratePowerShellInstallations ( ) { for await ( const defaultPwsh of enumerateDefaultPowerShellInstallations ( ) ) { if ( await defaultPwsh . exists ( ) ) { yield defaultPwsh ; } } } /**
* Returns the first available PowerShell executable found in the search order.
*/ export async function getFirstAvailablePowerShellInstallation ( ) { for await ( const pwsh of enumeratePowerShellInstallations ( ) ) { return pwsh ; } return null ; }