Create an npm package template with TypeScript and rollup.js

Subscribe to my newsletter and never miss my upcoming articles

Here is my repo for this article.
github.com/koji/npm-package-template

What is npm?

npm is a package manager for the JavaScript programming language. npm, Inc. is a subsidiary of GitHub, that provides hosting for software development and version control with the usage of Git. npm is the default package manager for the JavaScript runtime environment Node.js. Wikipedia

npmjs.com

For people who prefer to use js instead of ts.

In this article, I use typescript, but if you don't want to use it, you can skip things that are related to typescript such as installing typescript, generating tsconfig.json, and using ts extension and types in codes.

Set up a template project

First, we need to create a folder for the package template and run yarn init or npm init. I think package.json that is generated by npm init can be better than yarn's one since it covers the most basic items.

$ mypackagetemplate 
$ cd mypackagetemplate
$ yarn init or npm init

Install packages for the template

In this step, we install some packages for the template.

$ yarn add typescript --dev

# generate tsconfig.json
$ yarn tsc --init or node_modules/.bin/tsc --init

Update generated tsconfig.json like below.

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Write code

$ mkdir src
$ cd src
$ touch index.ts
$ touch odd_even.ts

odd_even.ts The code is super simple. Just pass a number and if the number is even, it will return true.

export default (value: number): boolean => {
  return value %2 ==0 ? true : false;
}

index.ts The following code allows us to import the code like below. import { odd_even } from 'package-name'

import odd_even from './odd_even';

export default {
  odd_even: odd_even,
}

Add ESLint Jest

In this step, we will set up eslint and Jest. We will install eslint and jest. yarn run eslint --init command allows us to create a config file intractively.

$ yarn add eslint jest ts-jest -D
$ yarn run eslint --init
# settings for this
# ❯ To check syntax and find problems
# ❯ JavaScript modules (import/export)
# ❯ None of these
# ? Does your project use TypeScript? › No / ❯ Yes
# ✔ Browser
# ✔ Node
# ❯ JavaScript
# ? Would you like to install them now with npm? › No / ❯ Yes

You will see .eslintrc.js in your project folder

module.exports = {
    "env": {
        "browser": true,
        "es2021": true,
        "node": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "plugins": [
        "@typescript-eslint"
    ],
    "rules": {
    }
};

We need to modify package.json to run eslint.

  "scripts": {
    "build": "rollup -c",
    "lint": "eslint --fix 'src/**/*.ts'",
    "test": "jest"
  },

Now we can do lint for codes under src.

$ yarn lint

Then, add configs for jest to package.json

"jest": {
    "moduleFileExtensions": [
      "ts",
      "js"
    ],
    "transform": {
      "^.+\\.ts$": "ts-jest"
    },
    "globals": {
      "ts-jest": {
        "tsconfig": "tsconfig.json"
      }
    },
    "testMatch": [
      "**/test/**/*.test.ts"
    ]
  }

Write test

First, creatae a test folder

$ mkdir test

test/odd_even.test.ts The test code is also simple as well as odd_even.ts. The code tests 2 cases, odd number(1) & even number(2).

import odd_even from '../src/odd_even';

describe('test odd_even', (): void => {
  test('odd', (): void => {
    const resp: boolean = odd_even(1);
    expect(resp).toBe(false);
  });

  test('even', (): void => {
    const resp: boolean = odd_even(2);
    expect(resp).toBe(true);
  });
});

Now we can run a test with the following command

$ yarn test

Set up rollup.js

The final step is setting up rollup.js(including Babel).

rollup.js

Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. It uses the new standardized format for code modules included in the ES6 revision of JavaScript, instead of previous idiosyncratic solutions such as CommonJS and AMD. ES modules let you freely and seamlessly combine the most useful individual functions from your favorite libraries. This will eventually be possible natively everywhere, but Rollup lets you do it today.

rollupjs.org/guide/en

If you don't like to use rollup.js, you can use others such as webpack.

$ yarn add rollup rollup-plugin-terser @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript tslib @babel/core @babel/preset-env -D

Add .babelrc.js

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

rollup.config.js

import { terser } from "rollup-plugin-terser";
import pluginTypescript from "@rollup/plugin-typescript";
import pluginCommonjs from "@rollup/plugin-commonjs";
import pluginNodeResolve from "@rollup/plugin-node-resolve";
import { babel } from "@rollup/plugin-babel";
import * as path from "path";
import pkg from "./package.json";

const moduleName = pkg.name.replace(/^@.*\//, "");
const inputFileName = "src/index.ts";
const author = pkg.author;
const banner = `
  /**
   * @license
   * author: ${author}
   * ${moduleName}.js v${pkg.version}
   * Released under the ${pkg.license} license.
   */
`;

export default [
  {
    input: inputFileName,
    output: [
      {
        name: moduleName,
        file: pkg.browser,
        format: "iife",
        sourcemap: "inline",
        banner,
      },
      {
        name: moduleName,
        file: pkg.browser.replace(".js", ".min.js"),
        format: "iife",
        sourcemap: "inline",
        banner,
        plugins: [terser()],
      },
    ],
    plugins: [
      pluginTypescript(),
      pluginCommonjs({
        extensions: [".js", ".ts"],
      }),
      babel({
        babelHelpers: "bundled",
        configFile: path.resolve(__dirname, ".babelrc.js"),
      }),
      pluginNodeResolve({
        browser: true,
      }),
    ],
  },

  // ES
  {
    input: inputFileName,
    output: [
      {
        file: pkg.module,
        format: "es",
        sourcemap: "inline",
        banner,
        exports: "named",
      },
    ],
    external: [
      ...Object.keys(pkg.dependencies || {}),
      ...Object.keys(pkg.devDependencies || {}),
    ],
    plugins: [
      pluginTypescript(),
      pluginCommonjs({
        extensions: [".js", ".ts"],
      }),
      babel({
        babelHelpers: "bundled",
        configFile: path.resolve(__dirname, ".babelrc.js"),
      }),
      pluginNodeResolve({
        browser: false,
      }),
    ],
  },

  // CommonJS
  {
    input: inputFileName,
    output: [
      {
        file: pkg.main,
        format: "cjs",
        sourcemap: "inline",
        banner,
        exports: "default",
      },
    ],
    external: [
      ...Object.keys(pkg.dependencies || {}),
      ...Object.keys(pkg.devDependencies || {}),
    ],
    plugins: [
      pluginTypescript(),
      pluginCommonjs({
        extensions: [".js", ".ts"],
      }),
      babel({
        babelHelpers: "bundled",
        configFile: path.resolve(__dirname, ".babelrc.js"),
      }),
      pluginNodeResolve({
        browser: false,
      }),
    ],
  },
];

Finally, we can build. If everything goes well, you will see dist folder and 4 js files(hellotslib.cjs.js, hellotslib.es.js, hellotslib.js, and hellotslib.min.js)

$ yarn build

Now you can add any functionalities that you want to bring to npm world via your npm package 😎

 
Share this
Proudly part of