A maven / grunt / typescript / JSX build workflow

512px-Maven_logo.svgNotice: sources are available on GitHub

As stated in my previous post, I started Mipod.X as a Vert.X application programmed in Java. There’s the choice to use Maven or Gradle as build management tool, I use the one I know the best, which is maven. Vert.X provides configuration samples for Maven and Gradle, so I just followed the documentation which is well written. Basically Vert.X expects a “fat-jar” to be provided.

I. Vert.X with Maven

For now at early development stage, Mipod.X consists on 1 parent maven module and 4 children modules: application, data-model, mpd-connector and web

Obviously, the entry-point of the application is in application. So let’s check this one first:

In pom.xml, I define the application entry point as a variable (the class must be a Verticle):

The “fat-jar” is created via the maven-shade-plugin:

The shade plugin will basically pick up every dependencies and aggregate them to create a single jar. So I have to add all my dependencies in this pom:

And there we are! A maven install on mipod.x will generate (and install) the fat-jar.

II. Writing some Javascript and ReactJS

Integrating ReactJS or any other Javascript with Vert.X is fairly easy. As described in the documentation, all static resources are taken by default from src/main/resources/webroot. So I create this folder in my web maven module, and add an index.html plus some external dependencies such as react, react-dom, sockjs and vertxbus.js. Vertxbus.js allows communication with the Vert.X event bus through sockjs (websockets). It’s as simple as:

This code listen to “info” messages on the event bus and write it in the HTML DOM, assuming there’s an element with id “main”. It also publishes an “init” event on the event bus.

To begin with React, we can add a dependency on babel in browser, as explained here. So I insert in my index.html the example code:

and we’ve got our first JSX lines of code working. However this is temporary and I’ll describe how to switch from babel to TypeScript.

On the java side, a Verticle will initialize the HTTP server and EventBus permissions. It’s quite easy once again:

I won’t talk about programming with Vert.X right here. Just notice how the server router is configured to accept in and out event bus messages from and to the client.

III. Switching to TypeScript

Babel is pretty cool, but I really miss static typing in my Javascript. So TypeScript is definitely the way to go for that (well, it could have been PureScript as well. I guess a similar process can be done with grunt-purescript). The goal is to integrate TypeScript in the maven build process. I’ll use Grunt for this, which you probably already know if you’re familiar with the Javascript ecosystem. It involves a couple of other tools to be properly configured:

  • Nodejs and its package.json. Nodejs will only be used in the building process and is not required at runtime.
  • Grunt itself and “grunt-cli”
  • A Grunt plugin for TypeScript: grunt-ts
  • The grunt-maven-plugin

I reorganized my web maven module structure in the following way:

To set up this workflow, first things are to install nodejs, npm and create your package.json file. You don’t need to create a full package.json, as it will only be used in your workflow, it doesn’t have to be published publicly.

My package.json looks like:

You can already run a npm install from the folder that contains package.json, so that you’ll get all the dependencies ready to be used.

Then you must configure the Gruntfile.js:

With this configuration, all TypeScript files in “<some root>/main” will be transpiled into a single file “<some root>/target/main/mipod.x.js“. I’ll explain later how “<some root>” is set, this is part of the grunt-maven-plugin.

There’s an important remark to be said here: TypeScript’s file concatenation can only work if there’s no external module links in the source files. It took me some time to find out this issue in my code, because I used to write TypeScript for nodejs with lots of external module dependencies. So I removed all import or require references from my client-side TypeScript files, and used Internal modules instead. This is well described on this blog post. Basically, you must use module namespaces and ///<reference… />. Setting “verbose: true” as ts:compile options in the Gruntfile helped me a lot.

Once this is done, there’s just two more steps to have a complete workflow: running grunt from maven, and copy the generated file at the right place.

To integrate grunt into maven, I use the grunt-maven-plugin. This is done through web‘s pom.xml:

What does it do? To sum up, it first takes all sources from “sourceDirectory/jsSourceDirectory”, that is “mipod.x/web/typescript” and copy them to “gruntBuildDirectory“, that is “mipod.x/web/target/grunt“. Then, grunt is invoked from that working directory (the “<some root>” mentioned above). Put all together, my final JS file will be “mipod.x/web/target/grunt/target/main/mipod.x.js“.

If you need to debug, you can add <gruntOptions><gruntOption>--verbose</gruntOption></gruntOptions> in the configuration.

Next and final step is to copy the generated JS file to the right place in webroot, before the jar is actually packaged by maven. This is done through the maven-resources-plugin at compile time:

Nothing fancy here, it speaks for itself. Link it in your index.html, and you’re done.

IV. What about JSX?

… done? Hum, we haven’t done anything for JSX yet. Hopefully this is a very easy step since it is correctly handled by tsc, the TypeScript transpiler.

Since you use TypeScript, you can write some TSX files. You’ll find more information on that from Microsoft’s GitHub. Put those .tsx files in the TypeScript directory (or subdirs), that is “web/typescript/main“.

And then, just add a task in the Gruntfile to compile TSX:

The task “ts:compile_tsx” will invoke tsc on all tsx files, with option “jsx: react”, and generate a single .jsx file similar to what we did for .ts/.js.


This entry was posted in MipodX and tagged , , , , , . Bookmark the permalink.

Leave a Reply