JavaScript library development using ANT and YUICompressor

Since I need to port a huge ActionScript project to JavaScript I see myself confronted with different practices of code organization, workspace structures and a lot more JavaScript specific stuff. I’m used to big, strongly typed ActionScript library development with hundreds of classes and interfaces but I’m not quite sure how to transfer my programming skills from ActionScript to JavaScript.

After reading loads of informations I think I created an reusable and understandable build process for my JavaScript project. I bet you’ve already read a lot about YUICompressor and ANT so here you’ll find just some brief informations.

This is my example workspace with file type src subfolders and even another subfolder within the js directory named like the JavaScript library it contains. All ANT files can be found in src/ant:
 

Now let’s have a look at system.properties. This file bundles the system specific settings and usually won’t need changes at all after your initial setup. Only YUI_COMPRESSOR_JAR and YUI_COMPRESSOR_CHARSET will affect the build.xml. I still don’t know what’s the correct charset, but my encoding problems might be caused by the editor. I’ll figure that out later…

#
# SYSTEM PROPERTIES
#
 
# Version number separated for easy changes
YUI_COMPRESSOR_VERSION=2.4.6
 
# Absolut path to YUICompressor home directory
YUI_COMPRESSOR_HOME=/Users/you/.../YUICompressor/${YUI_COMPRESSOR_VERSION}
 
# Absolute path to JAR file (used in build.xml!)
YUI_COMPRESSOR_JAR=${YUI_COMPRESSOR_HOME}/build/yuicompressor-${YUI_COMPRESSOR_VERSION}.jar
 
# Charset used with YUICompressor - MAKE SURE THIS IS CORRECT FOR YOUR SYSTEM!!!
YUI_COMPRESSOR_CHARSET=MacRoman
#YUI_COMPRESSOR_CHARSET=UTF-8
#YUI_COMPRESSOR_CHARSET=ANSI
#YUI_COMPRESSOR_CHARSET=ISO-8859-1

The project.properties currently only contains the basic workspace structure.

#
# PROJECT PROPERTIES
#
 
# Workspace root relative to build.xml
WORKSPACE_ROOT=../..
 
# Main binary directory
BIN_DIR=${WORKSPACE_ROOT}/bin
 
# JavaScript bin directory
BIN_JS_DIR=${BIN_DIR}/js
 
# Main source directory
SRC_DIR=${WORKSPACE_ROOT}/src
 
# JavaScript source directory
SRC_JS_DIR=${SRC_DIR}/js

Finally there is the main build.xml. I was trying hard to write an understandable documentation. Just follow these steps (also found more detailed within the file):

  1. Replace the default library name and prefix with your’s.
  2. Add your source files.
  3. Choose your browser and html file (works at least with Mac OS X).
  4. Run the ANT task called YOUR_LIBRARY_PREFIX_run().
<?xml version="1.0" encoding="UTF-8"?>
<project name="JavaScript Project Tasks" basedir=".">
 
	<!--
		Created without much experience ;) by derRaab(); - http://blog.derRaab.com
	-->
 
	<!-- Load external properties -->
	<property file="project.properties" />
	<property file="system.properties" />
 
	<!--
		_createWorkspace() - Creates the basic workspace structure..
	-->
    <target name="_createWorkspace()" description="Creates the basic workspace structure.">
    	<mkdir dir="${BIN_CSS_DIR}"/>
    	<mkdir dir="${BIN_JS_DIR}"/>
        <mkdir dir="${SRC_JS_DIR}"/>
        <mkdir dir="${SRC_CSS_DIR}"/>
    </target>
 
	<!--
		__openBrowser( browserName, targetFile ) - Opens a browser with a specific file.
 
		This might be Mac OS X specific, so please refer to
		http://www.darronschall.com/weblog/2007/12/launching-firefox-from-ant-on-osx.cfm
		for further informations.
 
		@param browserName		e.g. 'Firefox', 'Safari', 'Chrome'
		@param targetFile		e.g. 'path/to/index.html'
	-->
    <target name="__openBrowser()" description="Utility task - Opens a browser with a specific file.">
        <echo message="__openBrowser( ${browserName}, ${targetFile} )" />
    	<exec executable="open" spawn="yes">
    		<arg line="-a ${browserName}" />
    		<arg line="${targetFile}" />
    	</exec>
    </target>
 
	<!--
		_concatenate( inputFileListID, inputFileSetID, outputFile ) - Concatenates a FileSet into one fresh file.
 
		@param inputFileListID
		@param inputFileSetID
		@param outputFile		e.g. 'path/to/file/name.ext'
	-->
    <target name="__concatenate()" description="Utility task - Concatenates a FileSet into one fresh file.">
        <echo message="_concatenate( ${inputFileSetID}, ${outputFile} )" />
    	<delete file="${outputFile}"/>
        <concat destfile="${outputFile}" fixlastline="yes">
            <filelist refid="${inputFileListID}"/>
            <fileset refid="${inputFileSetID}"/>
        </concat>
    </target>
 
	<!--
		_yuiCompress( workDir, inputFile, outputFile ) - Compresses a single JavaScript file into one fresh file within the same directory.
 
		@param workDir			e.g. 'path/to/file'
		@param inputFile		e.g. 'name.js'
		@param outputFile		e.g. 'name.min.js'
	-->
    <target name="__compress()" description="Utility task - Compresses a single JavaScript file into one fresh file within the same directory.">
    	<echo message="_yuiCompress( ${workDir}, ${inputFile}, ${outputFile} )" />
    	<delete file="${workDir}/${outputFile}"/>
        <apply dest="${workDir}" executable="java" verbose="true">
            <fileset dir="${workDir}">
                <include name="${inputFile}" />
            </fileset>
            <arg line="-jar" />
            <arg path="${YUI_COMPRESSOR_JAR}" />	
            <arg value="--charset" />
            <arg value="${YUI_COMPRESSOR_CHARSET}" />
            <arg value="-o" />
            <targetfile />
            <mapper type="glob" from="${inputFile}" to="${outputFile}" />
        </apply>
    </target>
 
	<!--
 
		SETTING UP YOUR BUILD PROCESS:
 
		1.	Recognise the default name ('library') and prefix ('LIBRARY_JS') in this default build.xml 
		1.	Choose a unique name and prefix for your library.
		2.	Use your editor's case sensitive replace all functionality to change the default values
			('library'->'yourLibrary' and 'LIBRARY_JS_'->'YOUR_LIBRARY_JS_')
			NOTE: You can add multiple libraries to one build.xml
		3.	Add a version number (see ...VERSION) if needed.
		4.	Add files to your file list (see ...FILE_LIST) - OR! add a bunch of files to your file set (see ...FILE_SET)
		5.	Choose your browser (see ...RUN_BROWSER_NAME)
		6	Choose your run file (see ...RUN_FILE_NAME)
 
	-->
 
	<!--
		...NAME					Library name used in file system
		...VERSION				Optional library version suffix e.g. '-1.2.6' or ''
		...BIN_FILE_DIR			Library binary folder e.g. 'bin/js'
		...BIN_FILE_NAME		Full file name of concatenated file
		...BIN_MIN_FILE_NAME	Full file name of minimized file (must be different!)
		...SRC_DIR				Library source folder e.g. 'src/js/library'
		...RUN_BROWSER_NAME		Your test browser of choice (Mac OS X only?) e.g. 'Firefox', 'Safari', 'Chrome' 
		...RUN_FILE_NAME		Your test file e.g. 'index.html' 
	-->
	<property name="LIBRARY_JS_NAME" value="library"/>
	<property name="LIBRARY_JS_VERSION" value=""/>
	<property name="LIBRARY_JS_BIN_DIR" value="${BIN_JS_DIR}"/>
	<property name="LIBRARY_JS_BIN_FILE_NAME" value="${LIBRARY_JS_NAME}${LIBRARY_JS_VERSION}.js"/>
	<property name="LIBRARY_JS_BIN_MIN_FILE_NAME" value="${LIBRARY_JS_NAME}${LIBRARY_JS_VERSION}.min.js"/>
	<property name="LIBRARY_JS_SRC_DIR" value="${SRC_JS_DIR}/${LIBRARY_JS_NAME}"/>
	<property name="LIBRARY_JS_RUN_BROWSER_NAME" value="Safari"/>
	<property name="LIBRARY_JS_RUN_FILE_NAME" value="index.html"/>
 
	<!--
		...FILE_LIST	File list used within _concatenate()
		...FILE_SET		File set used within _concatenate()
 
		Important:
 
		1.	YUICompressor will use the ...FILE_LIST first and ...FILE_SET afterwards!
			So you can first assign a special file order and then additionally add a bunch of unordered files
		2.	Avoid duplicates since YUICompresser doesn't check if a file was already added!
		3.	It's a good approach to only use one of them and leave the other one empty.
	-->
    <filelist id="LIBRARY_JS_FILE_LIST" dir="${LIBRARY_JS_SRC_DIR}">
    	<file name="core.js"/>
    	<file name="more.js"/>
    	<file name="evenmore.js"/>
    </filelist>
    <fileset id="LIBRARY_JS_FILE_SET" dir="${LIBRARY_JS_SRC_DIR}">
    	<exclude name="**/*.*"/><!-- by default file set is not used! -->
    	<!--<include name="**/*.js"/> this is how you add all js files-->
    </fileset>
 
	<!--
		..._createWorkspace()	- Creates the library source dir.
		..._concatenate()		- Concatenates the library.
		..._compress()			- Compresses the library.
		..._openBrowser()		- Open's the index.html in your preferred browser.
		..._run()				- Compresses the library and open's the index.html in your preferred browser.
	-->
	<target name="LIBRARY_JS_createWorkspace()" depends="_createWorkspace()">
		<mkdir dir="${LIBRARY_JS_SRC_DIR}"/>
	</target>
	<target name="LIBRARY_JS_concatenate()" depends="LIBRARY_JS_createWorkspace()">
		<antcall target="__concatenate()">
			<param name="inputFileListID" value="LIBRARY_JS_FILE_LIST"/>
			<param name="inputFileSetID" value="LIBRARY_JS_FILE_SET"/>
			<param name="outputFile" value="${LIBRARY_JS_BIN_DIR}/${LIBRARY_JS_BIN_FILE_NAME}"/>
		</antcall>
	</target>
	<target name="LIBRARY_JS_compress()" depends="LIBRARY_JS_concatenate()">
		<antcall target="__compress()">
			<param name="workDir" value="${LIBRARY_JS_BIN_DIR}"/>
			<param name="inputFile" value="${LIBRARY_JS_BIN_FILE_NAME}"/>
			<param name="outputFile" value="${LIBRARY_JS_BIN_MIN_FILE_NAME}"/>
		</antcall>
	</target>
	<target name="LIBRARY_JS_openBrowser()">
		<antcall target="__openBrowser()">
			<param name="browserName" value="${LIBRARY_JS_RUN_BROWSER_NAME}"/>
			<param name="targetFile" value="${basedir}/${BIN_DIR}/${LIBRARY_JS_RUN_FILE_NAME}"/>
		</antcall>
	</target>
	<target name="LIBRARY_JS_run()">
		<antcall target="LIBRARY_JS_compress()"/>
		<antcall target="LIBRARY_JS_openBrowser()"/>
	</target>
 
</project>

Notice that it’s possible to have multiple libraries within a single build.xml. Simply use different prefixes!

In the end I found most of the core informations at http://www.samaxes.com and http://www.darronschall.com/weblog. Thank you guys! And of course the ANT and YUICompressor manuals accessible through Google ;).

And now I have two question:

  • Will you tell me your preferred editor?
  • Will you give me a hint according to code organization?

Please.

9 thoughts on “JavaScript library development using ANT and YUICompressor”

  1. hi raab, i think it’s funny to use our dev skills for javascript. in a current project we use backbone.js, it’s a nice little mvc framework with a cool event system.
    half year ago we used the puremvc for mootools, feels also nearly like ActionScript.
    we use NetBeans for editing but there is really bad code completion. at FFK11 somebody said http://www.jetbrains.com/webstorm/ is a nice js editor, maybe you give it a try.

    sooo, fireabend!!!!

    lg mad

  2. Hi Mr. flanderz. Did we met at FFK11??? I can’t remember.

    PureMVC is a good advice since the ActionScript project is also based on PureMVC. But PureMVC seems not to use a specific namespace but lots of globals (e.g. “Controller”, “Model”). In the case of my project I need to make sure it’s “sandboxed” as much als possible. Thanks anyway!

  3. @derRaab> I’m the creator of the PureMVC to JavaScript port. I also prefer to use namespaces to access all my classes in JavaScript project and would I preferred to do so for PureMVC port. But by the past a lot of JS dev (not coming from ActionScript) found this cumbersome and usefulness so we decided not to use them.

    There’s an hidden feature in the PureMVC JS port. Just declare a var HidePureMVC = true; on the top of your webpage before loading the JS file whic contains PureMVC and you can access the class with their namespace.

    This is not the full “org.puremvc.js.objs.core.Controller” but “puremvc.Controller” as using all those namespace on imports adds unwanted weight to the project but this is sufficient not to collide with others project.

    You can have more documentation on how to access or declare your own class + namespace on the library documentation page on Github I used to create the PureMVC JavaScript port : https://github.com/tekool/objs/wiki

    Hope this helps.

  4. Thanks a lot, really. But unfortunately I can’t use Objs because of it’s GPL v3 licence. 🙁

  5. @derRaab > What licensing model do you need? I’m the creator of the project and can write you any mail you need to derogate to this license problem. Licensing on PureMVC is more problematic but I’m pretty sure that Cliff Hall will never bug you with a licensing problem . As Objs also uses YUICompressor + Ant it would have been great for you.

  6. Yeah, I really like the basic syntax of Objs! It would be absolute perfect for me to use PureMVC based on Objs since I’m porting a big PureMVC based project.

    PureMVC comes with Creative Commons v3 so it would be nice if Objs would come with the same licence. Maybe I’m wrong, but I think it’s not possible to use a GPL v3 with the project I’m working on because it won’t be open sourced?

  7. In reality it’s not possible to directly use a Creative Commons license with a software. But you can embed GNUGPL software in a CC license. But as said before I can write you a line by e-mail to authorize you to use Objs in a non GPL software if you really need it.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.