Building a Small NPM Package

In development, we don't just write code to build stuff; often we need to optimize our workflows to increase efficiency and reduce the amount of repetitive actions we perform.

For a while, I've been using Semantic Versioning in my projects, but I've found it to be pretty inconvenient when pushing releases frequently. I found myself repeating these steps constantly:

  • Modify the version property in package.json.
  • Make a git commit with the version number and any notes.
  • Add a git tag with the version number.
  • Push the commit and push the tags.

These steps could be reasonably automated with a bash script, or maybe a git alias. Either option would work perfectly well, but out of these two, only a bash script could reduce this process to one step. Unfortunately, a bash script wouldn't be something that I could attach to my projects explicitly, and would take some effort to distribute to collaborators. With this in mind, it seemed to me that a good approach would be to build this utility as a node package and include it as a dev-dependency in my projects.

In a past project, I used nodegit, a library that provides a programmatic interface with git, to automate commits and pushes. I would describe my experience working with it as "challenging" at best, and I wanted to avoid going that route if at all possible. This led me to looking into and learning about the possibility of issuing bash instructions from Node, which I'd never done before. After a little reading, I was introduced to the idea of child processes, exactly what I needed!

With a plan for how I'd be running git commands, I needed to read the package.json file, modify its version property, and overwrite it. I accomplished this using fs-extra:

const fs = require('fs-extra'),
  path = require('path'),
  // This will be relative to where the script is being run from.
  projectRoot = process.cwd(),
  targetPackage = path.resolve(projectRoot, 'package.json');

Then I needed a little function to modify the package.json and overwrite it:

function setVersion(package, version) {
  package.version = version;
  fs.writeJSONSync(targetPackage, package, { spaces: 2 });

This function requires us to pass in a version, which I needed to programmatically determine based on an argument, specifying a version bump of major, minor, or patch.