Skip to content

Instantly share code, notes, and snippets.

@simbo
Last active August 29, 2015 14:26
Show Gist options
  • Save simbo/9a84cf39b21723071159 to your computer and use it in GitHub Desktop.
Save simbo/9a84cf39b21723071159 to your computer and use it in GitHub Desktop.
Create and publish a node package.

Create and publish a node package.

…or actually…

How to create and publish a node package using JSHint and JSCS for linting, Mocha for testing, Istanbul for code coverage, GitHub for hosting and Travis for continuous integration including Code Climate support and auto-deploy to npm registry.


Table of contents

Basic setup

We are going to create a project structure like this:

my-awesome-module/
 ├─╸ .git/
 ├─╸ coverage/
 ├─╸ lib/
 │    └─╸ index.js
 ├─╸ node_modules/
 ├─╸ test/
 │    └─╸ index.js
 ├─╸ .editorconfig
 ├─╸ .gitignore
 ├─╸ .jscsrc
 ├─╸ .jshintrc
 ├─╸ .travis.yml
 ├─╸ package.json
 └─╸ README.md

Run:

mkdir -p lib test && touch lib/index.js test/index.js .editorconfig .gitignore .jscsrc .jshintrc .travis.yml package.json README.md

git and GitHub

Create a repo on GitHub, initialize you local repo and add the remote.

git init
git remote add origin git://github.com/<USER>/<REPO>.git

lib/

That's where we put our code. The main file should be index.js and contain something like this:

'use strict';

module.exports = function() {
    return true;
};

See Node.js modules docs.

package.json

…should look like this:

{
  "name": "my-awesome-module",
  "description": "Description goes here.",
  "keywords": ["some", "related", "things"],
  "author": "Foo Bar <foo@bar.com> (http://foobar.com)",
  "license": "MIT",
  "version": "0.1.0",
  "repository": {
    "type" : "git",
    "url" : "git://github.com/<USER>/<REPO>.git"
  },
  "bugs": "https://github.com/<USER>/<REPO>/issues",
  "main": "lib/index.js",
  "files": [],
  "os" : ["darwin", "linux"],
  "engines" : {
    "node" : ">=0.11.10",
    "npm": ">=1.3.22"
  },
  "scripts": {
    "test": "mocha -R spec",
    "lint": "jshint ./lib/*.js & jscs ./lib/*.js",
    "cover": "istanbul cover ./node_modules/.bin/_mocha -- -R spec",
    "watch": "mocha -R spec -w",
    "codeclimate": "codeclimate-test-reporter < coverage/lcov.info"
  }
}

See package.json docs.

name your package wisely!
And as you hopefully know what you want to achieve, you don't need much time to think of a good description and keywords.

Your package's version number should stick to SemVer and start initially with 0.1.0.

files should contain all additional files or folders that should be included in our final package. The main file and the readme are included automatically (and in most cases no other includes are needed).

Ensure that your module works with defined os and engines.

Dependencies

We already defined some scripts in your package.json, now you need to install the testing and linting tools and save them as devDependencies:

npm install --save-dev mocha istanbul jshint jscs codeclimate-test-reporter

If your final module has other modules as direct dependencies, they should be installed and saved as dependencies, so you can require() them as usual:

npm install --save some-other-module

Tests

Add your tests to lib/index.js:

'use strict';

var assert = require('assert'),
    pkgJson = require('../package.json'),
    pkgName = pkgJson.name,
    pkg = require('..');

describe(pkgName, function() {
    it('should return true', function() {
        assert.equal(pkg(), true);
    });
});

Now you can run them via npm run test or watch and test on change using npm run watch.

Code coverage reports can be found in coverage/ after running npm run cover.

Linting

JSHint options are set via .jshintrc. See JSHint options docs and example .jshintrc.

{
  "node": true,
  "globals": {
    "describe": true,
    "it": true
  },
  "strict": true,
  "undef": true,
  "unused": true,
  "validthis": true,
  "maxcomplexity": 5
}

Same thing for JSCS and .jscsrc. See JSCS rules docs.

{
  "preset": "google",
  "validateIndentation": 4,
  "disallowMultipleVarDecl": null,
  "maximumLineLength": 120
}

Our package's code can now be linted via npm run lint.

You should setup your IDE to lint code automatically using option files in the project folder:

Travis CI

Install Travis command line client.

Run travis login && travis init within your project root.

Your .travis.yml should look like this:

language: node_js
node_js:
- '0.12'
- '0.11.10'

Go to your Travis CI profile and make sure Travis support is activated for your project's GitHub repository.

Setup npm deploy

Assuming you have an npm account, you need to get your npm api key now.

After running npm adduser and npm login you should find an entry liky this in your local ~/.npmrc:

registry.npmjs.org/:_authToken=THIS-IS-YOUR-API-KEY

Add the following lines to your .travis.yml, replacing email and repo path:

deploy:
  provider: npm
  email: foo@bar.com
  api_key: ${NPM_API_KEY}
  on:
    tags: true
    repo: <USER>/<REPO>

Add and encrypt NPM_API_KEY as global environment var:

travis encrypt NPM_API_KEY=PASTE-YOUR-API-KEY-HERE --add env.global

By using the api key via environment variable and not directly as value of deploy.api_key, we can reuse it later, i.e. for Code Climate exports.

Setup Code Climate reports

Add your repo to Code Climate then navigate to <REPO> / Settings / Test Coverage and find a line like this:

CODECLIMATE_REPO_TOKEN=YOUR-CUSTOM-REPO-TOKEN

Copy that part and add it as encrypted gloval environment variable to your .travis.yml:

travis encrypt CODECLIMATE_REPO_TOKEN=YOUR-CUSTOM-REPO-TOKEN --add env.global

Also add the following lines to your .travis.yml to run coverage generation and Code Climate reports after a build:

after_script:
- npm run cover
- npm run codeclimate

Other things

.gitignore

…should at least contain:

/coverage
/node_modules
npm-debug.log

.editorconfig

Keep consistency.

# editorconfig.org

root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[{*.md,*.markdown}]
trim_trailing_whitespace = false

[{*.json,*.yml,*.js*rc}]
indent_size = 2

README.md

…could contain some proud badges and look like this:

my-awesome-module
=================

  > Description goes here.

[![npm Package Version](https://img.shields.io/npm/v/NAME.svg?style=flat-square)](https://www.npmjs.com/package/NAME)
[![MIT License](http://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://USER.mit-license.org)
[![Dependencies Status](https://img.shields.io/david/USER/NAME.svg?style=flat-square)](https://david-dm.org/USER/NAME)
[![devDependencies Status](https://img.shields.io/david/dev/USER/NAME.svg?style=flat-square)](https://david-dm.org/USER/NAME#info=devDependencies)
[![Travis Build Status](https://img.shields.io/travis/USER/NAME/master.svg?style=flat-square)](https://travis-ci.org/USER/NAME)
[![Code Climate GPA](https://img.shields.io/codeclimate/github/USER/NAME.svg?style=flat-square)](https://codeclimate.com/github/USER/NAME)
[![Code Climate Test Coverage](https://img.shields.io/codeclimate/coverage/github/USER/NAME.svg?style=flat-square)](https://codeclimate.com/github/USER/NAME)

---

## Install

    npm install my-awesome-module

## Usage

    var myAwesomeModule = require('my-awesome-module')();

## License

[MIT](http://USER.mit-license.org/)

Publishing

After updating the version value in your package.json and committing all changes, tag your latest commit and push with tags to origin:

git tag -a 0.1.0 -m ""
git push origin --tags

Travis will be triggered and if the build is successful, your package and its test and coverage reports will be automatically published.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment