Using NPM for Code reuse


NPM

If you work on lots of client projects managing your shared code between projects can soon become a nightmare. Worse, if you need find a bug or add new feature this can be painful to paste over many projects.

Using modules for code reuse is not a new idea, look at gems for ruby, PEAR for PHP or CPAN for Perl. But with the rise of node and NPM it gives the ability to take this idea into the front-end.

So before I start I am going to make a few assumptions:

  • You have Node/NPM set up
  • You have and know GIT
  • Your comfortable on the command line
  • You've worked with module builder like browserify or webpack
  • You know javascript.

If your not comfortable I'd suggest brushing up - Node school is an excellent resource.

Let build something

I'm going to use a module I build called cookie_mgmt_fp for managing cookies in the browser. But you can create a module for anything you want of course.

So create a folder and cd into via the terminal. Once your in there run:

npm init

This will set you through some basic steps like title, author, description, etc.

Next lets create index.js in the root of the directory.

In it put:

module.exports = function(){
console.log("my first module!")
}

Now if you haven't set it up, sort out a git repository (I'd recommend github), making sure it's public.

In you main project, add this to your package.json, replacing the git path to where ever your module is:

"dependencies": {
"my-module": "git://github.com/[my username]/[my module].git",
}

Now you can include it into your javascript in something like so:

var my-module = require("my-module");

Now to make managing or module, where going to add a few build modules. You can change them to your preferred tooling, test suite, etc.

I'm going to use:

  • Babel - for es2015/2016 niceness
  • Browerify - For module management
  • eslint - for linting
  • Jasmine - for tests
  • karma - as a test runner

So by all means tweak but otherwise run:

npm install --save-dev  babel babel-cli babel-eslint babel-polyfill babel-preset-es2015  babelify browserify eslint jasmine-core karma karma-browserify karma-chrome-launcher karma-coverage karma-firefox-launcher karma-jasmine karma-phantomjs-launcher karma-safari-launcher karma-story-reporter phantomjs-prebuilt rewireify

I appreciate that might look a lot, but they are all useful in my opinion. I'll talk you through each one as we go.

So the first thing to do is set up a few folders for easier maintenance so create the following folders:

  • src - Source files of the modules (e.g. not run through Babel)
  • lib - Processed files (e.g. run through babel)
  • spec - To add your test suite.

In 'src' folder create your module file and add some code. Mine is call 'cookie_mgmt.js' and you can find the source code here, if you want to just use that.

Next to you package.json add:

"babel": {
"presets": [
"es2015"
]
},

This sets up the correct plugins for babel for you. If you want to see the feature of babel go here

If you have babel-cli install globally (just add -g to this install), just run the following on the command line:

babel src -x '.js' --source-maps  -d lib

This will process all the files (through babel), then output them into your lib folder. You should see the file in your lib folder now and if you inspect it should be processed.

This is great, but quite a lot to remember everytime you need to process it. So in your package.json find scripts and add the following:

"scripts": {
"build": "babel src -x '.js' --source-maps  -d lib",
"build:watch": "babel src -x '.js' -w --source-maps  -d lib",
},

We have now added to npm scripts so to build your files run:

npm run build

And to watch your files run:

npm run build:watch

Now in your index file add the following:

var mymodule = require("./lib/my_module");
module.exports = mymodule

We point it to the lb folder rather than source so people can use your module even if they are not using babel.

If you have several files to your module you can also ref them here passing them out as an object.

Linting and tests suite.

Your going to be sharing it across many projects. So it's important to make sure the code quality is 100% in module.

So first linting, this looks at your code and ensures your writing your code to specific style. This helps for code readability. Create a .eslintrc file and add your default rules. If you have installed eslint globally then run:

eslint --init

Checkout the docs here to sort your own rules, I like the google style guide.

Now in package.json add to your scripts the following:

"lint": "eslint src/**",

Now you can run the following to run the linter:

npm run lint

Tests

I'm not going to go into details of writing the test. I'm hoping that you are all up to speed with this and you are writing test for your code - If not you should be!

So if you have installed karma globally then just run:

karma init

I use Jasmine & browserify with babelify & rewireify, you can see my standard set up here

Once you've done that, add the following commands to your scripts in packages.json:

"test": "npm run lint && ./node_modules/.bin/karma start --browsers PhantomJS --single-run",
"test:watch": "karma start --browsers PhantomJS",
"test:browsers": "karma start --single-run --browsers Chrome,Firefox,Safari"

Now you have 3 commands:

  • "npm test" will run the linter and run your test suite once (good for ci).
  • "npm run test:watch" - Will watch your tests for when your developing
  • "npm run test:browsers" - Will run your test in desktop browser (this is set for mac but add IE if your on windows). ## Getting ready to Publish

We could continue adding our modules through github. This means we lose any of the niceties of versioning our modules through NPM. If we want to use different version of our modules you can without much problem. This means you can add upgrades without worrying it's going to effect other projects.

So the first thing you need to do is create an account on npm

Once you've done that you'll need to login on the command line:

npm adduser

Now I like to scope some my modules as there more for my use than generally. It also means you don't need to worry if someone else has registered a project in a similar name. My general rule is if I think it be useful to people other than my internal time I don't scope, if not I scope it.

If you want to scope your module run the following:

npm init --scope=<your username>

This will step your through your init steps with your defaults intact. You should see that the name of your module will have changed. It should now be /, e.g. for me it's "@djforth/cookie_mgmt_fp".

peerDependancies

One quick note before we publish your module. If your have any dependancies in your module (e.g. something like lodash) then I would use peerDependancies. Why I hear you ask? Well if the version you need is different to that in the project NPM will try to bring that version into the project. This means you can end up with many versions of the same library in a project, this can cause a lot of problems.

Setting it as a peerDependancy means that NPM will warn you if there is a mismatch, but not bring in another copy. This mean you can either resolve the issue or ignore it if you don't think it will cause an issue. I would also add the modules to your "devDependancies". This will make them available for testing.

Note: If you shrinkwrap your dependencies, please don't do this in a module. As explained above it can cause a lot dependency issues.

.npmignore

When you publish your module it will by default ignore all the files listed in your .gitignore file. There can be a few files that you have committed into your repo that you don't need in your published module. By adding a .npmignore file, you can select files in your repo that you don't want in your published module. It works exactly the same as .gitignore. I would add your spec(test) files and irrelevant files that users won't need to run the project.

Publishing and re-publishing

Ok now that you've added everything, publishing is pretty easy. Commit everything to github (or where your remote repo is), then run the following:

npm publish --access=public

If you haven't scoped your module or are using private modules then don't add "--access=public". Now if you go into your user page in npmjs.com you should see your module listed on your user page. Then you can include it your project by running:

npm install --save @<username>/<my-module>
// or as an example
npm install --save @djforth/cookie_mgmt_fp

Then you can now use it your project in the normal commonjs approach:

var mymodule = require("@<username>/<my-module>")
// or as an example
var cookies = require("@djforth/cookie_mgmt_fp")

Updating

OK that's great, but what about when you need to update your module. Well this is pretty easy too, now you could bump the version number in the package.json. But npm give you some helpful methods to manage it via semantic versioning. This allows you to add prereleases, patch, minor or major. For example to add a patch release:

npm version patch

There is a detailed explanation on the npm page, a brief explanation would be:

  • Patch - bug fixes
  • Minor - Small, non breaking updates or fixes
  • Major - Large breaking changes NPM version also syncs in with git. So it will check you've committed your code then commit the version update.

So there you have it...

Using NPM is a great way to manage your code. It will allow you to modularize and manage code that you use across many sites or apps in simple way.


0 Comment


Got Something to Say?

Don’t use these services? Create an account or