├── .gitattributes ├── Example ├── d0qx1fyr.png ├── module_adsips.png ├── module_pode.PNG ├── module_psclassutils.png ├── module_psfunctionexplorer.png ├── module_pshtml.PNG └── module_pshtml2.png ├── LICENSE ├── PSFunctionExplorer ├── Code │ ├── Classes │ │ └── Classes.psm1 │ └── Functions │ │ ├── Expand-Fufile.ps1 │ │ ├── Find-FUFunction.ps1 │ │ └── Write-FUGraph.ps1 ├── PSFunctionExplorer.psd1 ├── PSFunctionExplorer.psm1 └── tests │ └── PSFunctionExplorer.Tests.Ps1 ├── README.md └── azure-pipelines.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Example/d0qx1fyr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/Example/d0qx1fyr.png -------------------------------------------------------------------------------- /Example/module_adsips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/Example/module_adsips.png -------------------------------------------------------------------------------- /Example/module_pode.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/Example/module_pode.PNG -------------------------------------------------------------------------------- /Example/module_psclassutils.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/Example/module_psclassutils.png -------------------------------------------------------------------------------- /Example/module_psfunctionexplorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/Example/module_psfunctionexplorer.png -------------------------------------------------------------------------------- /Example/module_pshtml.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/Example/module_pshtml.PNG -------------------------------------------------------------------------------- /Example/module_pshtml2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/Example/module_pshtml2.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 LxLeChat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /PSFunctionExplorer/Code/Classes/Classes.psm1: -------------------------------------------------------------------------------- 1 | class FUFunction { 2 | $Name 3 | [System.Collections.ArrayList]$Commands = @() 4 | $Path 5 | hidden $RawFunctionAST 6 | 7 | FUFunction ([System.Management.Automation.Language.FunctionDefinitionAST]$Raw,$Path,[Bool]$TitleCase) { 8 | $this.RawFunctionAST = $Raw 9 | $this.Path = $path 10 | $this.GetCommands($TitleCase) 11 | 12 | If ( $TitleCase ) { 13 | $this.name = [FUUtility]::ToTitleCase($this.RawFunctionAST.name) 14 | } Else { 15 | $this.name = $this.RawFunctionAST.name 16 | } 17 | } 18 | 19 | FUFunction ([System.Management.Automation.Language.FunctionDefinitionAST]$Raw,$ExclusionList,$Path,[Bool]$TitleCase) { 20 | $this.RawFunctionAST = $Raw 21 | $this.Path = $path 22 | $this.GetCommands($ExclusionList,$TitleCase) 23 | 24 | If ( $TitleCase ) { 25 | $this.name = [FUUtility]::ToTitleCase($this.RawFunctionAST.name) 26 | } Else { 27 | $this.name = $this.RawFunctionAST.name 28 | } 29 | } 30 | 31 | hidden GetCommands ([Bool]$TitleCase) { 32 | 33 | $t = $this.RawFunctionAST.findall({$args[0] -is [System.Management.Automation.Language.CommandAst]},$true) 34 | If ( $t.Count -gt 0 ) { 35 | ## si elle existe deja, on ajotue juste à ces commands 36 | ($t.GetCommandName() | Select-Object -Unique).Foreach({ 37 | 38 | If ( $TitleCase ) { 39 | $Command = [FUUtility]::ToTitleCase($_) 40 | } Else { 41 | $Command = $_ 42 | } 43 | 44 | $this.Commands.Add($Command) 45 | }) 46 | } 47 | } 48 | 49 | ## Overload 50 | hidden GetCommands ($ExclusionList,[Bool]$TitleCase) { 51 | 52 | $t = $this.RawFunctionAST.findall({$args[0] -is [System.Management.Automation.Language.CommandAst]},$true) 53 | If ( $t.Count -gt 0 ) { 54 | ($t.GetCommandName() | Select-Object -Unique).Foreach({ 55 | $Command = [FUUtility]::ToTitleCase($_) 56 | If ( $ExclusionList -notcontains $Command) { 57 | If ( $TitleCase ) { 58 | $Command = [FUUtility]::ToTitleCase($_) 59 | } Else { 60 | $Command = $_ 61 | } 62 | $this.Commands.Add($Command) 63 | } 64 | }) 65 | } 66 | } 67 | } 68 | 69 | Class FUUtility { 70 | 71 | ## Static Method to TitleCase 72 | Static [String]ToTitleCase ([string]$String){ 73 | return (Get-Culture).TextInfo.ToTitleCase($String.ToLower()) 74 | } 75 | 76 | ## Static Method to return Function in AST Form, exclude classes 77 | [Object[]] static GetRawASTFunction($Path) { 78 | 79 | $RawFunctions = $null 80 | $ParsedFile = [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$Null) 81 | $RawAstDocument = $ParsedFile.FindAll({$args[0] -is [System.Management.Automation.Language.Ast]}, $true) 82 | 83 | If ( $RawASTDocument.Count -gt 0 ) { 84 | ## source: https://stackoverflow.com/questions/45929043/get-all-functions-in-a-powershell-script/45929412 85 | $RawFunctions = $RawASTDocument.FindAll({$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $($args[0].parent) -isnot [System.Management.Automation.Language.FunctionMemberAst] }) 86 | } 87 | 88 | return $RawFunctions 89 | } 90 | 91 | ## GetFunction, return [FuFunction] 92 | [FUFunction] Static GetFunction($RawASTFunction,$path,$TitleCase){ 93 | return [FUFunction]::New($RawASTFunction,$path,$TitleCase) 94 | } 95 | 96 | ## GetFunctions Overload, with ExclustionList, return [FuFunction] 97 | [FUFunction] Static GetFunction($RawASTFunction,$Exculde,$path,$TitleCase){ 98 | return [FUFunction]::New($RawASTFunction,$Exculde,$path,$TitleCase) 99 | } 100 | 101 | ## SaveTofile in current path 102 | [System.IO.FileSystemInfo] static SaveToFile ([FuFunction]$Function) { 103 | return New-Item -Name $([FUUtility]::FileName($Function.name)) -value $Function.RawFunctionAST.Extent.Text -ItemType File 104 | } 105 | 106 | ## SaveTofile Overload, with Specific path for export 107 | [System.IO.FileSystemInfo] static SaveToFile ([FuFunction]$Function,$Path) { 108 | return New-Item -Path $Path -Name $([FUUtility]::FileName($Function.name)) -value $Function.RawFunctionAST.Extent.Text -ItemType File 109 | } 110 | 111 | ## Construct filename for export 112 | [string] hidden static FileName ($a) { 113 | return "$a.ps1" 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /PSFunctionExplorer/Code/Functions/Expand-Fufile.ps1: -------------------------------------------------------------------------------- 1 | Function Expand-FUFile { 2 | <# 3 | .SYNOPSIS 4 | Export a FUFunction to a ps1 file. It's like a reverse build process. 5 | .DESCRIPTION 6 | Export a FUFunction to a ps1 file. It's like a reverse build process. 7 | .EXAMPLE 8 | PS C:\> Find-FUFunction -Path .\PSFunctionExplorer.psm1 | Expand-FUFile 9 | Répertoire : C:\ 10 | 11 | 12 | Mode LastWriteTime Length Name 13 | ---- ------------- ------ ---- 14 | -a---- 30/04/2019 23:24 658 Expand-FUFile.ps1 15 | -a---- 30/04/2019 23:24 3322 Find-Fufunction.ps1 16 | -a---- 30/04/2019 23:24 2925 Write-Fufunctiongraph.ps1 17 | 18 | Find all functions definitions inside PSFunctionExplorer.psm1 and save each function inside it's own ps1 file. 19 | .EXAMPLE 20 | PS C:\> Find-FUFunction -Path .\PSFunctionExplorer.psm1 | Expand-FUFile -Path C:\Temp 21 | Répertoire : C:\Temp 22 | 23 | 24 | Mode LastWriteTime Length Name 25 | ---- ------------- ------ ---- 26 | -a---- 30/04/2019 23:24 658 Expand-FUFile.ps1 27 | -a---- 30/04/2019 23:24 3322 Find-Fufunction.ps1 28 | -a---- 30/04/2019 23:24 2925 Write-Fufunctiongraph.ps1 29 | 30 | Find all functions definitions inside PSFunctionExplorer.psm1 and save each function inside it's own ps1 file, inside the C:\Temp directory. 31 | .INPUTS 32 | [FuFunction] 33 | .OUTPUTS 34 | [System.IO.FileSystemInfo] 35 | .NOTES 36 | #> 37 | 38 | [CmdletBinding()] 39 | param ( 40 | [Parameter(ValueFromPipeline=$True)] 41 | [Object[]]$FUFunction, 42 | [String]$Path 43 | ) 44 | 45 | begin { 46 | If ( $PSBoundParameters['Path']) { 47 | $item = get-item (resolve-path -path $path).path 48 | } 49 | } 50 | 51 | process { 52 | ForEach( $Function in $FUFunction) { 53 | 54 | If ( $PSBoundParameters['Path']) { 55 | [FUUtility]::SaveToFile($Function,$Item.FullName) 56 | } Else { 57 | [FUUtility]::SaveToFile($Function) 58 | } 59 | 60 | } 61 | } 62 | 63 | end { 64 | } 65 | } -------------------------------------------------------------------------------- /PSFunctionExplorer/Code/Functions/Find-FUFunction.ps1: -------------------------------------------------------------------------------- 1 | Function Find-FUFunction { 2 | <# 3 | .SYNOPSIS 4 | Find All Functions declaration inside a ps1/psm1 file and their inner commands. 5 | .DESCRIPTION 6 | Find All Functions declaration inside a ps1/psm1 file. 7 | Return an object describing a powershell function. Output a custom type: FUFunction. 8 | .EXAMPLE 9 | PS C:\> Find-FUFunction .\PSFunctionExplorer.psm1 10 | 11 | Name Commands Path 12 | ---- -------- ---- 13 | Find-Fufunction {Get-Command, Get-Alias, Select-Object, Get-Item...} C:\PSFunctionExplorer.psm1 14 | Write-Fufunctiongraph {Get-Item, Resolve-Path, Find-Fufunction, Graph...} C:\PSFunctionExplorer.psm1 15 | 16 | return all function present in the PSFunctionExplorer.psm1 and every commands present in it. 17 | .EXAMPLE 18 | PS C:\> Find-FUFunction .\PSFunctionExplorer.psm1 -ExcludePSCmdlets 19 | Name Commands Path 20 | ---- -------- ---- 21 | Find-Fufunction {} C:\Users\Lx\GitPerso\PSFunctionUtils\PSFunctionExplorer\PSFunctionExplorer.psm1 22 | Write-Fufunctiongraph {Find-Fufunction, Graph, Node, Edge...} C:\Users\Lx\GitPerso\PSFunctionUtils\PSFunctionExplorer\PSFunctionExplorer.psm1 23 | 24 | Return all function present in the PSFunctionExplorer.psm1 and every commands present in it, but exclude default ps cmdlets. 25 | .INPUTS 26 | Path. Accepts pipeline inputs 27 | .OUTPUTS 28 | A FUFunction custom object 29 | .NOTES 30 | General notes 31 | #> 32 | [CmdletBinding()] 33 | param ( 34 | [Alias("FullName")] 35 | [Parameter(ValueFromPipeline=$True,Position=1,ValueFromPipelineByPropertyName=$True)] 36 | [string[]]$Path, 37 | [Switch]$ExcludePSCmdlets, 38 | [Switch]$NoTitleCase = $False 39 | ) 40 | 41 | begin { 42 | If ( $PSBoundParameters['ExcludePSCmdlets'] ) { 43 | $ToExclude = (Get-Command -Module "Microsoft.PowerShell.Archive","Microsoft.PowerShell.Utility","Microsoft.PowerShell.ODataUtils","Microsoft.PowerShell.Operation.Validation","Microsoft.PowerShell.Management","Microsoft.PowerShell.Core","Microsoft.PowerShell.LocalAccounts","Microsoft.WSMan.Management","Microsoft.PowerShell.Security","Microsoft.PowerShell.Diagnostics","Microsoft.PowerShell.Host").Name 44 | $ToExclude += (Get-Alias | Select-Object -Property Name).name 45 | } 46 | } 47 | 48 | process { 49 | ForEach( $p in $Path) { 50 | $item = get-item (resolve-path -path $p).path 51 | If ( $item -is [system.io.FileInfo] -and $item.Extension -in @('.ps1','.psm1') ) { 52 | Write-Verbose ("[FUFunction]Analyzing {0} ..." -f $item.FullName) 53 | $t = [FUUtility]::GetRawASTFunction($item.FullName) 54 | Foreach ( $RawASTFunction in $t ) { 55 | If ( $PSBoundParameters['ExcludePSCmdlets'] ) { 56 | [FUUtility]::GetFunction($RawASTFunction,$ToExclude,$item.FullName,$NoTitleCase) 57 | } Else { 58 | [FUUtility]::GetFunction($RawASTFunction,$item.FullName,$NoTitleCase) 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | end { 66 | } 67 | } -------------------------------------------------------------------------------- /PSFunctionExplorer/Code/Functions/Write-FUGraph.ps1: -------------------------------------------------------------------------------- 1 | Function Write-FUGraph { 2 | <# 3 | .SYNOPSIS 4 | Generate dependecy graph for a function or a set of functions found in a ps1/psm1 file. 5 | .DESCRIPTION 6 | Generate dependecy graph for a function or a set of functions found in a ps1/psm1 file. 7 | .EXAMPLE 8 | PS C:\> $x = Find-FUFunction .\PSFunctionExplorer.psm1 9 | PS C:\> Write-FUGraph -InputObject $x -ExportPath c:\temp\fufuncion.png -outputformat png -ShowGraph 10 | 11 | Répertoire : C:\temp 12 | 13 | Mode LastWriteTime Length Name 14 | ---- ------------- ------ ---- 15 | -a---- 08/09/2019 15:08 71598 fufunction.png 16 | 17 | Will Find all function(s) declarations in the psfunctionexplorer.psm1 file, and create a graph name fufunction.png. Then display it. 18 | .EXAMPLE 19 | PS C:\> Find-FUFunction .\PSFunctionExplorer.psm1 | Write-FUGraph -ExportPath c:\temp\fufuncion.png -outputformat png -ShowGraph 20 | 21 | Will Find all function(s) declarations in the psfunctionexplorer.psm1 file, and create a graph name fufunction.png. Then display it. 22 | .INPUTS 23 | FullName Path. Accepts pipeline inputs. 24 | .OUTPUTS 25 | Outputs Graph, thanks to psgraph module. 26 | .NOTES 27 | First Draft. For the moment the function only output graphviz datas. Soon you ll be able to generate a nice graph as a png, pdf ... 28 | #> 29 | [CmdletBinding(DefaultParameterSetName = "Graph")] 30 | param ( 31 | [Alias("FullName")] 32 | [Parameter(ValueFromPipeline=$True)] 33 | [FUFunction[]]$InputObject, 34 | [System.IO.FileInfo]$ExportPath, 35 | [Parameter(ParameterSetName='Graph')] 36 | [ValidateSet('pdf',"png")] 37 | [String]$OutPutFormat, 38 | [Parameter(ParameterSetName='Graph')] 39 | [ValidateSet('dot','circo','hierarchical')] 40 | [String]$LayoutEngine, 41 | [Parameter(ParameterSetName='Graph')] 42 | [Switch]$ShowGraph, 43 | [Parameter(ParameterSetName='Dot')] 44 | [Switch]$AsDot 45 | ) 46 | 47 | begin { 48 | $Results = @() 49 | } 50 | 51 | process { 52 | 53 | Foreach ( $Function in $InputObject ) { 54 | $Results += $Function 55 | } 56 | 57 | } 58 | 59 | end { 60 | 61 | $ExportAttrib = @{ 62 | DestinationPath = If ( $null -eq $PSBoundParameters['ExportPath']) {$pwd.Path+'\'+[system.io.path]::GetRandomFileName().split('.')[0]+'.png'} Else {$PSBoundParameters['ExportPath']} 63 | OutPutFormat = If ( $null -eq $PSBoundParameters['OutPutFormat']) {'png'} Else { $PSBoundParameters['OutPutFormat'] } 64 | LayoutEngine = If ( $null -eq $PSBoundParameters['LayoutEngine']) {'dot'} Else { $PSBoundParameters['LayoutEngine'] } 65 | ShowGraph = If ( $null -eq $PSBoundParameters['ShowGraph']) {$False} Else { $True } 66 | } 67 | 68 | $graph = graph depencies @{rankdir='LR'}{ 69 | Foreach ( $t in $Results ) { 70 | If ( $t.commands.count -gt 0 ) { 71 | node -Name $t.name -Attributes @{Color='red'} 72 | } Else { 73 | node -Name $t.name -Attributes @{Color='green'} 74 | } 75 | 76 | If ( $null -ne $t.commands) { 77 | Foreach($cmdlet in $t.commands ) { 78 | edge -from $t.name -to $cmdlet 79 | } 80 | } 81 | } 82 | } 83 | 84 | Switch ( $PSCmdlet.ParameterSetName ) { 85 | 86 | "Graph" { 87 | $graph | export-PSGraph @ExportAttrib 88 | } 89 | 90 | "Dot" { 91 | If ( $PSBoundParameters['ExportPath'] ) { 92 | Out-File -InputObject $graph -FilePath $PSBoundParameters['ExportPath'] 93 | } Else { 94 | $graph 95 | } 96 | } 97 | } 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /PSFunctionExplorer/PSFunctionExplorer.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LxLeChat/PSFunctionExplorer/88d58a6e34c46c4b70727c71e08968e8febe2d05/PSFunctionExplorer/PSFunctionExplorer.psd1 -------------------------------------------------------------------------------- /PSFunctionExplorer/PSFunctionExplorer.psm1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation.Language 2 | 3 | class FUFunction { 4 | $Name 5 | [System.Collections.ArrayList]$Commands = @() 6 | $Path 7 | hidden $RawFunctionAST 8 | 9 | FUFunction ([System.Management.Automation.Language.FunctionDefinitionAST]$Raw,$Path,[Bool]$TitleCase) { 10 | $this.RawFunctionAST = $Raw 11 | $this.Path = $path 12 | $this.GetCommands($TitleCase) 13 | 14 | If ( $TitleCase ) { 15 | $this.name = [FUUtility]::ToTitleCase($this.RawFunctionAST.name) 16 | } Else { 17 | $this.name = $this.RawFunctionAST.name 18 | } 19 | } 20 | 21 | FUFunction ([System.Management.Automation.Language.FunctionDefinitionAST]$Raw,$ExclusionList,$Path,[Bool]$TitleCase) { 22 | $this.RawFunctionAST = $Raw 23 | $this.Path = $path 24 | $this.GetCommands($ExclusionList,$TitleCase) 25 | 26 | If ( $TitleCase ) { 27 | $this.name = [FUUtility]::ToTitleCase($this.RawFunctionAST.name) 28 | } Else { 29 | $this.name = $this.RawFunctionAST.name 30 | } 31 | } 32 | 33 | hidden GetCommands ([Bool]$TitleCase) { 34 | 35 | $t = $this.RawFunctionAST.findall({$args[0] -is [System.Management.Automation.Language.CommandAst]},$true) 36 | If ( $t.Count -gt 0 ) { 37 | ## si elle existe deja, on ajotue juste à ces commands 38 | ($t.GetCommandName() | Select-Object -Unique).Foreach({ 39 | 40 | If ( $TitleCase ) { 41 | $Command = [FUUtility]::ToTitleCase($_) 42 | } Else { 43 | $Command = $_ 44 | } 45 | 46 | $this.Commands.Add($Command) 47 | }) 48 | } 49 | } 50 | 51 | ## Overload 52 | hidden GetCommands ($ExclusionList,[Bool]$TitleCase) { 53 | 54 | $t = $this.RawFunctionAST.findall({$args[0] -is [System.Management.Automation.Language.CommandAst]},$true) 55 | If ( $t.Count -gt 0 ) { 56 | ($t.GetCommandName() | Select-Object -Unique).Foreach({ 57 | $Command = [FUUtility]::ToTitleCase($_) 58 | If ( $ExclusionList -notcontains $Command) { 59 | If ( $TitleCase ) { 60 | $Command = [FUUtility]::ToTitleCase($_) 61 | } Else { 62 | $Command = $_ 63 | } 64 | $this.Commands.Add($Command) 65 | } 66 | }) 67 | } 68 | } 69 | } 70 | 71 | Class FUUtility { 72 | 73 | ## Static Method to TitleCase 74 | Static [String]ToTitleCase ([string]$String){ 75 | return (Get-Culture).TextInfo.ToTitleCase($String.ToLower()) 76 | } 77 | 78 | ## Static Method to return Function in AST Form, exclude classes 79 | [Object[]] static GetRawASTFunction($Path) { 80 | 81 | $RawFunctions = $null 82 | $ParsedFile = [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$Null) 83 | $RawAstDocument = $ParsedFile.FindAll({$args[0] -is [System.Management.Automation.Language.Ast]}, $true) 84 | 85 | If ( $RawASTDocument.Count -gt 0 ) { 86 | ## source: https://stackoverflow.com/questions/45929043/get-all-functions-in-a-powershell-script/45929412 87 | $RawFunctions = $RawASTDocument.FindAll({$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $($args[0].parent) -isnot [System.Management.Automation.Language.FunctionMemberAst] }) 88 | } 89 | 90 | return $RawFunctions 91 | } 92 | 93 | ## GetFunction, return [FuFunction] 94 | [FUFunction] Static GetFunction($RawASTFunction,$path,$TitleCase){ 95 | return [FUFunction]::New($RawASTFunction,$path,$TitleCase) 96 | } 97 | 98 | ## GetFunctions Overload, with ExclustionList, return [FuFunction] 99 | [FUFunction] Static GetFunction($RawASTFunction,$Exculde,$path,$TitleCase){ 100 | return [FUFunction]::New($RawASTFunction,$Exculde,$path,$TitleCase) 101 | } 102 | 103 | ## SaveTofile in current path 104 | [System.IO.FileSystemInfo] static SaveToFile ([FuFunction]$Function) { 105 | return New-Item -Name $([FUUtility]::FileName($Function.name)) -value $Function.RawFunctionAST.Extent.Text -ItemType File 106 | } 107 | 108 | ## SaveTofile Overload, with Specific path for export 109 | [System.IO.FileSystemInfo] static SaveToFile ([FuFunction]$Function,$Path) { 110 | return New-Item -Path $Path -Name $([FUUtility]::FileName($Function.name)) -value $Function.RawFunctionAST.Extent.Text -ItemType File 111 | } 112 | 113 | ## Construct filename for export 114 | [string] hidden static FileName ($a) { 115 | return "$a.ps1" 116 | } 117 | 118 | } 119 | 120 | Function Expand-FUFile { 121 | <# 122 | .SYNOPSIS 123 | Export a FUFunction to a ps1 file. It's like a reverse build process. 124 | .DESCRIPTION 125 | Export a FUFunction to a ps1 file. It's like a reverse build process. 126 | .EXAMPLE 127 | PS C:\> Find-FUFunction -Path .\PSFunctionExplorer.psm1 | Expand-FUFile 128 | Répertoire : C:\ 129 | 130 | 131 | Mode LastWriteTime Length Name 132 | ---- ------------- ------ ---- 133 | -a---- 30/04/2019 23:24 658 Expand-FUFile.ps1 134 | -a---- 30/04/2019 23:24 3322 Find-Fufunction.ps1 135 | -a---- 30/04/2019 23:24 2925 Write-Fufunctiongraph.ps1 136 | 137 | Find all functions definitions inside PSFunctionExplorer.psm1 and save each function inside it's own ps1 file. 138 | .EXAMPLE 139 | PS C:\> Find-FUFunction -Path .\PSFunctionExplorer.psm1 | Expand-FUFile -Path C:\Temp 140 | Répertoire : C:\Temp 141 | 142 | 143 | Mode LastWriteTime Length Name 144 | ---- ------------- ------ ---- 145 | -a---- 30/04/2019 23:24 658 Expand-FUFile.ps1 146 | -a---- 30/04/2019 23:24 3322 Find-Fufunction.ps1 147 | -a---- 30/04/2019 23:24 2925 Write-Fufunctiongraph.ps1 148 | 149 | Find all functions definitions inside PSFunctionExplorer.psm1 and save each function inside it's own ps1 file, inside the C:\Temp directory. 150 | .INPUTS 151 | [FuFunction] 152 | .OUTPUTS 153 | [System.IO.FileSystemInfo] 154 | .NOTES 155 | #> 156 | 157 | [CmdletBinding()] 158 | param ( 159 | [Parameter(ValueFromPipeline)] 160 | [Object[]]$FUFunction, 161 | 162 | [String]$Path 163 | ) 164 | 165 | begin { 166 | If ( $PSBoundParameters['Path']) { 167 | $item = Get-Item (Resolve-Path -Path $path).Path 168 | } 169 | } 170 | 171 | process { 172 | ForEach( $Function in $FUFunction) { 173 | 174 | If ( $PSBoundParameters['Path']) { 175 | [FUUtility]::SaveToFile($Function,$Item.FullName) 176 | } Else { 177 | [FUUtility]::SaveToFile($Function) 178 | } 179 | 180 | } 181 | } 182 | 183 | end { 184 | } 185 | } 186 | 187 | Function Find-FUFunction { 188 | <# 189 | .SYNOPSIS 190 | Find All Functions declaration inside a ps1/psm1 file and their inner commands. 191 | .DESCRIPTION 192 | Find All Functions declaration inside a ps1/psm1 file. 193 | Return an object describing a powershell function. Output a custom type: FUFunction. 194 | .EXAMPLE 195 | PS C:\> Find-FUFunction .\PSFunctionExplorer.psm1 196 | 197 | Name Commands Path 198 | ---- -------- ---- 199 | Find-Fufunction {Get-Command, Get-Alias, Select-Object, Get-Item...} C:\PSFunctionExplorer.psm1 200 | Write-Fufunctiongraph {Get-Item, Resolve-Path, Find-Fufunction, Graph...} C:\PSFunctionExplorer.psm1 201 | 202 | return all function present in the PSFunctionExplorer.psm1 and every commands present in it. 203 | .EXAMPLE 204 | PS C:\> Find-FUFunction .\PSFunctionExplorer.psm1 -ExcludePSCmdlets 205 | Name Commands Path 206 | ---- -------- ---- 207 | Find-Fufunction {} C:\Users\Lx\GitPerso\PSFunctionUtils\PSFunctionExplorer\PSFunctionExplorer.psm1 208 | Write-Fufunctiongraph {Find-Fufunction, Graph, Node, Edge...} C:\Users\Lx\GitPerso\PSFunctionUtils\PSFunctionExplorer\PSFunctionExplorer.psm1 209 | 210 | Return all function present in the PSFunctionExplorer.psm1 and every commands present in it, but exclude default ps cmdlets. 211 | .INPUTS 212 | Path. Accepts pipeline inputs 213 | .OUTPUTS 214 | A FUFunction custom object 215 | .NOTES 216 | General notes 217 | #> 218 | [CmdletBinding()] 219 | param ( 220 | [Alias("FullName")] 221 | [Parameter(ValueFromPipeline=$True,Position=1,ValueFromPipelineByPropertyName=$True)] 222 | [string[]]$Path, 223 | [Switch]$ExcludePSCmdlets, 224 | [Switch]$NoTitleCase 225 | ) 226 | 227 | begin { 228 | 229 | If ( ! $PSBoundParameters['NoTitleCase'] ) { 230 | $NoTitleCase = $True 231 | } else { 232 | $NoTitleCase = $False 233 | } 234 | 235 | If ( $PSBoundParameters['ExcludePSCmdlets'] ) { 236 | $ToExclude = (Get-Command -Module "Microsoft.PowerShell.Archive","Microsoft.PowerShell.Utility","Microsoft.PowerShell.ODataUtils","Microsoft.PowerShell.Operation.Validation","Microsoft.PowerShell.Management","Microsoft.PowerShell.Core","Microsoft.PowerShell.LocalAccounts","Microsoft.WSMan.Management","Microsoft.PowerShell.Security","Microsoft.PowerShell.Diagnostics","Microsoft.PowerShell.Host").Name 237 | $ToExclude += (Get-Alias | Select-Object -Property Name).name 238 | } 239 | } 240 | 241 | process { 242 | ForEach( $p in $Path) { 243 | $item = get-item (resolve-path -path $p).path 244 | If ( $item -is [system.io.FileInfo] -and $item.Extension -in @('.ps1','.psm1') ) { 245 | Write-Verbose ("[FUFunction]Analyzing {0} ..." -f $item.FullName) 246 | $t = [FUUtility]::GetRawASTFunction($item.FullName) 247 | Foreach ( $RawASTFunction in $t ) { 248 | If ( $PSBoundParameters['ExcludePSCmdlets'] ) { 249 | [FUUtility]::GetFunction($RawASTFunction,$ToExclude,$item.FullName,$NoTitleCase) 250 | } Else { 251 | [FUUtility]::GetFunction($RawASTFunction,$item.FullName,$NoTitleCase) 252 | } 253 | } 254 | } 255 | } 256 | } 257 | 258 | end { 259 | } 260 | } 261 | 262 | Function Write-FUGraph { 263 | <# 264 | .SYNOPSIS 265 | Generate dependecy graph for a function or a set of functions found in a ps1/psm1 file. 266 | .DESCRIPTION 267 | Generate dependecy graph for a function or a set of functions found in a ps1/psm1 file. 268 | .EXAMPLE 269 | PS C:\> $x = Find-FUFunction .\PSFunctionExplorer.psm1 270 | PS C:\> Write-FUGraph -InputObject $x -ExportPath c:\temp\fufuncion.png -outputformat png -ShowGraph 271 | 272 | Répertoire : C:\temp 273 | 274 | Mode LastWriteTime Length Name 275 | ---- ------------- ------ ---- 276 | -a---- 08/09/2019 15:08 71598 fufunction.png 277 | 278 | Will Find all function(s) declarations in the psfunctionexplorer.psm1 file, and create a graph name fufunction.png. Then display it. 279 | .EXAMPLE 280 | PS C:\> Find-FUFunction .\PSFunctionExplorer.psm1 | Write-FUGraph -ExportPath c:\temp\fufuncion.png -outputformat png -ShowGraph 281 | 282 | Will Find all function(s) declarations in the psfunctionexplorer.psm1 file, and create a graph name fufunction.png. Then display it. 283 | .INPUTS 284 | FullName Path. Accepts pipeline inputs. 285 | .OUTPUTS 286 | Outputs Graph, thanks to psgraph module. 287 | .NOTES 288 | First Draft. For the moment the function only output graphviz datas. Soon you ll be able to generate a nice graph as a png, pdf ... 289 | #> 290 | [CmdletBinding()] 291 | param ( 292 | [Alias("FullName")] 293 | [Parameter(ValueFromPipeline=$True)] 294 | [FUFunction[]]$InputObject, 295 | [System.IO.FileInfo]$ExportPath, 296 | [Parameter(ParameterSetName='Graph')] 297 | [ValidateSet('pdf',"png")] 298 | [String]$OutPutFormat, 299 | [Parameter(ParameterSetName='Graph')] 300 | [ValidateSet('dot','circo','hierarchical')] 301 | [String]$LayoutEngine, 302 | [Parameter(ParameterSetName='Graph')] 303 | [Switch]$ShowGraph, 304 | [Parameter(ParameterSetName='Dot')] 305 | [Switch]$AsDot 306 | ) 307 | 308 | begin { 309 | $Results = @() 310 | } 311 | 312 | process { 313 | 314 | Foreach ( $Function in $InputObject ) { 315 | $Results += $Function 316 | } 317 | 318 | } 319 | 320 | end { 321 | 322 | $ExportAttrib = @{ 323 | DestinationPath = If ( $null -eq $PSBoundParameters['ExportPath']) {$pwd.Path+'\'+[system.io.path]::GetRandomFileName().split('.')[0]+'.png'} Else {$PSBoundParameters['ExportPath']} 324 | OutPutFormat = If ( $null -eq $PSBoundParameters['OutPutFormat']) {'png'} Else { $PSBoundParameters['OutPutFormat'] } 325 | LayoutEngine = If ( $null -eq $PSBoundParameters['LayoutEngine']) {'dot'} Else { $PSBoundParameters['LayoutEngine'] } 326 | ShowGraph = If ( $null -eq $PSBoundParameters['ShowGraph']) {$False} Else { $True } 327 | } 328 | 329 | $graph = graph depencies @{rankdir='LR'}{ 330 | Foreach ( $t in $Results ) { 331 | If ( $t.commands.count -gt 0 ) { 332 | node -Name $t.name -Attributes @{Color='red'} 333 | } Else { 334 | node -Name $t.name -Attributes @{Color='green'} 335 | } 336 | 337 | If ( $null -ne $t.commands) { 338 | Foreach($cmdlet in $t.commands ) { 339 | edge -from $t.name -to $cmdlet 340 | } 341 | } 342 | } 343 | } 344 | 345 | Switch ( $PSCmdlet.ParameterSetName ) { 346 | 347 | "Graph" { 348 | $graph | export-PSGraph @ExportAttrib 349 | } 350 | 351 | "Dot" { 352 | If ( $PSBoundParameters['ExportPath'] ) { 353 | Out-File -InputObject $graph -FilePath $PSBoundParameters['ExportPath'] 354 | } Else { 355 | $graph 356 | } 357 | } 358 | } 359 | 360 | } 361 | } -------------------------------------------------------------------------------- /PSFunctionExplorer/tests/PSFunctionExplorer.Tests.Ps1: -------------------------------------------------------------------------------- 1 | using module C:\Users\Lx\GitPerso\PSFunctionUtils\PSFunctionExplorer\PSFunctionExplorer.psm1 2 | 3 | InModuleScope -ModuleName PSFunctionExplorer -ScriptBlock { 4 | 5 | Describe '[FUFunction]-[Constructors]'{ 6 | 7 | $TestCaseFunctions = @' 8 | Function test-funcA { 9 | "plop" 10 | } 11 | 12 | Function test-funcB { 13 | test-funcA 14 | } 15 | 16 | '@ 17 | 18 | 19 | $FunctionScript = Join-Path -Path $Testdrive -ChildPath "fufunction.ps1" 20 | $TestCaseFunctions | Out-File -FilePath $FunctionScript -Force 21 | 22 | It '[FUFunction]-[Constructor]([System.Management.Automation.Language.FunctionDefinitionAST]Raw,Path) should Not Throw' { 23 | 24 | # -- Arrange 25 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 26 | 27 | [System.Management.Automation.Language.FunctionDefinitionAST]$Raw=$ast[0] 28 | 29 | $Path=$FunctionScript 30 | 31 | # -- Assert 32 | 33 | {[FUFunction]::New($Raw,$Path)} | Should Not Throw 34 | 35 | }# end of it block 36 | 37 | 38 | It '[FUFunction]-[Constructor]([System.Management.Automation.Language.FunctionDefinitionAST]Raw,ExclusionList,Path) should Not Throw' { 39 | 40 | # -- Arrange 41 | 42 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 43 | 44 | [System.Management.Automation.Language.FunctionDefinitionAST]$Raw=$Ast[0] 45 | 46 | $ExclusionList=@('Get-ChildItem','Get-Item') 47 | 48 | $Path=$FunctionScript 49 | 50 | # -- Assert 51 | 52 | {[FUFunction]::New($Raw,$ExclusionList,$Path)} | Should Not Throw 53 | 54 | }# end of it block 55 | 56 | }# end of Describe block 57 | 58 | 59 | Describe '[FUFunction] - Test returned Object' { 60 | 61 | $TestCaseFunctions = @' 62 | Function test-funcA { 63 | "plop" 64 | } 65 | 66 | Function test-funcB { 67 | test-funcA 68 | $null = get-item 69 | $null = get-childitem 70 | } 71 | 72 | '@ 73 | 74 | 75 | $FunctionScript = Join-Path -Path $Testdrive -ChildPath "fufunction.ps1" 76 | $TestCaseFunctions | Out-File -FilePath $FunctionScript -Force 77 | 78 | It '[FUFunction]-[Return] should return a function called Test-Funcb' { 79 | 80 | # -- Arrange 81 | 82 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 83 | 84 | [System.Management.Automation.Language.FunctionDefinitionAST]$Raw=$Ast[1] 85 | 86 | $Path=$FunctionScript 87 | 88 | # -- Assert 89 | 90 | ([FUFunction]::New($Raw,$Path)).Name | Should be "Test-FuncB" 91 | 92 | }# end of it block 93 | 94 | It '[FUFunction]-[Return] Test-FuncB Commands Property count should be 3' { 95 | 96 | # -- Arrange 97 | 98 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 99 | 100 | [System.Management.Automation.Language.FunctionDefinitionAST]$Raw=$Ast[1] 101 | 102 | $Path=$FunctionScript 103 | 104 | # -- Assert 105 | 106 | ([FUFunction]::New($Raw,$Path)).Commands.Count | Should be "3" 107 | 108 | }# end of it block 109 | 110 | It '[FUFunction]-[Return] Test-FuncB Commands Property count should be 1 after Exclusion of GCI & Get-Item' { 111 | 112 | # -- Arrange 113 | 114 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 115 | 116 | [System.Management.Automation.Language.FunctionDefinitionAST]$Raw=$Ast[1] 117 | 118 | $Path=$FunctionScript 119 | 120 | $ExclusionList = @('Get-ChildItem','Get-Item') 121 | 122 | # -- Assert 123 | 124 | ([FUFunction]::New($Raw,$ExclusionList,$Path)).Commands.Count | Should be "1" 125 | 126 | }# end of it block 127 | } 128 | Describe '[FUUtility]-[Methods]'{ 129 | 130 | $TestCaseFunctions = @' 131 | Function test-funcA { 132 | "plop" 133 | } 134 | 135 | Function test-funcB { 136 | test-funcA 137 | } 138 | '@ 139 | 140 | 141 | $FunctionScript = Join-Path -Path $Testdrive -ChildPath "fufunction.ps1" 142 | $TestCaseFunctions | Out-File -FilePath $FunctionScript -Force 143 | 144 | #Public Method 145 | It '[FUUtility] --> ToTitleCase($String) : [String] - should Not Throw' { 146 | 147 | # -- Arrange 148 | 149 | [string]$String = 'test-a' 150 | 151 | # -- Assert 152 | 153 | {[FUUtility]::ToTitleCase($String)} | Should Not Throw 154 | 155 | } #End It Block 156 | 157 | #Public Method 158 | It '[FUUtility] --> ToTitleCase($String) : [String] - should return type [String]' { 159 | 160 | # -- Arrange 161 | [string]$String = 'test-a' 162 | 163 | # -- Assert 164 | 165 | ([FUUtility]::ToTitleCase($String)).GetType().Name | should be String 166 | 167 | } #End It Block 168 | 169 | #Public Method 170 | It '[FUUtility] --> GetRawASTFunction($Path) : [Object[]] - should Not Throw' { 171 | 172 | # -- Arrange 173 | 174 | $Path = $FunctionScript 175 | 176 | # -- Assert 177 | 178 | {[FUUtility]::GetRawASTFunction($Path)} | Should Not Throw 179 | 180 | } #End It Block 181 | 182 | #Public Method 183 | It '[FUUtility] --> GetRawASTFunction($Path) : [Object[]] - should return type [Object[]]' { 184 | 185 | # -- Arrange 186 | $Path = $FunctionScript 187 | 188 | # -- Assert 189 | 190 | ([FUUtility]::GetRawASTFunction($Path)).GetType().Name | should be Object[] 191 | 192 | } #End It Block 193 | 194 | #Public Method 195 | It '[FUUtility] --> GetRawASTFunction($Path) : should count 2' { 196 | 197 | # -- Arrange 198 | $Path = $FunctionScript 199 | 200 | # -- Assert 201 | 202 | ([FUUtility]::GetRawASTFunction($Path)).count | should be 2 203 | 204 | } #End It Block 205 | 206 | #Public Method 207 | It '[FUUtility] --> GetFunction($RawASTFunction,$path) : [FUFunction] - should Not Throw' { 208 | 209 | # -- Arrange 210 | 211 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 212 | 213 | $RawASTFunction = $ast[0] 214 | 215 | $path = $FunctionScript 216 | 217 | # -- Assert 218 | 219 | {[FUUtility]::GetFunction($RawASTFunction,$path)} | Should Not Throw 220 | 221 | } #End It Block 222 | 223 | #Public Method 224 | It '[FUUtility] --> GetFunction($RawASTFunction,$path) : [FUFunction] - should return type [FUFunction]' { 225 | 226 | # -- Arrange 227 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 228 | 229 | $RawASTFunction = $ast[0] 230 | 231 | $path = $FunctionScript 232 | 233 | # -- Assert 234 | 235 | ([FUUtility]::GetFunction($RawASTFunction,$path)).GetType().Name | should be FUFunction 236 | 237 | } #End It Block 238 | 239 | #Public Method 240 | It '[FUUtility] --> GetFunction($RawASTFunction,$Exculde,$path) : [FUFunction] - should Not Throw' { 241 | 242 | # -- Arrange 243 | 244 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 245 | 246 | $RawASTFunction = $ast[0] 247 | 248 | $path = $FunctionScript 249 | 250 | $Exclude = @('get-childitem','get-stuff') 251 | 252 | 253 | # -- Assert 254 | 255 | {[FUUtility]::GetFunction($RawASTFunction,$Exclude,$path)} | Should Not Throw 256 | 257 | } #End It Block 258 | 259 | #Public Method 260 | It '[FUUtility] --> GetFunction($RawASTFunction,$Exculde,$path) : [FUFunction] - should return type [FUFunction]' { 261 | 262 | # -- Arrange 263 | $ast = [FUUtility]::GetRawASTFunction($FunctionScript) 264 | 265 | $RawASTFunction = $ast[0] 266 | 267 | $path = $FunctionScript 268 | 269 | $Exclude = @('get-childitem','get-stuff') 270 | 271 | # -- Assert 272 | 273 | ([FUUtility]::GetFunction($RawASTFunction,$Exclude,$path)).GetType().Name | should be FUFunction 274 | 275 | } #End It Block 276 | 277 | }#EndDescribeBlock 278 | 279 | }#End InModuleScope 280 | 281 | 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSFunctionExplorer 2 | A small set of functions to discover function(s) declaration(s), and their inner commands using AST and draw graph dependecy. 3 | The idea being we can explore a set of ps1/psm1 files without loading them.. i want to add some stuff to explorer functions help, and maybe some kind of reverse build .. 4 | 5 | FU* Stands for Function Utility :p 6 | 7 | # How it works 8 | I simply use the AST (Abstract Syntax Tree) to discover ```FunctionDefinitionsTypes``` inside the ps1/psm1 file and ```CommandAst``` types inside each function. 9 | I used classes to write my script... Why ? Cause CLASSES are AWSOME ! 10 | 11 | # Available Functions 12 | ### Find-FUFunction 13 | ```Find-FUFunction``` will help you find all function(s) declaration(s) within ps1/psm1 file(s). For each discovered function, the function will also find every commands within this function. It will output a custom ```FUFunction``` type. 14 | 15 | ``` 16 | PS >Find-FUFunction -Path ..\..\PSClassUtils\PSClassUtils\PSClassUtils.psm1 17 | Name Commands Path 18 | ---- -------- ---- 19 | Convertto-Titlecase {Get-Culture} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 20 | Find-Cuclass {Write-Verbose, Get-Childitem, Get-Cuclass, Where-Object...} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 21 | Get-Cuast {Write-Verbose} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 22 | New-Cugraphexport {Join-Path, Export-Psgraph} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 23 | New-Cugraphparameters {Out-Cupsgraph} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 24 | Out-Cupsgraph {Write-Verbose, Get-Module, Get-Module, Import-Module...} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 25 | Get-Cuclass {Get-Item, Resolve-Path, Get-Cuast, Get-Culoadedclass} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 26 | Get-Cuclassconstructor {Get-Item, Resolve-Path, Get-Cuclass} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 27 | Get-Cuclassmethod {Where-Object, Get-Item, Resolve-Path, Get-Cuclass} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 28 | Get-Cuclassproperty {Get-Cuclass} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 29 | Get-Cucommands {Get-Command} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 30 | Get-Cuenum {Throw, Get-Cuast, ?} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 31 | Get-Culoadedclass {Where-Object, Foreach-Object, Select-Object, Get-Cuast} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 32 | Get-Curaw {Get-Item, Resolve-Path} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 33 | Install-Cudiagramprerequisites {Get-Module, Get-Module, Write-Verbose, Install-Module...} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 34 | Test-Iscustomtype {Where} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 35 | Write-Cuclassdiagram {Test-Path, New-Object, Get-Item, Get-Childitem...} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 36 | Write-Cuinterfaceimplementation {} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 37 | Write-Cupestertest {Gci, Get-Cuclass, Get-Item, Group-Object...} C:\Users\Lx\PSClassUtils\PSClassUtils\PSClassUtils.psm1 38 | 39 | ``` 40 | You have every function declaration discovered in the psclassutils.psm1 file for the magnificient [PSCLASSUTILS](https://github.com/stephanevg/Psclassutils) module and for each function declaration, all its internal commands. 41 | 42 | #### Find-FUFunction Parameters 43 | * ```-Path``` fullpath of a ps1/psm1 file, accept values from the pipeline... 44 | * ```-ExcludePSCmdlets``` switch to exlcude default cmdlts and aliases... 45 | 46 | ### Expand-FUFile 47 | ```Expand-FUFile``` will export all discovered function definitions in it's owner ps1 file. 48 | 49 | ``` 50 | PS C:\> Find-FUFunction -Path .\PSFunctionExplorer.psm1 | Expand-FUFile 51 | Répertoire : C:\ 52 | 53 | 54 | Mode LastWriteTime Length Name 55 | ---- ------------- ------ ---- 56 | -a---- 30/04/2019 23:24 658 Expand-FUFile.ps1 57 | -a---- 30/04/2019 23:24 3322 Find-Fufunction.ps1 58 | -a---- 30/04/2019 23:24 2925 Write-FUGraph.ps1 59 | ``` 60 | 61 | #### Expand-FUFile Parameters 62 | * ```-FUFunction``` FUFunction Object Type.. 63 | * ```-Path``` Export Path, by default, will use the current directory.. 64 | 65 | 66 | ### Write-FUGraph 67 | ```Write-FUGraph``` will draw a graph of dependencies. Just check the [Examples](./Example) ... 68 | You will need the awsome [PSGraph](https://github.com/KevinMarquette/PSGraph) Module!.. 69 | PSFunctionExplore.psm1 file:.. 70 | 71 | ![Graph1](https://github.com/LxLeChat/PSFunctionExplorer/blob/master/Example/module_psfunctionexplorer.png) 72 | 73 | * Red node(s): The function has a dependency, to ..well follow the line :).. 74 | * Green node(s): Standalone function !.. 75 | * Black node(s): External function, in this example: graph, node, edge and export-psgraph are imported function from PSGRAPH... 76 | 77 | #### Write-FUGraph Parameters 78 | * ```-InputObject``` Takes ```FuFunction``` Objects generated with ```Find-FUFunction``` 79 | * ```-ExportPath``` FullName of the export file that will be generated. If not specified, a random filename in the current directory will be used 80 | * ```-OutPutFormat``` File output format available @ the moment ```pdf, png```. Default is ```png``` 81 | * ```-LayoutEngine``` Layout engine used by graphviz to generate the graph. Available @ the moment ```dot, circo, hierarchical```. Default ```dot``` 82 | * ```-ShowGraph``` Display the graph when it's generated 83 | * ```-AsDot``` Display graph data. Can be used on http://www.webgraphviz.com/ or http://viz-js.com/ for example. Works also with Gephi. You need to save the graph data in a dot file. For example i tried it for dbatools ... more thant 700 functions ...! 84 | 85 | # Why i did it 86 | I've tasked to study a huuuuuuge module, with no comment, no documentation etc... It helped me understand how each functions interacts with one another. 87 | 88 | # Inspiration 89 | Thanks to @stephanevg who inspired me to create this module and helped me discover AST and let me work on psclassutils ! 90 | His Github: https://github.com/Stephanevg/ 91 | 92 | [PSHtml](https://github.com/stephanevg/PSHtml) Graph: 93 | 94 | ![Awsome PSHTML](https://github.com/LxLeChat/PSFunctionExplorer/blob/master/Example/module_pshtml2.png) 95 | 96 | Thanks to @ChrisLGardner for his advices! 97 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'ubuntu-latest' 11 | 12 | steps: 13 | - script: echo Hello, world! 14 | displayName: 'Run a one-line script' 15 | 16 | - script: | 17 | echo Add other tasks to build, test, and deploy your project. 18 | echo See https://aka.ms/yaml 19 | displayName: 'Run a multi-line script' 20 | --------------------------------------------------------------------------------