Monday, December 14, 2015

How-to: develop a node module - part 2

In part 1 I briefly introduced how to get started with npm and making a module. I recommend that you check that out first if you haven't already.

So, in the previous post I didn't really make a proper module, and it completely lacked structure and relevance to a real world situation. In this post I'll show how I decided to set up my own module and touch lightly on some alternatives along the way. The next, and probably the last in this "series" will be about  testing and setting up a test environment for your npm module. So stay tuned for that.

First off, and with the risk of stating the obvious, but any who: You're not making an application. This means that there is no (graphical)interface, webpack and hot loading is useless, you can't test your module by running it in the browser and therefore there is no need to bundle all your source code and its dependencies into a single bundle etc. When making a module one does not want bloat the source code with an application as well, because when the module is published, we want it to contain only the relevant code and we want it to be as slick as possible.

Two things about project structure

Example / demo

So how do we develop something that we can't really test while we develop it and keep the repository clean and neat at the same time? I wrote that we don't want an application bloating down our repository, this might not always be completely true. A good way to show off a module might be to show our potential users a demo application or examples of how to use it. This is quite common and as long as the example or demo is completely separated from the module code, my opinion is: go for it! Other might disagree. You can develop an example application outside of the repository too in order to completely separate it from your repository.

Source code and distribution code

Just like when developing an application, a good practice is to keep the source code of the module in its own folder. The source code is not the code we publish to npm for others to use, this should be "built" just like when developing a regular app and it's bundled. The built code should not be bundled with its dependencies like webpack and babelify does it normally. The built code should just be transpiled versions of the source code. This allows you to code the module in any javascript version/language you like(regular, ES2015, TypeScript etc), and the users of your module can use any version of javascript they like because your published code is pure(tranpiled) javascript and your dependencies will be installed from your package.json.

Folder structure

Here is an example of what the structure could look like:

├── demo
├── dist
├── src
│   ├── components
│   │   ├── comp.js
│   │   └── class.js
├── models
│   │   └── class.js
│   ├── helpers
│   │   └── class.js
│   └── index.js
├── .babelrc
├── .gitignore
├── .npmignore
└── package.json

demo folder

In the demo folder a small example app can be made that uses the module, this is nice for people who want to see how it is used. Make sure the demo app does not use the code from the source folder.

dist folder

The dist folder is where the transpiled built version will be. This is the version we should publish to npm and this is the version the demo app should use. Some project call this folder "libs", this is a more common name if your module is a set of classes that can be used individually. Just like with React you can Import/require its "createClass" function with:
import {createClass} from 'react';


src folder

In src the source code is kept as per usual. Notice that there is a file called index.js, this is the "main" file of the module(you can name it what ever you want, look at part 1 where this is explained). When other use the module, this is the code that will be imported or required in their scripts. Other than that just structure your source code as you please.

package.json

{
  "name": "asd-nav",
  "main": "dist/index.js",
  //...
  "devDependencies": {
    "babel-cli": "^6.2.0", 
    "babel-preset-es2015": "^6.1.18",
    "babel-preset-react": "^6.1.18",
    "babelify": "^7.2.0",
    "browserify": "^12.0.1"
  },
  //...
  "scripts": {
    "demo": "browserify -t [ babelify ] demo/src/app.jsx -o demo/bundle.js",
    "build": "babel src --out-dir dist"
  }
}

As seen in this package.json, the "main" points to where the built version of my module will be, this is for npm to know what the entry file is when using the module. In this guide I use babel to transpile the source code into the folder "dist" seen by the build script.

The other script called demo, uses babelify(could use webpack or what ever you prefer) to bundle the demo application.

Note that all these are in the devDependencies! Make sure things that are only used in development are in the regular dependencies.

.babelrc

I use a .bablerc file to determine what transpilers to use.

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

.gitignore

In the git ignore, there is nothing special when using a structure like this. If however you're not using a demo application that in turn is using the built version in the dist folder. It might be a good idea to add the dist folder to the gitignore. Because there is no need to have the built version in your repository.

.npmignore

This works like the gitignore, but its only for when publishing the module to npm. The source code should not be published to npm also the demo should not be there. The only folder that you want published to npm is more or less the dist folder.

demo
src
.gitignore
.babelrc
*.log

Npm ignores some things by default, node_modules-folder for example so they don't need to be added. read more here.

That's it! Your repository is now set up for module development.

Don't forget to build your source code before bundeling your demo and that the demo application should use the built modulenot the source code!
import MyModule from './../dist/index.js';

No comments:

Post a Comment