Niels Brouwers
31 Dec 2009, 4:48 AM
I am currently working on a rather large ExtJS project and I was looking at build systems for ExtJS. Specifically I want to concatenate all the js files and run it through some compressor/obfuscator.
Ant is a nice tool which I use for a lot of Java projects, and it works really well for ExtJS stuff too. I drive ext-doc with it, as well as yuicompressor, and concatenating or copying files for deployment is a snap.
The 'concat' task can concatenate files, but I didn't want to have to manually enter a file list as we have a large number of files, especially since we generally have a separate js file for each class. We can't just pass the task a list of all files in our source directory, because inclusion order matters (Ext.extends being the number one load-time dependency).
Therefore, I wrote an Ant task called 'JsConcat' that does some quick parsing to detect dependencies between js files based on @class and @extends comments, sorts the file list, and concatenates them. Example build file:
<!-- Define the 'jsconcat' task -->
<taskdef name="jsconcat" classname="com.zarafa.jsconcat.JsConcatTask">
<classpath path="JsConcat.jar"/>
</taskdef>
<!-- Concatinates JavaScript files with automatic dependency generation -->
<target name="concat">
<jsconcat
verbose="false"
destfile="${debugfile}"
prioritize="\w+, Foo.bar.*"
>
<fileset
dir="${sourcedir}"
includes="**/*.js"
/>
</jsconcat>
</target>
If you're familiar with Ant, it should be pretty clear what this does. If not, go have a look at Ant and read the manuals, it's pretty easy. Essentially, the source directory is scanned recursively and all js files are input into JsConcat.
There's three ways to influence the inclusion order. First, there are explicit dependencies that you can enter in your file:
/*
* This file needs to be included after bar.js
* #depends /foo/bar.js
*/
Then there's the dependencies that are extracted from @class and @extends:
/**
* Foo extends Bar, so the file in which Bar is defined should come before this file.
* @class Foo
* @extends Bar
*/
And finally, formatted as a comma-separated list of regular expressions, the prioritise argument can be used to move groups of files up the list. Files that have classes defined in them have those full class names matched against the regexps. Files that match the first priority group have the highest priority, files that match the second group come after that, and so on. We use this mainly to move the 'core' and 'common' packages to the top of the list.
For example,"\w+, Foo.bar.*" will move all files which have classes in the 'root' package (i.e. 'Date', 'Foo') to the top of the list, after wich come all files which have classes in 'Foo.bar' or any of its descending packages, and finally all the files that match neither of these criteria.
The regular expression format is the standard Java format (http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html).
I appended the source and a compiled version, plus an example build file that can serve as a template in case you want to try it out but don't have tons of Ant experience. The code is fairly simple, but if you have any questions, bugs, or want to make adjustments don't hesitate to ask.
Edit: new version, fixed a small bug cobaltblue came across. :">
Ant is a nice tool which I use for a lot of Java projects, and it works really well for ExtJS stuff too. I drive ext-doc with it, as well as yuicompressor, and concatenating or copying files for deployment is a snap.
The 'concat' task can concatenate files, but I didn't want to have to manually enter a file list as we have a large number of files, especially since we generally have a separate js file for each class. We can't just pass the task a list of all files in our source directory, because inclusion order matters (Ext.extends being the number one load-time dependency).
Therefore, I wrote an Ant task called 'JsConcat' that does some quick parsing to detect dependencies between js files based on @class and @extends comments, sorts the file list, and concatenates them. Example build file:
<!-- Define the 'jsconcat' task -->
<taskdef name="jsconcat" classname="com.zarafa.jsconcat.JsConcatTask">
<classpath path="JsConcat.jar"/>
</taskdef>
<!-- Concatinates JavaScript files with automatic dependency generation -->
<target name="concat">
<jsconcat
verbose="false"
destfile="${debugfile}"
prioritize="\w+, Foo.bar.*"
>
<fileset
dir="${sourcedir}"
includes="**/*.js"
/>
</jsconcat>
</target>
If you're familiar with Ant, it should be pretty clear what this does. If not, go have a look at Ant and read the manuals, it's pretty easy. Essentially, the source directory is scanned recursively and all js files are input into JsConcat.
There's three ways to influence the inclusion order. First, there are explicit dependencies that you can enter in your file:
/*
* This file needs to be included after bar.js
* #depends /foo/bar.js
*/
Then there's the dependencies that are extracted from @class and @extends:
/**
* Foo extends Bar, so the file in which Bar is defined should come before this file.
* @class Foo
* @extends Bar
*/
And finally, formatted as a comma-separated list of regular expressions, the prioritise argument can be used to move groups of files up the list. Files that have classes defined in them have those full class names matched against the regexps. Files that match the first priority group have the highest priority, files that match the second group come after that, and so on. We use this mainly to move the 'core' and 'common' packages to the top of the list.
For example,"\w+, Foo.bar.*" will move all files which have classes in the 'root' package (i.e. 'Date', 'Foo') to the top of the list, after wich come all files which have classes in 'Foo.bar' or any of its descending packages, and finally all the files that match neither of these criteria.
The regular expression format is the standard Java format (http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html).
I appended the source and a compiled version, plus an example build file that can serve as a template in case you want to try it out but don't have tons of Ant experience. The code is fairly simple, but if you have any questions, bugs, or want to make adjustments don't hesitate to ask.
Edit: new version, fixed a small bug cobaltblue came across. :">