Saturday, January 26, 2008

Introducing YUI-Compressor Ant task

UPDATE April 08, 2009: There's a new version of the ant task: yui-compressor-ant-task-0.4, thanks to Brian Riddle, available here: http://code.google.com/p/javaflight-code/downloads/list. It will work correctly with YUI-Compressor 2.4.2

Also, the source code is now primarily hosted on GitHub (link to source), the SVN version on Google Code is now obsolete and will be deleted very soon.


UPDATE February 13, 2009: There's a new version of the ant task available here: http://code.google.com/p/javaflight-code/downloads/list. It will work correctly with YUI-Compressor 2.3.5

Slovenská verzia tohoto článku je tu.

YUI-Compressor is a JavaScript code minification tool. Minification is a process of removing all unneeded whitespace characters, while preserving the meaning of the code for a browser (or other JS interpreter). In this way, up to 40 percent of bandwidth can be saved. It could appear, that in the time of broadband connections, it is useless to save the bandwidth, but it is not ;-) The size of web 2.0 frameworks can rise to hundreds of kB's, or even more.

In order to use minification in ant builds, I have created an ant task for YUI-Compressor. I had used it on a project I was working on, and up until now, everything seems to work normal ;-)

Installation

Buildfile usage

First, we need to set the paths and classpath:

/* ${libs} is path to the downloaded jars */
<property
name="yui-compressor.jar"
location="${libs}/yuicompressor-2.1.2.jar" />
<property
name="yui-compressor-ant-task.jar"
location="${libs}/yui-compressor-ant-task-0.2-alpha1.jar" />

<path id="task.classpath">
<pathelement location="${yui-compressor.jar}" />
<pathelement location="${yui-compressor-ant-task.jar}" />
</path>


Define a new task:

/* yui-compressor task definition */
<taskdef
name="yui-compressor"
classname="net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask">

<classpath refid="task.classpath">
</taskdef>


Concatenate all the JS files to one, before the minification (optional):

/* concatenation */
<echo message="Building ${my-ajax-lib.file}" />
<concat destfile="${my-ajax-lib.file}" force="no">

<!-- Order in which these files are concatenated _IS_ IMPORTANT! -->

<!-- AJAX related tools -->
<fileset dir="${src.dir}" includes="**/ajax/utils.js" />
<fileset dir="${src.dir}" includes="**/ajax/request.js" />
</concat>


And finally, invoke the yui-compressor:

/* invoke compressor */
<yui-compressor
warn="false"
munge="true"
suffix=".js"
preserveallsemicolons="false"
fromdir="${output.build.dir}"
todir="${output.dist.dir}">

<include name="${my-ajax-lib.file}" />
</yui-compressor>


Future

YUI-Compressor is capable of CSS file minification as well. This functionality is already included in the 0.2-alpha1, but it will need some more love. After finishing this part, I will try to write a bit of documentation, a tutorial, and some examples.

All the code is released under an open license, so feel free to hack on it. I will be happy to get any help with this.

22 comments:

Anthony said...

Hi Viktor,

I've try the YUI-Compressor Ant task but it fails with the latests versions of YUI compressor. It seems that your task calls a method that doesn't exists anymore.

Viktor Lieskovsky said...

Hi Anthony,

I've updated the ant task to use the latest YUI-Compressor version (2.3.5). You can find the updated version 0.3 in downloads or in SVN repository.

cherouvim said...

Thanks. Just incorporated your ant in my build process!

Anonymous said...

i get an "BUILD FAILED
C:\Documents and Settings\\workspace\ant_scripts\yuiCompressor\build.xml:30: java.lang.NoSuchMethodError: com.yahoo.platform.yui.compressor.JavaScriptCompressor.compress(Ljava/io/Writer;IZZZ)V

Total time: 734 milliseconds
"

when running a large directory

tianbao said...

hi team
we use the latest yui-compressor-ant-task-03.zip and we run the ant , but when we use js,the first time it will be failue but if we repeat it ,it run successful. how to resolve it.

Viktor Lieskovsky said...

Hi tianbao,

What do you mean by the failure? It fails to minify the javascript file, or the minified javascript produces errors in browser?

Can you give me a simple example?

venkatrag said...

I tried using yui-compressor-ant-task-0.3.jar and yuicompressor-2.4.1.jar in my build process. When run, the build fails saying that 'suffix' attribute is not supported. If I remove the suffix attribute, it generates files successfully but the file names are appended with '-min' which I do not want to have. Is there a fix for this issue?

zxc05367 said...

Hi venkatrag,

You may use "jsSuffix" or "cssSuffix" instead of "suffix".
I could create ".js" files using "jsSuffix" attribute.

Anonymous said...

Im getting the same error as the other anonymous user :


java.lang.NoSuchMethodError: com.yahoo.platform.yui.compressor.JavaScriptCompressor.compress(Ljava/io/Writer;IZZZ)V
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.compressFile(YuiCompressorTask.java:125)
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.execute(YuiCompressorTask.java:97)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:357)
at org.apache.tools.ant.Target.performTasks(Target.java:385)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1337)
at org.apache.tools.ant.Project.executeTarget(Project.java:1306)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.apache.tools.ant.Project.executeTargets(Project.java:1189)
at org.apache.tools.ant.Main.runBuild(Main.java:758)
at org.apache.tools.ant.Main.startAnt(Main.java:217)
at org.apache.tools.ant.launch.Launcher.run(Launcher.java:257)
at org.apache.tools.ant.launch.Launcher.main(Launcher.java:104)



Interestingly enough, it creates blank files on the target directory.

Using yui-compressor 2.3.5 and yui-compressor-ant-task-0.2-alpha1.jar

Viktor Lieskovsky said...

It's because of mismatch between YUI-Compressor and YUI-Compressor-Ant-Task versions. You need to use the 0.3 version of the ant task in order to work with YUI-Compressor 2.3.5. Get the new version here: http://code.google.com/p/javaflight-code/downloads/list

Website Designer said...

Thanks for this nice post.

Deano said...

I've updated to the latest and tried using, however it keeps saying that there are no files to compress. I've checked the directories and the files are there. I've echo'd out the path and they are correct.
The following is my dir structure
Site
+-- build
| +-- js
+- website
| +-- js

I am trying to use the 'website/js' as the source and build to 'build/js'... anyone know why?

Viktor Lieskovsky said...

Deano: Could you please post relevant parts of your build.xml? Alternatively, you can send me zipped version of your project directory with the relevant parts, including build.xml to my email (viktor.lieskovsky at gmail.com), and I will try to help.

Anonymous said...

The css compression removes the space before !important .... is there a switch/fix to avoid moving whitespace and only removes new line characters?

yui-compressor
warn="true"
munge="false"
cssSuffix=".css"
preserveallsemicolons="false"
fromdir="${publication.webapps.dir}/staticContent/css"
todir="${project.dist.tar}/static/css"
/yui-compressor

Thx

Anonymous said...

I cannot get the 0.4 version to work with yui-compress 2.4.2 in Ant 1.6.5/Java 1.5.0.

My ant task looks like this:
<yuicompress warn="false" optimize="true" preserveallsemicolons="false"
fromdir="${src.web.dispatcher.dir}/js" todir="${dispatcher.min.dir}">
<include name="*.js" />
</yuicompress>

The task fails with a StringIndexOutOfBoundsException when processing any js-file.

The yui-compressor works in command line mode.

Any ideas are welcome...

raymex said...

Hi,

I've extended the task so that it can also minify the inline javascript blocks in HTML files. I can send you a patch file if you like.

Viktor Lieskovsky said...

Hi Raymex, that's very cool, happy to hear that!

I'm definitely interested in that patch. You can send it to me by email (viktor.lieskovsky at gmail.com) or, even better, use github.com for that.

Just clone the yui-compressor-ant-task repository, add your patch, and send me a pull request.

Thanks!

Viktor Lieskovsky said...

Anonymous,

Can you elaborate some more on that IndexOutOfBounds Exception? A stack-trace would help a lot.

Juliano said...

I am also getting an IndexOutOfBoundsException:

java.lang.StringIndexOutOfBoundsException: String index out of range: 74351
at java.lang.String.substring(String.java:1935)
at com.yahoo.platform.yui.compressor.JavaScriptCompressor.printSourceString(JavaScriptCompressor.java:267)
at com.yahoo.platform.yui.compressor.JavaScriptCompressor.parse(JavaScriptCompressor.java:330)
at com.yahoo.platform.yui.compressor.JavaScriptCompressor.<init>(JavaScriptCompressor.java:533)
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.createJavaScriptCompressor(YuiCompressorTask.java:145)
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.compressFile(YuiCompressorTask.java:125)
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.execute(YuiCompressorTask.java:98)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.taskdefs.Sequential.execute(Sequential.java:62)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.taskdefs.MacroInstance.execute(MacroInstance.java:394)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:357)
at org.apache.tools.ant.Target.performTasks(Target.java:385)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1337)
at org.apache.tools.ant.Project.executeTarget(Project.java:1306)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.apache.tools.ant.Project.executeTargets(Project.java:1189)
at org.apache.tools.ant.Main.runBuild(Main.java:758)
at org.apache.tools.ant.Main.startAnt(Main.java:217)
at org.apache.tools.ant.launch.Launcher.run(Launcher.java:257)
at org.apache.tools.ant.launch.Launcher.main(Launcher.java:104)

Anonymous said...

Not to dis your effort here, but why not use the <java> task?

What value does this yui compressor ant task add that I cannot get by simply running the jar file with the <java> task.

I have been using that without any issues.

Anonymous said...

It is very interesting for me to read that post. Thanx for it. I like such themes and everything connected to them. I would like to read a bit more on that blog soon.

Demiurg said...

Cannot get it working.

First, I had to add



But now it fails with the following messages:

BUILD FAILED
java.lang.NoClassDefFoundError: com/yahoo/platform/yui/compressor/JavaScriptComp
ressor
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.createJavaSc
riptCompressor(YuiCompressorTask.java:145)
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.compressFile
(YuiCompressorTask.java:125)
at net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask.execute(YuiC
ompressorTask.java:98)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.jav
a:106)