Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
September 18, 2020 01:46 pm GMT

Building and publishing your first NPM package

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, Ngx-indexed-db, 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.

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

npm install useToggle

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

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;

Lets 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:

mkdir useToggle // to create the foldercd useToggle // to navigate inside the foldernpm 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:

{  "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, lets install it via npm:

We are going to need useState from react for the package to work, so lets install it as normal dependency

npm install react

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

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 its 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

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

Hands-on, Lets code!

Writing tests

Lets 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:

mkdir src

Create three new files inside this folder:

index.js

useToggle.js

useToggle.spec.js

Our project now looks basically like this:

 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

"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. Lets create our first test then:

//useToggle.spec.jsimport { 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:

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

If we run npm run test now our tests will be red. Because we dont have anything inside the useToggle.js file. So lets create a simple function that will make our test turn green:

//useToggle.jsexport 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. Lets think and create some more tests of how we expect our hook to work:

//useToggle.spec.jsimport { 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 doesnt make anything happen actually. So lets change this and make our tests run green again.

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, lets create some more tests just for fun. The final test file will be something like this:

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:

// index.jsexport { default } from './useToggle';

Building

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

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

And create a build a build script inside our package.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. Lets 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:

"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


Original Link: https://dev.to/assuncaocharles/building-and-publishing-your-first-npm-package-1kpb

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To