Sam Hooke

Automating hugo development with npm scripts

I wanted one command I could run that would automatically watch my SCSS files and HTML files, and build them upon change, and serve up the resulting website. In order to avoid complicating development by introducing a task runner as an additional dependency (such as grunt or gulp) I decided to try using npm scripts. It turned out to be very simple and easy!

First, I install npm (OS is Arch Linux):

$ sudo pacman -S npm
$ npm --version
5.6.0

Then, from the root of my hugo project I use npm init to create my package.json. It asks a series of questions which are quite straightforward to follow:

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (example) example-package
version: (1.0.0) 0.0.1
description: Example package
entry point: (index.js)
test command:
git repository:
keywords:
author: My Name
license: (ISC)
About to write to /home/<user>/example/package.json:

{
  "name": "example-package",
  "version": "0.0.1",
  "description": "Example package",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "My Name",
  "license": "ISC"
}


Is this ok? (yes) yes

After this you will have a bare-bones package.json such as the one shown above. Note that we did not specify a test command, but npm created a stub for the test command anyway in the "scripts" section. We can run this with npm test:

$ npm test
> example-package@0.0.1 test /home/<user>/example
> echo "Error: no test specified" && exit 1

Error: no test specified
npm ERR! Test failed.  See above for more details.

As expected, the command fails with Error: no test specified. It is easy to add your own custom commands. As a simple example, if you want running npm serve to run hugo serve --buildDrafts, then add the following script to your package.json:

{
  [...]
  "scripts": {
    "serve": "hugo serve --buildDrafts"
  }
}

npm scripts can call system commands, other npm packages, and even call other npm scripts. This ability to chain npm scripts can be very useful for building up basic commands into useful compound commands.

For example, first make a sass:build command which builds your SCSS files into CSS. Then make a sass:watch command which uses npm onchange to watch your SCSS files and call sass:build if they are modified. Finally, create a start command which calls sass:build before using parallelshell to simultaneously run sass:watch and serve:

{
  [...]
  "scripts": {
    "start": "npm run sass:build && parallelshell 'npm run sass:watch' 'npm run serve'",
    "sass:build": "node-sass assets/scss/main.scss static/css/style.css",
    "sass:watch": "onchange 'assets/scss/**/*.scss' -- npm run sass:build",
    "serve": "hugo serve --buildDrafts"
  },
  "devDependencies": {
    "node-sass": "^4.7.2",
    "normalize-scss": "^7.0.1",
    "onchange": "^3.3.0",
    "parallelshell": "^3.0.2"
  }
}

The above allows you to simply call npm start, and then you can edit your SASS files or hugo HTML/Markdown files (etc) and they are automatically rebuilt and the browser reloads the page.