Private/Report/ConvertTo-TcReportHtml.ps1
|
function ConvertTo-TcReportHtml { <# .SYNOPSIS Assembles the single-file report from the template, the fonts and the data. .DESCRIPTION The template is cut at its markers first, then each piece is added in turn, so nothing inside a value can ever be read as a marker or as a replacement pattern. The policy of the page names the script by its hash: only this script runs, even if a value got past the escaping. HTML turns CRLF into LF before a browser hashes a script, so the script is hashed in that form. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)][AllowNull()]$Data, [Parameter(Mandatory)][AllowNull()]$Tables ) $template = Join-Path $PSScriptRoot 'Template' $utf8 = [System.Text.UTF8Encoding]::new($false) $read = { param([string]$Name) [System.IO.File]::ReadAllText((Join-Path $template $Name), $utf8).TrimStart([char]0xFEFF).Replace("`r`n", "`n") } $font = { param([string]$Name) [Convert]::ToBase64String([System.IO.File]::ReadAllBytes((Join-Path $PSScriptRoot 'Fonts' $Name))) } $styles = (& $read 'styles.css').Replace('__TEXT_FONT__', (& $font 'instrument-sans.woff2')).Replace('__CODE_FONT__', (& $font 'jetbrains-mono.woff2')) $code = @('core.js', 'tracking.js', 'views-summary.js', 'views-tenant.js', 'views-solutions.js', 'views-analysis.js', 'views-steering.js', 'startup.js' | ForEach-Object { & $read $_ }) -join "`n" $hash = [Convert]::ToBase64String([System.Security.Cryptography.SHA256]::HashData($utf8.GetBytes($code))) $parts = @{ '/*__STYLES__*/' = $styles '/*__SCRIPT_HASH__*/' = $hash '<!--__ICONS__-->' = & $read 'icons.svg' '/*__DATA__*/' = ConvertTo-TcReportJson -InputObject $Data '/*__TABLES__*/' = ConvertTo-TcReportJson -InputObject $Tables '/*__SCRIPT__*/' = $code } $page = [System.Text.StringBuilder]::new() foreach ($piece in [regex]::Split((& $read 'index.html'), '(/\*__[A-Z_]+__\*/|<!--__[A-Z_]+__-->)')) { [void]$page.Append($(if ($parts.ContainsKey($piece)) { $parts[$piece] } else { $piece })) } $page.ToString() } |