Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 104 additions & 99 deletions Utils/New-CommandDataFile.ps1
Original file line number Diff line number Diff line change
@@ -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
Loading