Functions/GenXdev.Coding.PowerShell.Modules/Get-GenXdevCmdletUsageAnalysis.ps1
<##############################################################################
Part of PowerShell module : GenXdev.Coding.PowerShell.Modules Original cmdlet filename : Get-GenXdevCmdletUsageAnalysis.ps1 Original author : René Vaessen / GenXdev Version : 1.268.2025 ################################################################################ MIT License Copyright 2021-2025 GenXdev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ################################################################################> <# .SYNOPSIS Analyzes GenXdev cmdlet usage patterns to identify most frequently called functions. .DESCRIPTION This script uses Get-GenXDevCmdlet to scan all GenXdev PowerShell modules and their functions to analyze which cmdlets are called most frequently by other cmdlets. This helps prioritize which functions to refactor to C# first, starting with the most commonly used ones. .PARAMETER OutputFormat Format for output: Table, List, or CSV. Default is Table. .PARAMETER Top Number of top results to show. Default is 50. .PARAMETER IncludeCallChains Include detailed call chain information showing which functions call which. .PARAMETER IncludeScripts Include script files in addition to module cmdlets. .EXAMPLE Get-GenXdevCmdletUsageAnalysis .EXAMPLE Get-GenXdevCmdletUsageAnalysis -Top 20 -OutputFormat List .EXAMPLE Get-GenXdevCmdletUsageAnalysis -IncludeCallChains -IncludeScripts | Export-Csv -Path "cmdlet-usage.csv" #> function Get-GenXdevCmdletUsageAnalysis { [CmdletBinding()] param( [ValidateSet('Table', 'List', 'CSV')] [string]$OutputFormat = 'Table', [int]$Top = 50, [switch]$IncludeCallChains, [switch]$IncludeScripts ) function GetPowerShellFunctionCalls { param( [string]$FilePath, [hashtable]$AllFunctions ) try { $content = Microsoft.PowerShell.Management\Get-Content -LiteralPath $FilePath -Raw -ErrorAction Stop # Remove comments and strings to avoid false positives $cleanedContent = $content -replace '#.*$', '' -replace '(?s)<#.*?#>', '' $cleanedContent = $cleanedContent -replace '"[^"]*"', '""' -replace "'[^']*'", "''" $calls = @() # Find function calls in various patterns $patterns = @( # Direct function calls: FunctionName, Module\FunctionName '(?:^|\s|;|\||\(|\{)([A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)*\\)?([A-Za-z][A-Za-z0-9]*-[A-Za-z][A-Za-z0-9]*)\s*(?:\s|\(|$)', # Ampersand calls: & FunctionName '&\s+([A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)*\\)?([A-Za-z][A-Za-z0-9]*-[A-Za-z][A-Za-z0-9]*)', # Dot sourcing: . FunctionName '\.\s+([A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)*\\)?([A-Za-z][A-Za-z0-9]*-[A-Za-z][A-Za-z0-9]*)' ) foreach ($pattern in $patterns) { $regexMatches = [regex]::Matches($cleanedContent, $pattern, 'IgnoreCase') foreach ($match in $regexMatches) { $fullName = if ($match.Groups[1].Value) { $match.Groups[1].Value + $match.Groups[2].Value } else { $match.Groups[2].Value } $functionName = $match.Groups[2].Value # Only include if it's a known GenXdev function if ($AllFunctions.ContainsKey($functionName) -or $AllFunctions.ContainsKey($fullName)) { $calls += $functionName } } } return $calls | Microsoft.PowerShell.Utility\Sort-Object -Unique } catch { Microsoft.PowerShell.Utility\Write-Warning "Error processing file ${FilePath}: $($_.Exception.Message)" return @() } } function BuildCallGraph { param([hashtable]$AllFunctions) $callGraph = @{} $usageCount = @{} $callChains = @{} # Initialize counts foreach ($funcName in $AllFunctions.Keys) { $usageCount[$funcName] = 0 $callChains[$funcName] = @() } $total = $AllFunctions.Count $current = 0 foreach ($function in $AllFunctions.GetEnumerator()) { $current++ Microsoft.PowerShell.Utility\Write-Progress -Activity "Analyzing GenXdev cmdlet usage patterns" -Status "Processing $($function.Key)" -PercentComplete (($current / $total) * 100) -CurrentOperation "$current of $total functions analyzed" $calledFunctions = GetPowerShellFunctionCalls -FilePath $function.Value.FilePath -AllFunctions $AllFunctions $callGraph[$function.Key] = $calledFunctions # Count usage foreach ($calledFunc in $calledFunctions) { if ($usageCount.ContainsKey($calledFunc)) { $usageCount[$calledFunc]++ $callChains[$calledFunc] += $function.Key } } } Microsoft.PowerShell.Utility\Write-Progress -Activity "Analyzing GenXdev cmdlet usage patterns" -Completed return @{ CallGraph = $callGraph UsageCount = $usageCount CallChains = $callChains } } function GetAllGenXdevFunctions { # Use the existing Get-GenXDevCmdlet to get all cmdlets $cmdlets = if ($IncludeScripts) { GenXdev.Helpers\Get-GenXDevCmdlet -IncludeScripts } else { GenXdev.Helpers\Get-GenXDevCmdlet } $allFunctions = @{} foreach ($cmdlet in $cmdlets) { if ($cmdlet.ScriptFilePath -and (Microsoft.PowerShell.Management\Test-Path -LiteralPath $cmdlet.ScriptFilePath)) { $allFunctions[$cmdlet.Name] = @{ FilePath = $cmdlet.ScriptFilePath ModuleName = $cmdlet.ModuleName RelativePath = if ($cmdlet.ScriptFilePath) { try { $relativePath = [System.IO.Path]::GetRelativePath((Microsoft.PowerShell.Management\Get-Location).Path, $cmdlet.ScriptFilePath) if ($relativePath.Length -gt 50) { "..." + $relativePath.Substring($relativePath.Length - 47) } else { $relativePath } } catch { [System.IO.Path]::GetFileName($cmdlet.ScriptFilePath) } } else { "Unknown" } Description = $cmdlet.Description Aliases = $cmdlet.Aliases } } else { Microsoft.PowerShell.Utility\Write-Warning "Skipping $($cmdlet.Name) - file not found: $($cmdlet.ScriptFilePath)" } } return $allFunctions } # Main execution try { # Import GenXdev module to ensure Get-GenXDevCmdlet is available Microsoft.PowerShell.Core\Import-Module GenXdev -Force # Discover all functions using the existing cmdlet Microsoft.PowerShell.Utility\Write-Progress -Activity "Initializing GenXdev cmdlet usage analysis" -Status "Discovering GenXdev functions" -PercentComplete 10 $allFunctions = GetAllGenXdevFunctions if (-not $allFunctions -or $allFunctions.Count -eq 0) { throw "No functions found in GenXdev modules" } # Build call graph and usage analysis Microsoft.PowerShell.Utility\Write-Progress -Activity "Initializing GenXdev cmdlet usage analysis" -Status "Building call graph for $($allFunctions.Count) functions" -PercentComplete 20 $analysis = BuildCallGraph -AllFunctions $allFunctions # Create results Microsoft.PowerShell.Utility\Write-Progress -Activity "Finalizing GenXdev cmdlet usage analysis" -Status "Processing results" -PercentComplete 80 $results = @() foreach ($func in $analysis.UsageCount.GetEnumerator()) { $result = [PSCustomObject]@{ FunctionName = $func.Key UsageCount = $func.Value ModuleName = $allFunctions[$func.Key].ModuleName RelativePath = $allFunctions[$func.Key].RelativePath FilePath = $allFunctions[$func.Key].FilePath Description = $allFunctions[$func.Key].Description Aliases = ($allFunctions[$func.Key].Aliases -join ", ") } if ($IncludeCallChains) { $result | Microsoft.PowerShell.Utility\Add-Member -NotePropertyName "CalledBy" -NotePropertyValue ($analysis.CallChains[$func.Key] -join ", ") $result | Microsoft.PowerShell.Utility\Add-Member -NotePropertyName "Calls" -NotePropertyValue ($analysis.CallGraph[$func.Key] -join ", ") } $results += $result } # Sort by usage count (descending) and take top N Microsoft.PowerShell.Utility\Write-Progress -Activity "Finalizing GenXdev cmdlet usage analysis" -Status "Sorting and formatting results" -PercentComplete 90 $sortedResults = $results | Microsoft.PowerShell.Utility\Sort-Object UsageCount -Descending | Microsoft.PowerShell.Utility\Select-Object -First $Top Microsoft.PowerShell.Utility\Write-Progress -Activity "Finalizing GenXdev cmdlet usage analysis" -Completed switch ($OutputFormat) { 'Table' { if ($IncludeCallChains) { $sortedResults | Microsoft.PowerShell.Utility\Format-Table -Property FunctionName, UsageCount, ModuleName, CalledBy -AutoSize } else { $sortedResults | Microsoft.PowerShell.Utility\Format-Table -Property FunctionName, UsageCount, ModuleName, Aliases -AutoSize } } 'List' { $sortedResults | Microsoft.PowerShell.Utility\Format-List } 'CSV' { $sortedResults | Microsoft.PowerShell.Utility\ConvertTo-Csv -NoTypeInformation } } # Summary statistics $totalFunctions = $results.Count $functionsWithUsage = ($results | Microsoft.PowerShell.Core\Where-Object { $_.UsageCount -gt 0 }).Count $maxUsage = ($results | Microsoft.PowerShell.Utility\Measure-Object UsageCount -Maximum).Maximum $avgUsage = [math]::Round(($results | Microsoft.PowerShell.Utility\Measure-Object UsageCount -Average).Average, 2) # Output summary to console Microsoft.PowerShell.Utility\Write-Output "`nGenXdev Cmdlet Usage Analysis Summary:" Microsoft.PowerShell.Utility\Write-Output "Total Functions: $totalFunctions" Microsoft.PowerShell.Utility\Write-Output "Functions with Usage > 0: $functionsWithUsage" Microsoft.PowerShell.Utility\Write-Output "Maximum Usage Count: $maxUsage" Microsoft.PowerShell.Utility\Write-Output "Average Usage Count: $avgUsage" Microsoft.PowerShell.Utility\Write-Output "`nTop Priority for C# Conversion:" $top5 = $sortedResults | Microsoft.PowerShell.Utility\Select-Object -First 5 foreach ($item in $top5) { Microsoft.PowerShell.Utility\Write-Output " $($item.UsageCount.ToString().PadLeft(3)) uses: $($item.FunctionName)" Microsoft.PowerShell.Utility\Write-Output "$($item.FilePath)" # Output filename to stdout if ($item.Aliases) { Microsoft.PowerShell.Utility\Write-Output " Aliases: $($item.Aliases)" } } # Return the full results for further processing return $sortedResults } catch { Microsoft.PowerShell.Utility\Write-Error "Analysis failed: $($_.Exception.Message)" Microsoft.PowerShell.Utility\Write-Error $_.ScriptStackTrace } } |