Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Own NPM modules - pitfalls.

Writing custom re-usable NPM modules and how not to mess it up.

Sydney, 10th June 2014

Nik Butenko

NPM modules. Why?

  1. Code separation
  2. Independence for team members
  3. Re-use code in multiple projects
  4. Re-use code in multiple libraries
  5. ???
  6. PROFIT

So, let's write app and some libraries.

  1. "Shared" library that will say "Yes!" or "No." depending on it's configuration
  2. "Lib" that will use "Shared" and always say "No."
  3. "App" that will use both libs

"Shared" package.json


{
	"name": "shared",
	"repository": {
		"type": "git",
		"url": "https://github.com/nkbt/nnj-shared.git"
	}
}
		

"Shared" index.js


var config = {
	sayYes: false
};

function say() {
	if (config.sayYes) {
		return "Yes!";
	}
	return "No.";
}

function sayYes(shouldSayYes) {
	config.sayYes = !!shouldSayYes;
}

exports.sayYes = sayYes;
exports.say = say;
		

"Lib" package.json


{
	"name": "nnj-lib",
	"dependencies": {
		"nnj-shared": "git://github.com/nkbt/nnj-shared.git"
	},
	"repository": {
		"type": "git",
		"url": "https://github.com/nkbt/nnj-lib.git"
	}
}
		

"Lib" index.js


var sharedLib = require('shared');
sharedLib.sayYes(true);

function alwaysYes() {
	return sharedLib.say();
}

exports.alwaysYes = alwaysYes;
		

"App" package.json


{
	"name": "nnj",
	"dependencies": {
		"nnj-lib": "git://github.com/nkbt/nnj-lib.git",
		"nnj-shared": "git://github.com/nkbt/nnj-shared.git"
	},
	"repository": {
		"type": "git",
		"url": "https://github.com/nkbt/nnj.git"
	}
}
		

"App" app.js


var shared = require('shared');
shared.sayYes(false);

function alwaysNo() {
	return shared.say();
}
console.log('alwaysNo()       should say "No."   ', alwaysNo());


var lib = require('lib');
console.log('lib.alwaysYes()  should say "Yes!"  ', lib.alwaysYes());


console.log('alwaysNo()       should say "No."   ', alwaysNo());
		

Running App. What we would expect?

 > node app

alwaysNo()       should say "No."    No.
lib.alwaysYes()  should say "Yes!"   Yes!
alwaysNo()       should say "No."    No.
		

Running App. Actual result

 > node app

alwaysNo()       should say "No."    No.
lib.alwaysYes()  should say "Yes!"   Yes!
alwaysNo()       should say "No."    Yes!
		

WAT?

Let's have a look at the files

app.js
package.json
node_modules
  lib
    index.js
    package.json
  shared
    index.js
    package.json

Feels like we are missing something

app.js
package.json
node_modules
  lib
    index.js
    package.json
    node_modules
      shared
        index.js
        package.json  shared
    index.js
    package.json

Why is this happening? NPM Bug?

 

 

What to do?

1. Do not use static module configuration, use closure


module.exports = function(sayYes) {
	return {
		say: function() {
			if (sayYes) {
				return "Yes!";
			}
			return "No.";
		}
	}
};


// Usage in app.js
var shared = require('shared')(true);
console.log(shared.say()); // Will say: "Yes!"
		

2. Do not use static module configuration, use objects


function Shared(sayYes) {
	this._sayYes = sayYes;
}

Shared.prototype.say = function () {
	if (this._sayYes) {
		return "Yes!";
	}
	return "No.";
};

module.exports = Shared;


// Usage in app.js
var Shared = require('shared');
var shared = new Shared(true);
console.log(shared.say()); // Will say: "Yes!"
		

3. Use bundledDependencies in package.json


{
	"name": "lib",
	"dependencies": {
		"nnj-shared": "git://github.com/nkbt/nnj-shared.git"
	},
	"bundledDependencies": [
		"nnj-shared"
	],
	"repository": {
		"type": "git",
		"url": "https://github.com/nkbt/nnj-lib.git"
	}
}
		
Thanks!

https:// github.com / nkbt / nnj

http:// butenko.me