feat: improve parse script

This commit is contained in:
itsscb 2025-04-23 22:21:46 +02:00
parent a3471ab94b
commit c62e9815ac

View File

@ -1,110 +1,160 @@
[cmdletbinding()]
Param(
[Parameter(
Mandatory= $true
)]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$Path
param (
[Parameter(Mandatory)]
[ValidateScript({
Test-Path $_ -PathType Leaf
})]
[string]$Path
)
begin
{
$system_types = @('Parameter','ValidatePattern','ValidateScript','ValidateSet')
}
process
{
$script_path_raw = Get-Item $Path
process {
Add-Type -AssemblyName System.Management.Automation
$script_path_raw = Get-Item $Path
$script_name = $script_path_raw.Name
$script_path = $script_path_raw | Select-Object -ExpandProperty FullName
$tokens = $null
$errors = $null
$ast = [System.Management.Automation.Language.Parser]::ParseFile($script_path, [ref]$tokens, [ref]$errors)
$parse_errors = @()
$AST = [System.Management.Automation.Language.Parser]::ParseFile($script_path, [ref]$null, [ref]$parse_errors)
if($null -eq $AST.ParamBlock)
{
throw "No 'Param(...)' block found: $($Path)"
}
$parameters = @()
foreach($param in $AST.ParamBlock.Parameters)
{
$param_name = $param.Name.Extent.Text
$param_type = $null
$param_mandatory = $false
$param_parameters = @()
$param_validations = @()
foreach($attr in $param.Attributes)
{
if($system_types -contains $attr.TypeName.Name)
{
switch -Wildcard ($attr.TypeName.Name)
{
"Parameter"
{
foreach($named_argument in $attr.NamedArguments)
{
if($named_argument.ArgumentName -eq 'Mandatory')
{
$param_mandatory = $named_argument.Argument.Extent.Text -eq '$true'
} else
{
$param_parameters += [pscustomobject]@{
name = $named_argument.ArgumentName
value = $named_argument.Argument.Extent.Text
}
}
}
}
"Validate*"
{
foreach($positional_argument in $attr.PositionalArguments)
{
if($positional_argument.ScriptBlock)
{
$value = $positional_argument.ScriptBlock.Extent.Text
} else
{
$value = $positional_argument.Value
}
$param_validations +=
[pscustomobject]@{
name = $attr.TypeName.Name
value = $value
}
}
}
}
} else
{
if($attr.Extent.Text -like '`[*`]')
{
if($param_type -ne $null)
{
Write-Warning "Multiple Types found for Parameter: $($attr.TypeName.Name) ($($Path))"
continue
}
$param_type = $attr.TypeName.Name
}
}
}
$parameters += [pscustomobject]@{
name = $param_name
type = $param_type
mandatory = $param_mandatory
arguments = $param_parameters
validations = $param_validations
}
}
return [pscustomobject]@{
name = $script_name
path = $script_path
parameters = $parameters
}
if ($errors.Count -gt 0) {
throw "Parse errors found: $($errors | ForEach-Object { $_.Message })"
}
$paramBlock = $ast.ParamBlock
if (-not $paramBlock) {
throw "No param() block found at the script level."
}
$cmdletBindingAttr = $paramBlock.Attributes | Where-Object { $_.TypeName.Name -eq 'CmdletBinding' }
$defaultParamSet = $cmdletBindingAttr.NamedArguments |
Where-Object { $_.ArgumentName -eq 'DefaultParameterSetName' } |
Select-Object -ExpandProperty Argument |
Select-Object -ExpandProperty Value
$parameters = @()
foreach ($param in $paramBlock.Parameters) {
$paramName = $param.Name.VariablePath.UserPath
# Fix the type name
$staticType = $param.StaticType
if ($staticType.IsGenericType -and $staticType.Name -eq 'Nullable`1') {
$innerType = $staticType.GenericTypeArguments[0].Name
$paramType = "Nullable[$innerType]"
} else {
$paramType = $staticType.Name
}
$aliases = @()
$validations = @()
$parameterSets = @()
$description = $null
$helpMessage = $null
foreach ($attr in $param.Attributes) {
switch ($attr.TypeName.Name) {
'Parameter' {
$mandatory = $false
$parametersetname = 'Default'
$position = $null
$frompipeline = $null
$frompipeline_by_property = $null
$helpmessage = $null
foreach ($arg in $attr.NamedArguments) {
switch ($arg.ArgumentName) {
'Mandatory' {
$mandatory = $arg.Argument.Extent.Text -eq '$true'
}
'Position' {
$position = $arg.Argument.Value
}
'ParameterSetName' {
$parametersetname = $arg.Argument.Value
}
'ValueFromPipeline' {
$frompipeline = $arg.Argument.Extent.Text -eq '$true'
}
'ValueFromPipelineByPropertyName' {
$frompipeline_by_property = $arg.Argument.Extent.Text -eq '$true'
}
'HelpMessage' {
$helpMessage = $arg.Argument.Value
}
}
}
$paramInfo = @{
name = $parametersetname
mandatory = $mandatory
}
if($null -ne $position) {
$paramInfo.position = $position
}
if($null -ne $frompipeline) {
$paramInfo.valueFromPipeline = $frompipeline
}
if($null -ne $frompipeline_by_property) {
$paramInfo.valueFromPipelineByPropertyName = $frompipeline_by_property
}
$parameterSets += [pscustomobject]$paramInfo
break
}
'Alias' {
$aliases += $attr.PositionalArguments.Value
break
}
{ $_ -like 'Validate*' } {
$val = @{
type = $attr.TypeName.Name
}
if ($attr.TypeName.Name -eq 'ValidateRange') {
$val.min = $attr.PositionalArguments[0].Value
$val.max = $attr.PositionalArguments[1].Value
} elseif ($attr.TypeName.Name -eq 'ValidateSet') {
$val.values = $attr.PositionalArguments.Value
} elseif ($attr.TypeName.Name -eq 'ValidateScript') {
$val.script = $attr.PositionalArguments.ScriptBlock.Extent.Text
}
$validations += [pscustomobject]$val
break
}
}
}
if ($parameterSets.Count -eq 0) {
$parameterSets += [pscustomobject]@{
name = "Default"
mandatory = $false
}
}
$parameters += [pscustomobject]@{
name = $paramName
type = $paramType
aliases = $aliases
description = $description
helpMessage = $helpMessage
validations = $validations
parameterSets = $parameterSets
}
}
$result = [pscustomobject]@{
name = $script_name
path = $script_path
parameters = $parameters
cmdletBinding = [pscustomobject]@{
defaultParameterSetName = $defaultParamSet ?? "Default"
}
}
return $result
}