Namek Dev
a developer's log
NamekDev

gulp.js is my build system

October 31, 2014

When I’ve seen Grunt (a build system) I have imagined that it would be awesome to have such setup making my code easy to maintain and optimized production version at the same time. When I tried to create my n-th Gruntfile.js, after some more play with Grunt, I realized that this system didn’t work as well as supposed to. That was the moment I gave a chance to gulp - “the streaming build system”. I’m gonna talk about why and why so much “yes”!

Let’s compare them quickly

  1. Grunt - Configuration over Code
  2. gulp - Code over Configuration

See the difference?

Now, follow me. Grunt simply takes some JavaScript objects and based on them configures paths, tasks, options and so on. Meanwhile, gulp doesn’t want to read crowdy JSON configuration. Instead, it works well with just JavaScript instructions giving a simple straightforward API to use. The power comes in when streaming data through pipes is shown. Take a look at this one simple pipe written in gulp:

gulp.src(Path.sassSrc)
 .pipe(watch())
 .pipe(plumber())
 .pipe(sass())
 .pipe(autoprefixer())
 .pipe(polyfill())
 .pipe(gulp.dest(Path.cssDest))

OMG what was that?! Yeah, pretty readable. Source path appers here only once. Files go through all operations (pipe parts) until they reach some destination. Just compare it to the Grunt version (it’s not 100% same but roughly the same):

grunt.initConfig({
    config: config,

    watch: {
        sass: {
            files: ['<%= config.sassSrc %>/{,*/}*.{scss,sass}'],
            tasks: ['newer:sass:dist', 'newer:autoprefixer', 'copy:compiledStyles']
        }
    },

    sass: {
        options: {
            loadPath: [
                'bower_components'
            ]
        },
        dist: {
            files: [{
                expand: true,
                cwd: '<%= config.sassSrc %>',
                src: ['*.scss'],
                dest: '.tmp/styles',
                ext: '.css'
            }]
        }
    },

    autoprefixer: {
        dist: {
            files: [{
                expand: true,
                cwd: '.tmp/styles/',
                src: '{,*/}*.css',
                dest: '.tmp/styles/'
            }]
        }
    },

    copy: {
        compiledStyles: {
            expand: true,
            dot: true,
            cwd: '<%= config.app %>',
            src: '.tmp/styles/{,*/}*/*.css',
            dest: '<%= config.cssDest %>'
        }
    }
});

There are no pipes, no love and instead - temporary folders and lots of configuration here and there.

Take look at above code once again. It’s Grunt. When Gruntfile grows bigger, it gets harder to read, understand and maintain. gulpfile.js wins the competition for sure.

Performance over Code

Let’s first talk about why I have even looked at gulp.

I was interested to have: minify/uglify, watch & livereload (html, laravel blade php, scss) and compressing project into one zip file (making a package).

Problem with plugins for Grunt: SLOW. I mean slow with Sass which is built on top of Ruby which takes about 2.5 seconds for a not that big scss file. Sure, I did try grunt-sass instead of grunt-contrib-sass, it’s a way faster (2.5 secs dropped down to about 150 ms) but Grunt alone still did a work for more than a one and a half second. Why? grunt-contrib-watch was the cause - after each found change of any file it spawned a new process for whole Gruntfile.js.

Thanks to discussions on github I have finally managed to workaround that by using {spawn: false} Now altogether scss file compilation took about 150ms instead of 4+ secs! But to make it work properly I had to link each watch task into concurrent task. There, in concurrent I had to set bigger limit. I hoped that it will finally work but I had to restart whole grunt due to some internal invisible crash or whatever - it just stopped working.

This is mess. If you understand any of it, you’re fine. If not - you’ll be fine too, because of **DON’T USE GRUNT FOR LIVERELOAD **advice. It’s a hell to configure it and generally buggy.

Code over Configuration

Grunt is fine. I mean, it was fine for me, until gulp’s streaming appeared. gulp is much more general. Firs of all, it’s a tad easier to develop grunt on top of gulp than opposite. And that’s because of the “code”. Grunt is much more conventional (the “configuration”) which doesn’t work when it grows.

Streaming… you mean pipes? Yeah, pipe. Actually, parts of pipe. When data comes through pipe it means that data is “moved” inside of memory - no hard disk usage here, excluding first read of needed files! Scripts (or rather - configurations) made in Grunt often create temporary files on hard disk. This is bad for both performance and purity. It matters when script stops by error and then we are left with lots of unwanted files on disk. It’s even worse when Gruntfile.js isn’t ours and we don’t actually know which files are just temporary. A little bit messy.

However, it’s not always easy to make some things done in gulp. It’s “code” so logical errors are shown in runtime. Coming from Grunt ground, gulp users often expierence EEONT error due to difference between those two systems:

  1. Grunt prefers sequential evaluation. Parallel can be done through conqurent plugin.
  2. gulp prefers parallell approach instead. Trying to clean and build at the same time won’t work properly. To do sequential we need node plugin called run-sequence or another alike solution.

Parallelling is good. Really. I think it’s even more general than sequential. Each parallel task can wait before start until another task ends. Using sequantial approach we’re not able to parallelize it.

In gulp it’s possible to start few parallel pipes and wait until all of them end. merge-stream plugin is needed but it’s pretty easy to do. And pretty hard to achieve in Grunt. Playing with concurrent like that makes me having a headache.

Judgement?

Pipe can be configured to handle errors in the way you want. It’s easier to read through coded pipe than scattered configuration for just one task. Just by looking at pipe you know what is the order of operations at first glance! Looking at scattered configuration takes more time to understand where and why actually something doesn’t work.

I judge pretty easily. When it’s hard to maintain things I look for solutions to do it easier and gulp is the one here!

Resources

web, javascript
comments powered by Disqus