Automatic JavaScript Compression with PowerShell


I'm looking to write a script that will take a bunch of .js files, compress them, and then replace the old ones with the new ones in that same folder. I've tried a few things but I find myself continuously encountering new problems one way or another, so I thought it'd be best to turn to the people who have a better understanding that I do and start fresh.

Can anyone point me in the right direction?

Update: I am using a set of commands similar to this:

>Get-ChildItem c:\NewFolder\ -recurse |
&java -jar yuicompressor-2.4.6

It does not seem like it wants to allow these sort of output usage though. I'm sure there is a way to make this work, but being fairly new still to PowerShell, I'm not too confident I can figure it out on my own just yet.

Update: Using the suggested command string below, I can get powershell to give me what seems to be a read out of a newly compressed .js but it won't replace the existing file with the compressed one or write it to the standard out which i believe to be in the same directory in [filename].min.js format.

Update: A modified version of the suggested command seems to do the trick!

>Get-ChildItem c:\NewFolder\ -exclude jquery*.js,*min.js -recurse | %{java -jar yuicompressor-2.4.6.jar ($_.fullname) -o ($_.fullname)}

However, when the command runs in PowerShell, oddly enough, I am getting an error message from PowerShell regarding the Java command...

java.exe : At line:4 char:72 + Get-ChildItem c:\Scripts\ -exclude jquery*.js,*min.js -recurse | %{java <<<< -jar yuicompressor-2.4.6.jar ($.fullname) -o ($.fullname)} + CategoryInfo : NotSpecified: (:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError Usage: java -jar yuicompressor-x.y.z.jar [options] [input file]

Global Options -h, --help Displays this information --type Specifies the type of the input file --charset Read the input file using --line-break Insert a line break after the specified column number -v, --verbose Display informational messages and warnings -o Place the output into . Defaults to stdout. Multiple files can be processed using the following syntax: java -jar yuicompressor.jar -o '.css$:-min.css' *.css java -jar yuicompressor.jar -o '.js$:-min.js' *.js

JavaScript Options nomunge Minify only, do not obfuscate preserve-semi Preserve all semicolons disable-optimizations Disable all micro optimizations

If no input file is specified, it defaults to stdin. In this case, the 'type' option is required. Otherwise, the 'type' option is required only if the input file extension is neither 'js' nor 'css'.

Any idea what PowerShell is trying to tell me?

Try doing like this:

Get-ChildItem c:\NewFolder\ -recurse | %{java -jar yuicompressor-x.y.z.jar $_.fullname}

%{..} is alias for foreach-object. You get a set of files from c:\Newfolder ( and its subdirs) and pass each of these, as objects to the next component in the pipeline. This part being an external component that supports neither pipelining nor the objects, you wrap it in the foreach and also supply the file in the form it can understand - the fullname of the file ( which includes the path).


This thread might offer you some answers. What do you use to minimize and compress JavaScript libraries?

That said, I believe the YUI compressor has a stand alone executable that could be launched from PowerShell.


So, I thought I'd finally give something back to this community. I should start by saying I never used Powershell before and really dislike it. I'm glad that bash is now shipped with windows. But, I had a little fun project for a friend that runs on win server and needed to write something to run after a code deploy of his .net app to his ec2 instance. I know of about a dozen better ways to do this using real build tools, but...sometimes a script is what you want. Hope you guys find it useful. It requires you to install java and have the closure jar; you can try with other minification tools.

I need to give credit to Chase Florell in this post for getting me started in the right direction: How to version javascript and css files as part of a powershell build process?

##################################################################
# minimizeJS.ps1                                                 #
# This file removes all non-minified javascript resources        #
# and then updates all html and aspx pages to point to the       #
# minified js if they are not already.                           #
#                                                                #
# Version 1.0                                                    #
# Usage: minimizeJS.ps1 -debug true|false                        #
##################################################################
param($debug=$true)  #you can remove the =$true to force a user to specify
$maxFiles = Get-ChildItem -Path ./* -Include *.js -Exclude *.min.js, *.min.comp.js -Recurse 
$filesContainingResourceRefs = Get-ChildItem -Path ./* -Include *.html, *.aspx -Recurse 
$fileCollection = New-Object System.Collections.ArrayList

$closureJAR = 'C:\closure\compiler.jar'
$javaLocation = 'C:\Program Files\Java\jre1.8.0_131\bin\java.exe'

#Make sure debug flag is set one way or the other
if (!$debug){
  Write-Host "Debug has not been set.  Please use the -debug true|false argument."
  Exit
}elseif ($debug -eq $true){
  Write-host "Running with debug mode set to $debug."
}elseif ($debug -eq $false){
  Write-host "Running wiht debug mode set to $debug."
}else{
  Write-host "Debug has not been set properly.  Please use the -debug true|false argument."
  Exit
}

#First find everything we have a minified js version of, create an object of their names and paths, and delete the non-min file
Write-Host "Beginning minification of JS files.  Debug is $debug"
foreach ($file in $maxFiles)
    {
        $fileOld = $file.FullName
        $fileNew = $file.FullName.Replace(".js", ".min.js")
        if ($debug -eq $true){
          #Write-Host java -jar $closureJAR --js $fileOld --js_output_file $fileNew
          Write-Host "  ArgList is:  -jar  $closureJAR --js $fileOld --js_output_file $fileNew"
        }else{
            Write-Host "  Minifying: $fileOld"
            Start-Process -FilePath $javaLocation `
             -ArgumentList "-jar $closureJAR --js $fileOld --js_output_file $fileNew" `
             -RedirectStandardOutput '.\console.out' -RedirectStandardError '.\console.err'
        }
    }
Write-Host "End minification of JS files"


#Second find everything we have a minified js version of, create an object of their names and paths, and delete the non-min file
Write-Host "Beginning Removal of files...will display below"
$minFiles = Get-ChildItem -Path ./* -Filter *.min.js -Recurse 
foreach ($file in $minFiles)
    {
        #if ($file.FullName.Replace(".min.js", ".js") exists) {
        $private:nonMinifiedVersionFull = $file.FullName -replace ".min.js", ".js" #.ToString().Replace(".min.js", ".js")
        $private:nonMinifiedVersion = $file -Replace ".min.js", ".js" #.ToString().Replace(".min.js", ".js")
        Write-Host "  Removing: " $private:nonMinifiedVersion
        if ($debug -eq $false) {Remove-Item $private:nonMinifiedVersionFull -ErrorAction SilentlyContinue}

    $temp = New-Object System.Object
    $temp | Add-Member -MemberType NoteProperty -Name "minFileName" -Value $file.ToString()
    $temp | Add-Member -MemberType NoteProperty -Name "minFileFullName" -Value $file.FullName.ToString()
    $temp | Add-Member -MemberType NoteProperty -Name "maxFileName" -Value $private:nonMinifiedVersion.ToString()
    $temp | Add-Member -MemberType NoteProperty -Name "maxFileFullName" -Value $private:nonMinifiedVersionFull.ToString()
    $fileCollection.Add($temp) | Out-Null
    }
Write-Host "End Removal of Files"

if ($debug -eq $true) {
   Write-Host "The fileCollection is:"
   $fileCollection
   }

Write-Host "Beginning update of references to point to minified"
#now go through all the files that could reference them and update the references
foreach ($file2 in $filesContainingResourceRefs)
    {
        $private:file = $file2.FullName

            $fixedContent = [System.IO.File]::ReadAllText($private:file)

            #Now loop through all the min and max files in the collection
           foreach ($line in $fileCollection) {

                    $strFind    = $line.maxFileName.ToString()
                    $strReplace = $line.minFileName.ToString()

                    $fixedContent = $fixedContent.replace($strFind, $strReplace)

            }            
            if ($debug -eq $false) {[System.IO.File]::WriteAllText($private:file, $fixedContent)}


           Write-Host "  Replaced non-minified references in: " $private:file

    }

Write-Host "End update of references to point to minified"