Writing an npm module with TypeScript and microbundle
Tags: javascript, typescript, productivity, npm
Published: 2018-05-02 12:32:49

---
title: Writing an npm module with TypeScript and microbundle
published: true
tags: javascript, typescript, productivity, npm
canonical_url: https://codewithhugo.com/writing-an-npm-module-with-typescript-and-microbundle/
---

For those looking to write a package and publish it to npm, TypeScript + microbundle is a low-friction way to build a high-quality library.

## Why TypeScript?

[TypeScript](https://www.typescriptlang.org/) is a JavaScript superset that adds static types to it.

Its other features also follow the ECMAScript specification (current and future) quite closely.

For library authors this means you provide the consumers of your library, even those who don’t use TypeScript with more details around expected types and for some editors/IDEs that integrate with TypeScript (like Visual Studio Code) nicer auto-complete. TypeScript also serves as inline documentation that shouts at you when you pass something you shouldn’t, which will come handy when you rediscover your code a couple of months down the line.

## Why microbundle?

[Microbundle](https://github.com/developit/microbundle) is a “zero-configuration bundler for tiny modules”. It’s a wrapper around [rollup](https://rollupjs.org/guide/en) with sane defaults (including minification/compression), nice outputted size stats, multiple target formats (ES modules, CommonJS, UMD). Most of all in the scope of this post, it has TypeScript support out of the box (_actually_ no configuration, not even a `tsconfig.json`).

It’s ridiculously easy to set up and allows library authors to focus on building a great library rather than setting up the plumbing to be able to ship a JavaScript library from ES6/TypeScript or other compile-to-JS tool 🙂.

# Zero-config bundling

To start, we’ll have to create setup our package run `npm init` and complete all the prompts.

Next run: `npm i --save-dev microbundle`.

Let’s create a src and dist folder: `mkdir src && mkdir dist`

And add the first TypeScript file: `touch src/index.ts`.

Let’s add a class to the index.ts so we’re not just compiling empty files:
`echo "export class MyMainClass {}" >> src/index.ts`

Microbundle looks at the `package.json` `"main"` and `"source"` fields (compiled entry point and source entry point), in our case that’s `dist/index.js` (which doesn’t exist yet) and `src/index.ts`.

Let’s edit it to have the following in the `package.json`:

    {
      "main": "dist/index.js",
      "source": "src/index.ts"
    }

This means microbundle knows how to compile our library now, run: `npx microbundle` (on versions of npm <5.x, you can also run `./node_modules/.bin/microbundle`).

This will compile your `src/index.ts` to the `dist` folder. If you look at the contents of the `dist` folder, you’ll see how much work microbundle does for you:

    $ ls dist
    index.d.ts       index.js.map     index.m.js.map   index.umd.js.map
    index.js         index.m.js       index.umd.js

Let’s explain what all these are:

* `index.js` is the CommonJS module. This is module type used by Node and it looks like ` const myModule = require(``'``my-module``'``) `
* `index.m.js` is the ECMAScript Module, as defined in ES6, it looks like `import MyModule from` ` '``my-module``' `
* `index.umd.js` is the UMD module
* `index.d.ts` is TypeScript type declaration file

Then there’s a matching `.map` file that maps back to the TypeScript source for each of the files.

Take a look inside `index.js`:

    $ cat dist/index.js
    var n=function(){return function(){}}();exports.MyMainClass=n;
    //# sourceMappingURL=index.js.map

Our `class MyMainClass {}` statement was compiled to its ES5 equivalent and the export to a CommonJS export.

`index.d.ts` is also interesting:

    $ cat dist/index.d.ts
    export declare class MyMainClass {
    }

This allows a TypeScript project to assign the correct type information back to the package… which _is_ a roundabout way of doing since a TypeScript project should be able to just import the `.ts` file. The separate type declaration means that non-TypeScript projects can also understand the public API of the module (eg. code editors can do smart autocomplete on unseen npm packages).

microbundle can also watch for changes: `npx microbundle watch`.

For ease of use we can put the watch and build tasks in the `package.json` as npm scripts:

    {
      "scripts": {
        "dev": "microbundle watch",
        "build": "microbundle"
      }
    }

# Publish the module

By leveraging microbundle we can publish the module as a CommonJS module (standard npm module), but also as an ES Module and a UMD module, to do this follow the guide at https://github.com/developit/microbundle#specifying-builds-in-packagejson.

**tl;dr**

* `"main": "dist/index.js"`
* `"umd:main": "dist/index.umd.js"`
* `"module": "dist/index.m.js"`
* `"source": "src/index.ts"`

With this `package.json` you can publish to npm with `npm publish`.
And if you enjoyed using [microbundle](https://github.com/developit/microbundle), definitely keep using it but also give it a [star on GitHub](https://github.com/developit/microbundle) and let Jason Miller aka [@\_developit](https://twitter.com/_developit) know you’re a fan on Twitter.