image

Sascha Depold

Engineering Manager

Blog

Farewell make ... Hello gulp ...

At work, we recently decided to switch our JavaScript coding style from a chaotic mess to a more consistant, disciplined and streamlined masterpiece. In order to achieve that goal, we decided to include JSHint in our build process and to configure it as strict as possible. As our codebase is relatively young, it was quite easy to get things green and to make the linter happy. Inspired by this move and by the consistent code style that followed, I thought about making the Sequelize CLI compatible as well.

Makefile

When the main repository of sequelize switched its test framework in 2013 from BusterJS to Mocha it came up with a Makefile which collected the paths to the test files and called mocha with them accordingly. In the first place I thought, that the file might look scary but that it is certainly good enough and that I will most likely never have to touch it again. Well, that has been until I opened it in my favorite text editor (which has been configured to convert all tabs to space automatically), made some slight adjustments, hit the save button and ran make test.

code, y u no work

Those of you who are familiar with Makefiles (and which most likely did the same mistake several times already) should know what followed:

Makefile:18: *** missing separator.  Stop.

So after some googling and debugging I finally found out about make's nature to dislike spaces and to love well formatted tabs. "Alright", I thought and went on with my changes. I finished my stuff, committed the changes and turned my focus to something more pleasant, meaning extending the Sequelize core and writing some additional tests. And while I was happily coding, I found myself opening the Makefile from time to time accidently, which sent me a shudder down my spine and which made me think, if it really has to be that ugly and unmaintainable.

And then ... one day ... I stumbled over Gulp.

Gulp

The first time I looked at that streaming build system for a simpler and more intuitive build, I was stunned by its simplicity and flexibility. It just makes everything exactly as one would expect it:

  1. You define a task with gulp.task by giving it a name and a function.
  2. You can define dependencies just by naming them in an array.
  3. You call the gulp CLI with the name of respective task.
  4. You see the results. Boom. Case closed.

Here is a very simple example of a gulp task which gets defined in a gulpfile.js:

gulp.task("task-name", ["dependency1", "dependency2"], function () {
  // * do something synchronous
  // * or something asynchronous by
  //   * returning a promise
  //   * or specifying and calling a callback param
});

Due to the fact that my main use case for the Makefile has always been to run the Mocha tests, I opened Google, searched for gulp mocha and found a nice looking library, that does not only exactly solve the problem I had, but also does that in a very minimal and simple manner:

gulp.task('test', function () {
  gulp
    .src(path.resolve(__dirname, "test", "**", "*.test.js"), { read: false })
    .pipe(mocha());
});

Once saved as gulpfile.js and executed via node_modules/.bin/gulp test (I like locally installed modules) it just worked. Cool! So next task was to actually lint/validate the codebase before running the tests. So I went to Google again, fired up a search for gulp jshint and again found exactly what I was looking for.

gulp.task("lint", function () {
  gulp
    .src([
      path.resolve(__dirname, "gulpfile.js"),
      path.resolve(__dirname, "bin", "sequelize"),
      path.resolve(__dirname, "lib", "**", "*.js"),
      path.resolve(__dirname, "test", "**", "*.js")
    ])
    .pipe(jshint())
    .pipe(jshint.reporter("default"));
});

Now, that was easy! The configuration of the plugin can be done via a .jshintrc which makes the gulpfile less cluttered and reflects the standard behavior for jshint.

Default task

When you run gulp without any additional parameters it will search for a task definition default. For the sequelize CLI the default task is to lint the project and to run the tests afterwards. The respective code looks like this:

gulp.task("default", ["lint", "test"], function () {});

It basically defines an empty task which is called default and which depends on the lint and the test. Thats it!

tl;dr

If you are in a Node.JS environment and need some kind of build/task runner system, I would highly recommend to at least take a look at Gulp. In my opinion, it is not only easier to use but also looks leaner and more like Node.JS that its alternatives.