Functions/GenXdev.Helpers/Get-GenXDevCmdlets.ps1
################################################################################ <# .SYNOPSIS Retrieves and lists all GenXdev cmdlets and their details. .DESCRIPTION Searches through installed GenXdev modules and script files to find cmdlets, their aliases, and descriptions. Can filter by name pattern and module name. .PARAMETER Filter Pattern to match cmdlet names or aliases. Supports wildcards (*). .PARAMETER BaseModuleName One or more GenXdev module names to search. Can omit GenXdev prefix. .PARAMETER NoLocal Skip searching in local module paths. .PARAMETER OnlyPublished Limit search to published module paths only. .PARAMETER FromScripts Search in script files instead of module files. .EXAMPLE Get-GenXDevCmdlets -CmdletName "Get-*" -BaseModuleName "Console" -NoLocal .EXAMPLE gcmds Get-* #> function Get-GenXDevCmdlets { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "Get-GenXDevCmdlets")] [Alias("gcmds")] param( ######################################################################## [parameter( Position = 0, Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = "Search pattern to filter cmdlets" )] [ValidateNotNull()] [Alias("Filter", "CmdLet", "Cmd", "FunctionName", "Name")] [SupportsWildcards()] [string] $CmdletName = "*", ######################################################################## [parameter( Position = 1, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "GenXdev module names to search" )] [ValidateNotNull()] [Alias("Module", "ModuleName", "BaseModule")] [SupportsWildcards()] [string[]] $BaseModuleName = @("GenXdev*"), ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Skip searching in local module paths" )] [switch] $NoLocal, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Only search in published module paths" )] [switch] $OnlyPublished, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Search in script files instead of modules" )] [switch] $FromScripts ######################################################################## ) begin { # log search criteria for diagnostics Write-Verbose ("Searching for cmdlets matching '$CmdletName' in modules: " + "$($BaseModuleName -join ',')") # resolve path to scripts directory $scriptFilePath = GenXdev.FileSystem\Expand-Path ` -FilePath "$PSScriptRoot\..\..\..\..\..\Scripts\*.ps1" ` -CreateDirectory } process { if ($FromScripts) { $CmdletName = @($CmdletName | ForEach-Object { "$([IO.Path]::GetDirectoryName($scriptFilePath))\$PSItem.ps1" } ) # process all ps1 files in scripts directory Get-ChildItem $CmdletName -File -ErrorAction SilentlyContinue | ForEach-Object { # skip test files to avoid processing test code if ($_.Name -like "*.Tests.ps1") { return } # read entire function content for processing $functionContent = [IO.File]::ReadAllText($_.FullName) # determine function definition start line $lineNo = Get-FunctionStartLine -Content $functionContent # extract function aliases from content $aliases = Get-FunctionAliases ` -FileName ($_.FullName) ` -FunctionContent $functionContent $scriptFilePath = $_.FullName # build path for corresponding test file $functionTestFilePath = GenXdev.FileSystem\Expand-Path ` -FilePath ([IO.Path]::ChangeExtension($_.FullName, ".Tests.ps1")) # return object with function details [PSCustomObject]@{ Name = [IO.Path]::GetFileNameWithoutExtension( $_.Name) ModuleName = "GenXdev.Scripts" BaseModule = "GenXdev.Scripts" LineNo = $lineNo Description = Get-FunctionDescription ` -FileName ($_.FullName) ` -FunctionContent $functionContent Aliases = $aliases -join ", " ScriptFilePath = $scriptFilePath ScriptTestFilePath = $functionTestFilePath } } | Sort-Object { $_.BaseModule + "_" + $_.ModuleName + "_" + $_.Name } return } # handle single cmdlet test scenario if (-not ([string]::IsNullOrWhiteSpace($CmdletName) -or ($CmdletName -eq "*"))) { Get-Command -Name $CmdletName -CommandType @("Cmdlet", "Function", "Alias") | ForEach-Object { $cmd = $_ if ($cmd -is [System.Management.Automation.AliasInfo]) { $cmd = $cmd.ResolvedCommand } [string] $BaseModule = $cmd.ModuleName $functionPath = GenXdev.FileSystem\Expand-Path "$PSScriptRoot\..\..\..\..\..\Modules\$($BaseModule)\1.128.2025\Functions\$($cmd.Name).ps1" Get-ChildItem ($functionPath) -File -Recurse -ErrorAction SilentlyContinue | ForEach-Object { # prepare function details $functionContent = [IO.File]::ReadAllText($_.FullName) if ([string]::IsNullOrWhiteSpace($functionContent)) { return } $lineNo = Get-FunctionStartLine -Content $functionContent $scriptFilePath = $_.FullName $functionTestFilePath = GenXdev.FileSystem\Expand-Path ` -FilePath "$([IO.Path]::GetDirectoryName($ScriptFilePath))\..\..\Tests\$([IO.Path]::GetFileName([IO.Path]::GetDirectoryName($ScriptFilePath)))\$( [IO.Path]::GetFileNameWithoutExtension($_.Name)).Tests.ps1" -CreateFile # return function information object [PSCustomObject]@{ Name = [IO.Path]::GetFileNameWithoutExtension($_.Name) ModuleName = ([IO.Path]::GetFileName([IO.Path]::GetDirectoryName($_.FullName))) BaseModule = $BaseModule LineNo = $lineNo Description = ( Get-FunctionDescription ` -FileName ($_.FullName) ` -FunctionContent $functionContent ) Aliases = $aliases -join ", " ScriptFilePath = $scriptFilePath ScriptTestFilePath = $functionTestFilePath } } } return; } # prepare module search parameters $invocationParams = GenXdev.Helpers\Copy-IdenticalParamValues ` -FunctionName "GenXdev.Helpers\Invoke-OnEachGenXdevModule" ` -BoundParameters $PSBoundParameters # process each matching module GenXdev.Helpers\Invoke-OnEachGenXdevModule @invocationParams -Script { param($module) if (-not ($module.Name -like "GenXdev*")) { return } [string] $BaseModule = $module.Name # process each function file in module @(Get-ChildItem .\Functions\*.ps1 -File -Recurse ` -ErrorAction SilentlyContinue) | ForEach-Object { $functionContent = [IO.File]::ReadAllText($_.FullName) if ([string]::IsNullOrWhiteSpace($functionContent)) { return } $aliases = @(Get-FunctionAliases ` -FileName ($_.FullName) ` -FunctionContent $functionContent) # prepare function details $lineNo = Get-FunctionStartLine -Content $functionContent $scriptFilePath = $_.FullName $functionTestFilePath = GenXdev.FileSystem\Expand-Path ` -FilePath "$([IO.Path]::GetDirectoryName($ScriptFilePath))\..\..\Tests\$([IO.Path]::GetFileName([IO.Path]::GetDirectoryName($ScriptFilePath)))\$( [IO.Path]::GetFileNameWithoutExtension($_.Name)).Tests.ps1" -CreateFile # return function information object [PSCustomObject]@{ Name = [IO.Path]::GetFileNameWithoutExtension( $_.Name) ModuleName = ([IO.Path]::GetFileName( [IO.Path]::GetDirectoryName($_.FullName))) BaseModule = $BaseModule LineNo = $lineNo Description = Get-FunctionDescription ` -FileName ($_.FullName) ` -FunctionContent $functionContent Aliases = $aliases -join ", " ScriptFilePath = $scriptFilePath ScriptTestFilePath = $functionTestFilePath } } }.GetNewClosure() | Sort-Object { $_.BaseModule + "_" + $_.ModuleName + "_" + $_.Name } } end { } } ################################################################################ <# .SYNOPSIS Helper function to extract function descriptions from sourcecode. .PARAMETER FileName The path to the script file. .PARAMETER FunctionContent The content of the function to parse. #> function Get-FunctionDescription { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "Get-FunctionDescription")] [OutputType([string])] param( [Parameter( Position = 0, Mandatory = $true )] [string]$FileName, [Parameter( Position = 1, Mandatory = $true )] [AllowEmptyString()] [AllowNull()] [string]$FunctionContent ) $FunctionContent = $null -eq $FunctionContent ? "" : $FunctionContent # check if file is in Scripts folder $FromScripts = $FileName.StartsWith((GenXdev.FileSystem\Expand-Path ` -FilePath "$PSScriptRoot\..\..\..\..\..\Scripts\")) try { # build regex pattern based on file location $pattern = $FromScripts ? "\.SYNOPSIS\s*`r`n([^\r\n]*[\r\n]*)\r\n\r\n([^#][^>])*#>" : ("\.SYNOPSIS\s*`r`n([^\r\n]*[\r\n]*)\r\n\r\n([^#][^>])*#>\s*" + "function\s+$([IO.Path]::GetFileNameWithoutExtension($FileName))") # extract synopsis $match = [regex]::Match( $FunctionContent, $pattern, ([System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.RegularExpressions.RegexOptions]::IgnorePatternWhitespace) ) if ($match.Success) { return $match.Groups[1].Value.Trim() } } catch { Write-Verbose "Failed to get description: $($_.Exception.Message)" } return "" } ################################################################################ <# .SYNOPSIS Helper function to extract function aliases from sourcecode. .PARAMETER FileName The path to the script file. .PARAMETER FunctionContent The content of the function to parse. #> function Get-FunctionAliases { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "Get-FunctionAliases")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "Get-FunctionAliases")] [OutputType([string])] param( [Parameter( Position = 0, Mandatory = $true )] [string]$FileName, [Parameter( Position = 1, Mandatory = $true )] [AllowEmptyString()] [AllowNull()] [string]$FunctionContent ) $FunctionContent = $null -eq $FunctionContent ? "" : $FunctionContent try { $content = $FunctionContent.ToLowerInvariant(); # extract aliases from module functions [int] $i = $content.IndexOf("#>"); $i = $content.IndexOf( "function $([IO.Path]::GetFileNameWithoutExtension($FileName).ToLowerInvariant())", [Math]::Max(0, $i)) if ($i -lt 0) { $i = $content.IndexOf("[cmdletbinding(", [Math]::Max(0, $i)) if ($i -lt 0) { $i = 0 } } $i2 = $content.IndexOf("param(", [Math]::Max(0, $i)) if ($i2 -lt 0) { $i2 = $content.ToLowerInvariant().IndexOf("[parameter", [Math]::Max(0, $i)) } $i = $content.IndexOf("[alias(", [Math]::Max(0, $i)) if ($i -ge 0 -and $i2 -gt $i) { $aliases = $content.Substring($i + 7) $aliases = $aliases.Substring(0, $aliases.IndexOf(")")).Replace( "'", "`"") $aliases = $aliases -replace "[\)\[\]\`"]", "" return (@($aliases -split ",") | ForEach-Object { $_.Trim() }) } } catch { Write-Verbose "Failed to get aliases: $($_.Exception.Message)" } return [string]::Empty } ################################################################################ <# .SYNOPSIS Helper function to find the starting line number of a function. .PARAMETER Content The content to search for the function start. #> function Get-FunctionStartLine { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "Get-FunctionStartLine")] [OutputType([int])] param( [Parameter( Position = 0, Mandatory = $true )] [AllowEmptyString()] [AllowNull()] [string]$Content ) $Content = $null -eq $Content ? "" : $Content $lineNo = $Content.IndexOf("#>") if ($lineNo -lt 0) { $lineNo = $Content.IndexOf("[CmdletBinding") } if ($lineNo -lt 0) { $lineNo = $Content.IndexOf("params") } if ($lineNo -lt 0) { $lineNo = $Content.IndexOf("begin {") } if ($lineNo -lt 0) { $lineNo = $Content.IndexOf("process {") } if ($lineNo -lt 0) { $lineNo = 0 } else { $lineNo = $Content.Substring(0, $lineNo).Split("`n").Count + 1 } return $lineNo } ################################################################################ |