npm-scripts as a Task Runner - Part 2

In the previous article (Part 1), we gave an introduction on how to use npm-scripts as a task runner, and we used a sample project to implement functionality for a CSS build task.

Here in Part 2, we will add a JavaScript build task, and an image compression task to the sample project. We will also implement functionality for executing them in parallel.

JavaScript Build Task

The JavaScript task to be implemented will resolve and link dependencies specified by the require() statements written in ES6 JavaScript code. It will then convert the code to the ES5 standard, compressing and obfuscating it before output.

You will need the following tools:

  • browserify
  • babelify
  • uglify-es
  • mkdirp
browserify
A tool that resolves dependencies, and allows you to use require() from Node.js in the browser.
browserify - npm
Browserify
babelify
Babel's transformation module for browserify. Used by specifying the -t option with browserify.
babelify - npm
Babel · The compiler for writing next generation JavaScript
uglify-es
A JavaScript compression/optimization tool that supports ES6+.
uglify-es - npm
mkdirp
If the specified directory does not exist, it creates directories recursively. It is the Node.js version of mkdir -p.
mkdirp - npm

Let's install them all together.

$ npm i -D browserify babelify uglify-es mkdirp

Next, let's define the task as shown below:

"scripts": {
    "build:js": "mkdirp dist/js && browserify -t babelify src/js/main.js | uglifyjs -c -m > dist/js/main.js"
},

First of all, create a folder with the mkdirp command to make sure that the destination folder exists. Then make sure that the tasks are executed sequentially by linking them with &&. Use browserify's -t(--transform) option to run babelify's transformation module. With shell scripts, when linking tasks, you use & for sequential execution and && for parallel execution.

Next, use a pipe to pass the execution result to uglifyjs. Specify the -c(--compress) and -m(--mangle) options to perform compression and obfuscation. Finally, use redirection to save the output to dist/js/main.js.

Execute the task using the following command:

$ npm run build:js

This completes the JavaScript build task.

Image Compression

Image compression is an essential step for improving web site performance. This time we will implement imagemin-cli, an image compression tool that supports JPG, PNG, GIF, and SVG.

imgaemin-cli
An image compression tool. imagemin's CLI.
imagemin-cli - npm

Install imagemin-cli.

$ npm i -D imagemin-cli

Add the task to scripts.

"scripts": {
    "build:images": "imagemin src/images/* -o dist/images"
},

The images task compresses the images in the src/images folder, and then outputs them to the dist/images folder. It is that simple.

Initialization of the dist Folder

Now that we have completed the image processing task, let's take a moment to think about the next step. During development, as images are added and deleted from the src/images folder, the dist folder could end up containing images that have already been deleted from the src folder. In order to avoid leaving the remote repository in a mess, after so many push operations, let's implement a task for resetting the dist folder.

Install the following tool:

rimraf
Recursively force deletes the contents of a directory. It is the Node.js version of rm -rf.
rimraf - npm

Install rimraf.

$ npm i -D rimraf

Add the task to scripts.

"scripts": {
    "clean": "rimraf dist/{css/*,js/*,images/*}"
},

When executed, this task empties the contents of the css, js, and images folders under the dist directory.

npm-run-all

Let's put together the JavaScript, CSS, and image tasks that we have created as shown below. → After resetting the dist folder, let's execute the JavaScript and CSS build tasks, and the image compression task in parallel.

Now, let's use the following tool:

npm-run-all
Allows you to execute multiple npm-scripts together, either sequentially or in parallel.
npm-run-all - npm

Install npm-run-all.

$ npm i -D npm-run-all

Put the tasks together as shown below:

"scripts": {
        "build": "npm run clean & npm-run-all -p build:*"
},

After resetting the dist folder first, subsequent tasks are executed together.

Use the -p(--parallel) option to execute the tasks in parallel. In addition, you can use wildcards to specify build:css, build:js, and build:images.

You can now use the following command to execute the necessary tasks at build time:

$ npm run build

At this point, package.json looks like this:

{
  "name": "sample",
  "version": "1.0.0",
  "scripts": {
    "clean": "rimraf dist/{css/*,js/*,images/*}",
    "build:css": "postcss src/css/main.pcss -o dist/css/main.css --no-map",
    "build:js": "mkdirp dist/js && browserify -t babelify src/js/main.js | uglifyjs -c -m > dist/js/main.js",
    "build:images": "imagemin src/images/* -o dist/images",
    "build": "npm run clean & npm-run-all -p build:*"
  },
  "devDependencies": {
    "babelify": "^7.3.0",
    "browserify": "^14.4.0",
    "cssnano": "^3.10.0",
    "imagemin-cli": "^3.0.0",
    "mkdirp": "^0.5.1",
    "npm-run-all": "^4.0.2",
    "postcss-cli": "^4.1.0",
    "postcss-cssnext": "^3.0.2",
    "postcss-import": "^10.0.0",
    "rimraf": "^2.6.1",
    "uglify-es": "^3.0.26"
  }
}

Let's move to Part 3 to complete the remaining tasks.