Last active
March 16, 2026 16:38
-
-
Save scriptingstudio/81c74d4a3af59fa917407f568c288494 to your computer and use it in GitHub Desktop.
Simple PowerShell script compressor. The script works like "ConvertTo-Json -Compress".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Removes comments and extra white space from a PS script. | |
| .DESCRIPTION | |
| Omits white space and indented formatting in the output. | |
| .PARAMETER Path | |
| Specifies the path to the PS file to compress. | |
| .PARAMETER ScriptBlock | |
| Specifies the PowerShell scriptblock to compress. | |
| .PARAMETER NoTest | |
| This parameter allows to skip a file path test. Useful for internal cases to avoid multiple checks. | |
| .LINK | |
| http://www.leeholmes.com/blog/2007/11/07/syntax-highlighting-in-powershell/ | |
| #> | |
| function Compress-ScriptBlock { | |
| [CmdletBinding(DefaultParameterSetName = 'File')] | |
| param ( | |
| [Parameter(Position = 0, Mandatory, ParameterSetName = 'File')] | |
| [ValidateNotNullOrEmpty()] | |
| [alias('FilePath','FileName')][String] $Path, | |
| [Parameter(Position = 0, Mandatory, ParameterSetName = 'ScriptBlock')] | |
| [ValidateNotNullOrEmpty()] | |
| [ScriptBlock] $ScriptBlock, | |
| [Parameter(ParameterSetName = 'File')] | |
| [Switch] $NoTest | |
| ) | |
| if ($Path) { | |
| if (-not $NoTest) {if (-not (Test-Path $Path)) {return}} | |
| $ScriptBlockString = [IO.File]::ReadAllText((Resolve-Path $Path).Path) | |
| $ScriptBlock = [ScriptBlock]::Create($ScriptBlockString) | |
| } else { | |
| # Convert the scriptblock to a string so that it can be referenced to extract script lines | |
| $ScriptBlockString = $ScriptBlock.ToString() | |
| } | |
| # Tokenize the scriptblock and return all tokens except for comments | |
| $Tokens = @([System.Management.Automation.PSParser]::Tokenize($ScriptBlock, [Ref] $Null)).Where{$_.Content -match '^#Requires' -or $_.Type -ne 'Comment'} | |
| # Preallocate memory to avoid dynamic allocations | |
| #$capacity = ($Tokens.Length | Measure-Object -Sum).Sum | |
| $strBuilder = [System.Text.StringBuilder]::new($ScriptBlockString.Length) | |
| $CurrentColumn = 1 | |
| $newLine = $false | |
| $prevToken = $null | |
| $joinLine = $false # indicates to join the previous line | |
| $i = 0 # token index to look ahead | |
| $null = foreach ($CurrentToken in $Tokens) { | |
| if ($CurrentToken.Type -match '^NewLine|^LineContinuation') { | |
| $CurrentColumn = 1 | |
| # Only insert a single newline. Sequential newlines are ignored in order to save space | |
| if (-not $newLine) { | |
| # experimental; TODO | |
| if ($prevToken.Content -match '[\{\(]$') {} | |
| elseif ($prevToken.Type -eq 'groupEnd') {$joinLine = $true} # skip extra newline | |
| # needs to know the next token's type; if groupEnd, skip extra newline | |
| elseif ($Tokens[$i+1].Type -eq 'groupEnd') {} | |
| else {$strBuilder.AppendLine()} | |
| $newLine = $true | |
| } | |
| } else { | |
| $newLine = $false | |
| # Do any indenting | |
| if ($CurrentColumn -lt $CurrentToken.StartColumn) { | |
| # Insert a single space in between tokens on the same line. | |
| # Extraneous whiltespace is ignored. | |
| if ($CurrentColumn -ne 1) { | |
| $strBuilder.Append(' ') | |
| } | |
| } | |
| # Insert newline inside token group | |
| # experimental; TODO | |
| if ($joinLine -and $prevToken.Type -eq 'NewLine' -and $CurrentToken.Type -ne 'groupEnd') { | |
| #if ($Tokens[$i+1].Type -eq 'NewLine') {$strBuilder.Append(';')} else {$strBuilder.AppendLine()} | |
| $strBuilder.AppendLine() | |
| $joinLine = $false | |
| } | |
| # Handle multi-line strings | |
| $tokenBody = $ScriptBlockString.Substring($CurrentToken.Start,$CurrentToken.Length) | |
| if ($CurrentToken.Type -eq 'String' -and $CurrentToken.EndLine -gt $CurrentToken.StartLine) { | |
| $strBuilder.Append($tokenBody.replace("`r`n","")) | |
| } else { # Write out a regular token | |
| $strBuilder.Append($tokenBody) | |
| } | |
| # Update our position in the column | |
| $CurrentColumn = $CurrentToken.EndColumn | |
| } # token type | |
| $prevToken = $CurrentToken | |
| $i++ | |
| } # token iterator | |
| $strBuilder.ToString() #.replace("`r`n`r`n","") | |
| } # END Compress-ScriptBlock |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment