JS-Maven Roundup and Roadmap

To those of you who’ve been paying attention to the original project on GitHub, you’ve probably noticed that there’ve been a lot of changes there recently without any subsequent updates here. I’d like to take a moment to summarize what we’ve been up to, and keep everyone in the loop on where we’re going.

Inevitably as you start into a project you realize the full scope of what you’re trying to do, and as you do the finish line moves farther and farther towards the horizon. Most of the changes made were in respect to that.

js-maven-plugin is now js-maven

I renamed and moved the project because it was rapidly growing beyond the scope of a single maven plugin to include core libraries, tooling, archetypes and reports. Rather than try to hammer all these into one single java project I felt that switching over to a multi-module layout would give me the room I needed to grow. So far this seems to have been a good decision, as I now have the sense that I can (mentally) stretch my arms a little.

No more specific version commitments.

I’m making a concession to my own prioritization style here, which is very much a “Hey, this sounds like an interesting problem to solve” method. Needless to say, this doesn’t really work well with preplanned version releases, as there are some projects that are more fun and some that are less fun, and it resulted in me skipping around a lot. Instead, until I’ve got a few more contributors on the project, I’m simply going to outline a feature set for the various point versions and go after features as they strike me.

I understand that for an enterprise this is not the most optimal approach, so I will make this suggestion: If a particular feature is business critical enough for you to write your own solution, why not contribute it while you’re at it?

Documentation!

I’ve finally gotten around to setting up all the maven:site documentation for the project, and it’s now hosted on github’s pages for the project http://krotscheck.github.com/js-maven/ . Similarly, the README.md on github has been reduced to a quick start (since we know we all prefer copy/paste), as I don’t feel that much more was necessary.

Version Dependencies

We now support Maven 3 and Java 1.7 only. Annoying for some of you, yes, however in keeping with my philosophy of using the latest-and-greatest whenever possible we’re just going to put a line in the sand. That’s not to say we won’t ever support other versions, however those ports will have to come from the community (such as it is).

Licensing

I’m still debating licenses. Right now the project is under the MIT license, though I might move it over to New BSD. This project will never be licensed under the GPL or any similar viral/copyleft license, as those tend to greatly inhibit adoption.

Roadmap Commitments for v0.2.0

If you’ll take a look at the roadmap, you’ll see the list of features which I want to have ready for the next minor version release. Notable additions were the reporting plugins for JSLint and JSDoc, as it didn’t make much sense to me to include those without making them accessible either. Here’s a list of where we stand right now, with some details:

Plugin Documentation [branch- jsdoc-improvments]

Now that I’ve got the maven site reports running, they decided to get all unruly and hurt my feelings by insulting my coding style. The inevitable cleanup phase is in progress, which also includes a bit of judicious refactoring as the actual usability of the plugin gets a little focus.

Cleanup

As this is my first large maven plugin project, I’m still learning my way around. Refactoring and cleanup is necessary – for example, sourceEncoding should be handled via the global parameter and not individually per Mojo.

Maven Reports: JSLint and JSDoc

These are two new features I realized would be necessary as I got the site up and running. I’m not at all familiar how plugins like this get wired into maven as a whole, so they might go fast or they might not.

Common JS Libraries in a repository [branch- jslib-redeploy-plugin]

Having a javascript maven framework isn’t much use if you don’t have any libraries that you can depend on, so I’m writing a plugin that’ll take a URI-styled token string and will redeploy any artifacts found into a maven repository. This is proving tricky as I cannot resolve their own version dependencies easily (jqueryUI -> jquery for instance), however it’s coming along nicely. Once this is complete I’ll be able to provide a few more archetypes for other projects. jQuery, JavascriptMVC, you name it.

Unit Testing

I’m still evaluating the best javascript unit testing framework out there. There are many solutions for “How to test a javascript library in different javascript engines and/or browsers, but nobody seems to have figured out the holy grail yet. This feature might get punted into its own release, if it turns out that the scope is too big.

Building a JSDoc Runner for Maven

The next step in this plugin is to ensure that we can generate documentation. While it may seem like a stretch to do this so early in the process, it forces us to build a few other pieces that we’ll need later.

Our documentation generator will be JSDoc, chosen because it’s fairly mature, has a strong following, and its markup is broadly supported across IDE’s. In fact JSDoc seems to be one of the few broadly adopted standards out there, though to speculate why is beyond the scope of this post. What matters is that it’s a documentation writer written in Javascript that is run via Mozilla’s Rhino runtime. To properly run it we’ll have to resolve the dependency, unpack it into a temporary directory, and then spin up Rhino to execute the code.

I encountered a problem in the way JSDoc 2.4.0 interacts with Rhino 1.7RC3, a bug introduced by two inline function declarations without semicolons in TokenParser.js. While on one side one might argue that this is a bug in Rhino, declaring functions inside of other functions is a big no-no in the javascript world. As a result I’ve had to generate and deploy my own patched version of JSDoc.

Resolving JSDoc

All of the plugin artifacts for a given mojo are easily available as a parameter, thus locating the proper version of JSDoc is fairly simple.

Full source available on github

/**
 * @parameter default-value="${plugin.artifacts}"
 */
private List<Artifact> pluginArtifacts;

public Artifact locate(String groupId, String artifactId) {

	getLog().info("Resolving " + groupId + ":" + artifactId);

	for (Iterator<Artifact> iterator = pluginArtifacts.iterator(); iterator.hasNext();) {
		Artifact artifact = (Artifact) iterator.next();

		if (artifact.getGroupId().equals(groupId) && artifact.getArtifactId().equals(artifactId)) {
			return artifact;
		}
	}

	getLog().error(String.format("Failed to locate %s:%s", groupId, artifactId));
	getLog().error("This probably means that you didn't specify it as a dependency in the pom.xml file");

	throw new RuntimeException(String.format("Failed to locate %s:%s", groupId, artifactId));
}

Unpacking JSDoc

As all jar files are zip files, we can simply write a zip extraction routine to unpack it.

Full source available on github

ZipFile zipFile = new ZipFile(artifact.getFile(), ZipFile.OPEN_READ);
Enumeration zipFileEntries = zipFile.entries();

while (zipFileEntries.hasMoreElements()) {
	ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();

	// Construct the new name
	String currentName = entry.getName();
	File destFile = new File(destinationDirectory, currentName);

	if (entry.isDirectory()) {
		// If we are a directory, create it.
		destFile.mkdirs();
	} else {
		// If we're a file, unzip it.
		BufferedInputStream is = new BufferedInputStream(zipFile.getInputStream(entry));
		int currentByte;

		// establish buffer for writing file
		byte data[] = new byte[BUFFER];

		// write the current file to disk
		FileOutputStream fos = new FileOutputStream(destFile);
		BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);

		// read and write until last byte is encountered
		while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
			dest.write(data, 0, currentByte);
		}
		dest.flush();
		dest.close();
		is.close();
	}
}

Configure JSDoc

At this point we’re back in the drudgery of maven configuration. Since we’d like to keep our configurations relatively separate (in a <jsdoc> sub tag in the mojo configuration), I’ve created a separate JSDocOptions class that can accept all of our options. I’m not going to bother copying it all here as there’s quite a bit of code, but you can see the full source code here.

Running JSDoc via Rhino

Lastly, we have to execute JSDoc, which is done via Rhino. This, again, is fairly straightforward.

Full source available on github

public void run(File inputDirectory, File outputDirectory, JSDocOptions options)
	throws JSDocRunnerException
{
	// Build the string arguments.
	String[] args = options.buildArguments(jsDocRoot, inputDirectory, outputDirectory);

	// Run the javascript engine.
	getLog().info("Building JSDoc...");

	// Create our logging streams
	MojoLoggerOutputStream errorStream = new MojoLoggerOutputStream(LogLevel.ERR);
	Main.setErr(new PrintStream(errorStream));

	MojoLoggerOutputStream outputStream = new MojoLoggerOutputStream(LogLevel.WARN);
	Main.setOut(new PrintStream(outputStream));

	Main.main(args);
	getLog().info("Complete");

}