In this post we will see how it is possible to configure an entire build sequence using npm only.

What we want to achieve:

One command npm run package should:

  • clean our build folder
  • compile our css from stylus
  • compile our html from jade
  • bundle and minify our javascript

1) Minimal project structure

Let’s assume our project is organized as follows:

1
2
3
4
5
6
7
8
9
|-- dist // Build will end up here
|-- src // All source code is inside this folder
|-- stylus
|-- site.styl // Our main stylus file
|-- index.jade // Our jade file
|-- js
|-- main.js // Our main JavaScript file
|-- jquery.js // jQuery library. Required in main.js
|-- package.json // Where our build is configured !

2) Compile our CSS and HTML

Stylus to CSS

We can compile our stylus with the following command:

stylus src/stylus/site.styl --out dist/

Let’s set this up in the package.json: Read this post if you want to know the basics of setting up scripts in npm.

Jade to HTML

In a similar fashion, we can compile our Jade templates to HTML using the following command:

jade src --out dist/

Add the npm tasks

In our package.json, we can now create the tow following scripts:

1
2
3
4
"scripts": {
"package:stylus": "stylus src/stylus/site.styl --out dist/",
"package:jade": "jade src --out dist/"
},

3) Bundle and minify the JavaScript

Bundle with browserify

(Here is an introduction to browserify if needed)

The command to bundle our JS into one file is browserify ./src/js/main.js -o ./dist/bundle.js

Minify with uglify

I am using uglifyJS2 for this task.

The task to minify the bundle.js file is uglifyjs -- compress ./dist/bundle.js -o ./dist/bundle.min.js

Piping the two

Now what we want is to pipe the output of the browserify task into the uglify task so that we can combine both into one npm task

To do this, we are going to use a unix pipe | and connect the STDOUT of the first task with the STDIN of the second.

browserify ./src/js/main.js | uglifyjs --compress > ./dist/bundle.min.js

Let’s set it up as a npm task:

1
2
3
4
"scripts": {
...
"package:js": "browserify ./src/js/main.js | uglifyjs --compress > ./dist/bundle.min.js"
},

4) Putting everything together

Now let’s combine all our tasks in one npm package task:

"package": "npm run package:stylus && npm run package:jade && npm run package:js"

This will run our 3 package tasks in a sequence.

1
2
3
4
5
6
"scripts": {
"package:stylus": "stylus src/stylus/site.styl --out dist/",
"package:jade": "jade src --out dist/",
"package:js": "browserify ./src/js/main.js | uglifyjs --compress > ./dist/bundle.min.js",
"package": "npm run package:stylus && npm run package:jade && npm run package:js"
},

Run npm run package to test the full build sequence.

5) Cleaning up the output directory

Before running the build, we would like to clean the output directory.

We will use rimraf’s CLI like so: rimraf dist/*.

Instead of creating another ‘package:clean’ task, we can use an npm script prehook. By prefixing an existing task (even custom) with pre or post, it will be automatically executed before or after our main task.

In our current project, if we create a task called <b>pre</b>package it will be executed automatically before our package task. In a similar way, <b>post</b>package would be executed after package.

Our final task:

"prepackage": "rimraf dist/*"

and here is the final scripts object in he package:

1
2
3
4
5
6
7
"scripts": {
"package:stylus": "stylus src/stylus/site.styl --out dist/",
"package:jade": "jade src --out dist/",
"package:js": "browserify ./src/js/main.js | uglifyjs --compress > ./dist/bundle.min.js",
"package": "npm run package:stylus && npm run package:jade && npm run package:js",
"prepackage": "rimraf dist/*"
},