Building Your Own NPM Library Using Rollup and TypeScript

Creating a modern JavaScript library can be quite an undertaking due to the myriad of tools and configurations available. Today, we’ll walk you through building your own NPM library using Rollup and TypeScript. Let’s break down the provided configuration files and understand the essentials.

Setting up Rollup Configuration

Rollup is a module bundler that allows you to bundle your JavaScript files into different formats such as CommonJS (cjs) and ECMAScript modules (esm). Here is the Rollup configuration:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import dts from 'rollup-plugin-dts'; // create type def files
import terser from '@rollup/plugin-terser'; // minify
import peerDepsExternal from 'rollup-plugin-peer-deps-external'; // add peer deps in bundle
import postcss from 'rollup-plugin-postcss';

const packageJson = require('./package.json');

export default [
  {
    input: 'src/index.ts',
    output: [
      {
        file: packageJson.main,
        format: 'cjs',
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: 'esm',
        sourcemap: true,
      },
    ],
    plugins: [
      peerDepsExternal(),
      resolve(),
      commonjs(),
      typescript({ tsconfig: './tsconfig.json' }),
      postcss({
        minimize: true,
      }),
      terser(),
    ],
    external: [
      'react',
      'react-dom'
    ],
  },
  {
    input: 'src/index.ts',
    output: [{ file: 'dist/types.d.ts', format: 'es' }],
    plugins: [dts.default()],
    external: [/\.css$/, /\.scss$/],
  },
];

Configuration Array

The configuration exports an array of configurations. Each configuration object tells Rollup how to create a separate bundle. This configuration will produce two bundles.

First Configuration (Main Library Bundle)

  1. input: 'src/index.ts'
    • This specifies the entry point for Rollup. It will begin bundling from this file.
  2. output:
    • An array detailing the outputs of the bundling process.
      • file: packageJson.main
        • The filename to output the bundled code. This value is dynamically sourced from a package.json file (presumed to be imported earlier).
        • format: 'cjs'
          • This specifies the output as CommonJS, typically utilized in Node.js environments.
        • sourcemap: true
          • Generates a sourcemap, which is useful for debugging.
      • file: packageJson.module
        • Another filename for the bundled output. Again, its value is fetched from package.json.
        • format: 'esm'
          • Specifies the output as ES modules, a modern JavaScript module system.
        • sourcemap: true
          • Generates a sourcemap for this format as well.
  3. plugins:
    • An array of plugins used during the bundling process.
      • peerDepsExternal(): Prevents peer dependencies from being bundled. This ensures peer dependencies are treated as ‘external’ to the bundle.
      • resolve(): Allows Rollup to resolve and bundle modules from ‘node_modules’.
      • commonjs(): Converts CommonJS modules to ES6, making them compatible with the Rollup bundle.
      • typescript({ tsconfig: './tsconfig.json' }): Compiles TypeScript files to JavaScript. The configuration is derived from ./tsconfig.json.
      • postcss({ minimize: true }): Processes CSS imports to minimize the CSS.
      • terser(): Minifies the bundled output, reducing its size.
  4. external: ['react', 'react-dom']
    • This configuration specifies that react and react-dom should be treated as ‘external’ dependencies. This means they won’t be bundled into the final output but are expected to be present in the consuming environment.

Second Configuration (Type Definitions)

  1. input: 'src/index.ts'
    • Uses the same entry point as the first configuration.
  2. output:
    • file: 'dist/types.d.ts'
      • The output file for type definitions.
    • format: 'es'
      • The format is set as ES modules.
  3. plugins:
    • An array of a single plugin.
      • dts.default(): This plugin produces a bundled TypeScript definition file.
  4. external: /\.css$/, /\.scss$/
    • Specifies that CSS and SCSS files are ‘external’, implying they won’t be included in this bundle. This makes sense, as this bundle is purely for type definitions.

In summary, this configuration is set up to produce two separate bundles:

  1. The main JavaScript library bundle, which is compiled from TypeScript, processed with PostCSS, and minified.
  2. A bundled TypeScript definition file, which describes the types in the library for TypeScript users.

TypeScript Configuration

You’ll also need two TypeScript configurations:

  1. The main tsconfig.json, which has configurations for the library itself.
  2. The tsconfig.node.json, designed to handle certain node configurations.
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "jsx": "react",
    "outDir": "dist",

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,

    /* Linting */
    "noImplicitAny": false,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}
// tsconfig.node.json
{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  }
}

Remember, TypeScript configurations describe how your TypeScript code is transpiled. Some important points from this configuration include:

  • Targeting ES2020.
  • Enabling JSX syntax for React.
  • Specifying linting options to ensure code quality.

PostCSS Configuration

Lastly, you need a PostCSS configuration. This indicates that your library is likely making use of styles. PostCSS will process your styles, and with these plugins.

module.exports = {
  plugins: {
    autoprefixer: {},
    'postcss-import': {}
  },
};

Steps to Build Your NPM Library

Now, with the provided configurations, here’s a step-by-step guide:

  1. Setup Your Project: Start with a fresh directory and initialize it with npm init. Fill in the necessary details.
  2. Install Dependencies: Based on your configurations, install the necessary dependencies:
   npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-dts @rollup/plugin-terser rollup-plugin-peer-deps-external rollup-plugin-postcss typescript postcss tailwindcss autoprefixer postcss-import
  1. Add the Provided Configurations: Add the provided Rollup, TypeScript, and PostCSS configurations into your project directory.
  2. Add the required configurations: Add this snippit to your package.json other project importing your library will know wherer to find the files needed:
 "main": "dist/cjs/index.js",
 "module": "dist/esm/index.js",
 "types": "dist/index.d.ts",
 "scripts": {
    "build": "rollup -c --bundleConfigAsCjs",
 },
  1. Write Your Library Code: Create a src directory and start coding your library. Remember, based on the configuration, the entry file is src/index.ts.
  2. Build the Library: Use the command npm run build to bundle your library. This will create the bundles as per your Rollup configuration.
  3. Publish to NPM: Once you’re ready to share your library, make sure you are logged in to your NPM account using npm login and then run:
   npm publish
  1. Done! Your library should now be available on NPM for others to use!

Leave a Reply

Your email address will not be published. Required fields are marked *