public/formatting/Format-SpectreJson.ps1

using module "..\..\private\completions\Completers.psm1"
using module "..\..\private\completions\Transformers.psm1"

function Format-SpectreJson {
    <#
    .SYNOPSIS
    Formats an array of objects into a Spectre Console Json.
 
    .DESCRIPTION
    This function takes an objects and converts them into syntax highlighted Json using the Spectre Console Json Library.
    Thanks to [trackd](https://github.com/trackd) for adding this!
    See https://spectreconsole.net/widgets/json for more information.
 
    .PARAMETER Data
    The array of objects to be formatted into Json.
 
    .PARAMETER Depth
    The maximum depth of the Json. Default is defined by the version of powershell.
 
    .PARAMETER JsonStyle
    A hashtable of Spectre Console color names and values to style the Json output.
    e.g.
    ```
    @{
        MemberStyle = "Yellow"
        BracesStyle = "Red"
        BracketsStyle = "Orange1"
        ColonStyle = "White"
        CommaStyle = "White"
        StringStyle = "White"
        NumberStyle = "Red"
        BooleanStyle = "LightSkyBlue1"
        NullStyle = "Gray"
    }
    ```
 
    .PARAMETER Border
    :::caution
    This parameter is deprecated and will be removed in the future.
    It takes no effect from version 2.0.
    To add a border wrap this object in a panel, use the `$data | Format-SpectreJson | Format-SpectrePanel` function.
    :::
 
    .PARAMETER Title
    :::caution
    This parameter is deprecated and will be removed in the future.
    It takes no effect from version 2.0.
    To add a border title to a panel, use the `$data | Format-SpectreJson | Format-SpectrePanel -Header "title"` function.
    :::
 
    .PARAMETER NoBorder
    :::caution
    This parameter is deprecated and will be removed in the future.
    It takes no effect from version 2.0.
    :::
 
    .EXAMPLE
    $data = @(
        [pscustomobject]@{
            Name = "John"
            Age = 25
            City = "New York"
            IsEmployed = $true
            Salary = 10
            Hobbies = @("Reading", "Swimming")
            Address = @{
                Street = "123 Main St"
                ZipCode = $null
            }
        }
    )
    Format-SpectreJson -Data $data
    #>

    [Reflection.AssemblyMetadata("title", "Format-SpectreJson")]
    [Alias('fsj')]
    param(
        [Parameter(ValueFromPipeline, Mandatory)]
        [object] $Data,
        [int] $Depth,
        [ValidateSpectreColorTheme()]
        [ColorThemeTransformationAttribute()]
        [hashtable] $JsonStyle = @{
            MemberStyle    = $script:AccentColor
            BracesStyle    = [Spectre.Console.Color]::Cyan1
            BracketsStyle  = [Spectre.Console.Color]::Orange1
            ColonStyle     = $script:AccentColor
            CommaStyle     = $script:AccentColor
            StringStyle    = [Spectre.Console.Color]::White
            NumberStyle    = [Spectre.Console.Color]::Cyan1
            BooleanStyle   = [Spectre.Console.Color]::LightSkyBlue1
            NullStyle      = $script:DefaultValueColor
        },
        [string] $Border,
        [string] $Title,
        [switch] $NoBorder
    )
    begin {

        if ($Border -or $Title -or $NoBorder) {
            Write-Warning "Format-SpectrePanel: The parameters Border, Title, and NoBorder are deprecated and will be removed in the future."
        }

        $requiredJsonStyleKeys = @('MemberStyle', 'BracesStyle', 'BracketsStyle', 'ColonStyle', 'CommaStyle', 'StringStyle', 'NumberStyle', 'BooleanStyle', 'NullStyle')
        if (($requiredJsonStyleKeys | ForEach-Object { $JsonStyle.Keys -contains $_ }) -contains $false) {
            throw "JsonStyle must contain the following keys: $($requiredJsonStyleKeys -join ', ')"
        }

        $collector = [System.Collections.Generic.List[psobject]]::new()
        $splat = @{
            WarningAction = 'Ignore'
            ErrorAction   = 'Stop'
        }
        if ($Depth) {
            $splat.Depth = $Depth
        }
        $ht = [ordered]@{}
    }
    process {
        if ($MyInvocation.ExpectingInput) {
            if ($data -is [string]) {
                if ($data.pschildname) {
                    if (-Not $ht.contains($data.pschildname)) {
                        $ht[$data.pschildname] = [System.Text.StringBuilder]::new()
                    }
                    return [void]$ht[$data.pschildname].AppendLine($data)
                }
                # assume we get the entire json in one go a string (e.g -Raw or invoke-webrequest)
                try {
                    $jsonObjects = $data | Out-String | ConvertFrom-Json -AsHashtable @splat
                    return $collector.add($jsonObjects)
                } catch {
                    Write-Debug "Failed to convert string to object, $_"
                }
            }
            if ($data -is [System.IO.FileSystemInfo]) {
                if ($data.Extension -eq '.json') {
                    Write-Debug "json file found, reading $($data.FullName)"
                    try {
                        $jsonObjects = Get-Content -Raw $data.FullName | ConvertFrom-Json -AsHashtable @splat
                        return $collector.add($jsonObjects)
                    } catch {
                        Write-Debug "Failed to convert json to object, $_"
                    }
                    
                }
                return $collector.add(
                    [pscustomobject]@{
                        Name     = $data.Name
                        FullName = $data.FullName
                        Type     = $data.GetType().Name.TrimEnd('Info')
                    })
            }
            Write-Debug "adding item from pipeline"
            return $collector.add($data)
        }
        foreach ($item in $data) {
            Write-Debug "adding item from input"
            $collector.add($item)
        }
    }
    end {
        if ($ht.keys.count -gt 0) {
            foreach ($key in $ht.Keys) {
                Write-Debug "converting json stream to object, $key"
                try {
                    $jsonObject = $ht[$key].ToString() | Out-String | ConvertFrom-Json -AsHashtable @splat
                    $collector.add($jsonObject)
                    continue
                } catch {
                    Write-Debug "Failed to convert json to object: $key, $_"
                }
            }
        }
        if ($collector.Count -eq 0) {
            return
        }
        try {
            $json = [Spectre.Console.Json.JsonText]::new(($collector | ConvertTo-Json @splat))
        } catch {
            Write-Error "Failed to convert to json, $_"
            return
        }

        $json.MemberStyle = $JsonStyle.MemberStyle
        $json.BracesStyle = $JsonStyle.BracesStyle
        $json.BracketsStyle = $JsonStyle.BracketsStyle
        $json.ColonStyle = $JsonStyle.ColonStyle
        $json.CommaStyle = $JsonStyle.CommaStyle
        $json.StringStyle = $JsonStyle.StringStyle
        $json.NumberStyle = $JsonStyle.NumberStyle
        $json.BooleanStyle = $JsonStyle.BooleanStyle
        $json.NullStyle = $JsonStyle.NullStyle

        return $json
    }
}