diff --git a/Utils/New-CommandDataFile.ps1 b/Utils/New-CommandDataFile.ps1 index 290390d73..d87e26ddc 100644 --- a/Utils/New-CommandDataFile.ps1 +++ b/Utils/New-CommandDataFile.ps1 @@ -1,115 +1,120 @@ -<# -.SYNOPSIS - Create a JSON file containing module found in $pshome and their corresponding exported commands - -.EXAMPLE - C:\PS> ./New-CommandDataFile.ps1 - - Suppose this file is run on the following version of PowerShell: PSVersion = 6.1.0, PSEdition = Core, and Windows 10 operating system. Then this script will create a file named core-6.1.0-windows.json that contains a JSON object of the following form: - { - "Modules" : [ - "Module1" : { - "Name" : "Module1" - . - . - "ExportedCommands" : {...} - } - . - . - . - ] - "JsonVersion" : "0.0.1" - } +[OutputType([System.IO.FileInfo])] +[CmdletBinding()] +param( + [string]$OutputPath = './' # Default is current directory +) -.INPUTS - None +function New-CommandDataFile { + <# + .SYNOPSIS + Create a JSON file containing modules found in $PSHOME and their corresponding exported + commands and aliases. -.OUTPUTS - None + .DESCRIPTION + This script creates a JSON file containing modules found in $PSHOME and their corresponding + exported commands and aliases. The JSON file is created in the current directory by default. + Be sure to run this script in a clean PowerShell session to avoid picking up any aliases + that may have been added by the user's profile script. -#> + .PARAMETER OutputPath + The path where the JSON file is created. If not specified, the file is created in the + current directory. -$jsonVersion = "0.0.1" -$builtinModulePath = Join-Path $pshome 'Modules' -if (-not (Test-Path $builtinModulePath)) -{ - throw new "$builtinModulePath does not exist! Cannot create command data file." -} + .EXAMPLE + New-CommandDataFile C:\Temp -Function IsPSEditionDesktop -{ - $edition = Get-Variable -Name PSEdition -ErrorAction Ignore - ($edition -eq $null) -or ($edition.Value -eq 'Desktop') # $edition is of type psvariable -} + If you run this example in PowerShell 7.6.4 on Windows, it creates a file named + core-7.6.4-windows.json in the C:\Temp directory. + + .INPUTS + None + + .OUTPUTS + System.IO.FileInfo + #> + + [OutputType([System.IO.FileInfo])] + [CmdletBinding()] + param( + [ValidateScript({ Test-Path $_ })] + [string]$OutputPath = './' # Default is current directory + ) -Function Get-CmdletDataFileName -{ - $edition = 'core' - $os = 'windows' - if ((IsPSEditionDesktop)) - { + $builtinModulePath = Microsoft.PowerShell.Management\Join-Path $PSHOME 'Modules' + if (-not (Microsoft.PowerShell.Management\Test-Path $builtinModulePath)) { + throw "$builtinModulePath does not exist! Cannot create command data file." + } + + ## Get the PowerShell edition, OS, and platform to create a unique file name for the JSON file + + $edition = Microsoft.PowerShell.Utility\Get-Variable -Name PSEdition -ErrorAction Ignore + if (($edition -eq $null) -or ($edition.Value -eq 'Desktop')) { $edition = 'desktop' + } else { + $edition = 'core' } - else - { - if ($IsLinux) - { - $os = 'linux' + if ($IsLinux) { + $os = 'linux' + } elseif ($IsMacOS) { + $os = 'macos' + } else{ + $os = 'windows' + } + + $joinPathSplat = @{ + Path = $OutputPath + ChildPath = "$edition-$($PSVersionTable.PSVersion.ToString())-$os.json" + } + $outputFileName = Microsoft.PowerShell.Management\Join-Path @joinPathSplat + + $jsonData = @{ + SchemaVersion = '0.0.1' + Modules = @() + } + + $modules = (Microsoft.PowerShell.Management\Get-ChildItem -Path $builtinModulePath -Directory).Name + $modules += 'Microsoft.PowerShell.Core' # Add the Microsoft.PowerShell.Core snap-in to the list + foreach ($module in $modules) { + Microsoft.PowerShell.Utility\Write-Verbose "Processing module $module" + $commands = Microsoft.PowerShell.Core\Get-Command -Module $module + if (-not $commands) { + Microsoft.PowerShell.Utility\Write-Error "No commands found for module '$module'; skipping." + continue } - elseif ($IsMacOS) - { - $os = 'macos' + $shortCommands = $commands | + Microsoft.PowerShell.Utility\Select-Object -Property Name, + @{Label = 'CommandType'; Expression = { $_.CommandType.ToString() } }, + @{Label = 'ParameterSets'; Expression = { $_.ParameterSets -join ' ' } } + <# + Microsoft.PowerShell.Core doesn't export aliases so we can't use $module.ExportedAliases. + Some aliases are preloaded during PowerShell startup. This code ensures that we find aliases + for Microsoft.PowerShell.Core or any other aliases that are preloaded. This has the side + effect of also finding aliases for commands that may have been added by the user's profile + script. Run this command in a clean PowerShell session to avoid picking up any aliases that + may have been added by the user's profile script. + #> + $aliases = Microsoft.PowerShell.Utility\Get-Alias * | + Microsoft.PowerShell.Core\Where-Object { ($commands).Name -contains $_.ResolvedCommandName } + if ($null -eq $aliases) { + $aliases = @() + } else { + $aliases = $aliases.Name } - # else it is windows, which is already set - } - $sb = New-Object 'System.Text.StringBuilder' - $sb.Append($edition) | Out-Null - $sb.Append('-') | Out-Null - $sb.Append($PSVersionTable.PSVersion.ToString()) | Out-Null - $sb.Append('-') | Out-Null - $sb.Append($os) | Out-Null - $sb.Append('.json') | Out-Null - $sb.ToString() -} -$jsonData = @{} -$jsonData['SchemaVersion'] = $jsonVersion -$shortModuleInfos = Get-ChildItem -Path $builtinModulePath ` -| Where-Object {($_ -is [System.IO.DirectoryInfo]) -and (Get-Module $_.Name -ListAvailable)} ` -| ForEach-Object { - $modules = Get-Module $_.Name -ListAvailable - $modules | ForEach-Object { - $module = $_ - Write-Progress $module.Name - $commands = Get-Command -Module $module - $shortCommands = $commands | Select-Object -Property Name,@{Label='CommandType';Expression={$_.CommandType.ToString()}},ParameterSets - $shortModuleInfo = $module | Select-Object -Property Name,@{Label='Version';Expression={$_.Version.ToString()}} - Add-Member -InputObject $shortModuleInfo -NotePropertyName 'ExportedCommands' -NotePropertyValue $shortCommands - Add-Member -InputObject $shortModuleInfo -NotePropertyName 'ExportedAliases' -NotePropertyValue $module.ExportedAliases.Keys -PassThru + $jsonData.Modules += [pscustomobject]@{ + Name = $module + Version = $commands[0].Version.ToString() + ExportedCommands = $shortCommands + ExportedAliases = $aliases + } } -} -# Microsoft.PowerShell.Core is a PSSnapin, hence not handled by the previous code snippet -# get-module -name 'Microsoft.PowerShell.Core' returns null -# whereas get-PSSnapin is not available on PowerShell Core, so we resort to the following -$psCoreSnapinName = 'Microsoft.PowerShell.Core' -Write-Progress $psCoreSnapinName -$commands = Get-Command -Module $psCoreSnapinName -$shortCommands = $commands | Select-Object -Property Name,@{Label='CommandType';Expression={$_.CommandType.ToString()}},ParameterSets -$shortModuleInfo = New-Object -TypeName PSObject -Property @{Name=$psCoreSnapinName; Version=$commands[0].PSSnapin.PSVersion.ToString()} -Add-Member -InputObject $shortModuleInfo -NotePropertyName 'ExportedCommands' -NotePropertyValue $shortCommands - -# Find the exported aliases for the commands in Microsoft.PowerShell.Core -$aliases = Get-Alias * | Where-Object { ($commands).Name -contains $_.ResolvedCommandName } -if ($null -eq $aliases) { - $aliases = @() -} -else { - $aliases = $aliases.Name + $jsonData | + Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 4 | + Microsoft.PowerShell.Utility\Out-File $outputFileName -Encoding utf8 + + Microsoft.PowerShell.Management\Get-Item $outputFileName } -Add-Member -InputObject $shortModuleInfo -NotePropertyName 'ExportedAliases' -NotePropertyValue $aliases -$allShortModuleInfos = $shortModuleInfos + $shortModuleInfo -$jsonData['Modules'] = $allShortModuleInfos -$jsonData | ConvertTo-Json -Depth 4 | Out-File ((Get-CmdletDataFileName)) -Encoding utf8 +New-CommandDataFile -OutputPath $OutputPath \ No newline at end of file