From Gulp to NPM Scripts

The modern web developer has to automate lots of little tasks, and there are lots of tools to help. Grunt? Gulp? Webpack? I’ve tried them all, and there are good things to be said about all of them. My favorite is Gulp, as it feels like writing real Javascript. The others feel like monkeying around the engine of somebody else’s car.

For a recent project refactor, I decided to give NPM scripts a go. It went really well.

Here are the tasks I need to perform for this project:

  • Postcss my CSS with some plugins
  • Browserify my javascript with some plugins
  • Minimize/optimize my images
  • Handlerbars my HTML
  • Create folders and copy files
  • Run a live reload server for development

Here’s what that ended up looking like in package.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
"scripts": {
"start": "npm-run-all --parallel watch:*",
"watch:js": "watchify app/js/app.js -o public/js/app.js",
"watch:css": "onchange 'app/css/**/*.css' -- postcss -m -u postcss-import -u autoprefixer --autoprefixer.browsers 'last 2 versions' -o public/css/main.css app/css/main.css",
"watch:html": "onchange 'app/*.html' -- npm run build:html",
"watch:serve": "browser-sync --server \"public\" start --files \"public/**/*.html\" \"public/**/*.css\" \"public/**/*.js\" ",
"build:html": "mkdirp public && node build/cachebuster.js",
"build:copy": "ncp app/style public/style",
"build:imagemin": "mkdirp public/img && imagemin app/img/* --out-dir=public/img",
"build:js": "mkdirp public/js && cross-env NODE_ENV=production browserify -g envify app/js/app.js | uglifyjs -c warnings=false -m > public/js/app.js",
"build:css": "mkdirp public/css && postcss -u postcss-import -u cssnano -u autoprefixer --autoprefixer.browsers '> 5%' -o public/css/main.css app/css/main.css",
"build": "npm-run-all --parallel build:*"
}

To build the project I type npm run build. To run the development environment, it’s npm start. To make sure it works on Windows and Linux, I’m eschewing system commands for node modules, like the node module mkdirp instead of bash’s mkdir -p. npm-run-all has a --parallel option to run scripts synchronously, speeding things up.

Further down in my package.json I set some modules plugins I need:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"babel": {
"presets": [
"es2015"
],
"plugins": [
"transform-runtime"
]
},
"browserify": {
"transform": [
"vueify",
"babelify"
]
},

If you have complex tasks that won’t fit or work on a command line, you can put them in a Javascript file and execute them via node. I literally use handlebars just to drop in a random number to invalidate cached CSS/JS. I couldn’t figure out how to execute that via CLI, so I made a tiny Javascript file:

1
2
3
4
5
6
7
8
9
10
11
12
13
let handlebars = require('handlebars'),
fs = require('fs'),
path = require('path');


var source = fs.readFileSync('./app/index.html', 'utf-8').toString();
var data = {
cachebuster: Math.floor((Math.random() * 100000) + 1)
};

var template = handlebars.compile(source);
var html = template(data);
fs.writeFileSync(path.join('./public/', 'index.html'), html);

It get excuted in NPM scripts via "build:html": "mkdirp public && node build/cachebuster.js".

This setup is much easier to grok and manage than my ever-swelling Gulp file, at least for me.

On a side note, when dealing with Browserify in conjunction with Reactify or Vueify, use watchify for your live server stuff. It does incremental rebuilds and really speeds things along.

Hat tip to Why npm Scripts? for code snippets and inspiring me to try this.