Transpilers/Include.psx.ps1

<#
.SYNOPSIS
    Includes Files
.DESCRIPTION
    Includes Files or Functions into a Script.
.Example
    {
        [Include("Invoke-PipeScript")]$null
    } | .>PipeScript
.Example
    {
        [Include("Invoke-PipeScript")]
        param()
    } | .>PipeScript
.EXAMPLE
    {
        [Include('*-*.ps1')]$psScriptRoot
    } | .>PipeScript
#>

[ValidateScript({
    if ($_ -is [Management.Automation.Language.CommandAst]) {
        return $_.CommandsElements[0].Value -in 'include','includes'
    }
})]
[Alias('Includes')]
param(
# The File Path to Include
[Parameter(Mandatory,Position=0)]
[string]
$FilePath,

# If set, will include the content as a byte array
[switch]
$AsByte,

# If set, will pass thru the included item
[switch]
$Passthru,

# The exclusion pattern to use.
[Alias('ExcludePattern')]
[string[]]
$Exclude = '\.[^\.]+\.ps1$',

[Parameter(Mandatory,ParameterSetName='VariableAST', ValueFromPipeline)]
[Management.Automation.Language.VariableExpressionast]
$VariableAst,


[Parameter(Mandatory,ParameterSetName='CommandAst',ValueFromPipeline)]
[Management.Automation.Language.CommandAst]
$CommandAst
)

process {

    if ($psCmdlet.ParameterSetName -eq 'CommandAst') {
        # Gather some information about our calling context
        $myParams = [Ordered]@{} + $PSBoundParameters
        # and attempt to parse it as a sentance (only allowing it to match this particular command)
        $mySentence = $commandAst.AsSentence($MyInvocation.MyCommand)
        $myCmd = $MyInvocation.MyCommand

        # Walk thru all mapped parameters in the sentence
        foreach ($paramName in $mySentence.Parameters.Keys) {
            if (-not $myParams[$paramName]) { # If the parameter was not directly supplied
                $myParams[$paramName] = $mySentence.Parameters[$paramName] # grab it from the sentence.
                foreach ($myParam in $myCmd.Parameters.Values) {
                    if ($myParam.Aliases -contains $paramName) { # set any variables that share the name of an alias
                        $ExecutionContext.SessionState.PSVariable.Set($myParam.Name, $mySentence.Parameters[$paramName])
                    }
                }
                # and set this variable for this value.
                $ExecutionContext.SessionState.PSVariable.Set($paramName, $mySentence.Parameters[$paramName])
            }
        }
    }

# Determine the command we would be including (relative to the current path)
$includingCommand = $ExecutionContext.SessionState.InvokeCommand.GetCommand($FilePath, 'All')
if (-not $includingCommand) { # if we could not determine the command, we may need to error out.
    if (-not $FilePath.Contains('*')) {
        Write-Error "Could not resolve $($FilePath)"
        return
    }
}

function IncludeFileContents {
    param($FilePath)
    process {
    if ($AsByte) {
        [ScriptBlock]::Create(
        "@'" + [Environment]::NewLine + 
        [Convert]::ToBase64String(
            [IO.File]::ReadAllBytes($FilePath),
            'InsertLineBreaks'
        ) + [Environment]::NewLine + 
        "'@")
    
    } else {
        [ScriptBlock]::Create(
        "@'" + 
            [Environment]::NewLine + 
            ([IO.File]::ReadAllLines($FilePath) -replace "^@'", "@''" -replace "^'@", "''@" -join [Environment]::NewLine) +
            [Environment]::NewLine + 
        "'@" + @'
-split "[\r\n]{1,2}" -replace "^@''", "@'" -replace "^''@", "'@" -join [Environment]::NewLine
'@

        )    
    }
    }
}

$includedScript = 
    if ($includingCommand -is [Management.Automation.CmdletInfo]) {
        Write-Error "Cannot Include Cmdlets"
        return
    }
    elseif ($includingCommand -is [Management.Automation.FunctionInfo]) {
        if ($VariableAst -and $VariableAst.VariablePath -notmatch '^null$') {
            # If we're including a function as a variable, define it as a ScriptBlock
[ScriptBlock]::Create(@"
{
    $($includingCommand.ScriptBlock)
}
"@
)
        } else {
            # If we're including a function, define it inline
        [ScriptBlock]::Create(@"
function $($includingCommand.Name) {
    $($includingCommand.ScriptBlock)
}$(
if ($Passthru) {
    [Environment]::NewLine +
        "`$executionContext.SessionState.InvokeCommand.GetCommand(`"$($includingCommand.Name)`",'Function')"
})
"@
)
        }        
    } elseif ($includingCommand.ScriptBlock) {
        # If we're including a command with a ScriptBlock, assign it to a variable
        [ScriptBlock]::Create(@"
`${$($includingCommand.Name)} = {
    $($includingCommand.ScriptBlock)
}$(
if ($Passthru) { [Environment]::NewLine + "`${$($includingCommand.Name)}"}
)
"@
)
        
    } 
    elseif ($includingCommand.Source -match '\.ps1{0,}\.(?<ext>[^.]+$)') {
        $transpiledFile = Invoke-PipeScript -CommandInfo $includingCommand
        if (-not $transpiledFile) {
            Write-Error "Could not transpile $($includingCommand.Source)"
            return
        }
        IncludeFileContents $transpiledFile.Fullname
    }
    elseif ($includingCommand.Source -match '\.ps$') {
        [ScriptBlock]::Create(@"
`${$($includingCommand.Name)} = {
    $([ScriptBlock]::Create([IO.File]::ReadAllText($includingCommand.Source)) | .>PipeScript)
}$(
if ($Passthru) { [Environment]::NewLine + "`${$($includingCommand.Name)}"}
)
"@
)
        
    } elseif ($includingCommand) {
        IncludeFileContents $includingCommand.Source    
    }

if ($psCmdlet.ParameterSetName -eq 'ScriptBlock' -or 
    $VariableAst.VariablePath -match '^null$') {
    if ($ScriptBlock -and $ScriptBlock.ToString().Length) {
        [ScriptBlock]::Create("$ScriptBlock" + [Environment]::NewLine + $includedScript)
    } else {
        $includedScript
    }
    
} elseif ($VariableAst.VariablePath -and $includingCommand) {    
    [ScriptBlock]::Create("$($VariableAst) = $IncludedScript")
} elseif ($VariableAst.VariablePath -notmatch '^null$') {
    [ScriptBlock]::Create(@"
:ToIncludeFiles foreach (`$file in (Get-ChildItem -Path "$($VariableAst)" -Filter "$FilePath" -Recurse)) {
    if (`$file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1
    foreach (`$exclusion in '$($Exclude -replace "'","''" -join "','")') {
        if (-not `$exclusion) { continue }
        if (`$file.Name -match `$exclusion) {
            continue ToIncludeFiles # Skip excluded files
        }
    }
    . `$file.FullName$(
    if ($Passthru) { [Environment]::NewLine + (' ' * 4) + '$file'}
    )
}
"@
)
}

}