Building AEM templates and assets outside of Maven using Gulp

Over the last 6+ months at my new place of employment, I have had the pleasure of being introduced to the Adobe Experience Manager (or AEM) platform. In case you are not familiar with AEM, this JAVA-based content management system is the flagship product from Adobe's Marketing Cloud suite of products; especially designed for enterprise level integrations. While this system can be somewhat difficult to work with at times, the power and flexibility that it provides far beyond outweighs the negatives that can be associated with it. What's more, when combined with additional services within the Adobe Marketing Cloud suite, the value provided to our clients is unprecedented.

Disclaimer: As I am not a AEM expert, I apologize if some of the processes and/or terminology are not exact.

The problem

As I mentioned above, AEM is a JAVA-based system. This means that the project must be compiled and then packaged for the server to run the application using Maven. During initial development, especially when working with front-end assets and dependencies, this constant building and refactoring can be extremely time consuming; not to mention frustrating. Any modifications to a single SASS/SCSS file or component template would require a developer to run the Maven project build command which could take upwards of 20-35 seconds to complete. Again, time consuming.

Using WebDAV wasn't too great

Initially when I was introduced to this system, our development team was mounting our local instance of AEM to our computer like that of a USB drive. This would allow us to access AEM's JCR (file management system) and then save files to this system, thus immediately injecting them into the live environment. We would then run a couple Gulp tasks to compile our SCSS into a single stylesheet and then save them to our local instance without re-running our Maven build. Great right? This only worked on front-end assets that were stored within the clientlib. So if you made a change to a template, you had to run a Maven build. However this workflow was still better than re-running the build with every Javascript/SCSS change.

If you are a twitchy saving developer like myself, where I save the file that I am currently working on multiple times over and over again with quick succession, this can cause some file status/permission issues. Let me explain.

More than once throughout a project I would be saving a stylesheet, make a quick change, and then save the file again in quick succession. While I was performing these saves, the Gulp process was trying to compile the SCSS and then rewrite the compiled CSS stylesheet within WebDAV. If the file save had not completed from the previous save (a lot of which would depend on the speed of your local machine), the JCR would respond with a busy error and the new stylesheet would not save. What's worse, my computer would then lock this file permanently and from then on while no amount of shell commands could fix it. My only remedy would be to completely delete my AEM instance and re-install it. This wouldn't be that big of problem if you weren't running 5-8 websites worth of content off of the same AEM instance, which I am. #bollocks

A new solution was needed

The current workflow that we had been using had to change. For a developer like myself having to completely destroy a development environment every couple weeks just wasn't a great way to live.

After speaking with a member of our team, Tedd Schofield, he said that there was in fact a REST API that would allow you to essentially post files into AEM's JCR outside of the file management system. While this was all gravy, since we were using Gulp to compile our assets, I couldn't figure out a way to be able to utilize this API.

Using slang

After some research I happened upon a node package called gulp-slang. This package would allow you create a Gulp watch task on any type of file that if the file would change, you would pass this file name to gulp-slang and then it would in turn post the file into the JCR. This is what we needed!

For example, if you saved a javascript file that contained some AngularJS code, it would be sent to the JCR like so:

var gulp = require('gulp');
var slang = require('gulp-slang');
var jsBaseFolder = '../../footer/js/';

gulp.watch(jsBaseFolder + '**/*.js', function(e) {
  var path = e.path;
  console.log('gulp watch JS triggered: ' + path);
  return gulp.src(path)
    .pipe(slang(path, {
      port: 4502
    }));
});

Further discovery yielded this process would also work with templates as well (both Sightly and JSP templates). WebDAV couldn't even do that. An added bonus that I discovered was that the time required to complete this process was insanely fast; probably around a few milliseconds. Even when you chained SCSS compilation with the file posting process:

var gulp = require('gulp');
var sass = require('gulp-sass');
var slang = require('gulp-slang');

var cssBaseFolder = '../../dev/scss/';
var cssLocalTargetFolder = '../../global/css/';

gulp.task('sass', function(){
  return gulp.src([
      cssBaseFolder + 'style.scss'
    ])
    .pipe(sass({errLogToConsole: true}))
    .on('error', onError)
    .pipe(gulp.dest(cssLocalTargetFolder));
});

function onError(err) {
  console.log(err);
  this.emit('end');
}

gulp.task('copy-css', function() {
  return gulp.src([
      cssLocalTargetFolder + 'style.css'
    ],{ dot: true })
    .pipe(slang([
      cssLocalTargetFolder + 'style.css'
    ], {
      port: 4502
    }));
});

gulp.task('compile-sass', function(callback) {
  runSequence(
    'sass',
    'copy-css',
    callback);
});

gulp.watch(cssBaseFolder + '**/*.scss', ['compile-sass']);

The verdict

This workflow of saving and compiling our assets for our AEM projects has been absolutely revolutionary to our development process. By posting our AEM templates into the JCR alone has saved our development team hours of time; hours that we were able to repurpose to other efforts. We are not only a more efficient team, but more profitable as well.

An example

If you would like an example of how I was able to implement this workflow in my own project, I have created a GitHub gist. The link is below.

Gulp slang GitHub gist