Publishing Node.js Modules and Contributing to Open Source
One of the key factors that contributed to the rapid growth of the Node.js module ecosystem is its open-source nature and robust packaging systems (with registry). As of April 2013, JavaScript and Node.js had already surpassed any other language/platform in the number of packages contributed per year (source)(http://caines.ca/blog/programming/the-node-js-community-is-quietly-changing-the-face-of-open-source/):
This year’s (2014) numbers are even higher, and expectations are that, by mid 2014, Node.js will surpass other platforms, in absolute numbers, with Maven and Rubygems being the top dogs (source) (http://modulecounts.com/).
Other factors that attribute to the Node.js popularity include:
With this in mind, many Node.js enthusiasts find it rewarding to contribute to the ever-growing open-source community. When doing so, there are a few conventions to follow as well as concepts to understand:
Recommended Folder Structure
Here is an example of a good, structured NPM module:
Webapp
/lib
webapp.js
index.js
package.json
README.md
The index.js file does the initialization whereas lib/webapp.js has all the principal logic.
If you’re building a command-line tool, add the bin folder:
Webapp
/bin
webapp-cli.js
/lib
webapp.js
index.js
package.json
README.md
Also, for the CLI module, add the following to package.json:
...
"bin": {
"webapp": "./bin/webapp-cli.js"
},
...
The webapp-cli.js file starts with the line #! /usr/bin/env node, but then has normal Node.js code.
It’s a good idea to add unit tests to your external module, which increases confidence and the likelihood of other people using it. Some programmers go as far as not using a module that doesn’t have any tests! The added benefit is that tests serve as a poor man’s examples and documentation.
TravisCI, which we covered in previous chapters, allows free testing for open-source projects. Its badges, which turn from red to green, depending on the status of tests (failing or passing), became the de facto standard of quality and are often seen on the README pages of the most popular Node.js projects.
Required Patterns
There are a few common patterns for writing external (meant for use by other users, not just within your app) modules:
Here is an example of the module.exports as a function pattern:
var _privateAttribute = 'A';
var _privateMethod = function () {...};
module.exports = function(options) {
//initialize module/object
object.method = function() {...}
return object;
}
And here is an example of an equivalent with a function declaration:
module.exports = webapp;
function webapp (options) {
//initialize module/object
object.method = function() {...}
return object;
}
Tip For info about named function expressions vs. function declarations, visit the comprehensive resource, Named Function Expressions Demystified. (http://kangax.github.io/nfe/#named-expr)
The file in which we include the module looks like this:
var webapp = require('./lib/webapp.js'),
var wa = webapp({...}); // initialization parameters
More succinctly, it looks like this:
var webapp = require('./lib/webapp.js')({...});
The real-life example of this pattern is the Express.js module (source code) (https://github.com/visionmedia/express/blob/master/lib/express.js#L26).
The module.exports as a class pattern uses the so-called pseudoclassical instantiating/inheritance pattern (http://javascript.info/tutorial/pseudo-classical-pattern), which can be recognized by the use of this and prototype keywords:
module.exports = function(options) {
this._attribute = 'A';
...
}
module.exports.prototype._method = function() {
...
}
Notice the capitalized name and the new operator in the including file:
var Webapp = require('./lib/webapp.js'),
var wa = new Webapp();
...
The example of this module.exports as a class pattern is the OAuth module (source code)(https://github.com/ciaranj/node-oauth/blob/master/lib/oauth.js#L9).
The module.exports as an object pattern similar to the first pattern (functional), only without the constructor. It may be useful for defining constants, locales, and other settings:
module.exports = {
sockets: 10,
limit: 200,
whitelist: [
'azat.co',
' webapplog.com ',
' apress.com '
]
}
The including file treats the object as a normal JavaScript object. For example, we can set maxSockets with these calls:
var webapp = require('./lib/webapp.js'),
var http = require('http'),
http.globalAgent.maxSockets = webapp.sockets;
Note The require method can read JSON files directly. The main difference is that JSON standard has mandatory double quotes (") for wrapping property names.
The exports.NAME pattern is just a shortcut for module.exports.NAME when there’s no need for one constructor method. For example, we can have multiple routes defined this way:
exports.home = function(req, res, next) {
res.render('index'),
}
exports.profile = function(req, res, next) {
res.render('profile', req.userInfo);
}
...
And we can use it in the including file the following way:
var routes = require('./lib/routes.js'),
...
app.get('/', routes.home);
app.get('/profile', routes.profile);
...
package.json
Another mandatory part of an NPM module is its package.json file. The easiest way to create a new package.json file, if you don’t have one yet (most likely you do), is to use $ npm init. The following is an example produced by this command:
"name": "webapp",
"version": "0.0.1",
"description": "An example Node.js app",
"main": "index.js",
"devDependencies": {},
"scripts": {
"test": "test"
},
"repository": "",
"keywords": [
"math",
"mathematics",
"simple"
],
"author": "Azat <[email protected]>",
"license": "BSD"
}
The most important fields are name and version. The others are optional and self-explanatory, by name. The full list of supported keys is located at the NPM web site. (https://www.npmjs.org/doc/json.html)
Warning package.json must have double quotes around values and property names, unlike native JavaScript object literals.
It’s worth noting that package.json and NPM do not limit their use. In other words, you are encouraged to add custom fields and devise new conventions for their cases.
Publishing to NPM
To publish to NPM, we must have an account there. We do this by executing the following:
$ npm adduser
Then, simply execute from the project folder:
$ npm publish
Some useful NPM commands are as follows:
Locking Versions
The rule of thumb is that when we publish external modules, we don’t lock dependencies’ versions. However, when we deploy apps, we lock versions in package.json. This is a common convention that many projects on NPM follow (i.e., they don’t lock the versions). So, as you might guess, this may lead to trouble.
Consider this scenario: We use Express.js that depends on, say, Jade of the latest version (*). Everything works until, unknown to us, Jade is updated with breaking changes. Express.js now uses Jade that breaks our code. No bueno.
The solution: Commit node_modules! The following article describes nicely why committing your application’s node_modules folder (not the one for the external module!) to Git repo is a good idea: node_modules in git (http://www.futurealoof.com/posts/nodemodules-in-git.html).
Why do this? Because, even if we lock dependency A in our package.json, most likely this module A has a wild card * or version range in its package.json. Therefore, our app might be exposed to unpleasant surprises when an update to the A module dependency breaks our system.
One significant drawback is that binaries often need to be rebuilt on different targets (e.g., Mac OS X vs. Linux). So, by skipping $ npm install and not checking binaries, development operations engineers have to use $ npm rebuild on targets.
On the other hand, the same problem might be mitigated by using $ npm shrinkwrap (official docs) (https://www.npmjs.org/doc/cli/npm-shrinkwrap.html). This command creates npm-shrinkwrap.json, which has every subdependency listed/locked at the current version. Now, magically, $ npm install skips package.json and uses npm-shrinkwrap.json instead!
When running Shrinkwrap, be careful to have all the project dependencies installed and to have only them installed (run $ npm install and $ npm prune to be sure). For more information about Shrinkwrap and locking versions with node_modules, see the article by core Node.js contributors: “Managing Node.js Dependencies with Shrinkwrap”. (http://blog.nodejs.org/2012/02/27/managing-node-js-dependencies-with-shrinkwrap/)
Summary
Open-source factors have contributed to the success and widespread use of the Node.js platform. It’s relatively easy to publish a module and make a name for yourself (unlike other mature platforms with solid cores). We looked at the recommended patterns and structures, and explored a few commands to get started with publishing modules to NPM.
Practical Node.js Conclusion
Lo and behold, this is the end of the book. There was a study that showed that the majority of programmers read zero books per year (source) (http://blog.codinghorror.com/programmers-dont-read-books-but-you-should/). So, pat yourself on the back, because you’re on the road to awesomeness when it comes to building Node.js web apps. J
Regarding the material covered in Practical Node.js, we explored real-world aspects of the Node.js stack. To do this, many things were essential, and by now you should have an awareness of how pieces fit together. For some technologies such as Jade and REST API, our coverage was quite extensive. However, most of the packages are very specific and tailored to our apps’ goals, so those topics were given a brief introduction, with references for further learning. Here’s a list of topics we covered:
Further Reading
If you enjoyed this book, you might like the programming blog about software engineering, startups, Agile development, and Node.js: webapplog.com (http://webapplog.com). You can also follow the author of this book on Twitter at @azat_co (http://twitter.com/azat_co) for tips and news about Node.js.
Here’s the list of other books by the author Azat Mardan:
Errata and Contacts
If you spotted any mistakes (I’m sure you did), please open an issue or even better fix it and make a pull request to the GitHub repository of the book’s examples: https://github.com/azat-co/practicalnode. For all other updates and contact information, the canonical home of the Practical Node.js book on the Internet is http://practicalnodebook.com.
3.15.3.167