Building a Node package: what, why, and how
This post is a guide to writing and publishing your own Node package. In the tutorial, I’ll focus on creating a simple Node package that parses dates in a very particular way. But the steps involved are generally applicable to any Node package.
Assumptions
Let’s re-cap: Node is a way to use JavaScript on the server-side. When writing JavaScript in Node, you almost always need to require
packages that enable you to perform certain tasks.
This even goes for basic native tasks like accessing a file which usually means you need the line var fs = require("fs")
near the top of your file. After doing this you can use the properties of the object fs
to call the many Node file-system related functions.
But I’m going to assume you know most of this. That’s why I said “Let’s re-cap”. If you don’t know this, you can look at the official introduction to Node. But this post is focused on helping you to build your own packages.
Key questions
There’s already a good guide on npmjs.org explaining in simple steps how to create a package. But I wanted to convey the reasoning as to why you might want to do this yourself. Hopefully this will increase understanding of the steps involved.
Before starting to build anything, including a Node package, you’ll want to ask what it is exactly that you’re building, why you’re building it, and then how you’re going to go about building it.
1. What to build
You could, of course, build a package that gives you a function for adding two numbers together. Tutorials and guides are normally full of basic examples like this because they help explain the process without getting bogged down with it.
But they’re not really useful ideas that give you a sense of where to start building your own package. So I want to use a simple but useful idea I had for a package.
In my original code for the first “playpool” chart I created (which I described developing in my post on “Playpool charts”), I wrote a simple function specifically for parsing dates in the way that I needed to.
For example, it would take an object with an incomplete start date 198
, to represent the 1980s, and it would return an object containing a start date of 19800101000000
(to mark midnight on the first day of 1980) and an end date of 19891231235959
(the final second of 1989). It would also contain a precision index of to show that the original date hadn’t been that specific.
In other words, I could pass the function an object containing an incomplete start date and an optional and potentially incomplete end date. The function would complete the dates as necessary and return an index of precision regarding the original date.
2. “Why build a Node package?”
Some might say my date parsing function is too simple an idea to build a whole package for.
But I realised this function might be more generally useful for other visualisations I’m working on. Rather than duplicating the function across various of my experiments, and having to change it in all places, I decided therefore that I could more usefully abstract into a general-use package. After all, it might be of use to others too.
There’s a more general point in this. Node packages often contain code that can be usefully abstracted and reused across several projects, be they your own or potentially other people’s.
Furthermore, abstracting this simple piece of code into its package makes me think about all the other things I might want related functions to do around handling dates.
3. How to build a Node package
There’s quite a useful tutorial on the Node Package Manager (NPM) site itself, although it is brief. The rest of this post goes into a bit more detail and uses a specific example.
Initial set-up
There’s a bit of initial set-up involved in starting work on a Node package, even if you have some or all of the code written already. These are the steps I’ve followed.
Individual folder
I’ve created an individual folder for the space where I would create the package. Although I already had a working function in my visualisation code, I knew that I would want to version further changes and refactoring. To accommodate this I’ve made the folder a Git repository using git init
.
Beyond versioning this will also enable me to back-up my code online.
This is also a good time to think about a name for your package.
Naming your package
Your package name should be memorable, related to the function your package performs or the features it offers, and ideally unique.
Once you’ve come up with an idea for a name, it’s good to search npmjs.org for it to ensure it hasn’t been taken. If it has, you can try variations on it or you can try scoping the package to your username by following the guide on package scoping.
Why dickie-dates
?
I’ve decided to call my simple date-parsing package dickie-dates
. Why? Several reasons:
- My package is about handling awkward dates – as, in fact detailed in my first post on awkward dates. Instead of saying “awkward”, you might say that the dates I’m concerned with are “unsound”, “unsteady”, perhaps even “unreliable” – or, to use the British English colloquial term, “dickie”.
- The name was inspired by the fact that many of the dates I’ve been trying to interpret are from a study of and detailed notes from Mafia Republic, a book by UCL academic and mafiologist John Dickie.
- The name is unique I need an unusual and unique name for the package to be successfully published on the Node Package Manager (NPM) site.
- I like the alliteration of it.
Github repo
Next I went to create a Github repository for new package code, using the name I’ve decided on.
Once I had an address for the repository I could add it as a remote to my local repository using git remote add origin
followed by the address.
NPM init
Finally, as part of the set-up, I used the npm init
command.
The Node Package Manager ecosystem requires that your code repository has a file called package.json
. This file contains information about the name of your package, your name, and the address of the Github repository address. More crucially, however, it can contain lists of dependencies – which other packages your package requires to work – and details about testing your package.
The command npm init
simply helps you get set up in a quick and easy way.
Questions
When entering this command, you will get a series of questions about your package, most of which you can skip through if you’re starting out and change the answers later if you need to:
name
This defaults to name of folder (in my casedickie-dates
). So if you’ve named the folder correctly you can simply hit “Enter”.version
This defaults to1.0.0
. I normally change this to0.0.0
but if you’re comfortable with a major release simply hit “Enter”.description
This should be a one-line summary of what your package can do … or can simply be left blank by hitting “Enter”.entry point
[defaults to index.js] As long as you’re happy to call your code fileindex.js
, just hit “Enter” again.test command
I talk about testing further down in this post. Hit “Enter” for now to skip this one.git repository
Here you should type in your GitHub username following by a slash, followed by name of your repo. And then, of course, hit “Enter”.keywords
Allows you to add searchable words for package. For now you’re probably better off coming back to it another time. Skip this one by hitting “Enter”.author
Here you should give your name and if you want put your email address in angled brackets. This is mine:Guy Pursey <guypursey@gmail.com>
license
This will default to a permissive ISC license. Unless you have specific restrictions or a specific license in mind, simply hit “Enter”.
Packaged up
Once you’ve done this you’ll be shown the results and asked to check you’re to proceed. Once you’ve approved it, you can see and change the results for yourself by opening the newly created package.json
.
But with that set-up out of the way, you’re probably keen to get to your actual code.
The code
I already had the beginnings of the code that I want to use. It’s a function that exists in code I have already written elsewhere so I just need to extract it and turn it into a Node package.
So I only need to copy the function to a file within my Node package folder, which I will call index.js
. This aligns with what I originally entered in package.json
as the “entry point” file for the package above. It’s list in the main
property in package.json
.
Minor tweak
One minor tweak to make to my code which you will also need to make to yours – the function must be made available to the Node environment by means of module.exports
. As we will see this is what allows the function to be accessible when a package is required.
In line 149 of my original visualisation code, I simply declare the function as a function:
var parseDate = function (date) {
// function code is here
}
In using the code in a Node package, I’ve made the function a property of an object – an object which is then passed to Node’s module.exports
property:
module.exports = {
parseDate: function (date) {
// function code is here
}
}
By doing this, I’m able to access the function by requiring the package. Let’s test this out.
Local testing and usage
Testing your code early and often will give you the reassurance that you are on the right track.
First, we’ll create a test from outside the folder to prove the package is working as a package, then we’ll work on creating a more formal test within the package that will tell us whether the package is working as expected at any stage of development.
Try it out
The first basic test that will give you confidence your package is set up correctly is writing a piece of code that calls on your function.
Say you create a quick test file in the working folder itself. You could call it test.js
. If you don’t want to version it you could simply add test.js
to your .gitignore
file to reduce the chance it being picked up by NPM if you decide to publish.
All your test file need do is require the file and use the function within it. In my case, that means:
var dickiedates = require("./index.js")
console.log(dickiedates.parseDate("198"))
With those two lines, I can run a simple test.
I save the file, go back to command-line, and use the command node test.js
. This then gives me the result that I want.
Depending on what you expect your function to return you may log it as I have in the example lines above or you may try to assert that the value returned is equivalent to something or has some property. So you change the test accordingly.
In actuality, you are very likely going to want to test a number of things about the results you get from your new function. You’ll also want to try a number of inputs to ensure your code is working. You could keep adding to your test.js
file but there is a better way.
Automated testing
When running npm init
to set up your package.json
file, you probably skipped over the test part. I even recommended doing so in the steps above so I could cover it in more detail here.
As a result your package.json
file will contain something like this:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
All this means is that when you run npm test
, you will see the message Error: no test specified.
because by skipping the test in the set-up that’s effectively what you’ve asked NPM to do.
This is easy to fix however when you’re ready to start setting up tests.
Example test suite: Mocha
For example, you can replace this error logging command with the word mocha
like so.
"scripts": {
"test": "mocha"
}
Mocha is an automated testing suite. In order to use this, I need to add the following to my package.json
file:
"devDependencies": {
"chai": "^3.3.0",
"mocha": "^2.3.3"
},
Both Mocha and Chai are the testing suites I’ll be using to write my tests but other options are available. These are values are stored in a devDependencies
object to indicate to NPM that there’s no no need for a casual user to download these packages too. They can simply get a production version, without these dependencies, by using npm install --production
. But because I’ll need them, I’ve had to use the command npm install
to ensure that the packages are there when I’m testing.
Creating a test folder
Mocha will look for test files in a folder called test
. So create this.
mkdir test
You could then move your test.js
file, created earlier into this folder. But Mocha will expect to see your testing file written in a particular style. This is where Chai comes in.
Writing with Chai
Chai is just one type of “assertion library” that you can use to write tests in. Rather than simply logging the results of a test directly to the console, asserting libraries get you to follow a particular set of conventions to allow for easier reporting.
// Remember we're accessing `index.js` from a subfolder now.
const dickiedates = require("../index.js")
// This is a feature of the Chai package used to check results.
const expect = require("chai").expect
// Build the beginning of a suite of tests.
describe("Using `dickie-dates` like `198` to represent the 1980s", function () {
// This is where my package is actually used.
let result = dickiedates.parseDate("198")
// This is where the result is checked.
it("should return an object", function () {
// All I'm saying here is that I expect my package to have returned an object.
expect(result).to.be.an("object")
})
}
}
}
This code merely checks that the result returned is an object. I can add multiple expectations to the suite of tests in the file and build suites within suites.
By writing my tests in this style, I can perform a number of tests very quickly with a single npm test
command and generate a quick and legible report on the state of my package. That way if I’m working on some new feature, my test will quickly tell me if I’ve broken an existing feature.
And that’s not all.
Aspirational tests
I can actually write tests for features that don’t exist yet. Of course, they will fail straight away but making them pass then becomes the aim of any further development. The testing doesn’t just have to check whether your code has regressed or broken – it can also be used to measure whether you’ve progressed or improved it.
You can read more about this approach in my post on behaviour-driven development (BDD).
Publishing the package
Finally, if and when you’re ready, there’s a very clear and useful guide on NPM’s own site for publishing your new package.
I link to this page because it goes into the specifics of setting up a user account with NPM and logging in via the command line. Not very complicated but it doesn’t need to be duplicated here.
You can also look at the results of the package I created as an example for this guide.
Not my first
This isn’t my first Node package. I’ve also written and published a package called hithercontent
which you can see on the Node Package Manager (NPM) site, specifically on the hithercontent
package page.
Conclusion
And that’s it. An introductory guide to what you might create as a Node package, why, and the details on how to get started.
Next: enhancing your Node package
As I develop my dickie-dates
package further, I’m tempted to write a further guide for other aspects of a package, from versioning to README
files to build status icons.
There are other aspects of releasing a package that I’m not familiar with myself yet, so it will be a good way for me to learn.
A personal aside
I’ve been away on holiday recently and, following that, pretty ill. This has thrown me off my regular blog posting routine which I’ll be trying my best to get back into over the next couple of weeks. But it’s been good to write something that is more than a simple log of my activity. Writing a guide like this, that isnt merely step-by-step has been a challenge.
Hopefully, this will prove useful as a general guide or as a measure of reassurance for those experimenting with this for the first time – or those coming back to it after a while.
Coming soon
In terms of upcoming content, next I hope to do some refactoring of the dickie-dates
package and start incorporating this into further “playpool” visualisation experiments.