public/Resolve-DynamicFunctionDefinition.ps1

$code = @'
using System;
using System.Collections.Generic;
using System.Management.Automation;

    public class DynamicAttribute : System.Attribute
    {
        private ScriptBlock sb;

        public DynamicAttribute(ScriptBlock condition)
        {
            sb = condition;
        }
    }
'@


$null = Add-Type -TypeDefinition $code *>&1

function Resolve-DynamicFunctionDefinition {
    <#
        .SYNOPSIS
            Generates a scriptblock defining an interpreted function based on the provided function info

        .DESCRIPTION
            Module adds a new attribute named [Dynamic()] that can be used to turn static parameters into dynamic parameters.

            The attribute definition will be defined above a standard parameter definition and must contain a scriptblock that
            evaluates truthiness. Resolve-DynamicFunctionDefinition will then interpret the [Dynamic(...)] attributes into standard
            PowerShell dynamic parameter declarations and return back a scriptblock with the new function definition.

            Example Dynamic Parameter Declaration:

                [Dynamic({$OtherParameter -match "(Value1|Value2)"})]
                [Parameter(Mandatory)]
                [string]$Name

        .INPUTS
            System.Management.Automation.FunctionInfo

        .OUTPUTS
            scriptblock

        .EXAMPLE
            Resolve-DynamicFunctionDefinition -FunctionInfo Value

            Evaluates function definition for dynamic parameters and returns a new scriptblock containing
            a function definition to properly handle the defined dynamic parameters

        .NOTES
            Inspired in large part by Dr. Tobias Weltner (https://github.com/TobiasPSP/) and his amazing work at https://powershell.one/
    #>


    [CmdletBinding()]
    param
    (
        # FunctionInfo object for the function to evaluate and process for parameters with the [Dynamic()] attribute
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.FunctionInfo]$FunctionInfo,

        # Forces functions missing the [CmdletBinding()] attribute, to be interpreted as advanced functions
        [Parameter()]
        [switch]$Force
    )

    process {
        try {
            # This is the object that we will use to build up the new function definition
            $result = New-Object -TypeName System.Text.StringBuilder

            # Store dynamic parameter default values
            $defaultValues = @{}

            # Store list of dynamic parameters
            $dynParamList = [System.Collections.ObjectModel.Collection[string]]::new()

            # Store list of static parameters
            $paramList = [System.Collections.ObjectModel.Collection[string]]::new()

            # Store list of non-pipeline bound parameters
            $standardParamList = [System.Collections.ObjectModel.Collection[string]]::new()

            $commentBasedHelpString = Get-DynamicFunctionCommentBasedHelp -FunctionInfo $FunctionInfo
            if ($commentBasedHelpString) {
                $null = $result.AppendLine($commentBasedHelpString)
            }

            # If function does not contain cmdlet binding, return the original definition
            if (Assert-DynamicFunctionIsAdvanced -FunctionInfo $FunctionInfo) {
                $null = $result.AppendLine("function $($FunctionInfo.Name) {")
            } else {
                if (-not $Force) {
                    Write-Warning "$($FunctionInfo.Name) is not an advanced function, using original function content"
                    return Resolve-DynamicSimpleFunction -FunctionInfo $FunctionInfo
                } else {
                    $null = $result.AppendLine("function $($FunctionInfo.Name) {")
                    $null = $result.AppendLine(" [CmdletBinding()]")
                }
            }

            $paramBlockAst = Get-DynamicFunctionParamBlockContent -FunctionInfo $FunctionInfo

            Add-DynamicFunctionAttribute -StringBuilder $result -ParamBlockAst $paramBlockAst

            # Add a placeholder that will be replaced after processing for [Dynamic()] tagged parameters
            Add-DynamicFunctionParamBlockPlaceholder -StringBuilder $result

            $dynamicParameters = $paramBlockAst.Parameters | Get-DynamicFunctionParameter -Type 'dynamic'
            $staticParameters = $paramBlockAst.Parameters | Get-DynamicFunctionParameter -Type 'static'

            if ($dynamicParameters) {
                $dynamicParameters | Add-DynamicFunctionDynamicParamBlock -StringBuilder $result -DefaultValueTable $defaultValues
                foreach ($dynamicParameter in $dynamicParameters) {
                    $parameterName = $dynamicParameter.Name.VariablePath.UserPath
                    $dynParamList.Add($parameterName)
                }
                $pipelineParamList = $dynamicParameters | Get-DynamicFunctionPipelineParameterName
            }

            if ($staticParameters) {
                foreach ($staticParameter in $staticParameters) {
                    $staticParameterCommentHelp = Get-DynamicFunctionParameterCommentHelp -ParameterAst $staticParameter -FunctionInfo $FunctionInfo

                    $staticParamStringBuilder = New-Object -TypeName System.Text.StringBuilder

                    if ($staticParameterCommentHelp) {
                        $null = $staticParamStringBuilder.AppendLine($staticParameterCommentHelp)
                        $null = $staticParamStringBuilder.Append(" " + $staticParameter.Extent.Text)
                    } else {
                        $null = $staticParamStringBuilder.Append($staticParameter.Extent.Text)
                    }


                    $paramList.Add($staticParamStringBuilder.ToString())
                }
            }

            $dynParamList | ForEach-Object {
                $dynParam = $_
                if ($dynParam -notin $pipelineParamList) {
                    $null = $standardParamList.Add($dynParam)
                }
            }

            $beginBlockContent = Get-DynamicFunctionScriptBlockContent -Name begin -FunctionInfo $FunctionInfo
            $needsBeginBlock = $dynParamList.Count -or -not [string]::IsNullOrWhiteSpace($beginBlockContent)
            if ($needsBeginBlock) {
                Add-DynamicFunctionScriptBlock -StringBuilder $result -Name begin -Content $beginBlockContent -Parameter $dynParamList -DefaultValueTable $defaultValues
            }

            $processBlockContent = Get-DynamicFunctionScriptBlockContent -Name process -FunctionInfo $FunctionInfo
            $needsProcessBlock = -not [string]::IsNullOrWhiteSpace($processBlockContent) -or $pipelineParamList.Count
            if ($needsProcessBlock) {
                Add-DynamicFunctionScriptBlock -StringBuilder $result -Name process -Content $processBlockContent -Parameter $pipelineParamList
            }

            $endBlockContent = Get-DynamicFunctionScriptBlockContent -Name end -FunctionInfo $FunctionInfo
            if (-not [string]::IsNullOrWhiteSpace($endBlockContent)) {
                Add-DynamicFunctionScriptBlock -StringBuilder $result -Name end -Content $endBlockContent
            }

            # Add the function block's closing brace
            $null = $result.Append('}')

            Set-DynamicFunctionParamBlockPlaceholder -StringBuilder $result -ParameterList $paramList

            Test-DynamicFunctionDefinition -Definition $result
        } catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
}