Over the last two years, Javascript's ecosystem of build/scaffodling/dependency management tools has really improved. These days it is quite easy to bootstrap and manage your app using Yeoman, for example.

Now I do use Javascript as the frontend language, but most of the time my backend is written in Java on Google App Engine, and uses good old Maven as the build and dependency management tool.

Luckily, there is a way to integrate Yeoman into the Maven package, you just have to use the yeoman-maven-plugin by Thomas Recloux. Here is how to integrate it in your pom.xml :

<build>
<plugins>
<plugin>
<groupid>com.github.trecloux</groupid>
<artifactid>yeoman-maven-plugin</artifactid>
<version>0.2</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

The next issue is, during development tasks, how do you wire your local development server (for example the App Engine local server or a Jetty instance) so that the "grunt server" command can leverage it ?

You can do this with the "proxy" feature in Grunt, you will find below what I added to my Gruntfile.js to make it work. You can also find the full example on Github.

The biggest issue I still have with this workflow is that I cannot keep the "grunt server" task running when I package my war, for example to upload on App Engine. Otherwise the Maven execution fails.

connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729
}, proxies: [
{
context: [
'/_ah',
'/admin'
],
host: 'localhost',
port: 8080,
https: false,
changeOrigin: false,
xforward: false
}
], livereload: {
options: {
open: true,
base: [
'.tmp',
'<%= yeoman.app %>'
],
middleware: function (connect, options) {
if (!Array.isArray(options.base)) {
options.base = [options.base];
}

// Setup the proxy
var middlewares = [require('grunt-connect-proxy/lib/utils').proxyRequest];

// Serve static files.
options.base.forEach(function (base) {
middlewares.push(connect.static(base));
});

// Make directory browse-able.
var directory = options.directory || options.base[options.base.length - 1];
middlewares.push(connect.directory(directory));

return middlewares;
}
}
},
...
grunt.loadNpmTasks('grunt-connect-proxy');
...
grunt.registerTask('server', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}

grunt.task.run([
'clean:server',
'concurrent:server',
'autoprefixer',
'configureProxies:server',
'connect:livereload',
'watch'
]);
});