Node Modules

This week I worked on and helped publish a node module for pushing source maps to a public API. It’s only the second time I’ve worked on a from-scratch node module, and it’s enlightening — like pulling back the curtain to reveal the truth behind the Wizard of Oz.

Node modules aren’t magical (though they’re no humbugs either). They fulfill a pretty specific purpose: a way to encapsulate reusable code in a way that’s easily managed.

Why node modules matter

Before node modules were really a thing, reusable code snippets were often pulled out into Immediately Invoked Function Expressions (IIFEs) or stored inside of variables (the Revealing Module pattern).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var sayAName = (function () {
     var privateName = 'name is not set yet';

     function publicSetName (name) {
          privateName = name;
     }

     function publicSayName () {
          console.log('Hi, ' + privateName);
     }

     return {
          setName: publicSetName,
          sayName: publicSayName
     };
})()

sayAName.setName('emily')
sayAName.sayName()
// 'emily'
Example inspired by Addy Osmani’s Design Patterns

But with this pattern, you don’t have much ability to manage dependencies or make explicit versioning changes. That’s where node modules are so handy. Not only is the code easily reusable and self-contained, but a version change is as easy as bumping a number in the package.json.

Node modules come in a couple different formats, but the new ES6 format is the one I’m most familiar with.

1
2
3
4
5
6
7
// lib/bye.js
export function sayGoodbye(){
  console.log('Bye');
}

// app.js
import { sayGoodbye } from './lib/bye'

ES6’s native module format uses export and import tokens to share consts, functions, and objects between files.

(I’ve also used module.exports and require, the CommonJS module format, in various projects depending on whether we’re transpiling to support ES5.)

1
2
3
4
5
6
var dep1 = require('./dep1');
var dep2 = require('./dep2');

module.exports = function(){
  // ...
}

Publishing node modules

Publishing a node module to npm is a pretty sweet feeling – like getting a book published, except much easier. Although versioning comes with its own quirks.

When working on the node module for pushing source maps, we made several small changes to the 4.0.1 release to fix some tiny bugs. We published those changes to npm as pre-releases, indicating that the changes were not yet stable. The pre-release version numbers looked like this: 4.0.1-2

When you installed @latest or an unspecified version number, though, you got the pre-release instead of the release (4.0.1). That’s not ideal behavior; you want people to be able to opt into prereleases.

Here’s the trick: when publishing to npm, you can tag a release with prerelease or ‘next’ explicitly, which will override the default tag of latest. Then you can make incremental changes to your heart’s content until it’s time for the next real version bump!

Sources