20 Oct 2012
In the previous post we
described how to use YUI Compressor to minify your CSS and JavaScript when deploying
your application using Unfold. The next day,
Andrew Wooldridge pointed me to a blog post called
State of YUI Compressor.
YUI Compressor will be going through a deprecation process as the YUI team moves
to using a custom-wrapped UglifyJS for JavaScript compression and a more fully
updated and maintained version of CSSMin for CSS compression. This will allow
YUI to move beyond the limitations of the current YUI Compressor as well as take
advantage of the current state of the art in compression.
The new compression utilities are wrapped into a single node package called yuglify
and new issues can be filed against this project on GitHub.
So, let's convert the minify
task we wrote to use yuglify
instead of the soon to be deprecated YUI Compressor.
The steps
- We added the
yui-compressor.jar
to a tools folder in our scm. We can now remove that, we
will depend on yuglify being available on the PATH
- yuglify is a node.js package, so to install it on our system we need to install
node.js. Note that it needs to be available on the target machine of
our deployment because that's where the minification will happen. (unless you're using the
local build option, more on that later)
Open a command prompt and install yuglify through npm
npm install -g yuglify
Now we can alter the task to use yuglify:
task minify {
# Invoke-Script, so this gets executed on the target
Invoke-Script {
# Define a function, that can minify a folder that's under web
function Minify($folder, $type) {
Write-Host "Minifying folder $folder" -Fore Green
If(-not $type) {
$type = $folder
}
# get all files inside the web folder and minify them
Foreach($file in (Get-ChildItem "$($config.releasepath)\web\$folder")) {
Write-Host "Minifying $file"
# Wrap inside psake exec function so the build
# fails if this operation fails
Exec {
# get the file's content and pass it to yuglify
Get-Content $file.FullName `
| yuglify --terminal --type $type `
--output $file.FullName
}
}
}
# Now call the function for each folder we want to minimize
Minify "js"
Minify "css"
}
}
Set-AfterTask release minify
That's it
We're now ready for the future, using yuglify instead of the deprecated YUI Compressor.
11 Oct 2012
Up until now we've mostly discussed the inner workings of
Unfold and which default tasks
come out-of-the-box. This works for standard applications, but most of the time you'll want more.
For the biggest part you can simply rely on Unfold itself: it will get your application from source control, build it, setup
IIS, etc. But let's say your site has a bunch of CSS and JavaScript files that you want to minimize when you're deploying to
production. I know there are libraries out there
that do the work for you, but in this case you just want to minimize your files beforehand using YUI Compressor.
A word on hooks
Unfold comes with support for task hooks. This concept allows you to attach tasks you create onto the default task flow. In
our previous post we explained the several tasks that compose the deployment operation: setup,
updatecode, build, release, setupapppool, ...
If you want to extend the operations that happen during a given task, then what you have to do is pretty simple
- define a psake task that contains these extra operations
- indicate that you want to execute the task before or after another task. This is done using the
Set-BeforeTask
and
Set-AfterTask
functions.
Back to the minification
Now that you know how to extend the deployment, you can start adding minification to it.
- We'll create a psake task called
minify
that will perform the minification
- We're going to attach it to the
release
task. As described in the previous post the release
task moves your compiled
application into a separate folder that we later use for pointing IIS at.
Minifying the js and css that's inside is a natural extension to it.
So let's start. Add this to your deploy.ps1
file:
# 1. create the task
task minify {
# minification goes here
}
# 2. attach it execute after the release task
Set-AfterTask release minify
There is also a Set-BeforeTask
which performs the opposite operation: it mark a task to be executed before another task
kicks off.
Performing the minification
UPDATE 2012/10/20
YUI Compressor will be
deprecated, the following blog post describes how to use yuglify instead of YUI Compressor.
To perform the minification we're using YUI Compressor. To use it, simply download
the latest version from their GitHub downloads page for example. Extract it
and add the final jar
file somewhere to your source repository for example under tools\yui\yuicompressor-2.4.2.jar
.
By doing so, we're sure that it is available when we're performing the minification because we always release based on what's in
source control.
Now that the tooling is in place, we can write the minify task. Here goes:
task minify {
# Invoke-Script, so this gets executed on the target
Invoke-Script {
# Define a function, that can minify a folder that's under web
function Minify($folder) {
Write-Host "Minifying folder $folder" -Fore Green
# get all files inside the web folder and minify them
Foreach($file in (Get-ChildItem "$($config.releasepath)\web\$folder")) {
Write-Host "Minifying $file"
# Wrap inside psake exec function so the build
# fails if this operation fails
Exec {
# the code folder will contain the current code checkout, and
# that's where the jar will be. We use it to compress the
# found file
java -jar ".\code\tools\yui\yuicompressor-2.4.2.jar" `
--charset utf8 `
-o "$($file.FullName)" `
"$($file.FullName)"
}
}
}
# Now call the function for each folder we want to minimize
Minify "js"
Minify "css"
}
Only applying it on production
As the task is defined now, it will always perform the minification, but as we said before, we only want this on production.
That way we can still easily debug problems on our dev environment for example. What we're going to do is simple: we'll
change the configuration. Move to the configuration part of the deploy.ps1
file and add the following
# Normally we don't minimize
Set-Config minimize $false
# On production however, we enable it
Set-Environment production {
... other configuration settings ...
Set-Config minimize $true
}
Now that these settings are available we can extend the minify
task just a little bit more. If the $config.minimize
setting
is set to $false
, we simply abort the task.
task minify {
If(-not $config.minimize) {
return
}
Invoke-Script {
... rest of task
And there you go, the minification is only applied on production.
Conclusion
After this task has executed, all css and javascript files within your web folder will be minimized. Just what we need. By
hooking the minify
task up with the release
task we can extend the standard flow and tailor it to perform completely
custom deployment operations. We've also explained how you can define custom configuration options and use them inside your
tasks.