Publishing Javascript Library - From Zero to Hero
Tags: javascript, webdev, tutorial, opensource
Published: 2022-02-27 12:20:34

Publishing a JavaScript library is a great way to share your code with the world. But for newcomers, it can be a bit confusing. This article will help you to achieve this goal easily.

Hello guys, and happy new year! I hope you are doing well.

Lately, I've been working on a javascript library I made called [react-plock](https://github.com/itsrennyman/react-plock), a simple react masonry implementation. And I decided to publish it on npm, and make it available for everyone.

So, being my first time publishing a library (in a correct way.. 😅) I searched for a good way to do it on the web essay, but the problem I encountered is that there are a LOT of different ways to do it, some people use webpack, some others use rollup, some actually does use nothing.. and others again use Bit, everyone telling that theirs is the "definitive way to do it", and for me (and probably for most of us), it was a bit confusing to find a sort of 'standardized' way to do it.

So, after some research, i found a very good [free course](https://egghead.io/courses/how-to-write-an-open-source-javascript-library) by @kentcdodds on egghead.io and i decided to invest my time and follow it. This was one of the best decisions I made, and I'm very happy with the result I achieved, but the course it's a little bit old (written about 7 years ago 📅), so I decided to update the publishing part writing step by step what I've done to publish my first javascript library on npm.

The topics that will be covered in this article are:

- How a library is read by your project.
- How to publish a JavaScript library.

> There will be also an article about how to manage the versioning of your library. I will attach it to this article when it will be ready.

## How a library is read by your project

The first thing that you need to understand before publishing a library, is actually how it's read by the project that uses it. It is a very simple, but important concept that you must know. So let's start with an example. Let's pretend that we have a project called "my-project" that uses the library "react-plock" we just published 🙋. In order to use it, we need to import it into our project.

```js
import { Plock } from "react-plock"; // Look at me 👀!

export default function App() {
  return (
    <div className="container">
      <Plock />
    </div>
  );
}
```

The question is: how the _fu\*k_ is the library "react-plock" read by the project "my-project"?

Let's try to answer this question by looking at it from another perspective. Imagine you have in your amazing "my-project" a custom component made by you called `Banana`, and you wanna use it in the App component. How can you accomplish this?

```js
import { Banana } from "./components/Banana"; // Look at me 👀!

export default function App() {
  return (
    <div className="container">
      <Banana />
    </div>
  );
}
```

Notice that the only thing that's changed from the previous example is the path to the component source code, that in this case, is a **relative path**, not a library name.

In turn, the `Banana` could be structured like this:

```js
// Look at the export statement 👀!
export function Banana() {
  return (
    <div className="bomb">
      <h1>I am a Banana</h1>
    </div>
  );
}
```

So, the App component imports the `Banana` component, by reading an exported function called `Banana` from the `./components/Banana.js` file.

> **Note:** There is also the default export available as an option, but I personally prefer the named export when I work with modules.

Well, but, how about the `react-plock` library?

By now you should have understood that in order to import a module, you need to specify two fundamental things. The first one is **the path to the module**, and the second one is **the name of the module**.

Our beloved `package.json` will come to our aid to import an external library. It's a file that contains all the information about a library, and every library has its own `package.json`.

If you have a look at the [`package.json`](https://github.com/itsrennyman/react-plock/blob/main/package.json#L6) file of the `react-plock` library, you'll see some interesting information that I will highlight for you:

```json
{
  "name": "react-plock",
  "main": "dist/index.js"
}
```

Did you notice the `main` property? It's the path to the **library source code**. This is the file that will be imported when the library will be used!

So, returning to our question, _how the library "react-plock" is read by the project "my-project"_? I attach again the initial example to avoid you scrolling again to the top of the page:

```js
import { Plock } from "react-plock"; // Look at me 👀!

export default function App() {
  return (
    <div className="container">
      <Plock />
    </div>
  );
}
```

Guessed? Well, once the App.js file is included, and it evaluates the Plock import, node will look for the `package.json` file of the `react-plock` library (included in the _node_modules_ folder of your project during the installation), once it finds it, it will read the `main` property and will import the library source code!

> **Note:** The `main` property can be any file generated in your library, but the most common way to do it is to use the `dist` folder.

## How to publishing a JavaScript library

So, now that we have the basic knowledge importing a library works, let's go to the next topic: how to publish a JavaScript library.

We will create an amazing library called `gimme-a-pokemon` and publish it on npm. The package will provide a function that will return a random pokemon name from an array (The package of the year 🌻).

Let's start by creating the index.js file of the library. This will be our `main` file in the `package.json`, and it will contain the function that will return the pokemon name. We will use the _commonjs_ syntax to export the function to be compatible with older versions of node and not use any transpiler in the process. In a second step, we will add the transpiled version of the library to the `dist` folder.

```js
module.exports = {
  gimmeAPokemon: () => {
    const pokemon = [
      "Pichu",
      "Pikachu",
      "Raichu",
      "Sandshrew",
      "Sandslash",
      "Nidorina",
      "Nidoking",
      "Clefairy",
      "Clefable",
      "Vulpix",
    ];

    return pokemon[Math.floor(Math.random() * pokemon.length)];
  },
};
```

The code is pretty simple, call the `gimmeAPokemon` function and return a random pokemon name.

> We will skip testing the library for now because it's very simple.

Now it's time to create the `package.json` file. This file will contain all the information about the library, and it will be used by npm to publish it. You can run the following command to create the `package.json` file:

```bash
$ npm init
```

The `npm init` command will create a `package.json` file in the current directory, and it will ask you some questions about the library.

After the command is executed, you will have a `package.json` file in your current directory, here's a quick description of some of the properties in the file:

| Property      | Value                                                              |
| ------------- | ------------------------------------------------------------------ |
| `name`        | The name of the library                                            |
| `description` | A short description of the library                                 |
| `version`     | The current version of the library                                 |
| `private`     | If the library is private, it will not be available for the public |
| `main`        | The path to the library's main file                                |
| `files`       | The files that will be published on the registry                   |
| `author`      | The author of the library                                          |
| `license`     | The license of the library                                         |

> **Note:** There are some keys like `main` and `files` which are not present by default. Also, there are other keys that you can use, but there are out of the scope of this article.

This is the `gimme-a-pkmn` real library example, I omitted **for this example** the useless keys like `scripts`, `devDependencies`, `keywords`, etc.. you can look at the entire file [here](https://github.com/itsrennyman/gimme-a-pkmn/blob/c2391ba9e498d9b39f62da576390aa2956ecc7ec/package.json)

```json
{
  "name": "gimme-a-pkmn",
  "private": false,
  "version": "0.0.1",
  "description": "A function to get a random pokemon name",
  "main": "index.js",
  "files": ["index.js"],
  "author": "Renato <renato.pozzi.dev@gmail.com> (https://renatopozzi.me/)",
  "license": "MIT"
}
```

Once you have configured the file, all we need to do is to login in to your npm account, and publish the library.

```bash
$ npm login
$ npm publish
```

And that's it! The library is published on npm with the version `0.0.1` and you will be able to install it in your project using `npm install gimme-a-pkmn`.

### Using a transpiler to write using ES6 features

Yeah, our library works great, but we can't use the ES6 features in our code. Or better, we could, but not every environment supports ES6, so we could have a problem. Let's see how to solve this problem using a transpiler.

> Unpopular opinion: Transpilers are simple. They are just a tool to convert ES6+ features to ES5.

I told you this because if you look at almost every tutorial on the internet you will see tons of lines of webpack, rollup configurations without any explanation of what they actually do. So it may scare you, but trust me it's not a big deal. I will explain to you like I would explain a child.

The concept is simple, as I said just before, transpilers are just a tool to convert ES6+ features to ES5, so they take an **input** file and **output** a transpiled file. This is currently all you need to know about transpilers. Simple right?

Look at this example, this is a modern arrow function:

```js
const arrowFunction = () => {
  console.log("This will be transpiled!");
};
```

If you try to run this code in an old browser, you will see that it will not work. In order to fix this, we need to use a transpiler. Let's see how the code becomes after the transpilation:

```js
var arrowFunction = function arrowFunction() {
  console.log("This will be transpiled!");
};
```

Did you see the difference? The transpiler will convert the arrow function to a regular function, and it will add the `function` keyword and the `var` keyword.

You can so some experiments with this configuration [here](https://babeljs.io/repl/#?browsers=&build=&builtIns=false&corejs=false&spec=false&loose=false&code_lz=MYewdgzgLgBAhgJwSA7gMQK5mFAluGAXhgAoBKIgPhgG8AoGGUSEAGwFMA6VkAcxIDkAFQAWuCDBS5WrGACN2MKAjiQADtPYATAIQCyAbjoBfIA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=script&lineWrap=true&presets=env&prettier=true&targets=&version=7.16.9&externalPlugins=&assumptions=%7B%7D).

> Obviously there's a lot more to know about transpilers, but for now, let's focus on the basics.

### Integrating a transpiler with a bundler

Another scary name you should know is **bundler**. Bundlers are tools that take a bunch of files and compile them into a single file. Usually are useful because they have also a lot of plugins, so you can easily integrate a transpiler like babel we just saw, with a bundler. I personally find rollup the bundler which i'm most comfortable with. So we will use rollup with its babel plugin to compile our code, don't worry, it's not a big deal.

So, our goal now is: we want to use rollup to bundle our code, and we want to use babel to transpile our code. Let's see how to do it.

First, we need to install the rollup and babel plugin packages as dev dependencies.

```bash
$ npm install --save-dev rollup @rollup/plugin-babel @babel/preset-env
```

Once the packages are installed, we need to create a `rollup.config.js` file. This file will contain all the configuration for your bundling process.

```js
import { babel } from "@rollup/plugin-babel";

// We will use the main configuration property directly from the package.json file
const packageJson = require("./package.json");

/* eslint-disable */
export default {
  input: "index.js", // The path to the file that will be bundled
  output: [
    {
      file: packageJson.main, // The path to the bundled file
      format: "cjs", // The format of the bundled file (commonjs in this case)
    },
  ],
  plugins: [
    // The plugin that will be used to transpile our code, in this case we will use babel
    babel({ babelHelpers: "bundled", presets: ["@babel/preset-env"] }),
  ],
};
```

Please take some time to read again the code, it's pretty simple, but it's important to know how it works. Remember, also in bundling **there is an input, and there is an output**. And the process between is called **bundling**.

Ok now we need to add a command to run the bundler. Again we will use the package.json file, we can insert a new script called "build":

```js
"scripts": {
  "build": "rollup -c",
  // ...other scripts
},
```

And the last thing we need to do is to change the `main` property of the package.json to another destination, otherwhise our `index.js` file will be overwritten. Also the `files` property will be updated as well because we need to publish the dist directory.

```js
{
  "main": "dist/index.js" // Here again our lovely dist folder! 💖
  "files": [
    "dist"
  ],
}
```

Now we are ready to run the bundler. Go ahead and run the command `npm run build`. You should see the bundled file in the just created `dist` folder. Check the differences between the two files! 🥸

Now your code can be used in any modern browser, and it will work just fine. You can also rewrite it using modern ES6 features:

```js
export const gimmeAPokemon = () => {
  const pokemon = [
    "Pichu",
    "Pikachu",
    "Raichu",
    "Sandshrew",
    "Sandslash",
    "Nidorina",
    "Nidoking",
    "Clefairy",
    "Clefable",
    "Vulpix",
  ];

  return pokemon[Math.floor(Math.random() * pokemon.length)];
};
```

By running the command `npm run build` again, you should see the bundled file with the new code, fully compatible with modern and old browsers.

You can also publish your new code to npm, just run the command `npm publish`. But remember to bump the version number in the package.json file, otherwise the new version won't be published.

You can find the source code used for this tutorial on [GitHub](https://github.com/itsrennyman/gimme-a-pkmn), also you can find the package on [npm](https://www.npmjs.com/package/gimme-a-pkmn).

### Common Problems

There are some common problems that you might encounter when publishing a JavaScript library. Be sure to check the following:

- The library name must be unique, and it must be a valid npm package name.
- You cannot publish a library version that is already published, so you need to bump the version number before publishing.

## Conclusion

Phew! That was a lot of work, but you have learned a lot about JavaScript libraries. Now you can use them in your projects and you can publish them on npm. You can even use them in your own projects! 🎉 🎉 🎉

Soon i will publish also a detailed guide on how to manage the version of your libraries, totally automated, using Github actions. So stay tuned!

If you have any questions, feel free to contact me on [Twitter](https://twitter.com/imarenny).

Cheers! 🍻