AST.psm1
[CmdletBinding()] param() $baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) $script:PSModuleInfo = Test-ModuleManifest -Path "$PSScriptRoot\$baseName.psd1" $script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } $scriptName = $script:PSModuleInfo.Name Write-Debug "[$scriptName] - Importing module" #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [Core] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - Processing folder" #region [functions] - [public] - [Core] - [Get-FunctionAST] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-FunctionAST] - Importing" function Get-FunctionAST { <# .SYNOPSIS Retrieves the abstract syntax tree (AST) of function definitions from a PowerShell script. .DESCRIPTION Parses a specified PowerShell script file and extracts function definitions matching the given name. By default, it returns all function definitions if no specific name is provided. .EXAMPLE Get-FunctionAST -Path "C:\Scripts\MyScript.ps1" Retrieves all function definitions from "MyScript.ps1". .EXAMPLE Get-FunctionAST -Name "Get-Data" -Path "C:\Scripts\MyScript.ps1" Retrieves only the function definition named "Get-Data" from "MyScript.ps1". .LINK https://psmodule.io/AST/Functions/Core/Get-FunctionAST/ #> [CmdletBinding()] param ( # The name of the function to search for. Defaults to all functions ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. [Parameter(Mandatory)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string] $Path ) begin {} process { # Parse the script file into an AST $ast = Get-ScriptAST -Path $Path # Extract function definitions $ast.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Where-Object { $_.Name -like $Name } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-FunctionAST] - Done" #endregion [functions] - [public] - [Core] - [Get-FunctionAST] #region [functions] - [public] - [Core] - [Get-ScriptAST] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-ScriptAST] - Importing" function Get-ScriptAST { <# .SYNOPSIS Parses a PowerShell script or script file and returns its Abstract Syntax Tree (AST). .DESCRIPTION This function parses a PowerShell script from a file or a string input and returns its AST. It can optionally provide token and error information. .EXAMPLE Get-ScriptAST -Path "C:\Scripts\MyScript.ps1" Parses the PowerShell script at "MyScript.ps1" and returns its AST. .EXAMPLE Get-ScriptAST -Script "Get-Process | Select-Object Name" Parses the provided PowerShell script string and returns its AST. .EXAMPLE Get-ScriptAST -Path "C:\Scripts\MyScript.ps1" -Tokens ([ref]$tokens) -Errors ([ref]$errors) Parses the script file while also capturing tokens and errors. .LINK https://psmodule.io/AST/Functions/Core/Get-ScriptAST/ #> [outputType([System.Management.Automation.Language.ScriptBlockAst])] [CmdletBinding()] param ( # The path to the PowerShell script file to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] # Validate using Test-Path [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string] $Path, # The PowerShell script to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Script' )] [string] $Script, # Reference variable to store parsed tokens. [Parameter()] [ref] $Tokens, # Reference variable to store parsing errors. [Parameter()] [ref] $Errors ) begin {} process { switch ($PSCmdlet.ParameterSetName) { 'Path' { [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$Tokens, [ref]$Errors) } 'Script' { [System.Management.Automation.Language.Parser]::ParseInput($Script, [ref]$Tokens, [ref]$Errors) } } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Core] - [Get-ScriptAST] - Done" #endregion [functions] - [public] - [Core] - [Get-ScriptAST] Write-Debug "[$scriptName] - [functions] - [public] - [Core] - Done" #endregion [functions] - [public] - [Core] #region [functions] - [public] - [Functions] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - Processing folder" #region [functions] - [public] - [Functions] - [Get-FunctionAlias] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-FunctionAlias] - Importing" function Get-FunctionAlias { <# .SYNOPSIS Retrieves function aliases from a PowerShell script file. .DESCRIPTION Parses a specified PowerShell script file to identify function definitions and extract their associated aliases. Returns a custom object containing function names and their corresponding aliases. .EXAMPLE Get-FunctionAlias -Path "C:\Scripts\MyScript.ps1" Retrieves all function aliases defined in the specified script file. .EXAMPLE Get-FunctionAlias -Name "Get-Data" -Path "C:\Scripts\MyScript.ps1" Retrieves the alias information for the function named "Get-Data" from the specified script file. .LINK https://psmodule.io/AST/Functions/Functions/Get-FunctionAlias/ #> [CmdletBinding()] param ( # The name of the function to search for. Defaults to all functions ('*'). [Parameter()] [string] $Name = '*', # The path to the PowerShell script file to be parsed. [Parameter(Mandatory)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string] $Path ) # Extract function definitions $functions = Get-FunctionAST -Path $Path # Process each function and extract aliases $functions | ForEach-Object { $funcName = $_.Name $attributes = $_.FindAll({ $args[0] -is [System.Management.Automation.Language.AttributeAst] }, $true) $aliasAttr = $attributes | Where-Object { $_ -is [System.Management.Automation.Language.AttributeAst] -and $_.TypeName.Name -eq 'Alias' } if ($aliasAttr) { $aliases = $aliasAttr.PositionalArguments | ForEach-Object { $_.ToString().Trim('"', "'") } [PSCustomObject]@{ Name = $funcName Alias = $aliases } } } | Where-Object { $_.Name -like $Name } } Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-FunctionAlias] - Done" #endregion [functions] - [public] - [Functions] - [Get-FunctionAlias] #region [functions] - [public] - [Functions] - [Get-FunctionName] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-FunctionName] - Importing" function Get-FunctionName { <# .SYNOPSIS Extracts function names from a specified PowerShell script. .DESCRIPTION Parses the given PowerShell script file and retrieves all function names defined within it. This function utilizes the PowerShell Abstract Syntax Tree (AST) to analyze the script and extract function definitions. .EXAMPLE Get-FunctionName -Path "C:\Scripts\MyScript.ps1" Retrieves all function names defined in the specified script file. .NOTES Uses PowerShell's AST to analyze script structure. .LINK https://psmodule.io/AST/Functions/Functions/Get-FunctionName/ #> [CmdletBinding()] param ( # The path to the script file to parse [Parameter(Mandatory)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string] $Path ) # Extract function definitions $functions = Get-FunctionAST -Path $Path # Process each function and extract the name $functions | ForEach-Object { $_.Name } } Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-FunctionName] - Done" #endregion [functions] - [public] - [Functions] - [Get-FunctionName] #region [functions] - [public] - [Functions] - [Get-FunctionType] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-FunctionType] - Importing" function Get-FunctionType { <# .SYNOPSIS Extracts function types from a specified PowerShell script. .DESCRIPTION Parses the given PowerShell script file and retrieves all function types defined within it. This function utilizes the PowerShell Abstract Syntax Tree (AST) to analyze the script and extract function definitions. .EXAMPLE Get-FunctionType -Path "C:\Scripts\MyScript.ps1" Retrieves all function types defined in the specified script file. .LINK https://psmodule.io/AST/Functions/Functions/Get-FunctionType/ #> [OutputType([pscustomobject])] [CmdletBinding()] param( # The path to the PowerShell script file to be parsed. [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Path' )] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string] $Path ) begin {} process { $functionAST = Get-FunctionAST -Path $Path $functionAST | ForEach-Object { $type = $_.IsFilter ? 'Filter' : $_.IsWorkflow ? 'Workflow' : $_.IsConfiguration ? 'Configuration' : 'Function' [pscustomobject]@{ Name = $_.Name Type = $type } } } end {} } Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - [Get-FunctionType] - Done" #endregion [functions] - [public] - [Functions] - [Get-FunctionType] Write-Debug "[$scriptName] - [functions] - [public] - [Functions] - Done" #endregion [functions] - [public] - [Functions] #region [functions] - [public] - [Lines] Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - Processing folder" #region [functions] - [public] - [Lines] - [Get-LineComment] Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - [Get-LineComment] - Importing" filter Get-LineComment { <# .SYNOPSIS Extracts the inline comment from a single line of PowerShell code. .DESCRIPTION Parses a given line of PowerShell code and extracts any inline comment. If an inline comment exists, the function returns the comment text; otherwise, it returns nothing. .EXAMPLE 'Get-Process # This retrieves all processes' | Get-LineComment Returns: '# This retrieves all processes' .EXAMPLE 'Write-Host "Hello World"' | Get-LineComment Returns: $null (no comment found) .LINK https://psmodule.io/AST/Functions/Lines/Get-LineComment/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Does not change state' )] [OutputType([string])] [CmdletBinding()] param ( # Input line of PowerShell code from which to extract the comment. [Parameter( Mandatory, ValueFromPipeline )] [string] $Line ) # Parse the line using the PowerShell parser to obtain its tokens. $tokens = $null $null = [System.Management.Automation.Language.Parser]::ParseInput($Line, [ref]$tokens, [ref]$null) # Find comment token(s) in the line. ($tokens | Where-Object { $_.Kind -eq 'Comment' }).Extent.Text } Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - [Get-LineComment] - Done" #endregion [functions] - [public] - [Lines] - [Get-LineComment] Write-Debug "[$scriptName] - [functions] - [public] - [Lines] - Done" #endregion [functions] - [public] - [Lines] #region [functions] - [public] - [Scripts] Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - Processing folder" #region [functions] - [public] - [Scripts] - [Get-ScriptCommand] Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - [Get-ScriptCommand] - Importing" function Get-ScriptCommand { <# .SYNOPSIS Retrieves the commands used within a specified PowerShell script. .DESCRIPTION Analyzes a given PowerShell script and extracts all command invocations. Optionally includes call operators (& and .) in the results. Returns details such as command name, position, and file reference. .EXAMPLE Get-ScriptCommand -Path "C:\Scripts\example.ps1" Extracts and lists all commands found in the specified PowerShell script. .EXAMPLE Get-ScriptCommand -Path "C:\Scripts\example.ps1" -IncludeCallOperators Extracts all commands, including those executed with call operators (& and .). .LINK https://psmodule.io/AST/Functions/Scripts/Get-ScriptCommand/ #> [Alias('Get-ScriptCommands')] [CmdletBinding()] param ( # The path to the PowerShell script file to be parsed. [Parameter(Mandatory)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string] $Path, # Include call operators in the results, i.e. & and . [Parameter()] [switch] $IncludeCallOperators ) # Extract function definitions $ast = Get-ScriptAST -Path $Path # Gather CommandAsts $commandAST = $ast.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true) if (-not $IncludeCallOperators) { $commandAST = $commandAST | Where-Object { $_.InvocationOperator -notin 'Ampersand', 'Dot' } } $commandAST | ForEach-Object { $invocationOperator = switch ($_.InvocationOperator) { 'Ampersand' { '&' } 'Dot' { '.' } } $_.CommandElements[0].Extent | ForEach-Object { [pscustomobject]@{ Name = [string]::IsNullOrEmpty($invocationOperator) ? $_.Text : $invocationOperator StartLineNumber = $_.StartLineNumber StartColumnNumber = $_.StartColumnNumber EndLineNumber = $_.EndLineNumber EndColumnNumber = $_.EndColumnNumber File = $_.File } } } } Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - [Get-ScriptCommand] - Done" #endregion [functions] - [public] - [Scripts] - [Get-ScriptCommand] Write-Debug "[$scriptName] - [functions] - [public] - [Scripts] - Done" #endregion [functions] - [public] - [Scripts] Write-Debug "[$scriptName] - [functions] - [public] - Done" #endregion [functions] - [public] #region Member exporter $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Get-FunctionAST' 'Get-ScriptAST' 'Get-FunctionAlias' 'Get-FunctionName' 'Get-FunctionType' 'Get-LineComment' 'Get-ScriptCommand' ) } Export-ModuleMember @exports #endregion Member exporter |