Public/utils/ConvertTo-Markdown.ps1
|
#Requires -Version 5.1 function ConvertTo-Markdown { <# .SYNOPSIS Converts PowerShell objects into a GitHub-Flavored Markdown table .DESCRIPTION Converts pipeline input or explicitly supplied objects into one complete GitHub-Flavored Markdown table. Column and row order are deterministic: explicit properties retain the requested order, while automatic columns follow first-seen property order and append properties found later. Pipe characters are escaped, line breaks become <br>, arrays are joined with comma-space separators, and optional Markdown decorations can be emitted before or after the table. .PARAMETER InputObject One or more PowerShell objects to convert. Accepts input from the pipeline and preserves the order in which objects arrive. .PARAMETER Property Optional property names to render. The supplied order is authoritative; property names are matched case-insensitively and missing values are empty. .PARAMETER Title Optional level-one Markdown heading emitted before the table. .PARAMETER PreContent Optional Markdown lines emitted before the table, one element per line. .PARAMETER PostContent Optional Markdown lines emitted after the table, one element per line. .EXAMPLE Get-ScheduledTaskFailure | ConvertTo-Markdown Converts scheduled task failure objects into a Markdown table. .EXAMPLE Get-Service | ConvertTo-Markdown -Property Status, Name, DisplayName Renders the selected properties in the exact order supplied. .EXAMPLE 'SRV01', 'SRV02' | ConvertTo-Markdown -Title 'Targets' Converts pipeline string values into a Markdown table with a Value column. .OUTPUTS System.String One complete Markdown document containing the requested decorations and table. .NOTES Author: Franck SALLET Version: 1.0.0 Last Modified: 2026-09-05 Requires: PowerShell 5.1+ / Windows only Permissions: None required Module: PSWinOps .LINK https://github.com/k9fr4n/PSWinOps .LINK https://github.github.com/gfm/#tables-extension- #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true)] [PSObject[]]$InputObject, [Parameter(Mandatory = $false)] [string[]]$Property, [Parameter(Mandatory = $false)] [AllowEmptyString()] [string]$Title, [Parameter(Mandatory = $false)] [AllowEmptyCollection()] [string[]]$PreContent, [Parameter(Mandatory = $false)] [AllowEmptyCollection()] [string[]]$PostContent ) begin { $rows = [System.Collections.Generic.List[object]]::new() } process { if ($PSBoundParameters.ContainsKey('InputObject')) { foreach ($item in @($InputObject)) { [void]$rows.Add($item) } } } end { $lines = [System.Collections.Generic.List[string]]::new() if ($PSBoundParameters.ContainsKey('Title')) { [void]$lines.Add("# $Title") } if ($PSBoundParameters.ContainsKey('PreContent')) { foreach ($line in $PreContent) { [void]$lines.Add($line) } } if ($rows.Count -gt 0) { $allRowsAreScalar = $true foreach ($row in $rows) { if ($null -ne $row -and $row -isnot [string] -and $row -isnot [System.ValueType]) { $allRowsAreScalar = $false break } } $columns = [System.Collections.Generic.List[string]]::new() if ($null -ne $Property -and $Property.Count -gt 0) { foreach ($propertyName in $Property) { [void]$columns.Add($propertyName) } } elseif ($allRowsAreScalar) { [void]$columns.Add('Value') } else { $knownColumns = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::OrdinalIgnoreCase ) foreach ($row in $rows) { if ($null -eq $row -or $row -is [string] -or $row -is [System.ValueType]) { if ($knownColumns.Add('Value')) { [void]$columns.Add('Value') } continue } foreach ($propertyInfo in $row.PSObject.Properties) { if ($knownColumns.Add($propertyInfo.Name)) { [void]$columns.Add($propertyInfo.Name) } } } } $escapeCell = { param([object]$Value) if ($null -eq $Value) { return '' } if ($Value -is [System.Array]) { $Value = $Value -join ', ' } $text = [string]$Value $text = $text -replace '\|', '\|' $text = $text -replace "`r`n|`r|`n", '<br>' return $text } $headerCells = foreach ($column in $columns) { & $escapeCell $column } [void]$lines.Add("| $($headerCells -join ' | ') |") $separatorCells = foreach ($column in $columns) { '---' } [void]$lines.Add("| $($separatorCells -join ' | ') |") foreach ($row in $rows) { $cells = foreach ($column in $columns) { $value = $null $found = $false if ($column -ieq 'Value' -and ($null -eq $row -or $row -is [string] -or $row -is [System.ValueType])) { $value = $row $found = $true } elseif ($null -ne $row -and $row -isnot [string] -and $row -isnot [System.ValueType]) { foreach ($propertyInfo in $row.PSObject.Properties) { if ($propertyInfo.Name -ieq $column) { $value = $propertyInfo.Value $found = $true break } } } if ($found) { & $escapeCell $value } else { '' } } [void]$lines.Add("| $($cells -join ' | ') |") } } if ($PSBoundParameters.ContainsKey('PostContent')) { foreach ($line in $PostContent) { [void]$lines.Add($line) } } $lines -join "`n" } } |