npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

jam3-lesson-module-creation

v1.0.6

Published

introduction to creating a new npm module

Downloads

18

Readme

jam3-lesson » module-creation

Creating a Module

This is a continuation of the module basics lesson. Here we'll learn to write a new module, work with Node's CommonJS syntax, and publish the code to npm.

steps

Once you're comfortable with the process, splitting code into its own module becomes trivial. The benefits of reusability and semantic versioning often outweigh the small upfront cost of creating a new module.

first steps

So, you've got a great idea for a module? The first step is to search npmjs.com to make sure you aren't re-inventing the wheel. If something already exists on npm, there is a chance it might be exactly what you need, but better tested and more depended-upon.

But let's say your module is unique, or specific to your needs, or the existing modules are dangerous to depend on (i.e. broad scope, no tests). This is where you can make your own module.

For this tutorial, we'll create a module that converts HSL to RGB values for a color picker. We can imagine an interface like this:

hsl2rgb([0, 0.5, 0.5]) 
// -> [0.75, 0.25, 0.25]

We use floats (0 .. 1) and bare arrays since these will compose well with other modules and various functional paradigms.

setup

Module names are lowercase and dash-separated. They are also unique, so make sure the name isn't already taken on npm!

Let's call our module float-hsl2rgb since it describes the problem we are trying to solve. In the terminal, make a new folder and move into it:

cd /path/to/npm-modules
mkdir float-hsl2rgb
cd float-hsl2rgb

Now, we need to set up our module. The simplest way of doing this is with npm init. This command will generate a package.json in your current directory. This file holds some information about your module.

npm init

Fill out the description and keyword fields as best you can. Leave the rest of the options unchanged for now.

npm init

entry point

Now let's create an entry point for our module. This is the code that will be used when people "import" the module into their applications. Create an empty file like so:

touch index.js

Note: In package.json, the "index" field must point to this file!

Now open the file in your editor, and copy the following method stub:

function hsl2rgb(hsl) {
  var h = hsl[0];
  var s = hsl[1];
  var l = hsl[2];

  //... HSL -> RGB logic ...

  return [ r, g, b ];
}

module.exports = hsl2rgb;

Node uses CommonJS as its module syntax. Here the module.exports is telling Node that our default export is the hsl2rgb function. You can export anything with this: like a string, or an object, or a class.

Now, when somebody installs our module, they will require it like so:

var hsl2rgb = require('float-hsl2rgb');
console.log(hsl2rgb(0.5, 0.25, 0.75));

You can also export multiple functions from a single entry point:

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

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

Then, they can be required individually:

var foo = require('some-module').foo;
var bar = require('some-module').bar;

development

Before we can start adding logic to our function, we need a way to test it and make sure its working as expected. Let's create a test file:

touch test.js

And copy the following into that file:

var hsl2rgb = require('./index.js');

console.log('is a function?', typeof hsl2rgb === 'function');

Here we are using a "relative" require statement. Require statements come in three flavours:

  • if the path starts with ./ or ../, the search is relative to the current file
  • if the path starts with /, the file path is assumed to be absolute
  • otherwise, the search looks within your node_modules folder for an installed module

When a folder is encountered, Node looks for the package.json "index" field, or defaults to index.js if there is none specified. The following are all valid require statements:

//these all resolve to ./foo/bar/index.js
require('./foo/bar/index.js');
require('./foo/bar/index');
require('./foo/bar');

//looks within the local node_modules folder
require('domready');

//some modules come built-in with Node
require('url');
require('fs');

test runner

We can test our file like this:

node test.js

It should print true because of the console.log we copied earlier.

Instead of re-typing this command all the time, we can use a tool to re-run the test on file change. Install nodemon globally like this:

npm install nodemon -g

Now we can run it on our file:

nodemon test.js

Now changing either test.js or index.js will re-run the script.

nodemon

testing

Now let's add some logic to our function. Usually, you would be writing it yourself, but for this tutorial you can copy the implementation here into index.js.

While we add the code, let's also test it to make sure its working as expected. We can start adding some assertions like this to test.js:

var hsl2rgb = require('./index.js');

console.log('is a function?', typeof hsl2rgb === 'function');

var rgb1 = hsl2rgb([0, 0, 0]);
console.log('returns array of 3 values?', rgb1.length === 3);

var redHSL = [0 / 360, 1, 0.5]; //(hue=0,   sat=100%, light=50%)
var redRGB = [1, 0, 0];         //(red=255, green=0,  blue=0)
var rgb2 = hsl2rgb(redHSL);
console.log('actual:', rgb2, 'expected:', redRGB);

Now the nodemon process should print the following:

is a function? true
returns array of 3 values? true
actual: [ 1, 0, 0 ] expected: [ 1, 0, 0 ]

automated testing

You'll notice the last step involves a lot of eye-balling. When you have a lot of assertions, you can start to miss things. It's better to automate the tests so we always know when something is broken. For this, we will use the tape module.

npm install tape --save-dev

This will save the result into node_modules/tape. The --save-dev flag updates our package.json with the new module.

Since this is only used for testing, we are saving it as a "devDependency". If our index.js depended on another module to work, we would list it as a "dependency" and use the --save flag instead.

We can change the test.js file to the following:

var hsl2rgb = require('./index.js');
var test = require('tape');

test('converts [H,S,L] to [R,G,B]', function (t) {
  t.equal(typeof hsl2rgb, 'function', 'is a function');
  t.equal(hsl2rgb([0, 0, 0]).length, 3, 'returns array of 3 values');
  t.deepEqual(hsl2rgb([0 / 360, 1, 0.5]), [1, 0, 0], 'converts red');
  t.end();
});

Now the nodemon process will report whether any of the tests failed.

test

You can see here for some examples of other HSL to RGB assertions.

README.md

Now that everything works, add a README.md file with some details on your module and how to use it. This uses Markdown for styling.

touch README.md

You can see an example readme here.

GitHub repository

Next, we can make a new GitHub repository for our module. Before we make our repository, make sure to include a .gitignore file:

node_modules
*.log
.DS_Store

Tip: Copy the above and run pbpaste > .gitignore to create a new file.

For a quick way to publish a new repository, you can use ghrepo which is geared toward npm modules. Install it like so:

npm install ghrepo -g

Then run it in your module folder:

ghrepo -m 'first commit'

This will create a new repository on your account and push your current folder to it.

github

publishing to npm

(The float-hsl2rgb module already exists on npm, so this is hypothetical.)

The final step is to publish the module to npm. The first time around, you'll need to create an account:

npm adduser

Now you can publish the module to the npm database like so:

npm publish

And it should be live on npmjs.com:
https://www.npmjs.com/package/float-hsl2rgb

maintenance

When you need to make changes to your module, make sure to use semantic versioning. You can use the following commands to bump your module's version and create a new git tag:

npm version major
npm version minor
npm version patch

further reading