Using NPM as a Build Tool

NPM isn't just a package management tool, it is much more than that.

You've probably seen the "scripts" attribute of the "package.json" file and noticed you can add your run and test directives there. You can do a lot more than just "run" and "test".

I've used Grunt (mainly) and Gulp build tools, but using npm is so much better IMHO. You can use a combination of these build tools if you want, nothing in this universe is totally black or white. Whatever works.

Running scripts

We're used to running scripts like npm test and npm start, so running your npm scripts are similar except with "run" added: npm run my-cool-script. The "start" and "test" omit the "run" as a shortcut.

Operators

NPM also has some operators you can use with your scripts:

  • > compiles the lhs into the rhs file, i.e. browser/js/main.js > public/js/app.js
  • | pipes lhs into rhs, i.e. browserify browser/js/main.js | uglifyjs --compress --mangle > public/js/app.js
  • && run commands sequentially, i.e. lint && mocha
  • & runs commands in parallel i.e. npm run watch-css & npm run watch-js

Types of scripts

For this example I'm going to categorise scripts into test, build (JavaScript, SASS/SCSS, other assets), and watch. There are others you may want to use such as deploy, pre-build et al. What I want my scripts to do is this:

  • run tests on our JavaScript
  • convert our SASS/SCSS files into css
  • concat, uglify, and export JavaScript into the public/js folder
  • in dev mode, watch our server files and restart on change

Tests

We can leave our default test for individual tests but setup a watcher for our test files. Mocha has a flag -w which will run tests, and then watch for changes in the "test" directory by default. I'm going to include any "linting" in this stage too.

In our script we can add this:

  "scripts": {
    "test": "mocha",
    "lint": "jshint app.js server/*/*.js client/js/*.js",
    "watch-test": "mocha -w"
  }

Builds

SASS/SCSS

For our SASS/SCSS files we can use "node-sass".

  "scripts": {
    "test": "mocha",
    "lint": "jshint app.js server/*/*.js client/js/*.js",
    "watch-test": "mocha -w",
    "build-css": "node-sass --include-path scss client/scss/main.scss public/css/style.css",
  }

JavaScript

What we want to do is concatenate our files together, and then minify it. We can do this with "browserify" and "uglify".

  "scripts": {
    "test": "mocha",
    "lint": "jshint app.js server/*/*.js client/js/*.js",
    "watch-test": "mocha -w",
    "build-css": "node-sass --include-path scss client/scss/main.scss public/css/style.css",
    "build-js": "browserify client/js/main.js | uglifyjs --compress --mangle > public/js/app.js",
    "watch-css": "nodemon -e scss -x 'npm run build-css'",
    "watch-js": "nodemon --watch client/js -x 'npm run build-js'",
    "watch-server": "nodemon --watch app.js --watch server",
    "dev": "npm run watch-js & npm run watch-css & npm run watch-test & npm run watch-test"
  }

Notice the pipe operator "|" which pipes the result of calling "browserify" to "uglify".

I'm using "nodemon" and setting it to watch any files with the ".scss" extension to trigger the "build-css" script. Likewise for the JavaScript files, any changes in "client/js" will trigger the "build-js" script.

I'm also setting "nodemon" to run our server too. "dev" brings it all together at this point with the script "dev". Running "npm run dev" will build the scripts, css, and run tests.

Github repo

I've set up a basic github repo for the above. npm rocks!

Resources