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';

Thursday, December 10, 2015

Warning: Testing React Stateless Components

TL;DR;
Doesn't work right now(React 0.14)

After a long(long long long) time of trying to set up a test environment for my project that was built with stateless components, I had to give up that thought and accept defeat. Because it seems that it is not possible with the current version of React Test Utils.

What is a stateless component

(aka Stateless Functional Components) They are just what it sounds like, it a component that does not use the state at all. It comes with some nice syntax that allows one to write less code because they consist of only one function, the render function. Therefore also doesn't have to extend from "React.Component" or be created with "React.createComponent"-function.

Here's a small example:
var Hello = (props) => {
    return <h1>Hello {props.name}</h1>;
};
<hello name="Anakin"/>

Kinda sweet huh? 

Why would one want to test something so small and simple that only renders some DOM?

It might seem unnecessary to test something like this, but even in the small example above things can go wrong. What happens if we do not supply a name attribute to the Hello-"component"? Well it will render a h1-tag with no name, that seems like something we would want to test, and maybe refactor in the future. An empty h1 is kind of useless and maybe should not be rendered at all? Maybe a default text should be used if the title is empty or not supplied thought the title-attribute.

Here's the keyword "functional". The components are stateless functional components, they can and most likely will have logic in them. A stateless component with no functionality is essentially just a tag and maybe that should not even be a component at all.

Why is this a problem when testing? 

React has a suite of test tools called "React Test Utils", these have tons of uses, one of them is to "fake render" a component so we can inspect it and make sure it behaves as we expect it to. The test util for this is called "renderer" which renders the component in "shadow-DOM" that allows us to inspect it. When trying to use the renderer with a stateless component it will throw an error saying that the argument(the stateless component) is of the wrong type.

There are some issues on github ragarding this. Some people claim they have found work-arounds most of them seems to be about making a wrapper class for stateless components. Personally this feels like the wrong approach and I will take a step back to revert my stateless components to regular ones extending React.Component and await v0.15 where this hopefully is fixed.