Building and publishing your first NPM package
Tags: javascript, react, codenewbie, webdev
Published: 2020-09-18 13:46:14

You have created a new reusable piece of code and want to share it with everyone or maybe you just have an idea that can be useful in different projects. But you are completely lost about how to start to code and create a npm package or even how to publish the code you already have. 

I've been there, I created some small packages such as [ICollections](https://www.npmjs.com/package/icollections), [Ngx-indexed-db](https://www.npmjs.com/package/ngx-indexed-db), [React-indexed-db](https://www.npmjs.com/package/react-indexed-db) and now I want to help you to create and publish your first package. This tutorial will be exclusively focused how to create a simple package, I will not be covering several things a project can benefit from, such as use of TypeScript, semantic-release, CI, and etc.

We will build together a custom hook for React that can be very useful in daily basis, a simple toggle state. If you are not familiar with React Hooks, check this link: [React Hooks Docs](https://reactjs.org/docs/hooks-intro.html).

The idea is to be able to install the package via NPM running

```shell
npm install useToggle
```

And then use it in any project as in the code bellow:

```javascript
import React from 'react';
import useToggle from 'useToggle';

const App = () => {
  const [isLoading, toggleLoading] = useToggle(true);
  return (
    <div>
      <button onClick={toggleLoading}>Toggle</button>
      {isLoading ? <div>loading...</div> : <div>Content</div>}
    </div>
  );
};

export default App;
```

Let’s start creating a folder I will name `useToggle`, navigating to inside the folder and initializing it as a npm package. 

Run the following commands in your console: 

```shell
mkdir useToggle // to create the folder
cd useToggle // to navigate inside the folder
npm init // to initialize the the npm inside the folder
```

When we run `npm init` we have to answer some questions that should be straightforward. Here is my final result for this last command:

```json
{
  "name": "usetoggle",
  "version": "1.0.0",
  "description": "React hook to facilitate the state toggle",
  "main": "lib/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "react",
    "hooks",
    "toggle"
  ],
  "author": "Charles Assuncao",
  "license": "ISC"
}
```

## Installing the dependencies

We are going to need some things to create the project, let’s install it via npm:

We are going to need `useState` from react for the package to work, so let’s install it as normal dependency 

```shell
npm install react
```

We are going to use babel here to transpile and minify the final code: 

```shell
npm install --save-dev @babel/core  @babel/cli  @babel/preset-env babel-preset-minify
```

Notice that this time we passed the flag `--save-dev` to signalize that this dependency are only needed to develop our code but it’s not a dependency to the package to work. 

We want to test our code and be sure everything works as expected, remember:  not tested code is broken code! Since we are creating a custom hook we will need React Hooks Testing Library

```shell
npm install --save-dev jest @testing-library/react-hooks react-test-renderer
```

## Hands-on, Let’s code!

### Writing tests

Let’s start writing tests and caring more how we expect our code works. Test Driven Development has several advantages and I definitely recommend a deeper reading about it.  

Create the folder where we are going to keep our code:

```shell
mkdir src
```

Create three new files inside this folder:

`index.js`

`useToggle.js`

`useToggle.spec.js`

Our project now looks basically like this:

```shell
├── package-lock.json
├── package.json
├── node_modules
├── src
│   ├── index.js
│   ├── useToggle.js
│   ├── useToggle.spec.js
```

Since we installed jest to run our tests we need now to create a test script in our `package.json`

```json
"scripts": {
    "test": "jest"
}
```

I love this simplicity of jest, needless configuration. Now we are able to run `npm run test` to execute our specs files. Let’s create our first test then: 

```javascript
//useToggle.spec.js

import { renderHook } from '@testing-library/react-hooks';
import useToggle from './useToggle';

describe('useToggle Hook', () => {
  test('Should initiate with false as default', () => {
    const { result } = renderHook(() => useToggle());
    expect(result.current[0]).toBe(false);
  });
});
```

What is happening here? 

We create a test suit for our Hook ‘useToggle Hook’ and our first test is to check the default value initialized in our hook. `renderHook` execute our hook and return an object that contains the hooks returned value inside of result.current. In our case our hook will return an array with the state value and a function to mutate the state. So basically:

```javascript
result.current[0] // is our state, by default false
result.current[1] // is the toggleState function
```

If we run `npm run test` now our tests will be red. Because we don’t have anything inside the `useToggle.js` file. So let’s create a simple function that will make our test turn green:

```javascript
//useToggle.js

export default function useToggle(initialState = false) {
  return [initialState];
}
```

Run the tests now and see it green as the happiness  

Our function is already returning array having the default initial value as false. Let’s think and create some more tests of how we expect our hook to work:

```javascript
//useToggle.spec.js
import { renderHook, act } from '@testing-library/react-hooks';
import useToggle from './useToggle';

describe('useToggle Hook', () => {
  test('Should initiate with false as default', () => {
    const { result } = renderHook(() => useToggle());
    expect(result.current[0]).toBe(false);
  });

  test('Should initiate with the provided value', () => {
    const { result } = renderHook(() => useToggle(true));
    expect(result.current[0]).toBe(true);
  });

  test('Should toggle the value from false to true', () => {
    const { result } = renderHook(() => useToggle());
    act(() => {
      result.current[1]();
    });
    expect(result.current[0]).toBe(true);
  });
});
```

The first two tests will pass, our `useToggle` function is returning an mock of the state that fulfill the requirements for the two initial tests. But our hook doesn’t make anything happen actually. So let’s change this and make our tests run green again.

```javascript
import { useState } from 'react';

export default function useToggle(initialState = false) {
  const [state, setState] = useState(initialState);

  function toggleState() {
    setState(!state);
  }

  return [state, toggleState];
}
```

We imported `useState` from react and we are using it to hold our initial value and to change it through the function `setState` but instead of returning the `setState` function we create a closure that toggles the state value behaving as we expected. 

Run the tests now and see your console sparkling joy with all tests passing. But, let’s create some more tests just for fun. The final test file will be something like this:

```javascript
import { renderHook, act } from '@testing-library/react-hooks';
import useToggle from './useToggle';

describe('useToggle Hook', () => {
  test('Should initiate with false as default', () => {
    const { result } = renderHook(() => useToggle());
    expect(result.current[0]).toBe(false);
  });

  test('Should initiate with the provided value', () => {
    const { result } = renderHook(() => useToggle(true));
    expect(result.current[0]).toBe(true);
  });

  test('Should toggle the value from false to true', () => {
    const { result } = renderHook(() => useToggle());
    act(() => {
      result.current[1]();
    });
    expect(result.current[0]).toBe(true);
  });

  test('Should toggle the value from true to false', () => {
    const { result } = renderHook(() => useToggle(true));
    act(() => {
      result.current[1]();
    });
    expect(result.current[0]).toBe(false);
  });

  test('Should execute multiple toggles', () => {
    const { result } = renderHook(() => useToggle()); //init false
    // false -> true
    act(() => {
      result.current[1]();
    });
    // true -> false
    act(() => {
      result.current[1]();
    });
    // false -> true
    act(() => {
      result.current[1]();
    });
    // true -> false
    act(() => {
      result.current[1]();
    });
    expect(result.current[0]).toBe(false);
  });
});
```

Last but not least, we should export our hook from our entry point `index.js`. Only one line will do the job:

```javascript
// index.js

export { default } from './useToggle';
```

## Building

Let’s configure the build script, we are going to need babel for that. So let’s create a babel configuration file (babel.config.js). Our configuration should be damn simple:

```javascript
//babel.config.js

module.exports = {
  presets: ['@babel/preset-env', 'minify'],
};
```

And create a build a build script inside our package.json:

```json
"scripts": {
    "test": "jest",
    "build": "babel src --out-dir lib"
}
```

Now we can run `npm run build` and it will generate the `lib/index.js` file.

## Publish

We need to make small changes in our `package.json` in order to publish it. Let’s configure the files that should be included in the package and a special script that will run every time when we try to publish the package. Also, we are going to change the react dependency to be a peerDependency, since we are expecting the project using our package already has its own react version: 

```json
"files": [
    "lib"
  ],
  "scripts": {
    ...
    "prepublish": "npm run build"
  }, 
  . . .
  "peerDependencies": {
    "react": "^16.9.0"
  },
```

Run `npm login` and use the credentials that we created in the npm site previously. After successfully login you can run now `npm publish`. Now your package lives in the wild world of npm packages and can be used for anyone only running `npm install useToggle`