An article entitled Why I Left Gulp and Grunt for npm Scripts has been doing the rounds on Twitter over the last couple days, and I figured I'd share my thoughts about why I disagree with the article and the many similar articles like it.
#Complicated tasks: an example
Our browserify task is a good example of a task that would be near impossible without gulp. This is what it does:
- It calls browserify, which does a few things
- It (obviously) packages our modules into one file
- It calls babel, which deals with our ES6 and JSX
- It sets up hot module loading
- It inlines some environmental variables
- We add our own copy of jQuery so that if a dependency tries to add jQuery@2, it can't: more on that here
- Then, we transform the browserify stream into a gulp stream and call a few gulp plugins:
- We remove
console.log()statements with gulp-strip-debug
- We call a function which combines a few gulp tasks which we call after every task which does this:
You can find the full source of our browserify task here.
There are two things that gulp make very nice here. The first is sourcemaps.
The revision stuff mentioned previously is another case where gulp shines. We want a load of our tasks (such as js, css, responsive-images, svg fallback) to generate revisioned assets. What do we do with gulp? We use multipipe to merge all the common streams we want to pass every task to, and create just one function. We can then treat this function just like any other gulp plugin: this is a big advantage of using streams like gulp does.
Here is our
#Not everyone is using tools like browserify
Sure, if you're using browserify, you can just call it directly. But what if you don't want to do that? What if you just want to concatenate three files and then minify them? You want to use streams for this, and you don't want to rewrite Vinyl, so we're going to use bash for this.
You now have a couple problems:
#Which tool do you use?
With gulp, we would use the gulp plugin for Uglify2. However, while it has a command line interface, it doesn't seem to accept streams, so we can't do anything like the following:
$ cat src/**/*.js | uglify2 > app.min.js
That's a shame. Now we need to find a different tool, because we can't use that one.
If I ask a junior developer on my team to write me a gulp task to concatenate and minify a few files, they'll be able to do it in minutes. It's super easy.
Junior developers can comfortably work with gulp. npm scripts, not so much.
#npm scripts are powerful enough: but are less verbose
In gulp, if I want to make a build task that runs a
lint task, then runs the
css tasks simultaneously, I do the following:
gulp.task('build', gulp.series('lint', gulp.parallel('js', 'css')));
To do that in an npm script, I do this:
"build": "npm run lint && ( npm run css & npm run js )"
I'm sure other people will disagree with me, but I prefer the former syntax.
#More complicated tasks
gulp makes working with multiple libraries together a lot easier.
#Dependence on Plugin Authors
I'll admit, this one can sometimes be a genuine pain point. If a plugin author stops using their plugin, it can be a while before they update it. However, this barely ever happens, and you can usually work around it! Because of the way npm works, you can specify your own version of the library in the root
package.json and the plugin will start using that version instead.
It's also worth noting that the gulp plugins of most of the libraries he mentioned are actually maintained by the same people who write the plugin itself: for example, gulp-babel is hosted on the babel organisation. mocha, webpack, and browserify should all be called directly, not by gulp plugins: has the author quite understood what gulp is for?
You can also send a pull request updating the plugin or fork the project!
#Number of plugins
The author uses this image to illustrate that there are more plugins available for npm than for gulp and Grunt.
This argument is wrong.
Gulp makes things that are near impossible without gulp seamless and easy, such as working with sourcemaps across multiple libraries and composing multiple libraries together for use over multiple tasks.
It's all very well saying "if you want to use this library, you can call it directly", but when you want multiple libraries to work together, that's where gulp shines: it provides a consistent API that plugins (and libraries directly) can use so you don't end up creating a mess of different temporary files which each plugin reads to and the next one writes from. Not all libraries support streams.