CLI.ps1

<#
.SYNOPSIS
    PugPS - A Pug to HTML converter for PowerShell and Pode.
     
.DESCRIPTION
    Copyright (c) 2026 Nabil Redmann
    Licensed under the MIT License.
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files.
#>


Function Invoke-PUG {
    [CmdletBinding(DefaultParameterSetName='Path')]
    param(
        [Parameter(ParameterSetName='Path', Mandatory=$true, Position=0, HelpMessage="The path to the Pug template file.")]
        [string]$Path,

        [Parameter(ParameterSetName='Content', Mandatory=$true, ValueFromPipeline=$true, HelpMessage="The raw Pug template content.")]
        [string[]]$InputContent,

        [Parameter(ParameterSetName='Path', Mandatory=$false, Position=1, HelpMessage="The data to be passed to the Pug template as `$data. Can be a hashtable. Default is an empty hashtable.")]
        [Parameter(ParameterSetName='Content', Mandatory=$false, Position=0, HelpMessage="The data to be passed to the Pug template as `$data. Can be a hashtable. Default is an empty hashtable.")]
        [hashtable]$Data = @{},

        [Parameter(Mandatory=$false, HelpMessage="The path to the filters file (ps1) to be imported or a scriptblock with the filter functions.")]
        [AllowNull()]
        $Filters = $Null,

        [Parameter(Mandatory=$false, HelpMessage="The default file extension to use for included files if not specified.")]
        [string]$Extension = 'pug',

        [Parameter(Mandatory=$false, HelpMessage="The root directory used to resolve absolute include/extend paths (those starting with / or \). If empty, absolute paths are resolved relative to the current file.")]
        [string]$BaseDir = "",

        [Parameter(Mandatory=$false, HelpMessage="When true (default), boolean attributes are rendered as 'attr'. When false, they are rendered as 'attr=''attr'''.")]
        [bool]$Properties = $true,

        [Parameter(Mandatory=$false, HelpMessage="When true, standard void tags (like img, br) are rendered with a self-closing slash (e.g., <img />). Default is false.")]
        [bool]$VoidTagsSelfClosing = $false,

        [Parameter(Mandatory=$false, HelpMessage="When true, empty container tags (like div, span) with no content or children are rendered as self-closing (e.g., <div />). Default is false.")]
        [bool]$ContainerTagsSelfClosing = $false,

        [Parameter(Mandatory=$false, HelpMessage="When true, CamelCase in PUG is converted to kebab-case. Default is true.")]
        [bool]$KebabCaseHTML = $true,

        [Parameter(Mandatory=$false, HelpMessage="Number of context lines to show before and after the error line.")]
        [int]$ErrorContextRange = 2
    )

    begin {
        $accumulatedContent = New-Object System.Collections.Generic.List[string]
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'Content') {
            if ($InputContent) {
                $accumulatedContent.AddRange($InputContent)
            }
        }
    }

    end {
        . $PSScriptRoot\parser.ps1

        try {

            if ([string]::IsNullOrWhiteSpace($Filters)) {
                # param not used.
            }
            # 1. Import filter from scriptblock
            elseif (($Filters).getType().Name -eq 'ScriptBlock') {
                . $Filters
            }
            # 1. Import filters file if it exists
            elseif (($Filters).getType().Name -eq 'String' -and (Test-Path (Join-Path $PWD $Filters) -PathType Leaf)) {
                . (Join-Path $PWD $Filters)

                #Import-PodeModule -Path $Filters # .\helpers\helper.ps1
            }
            else {
                $exFn = New-Object System.Exception("Filters not found: " + $Filters)
                throw $exFn
            }

            if ($PSCmdlet.ParameterSetName -eq 'Path') {
                $psCode = Convert-PugToPowerShell `
                    -Path $path `
                    -Extension $Extension `
                    -BaseDir $BaseDir `
                    -Properties $Properties `
                    -VoidTagsSelfClosing $VoidTagsSelfClosing `
                    -ContainerTagsSelfClosing $ContainerTagsSelfClosing `
                    -KebabCaseHTML $KebabCaseHTML `
                    -ErrorContextRange $ErrorContextRange
            }
            else {
                $psCode = $accumulatedContent | Convert-PugToPowerShell `
                    -Extension $Extension `
                    -BaseDir $BaseDir `
                    -Properties $Properties `
                    -VoidTagsSelfClosing $VoidTagsSelfClosing `
                    -ContainerTagsSelfClosing $ContainerTagsSelfClosing `
                    -KebabCaseHTML $KebabCaseHTML `
                    -ErrorContextRange $ErrorContextRange
            }


            $sb = [scriptblock]::Create($psCode)

            # 3. Execute, passing $Data into the scope
            $html = (& $sb $Data)

            return $html

        } catch {
            $ex = $_.Exception
            $niceMsg = ""

            # Check if this is a Parser-thrown error (already has custom data fields)
            if ($ex.Data.Contains('Line')) {
                # Just re-use the formatted message if it was a New-PugError
                $niceMsg = $ex.Message
            } 
            # Check if this is a Runtime-thrown error (caught inside the generated script)
            elseif ($ex.Data.Contains('PugLine') -and $ex.Data['PugLine'] -gt 0) {
                $runtimeLine = $ex.Data['PugLine']
                $runtimePath = $ex.Data['PugPath']
                
                # Use the parser helper to generate the nice message
                $niceMsg = Get-PugErrorContext `
                    -Path $runtimePath `
                    -LineNumber $runtimeLine `
                    -Detail $ex.Message `
                    -ContextRange $ErrorContextRange
            }
            else {
                # Fallback for errors that didn't get tracked
                $niceMsg = "An unexpected error occurred:`n$($ex.Message)`n$($ex.StackTrace)"
            }
            
            return $niceMsg
        }
    }
}

#Export-ModuleMember -Function Invoke-PUG