Skip to content

Instantly share code, notes, and snippets.

@nurmdrafi
Last active July 16, 2025 07:37
Show Gist options
  • Save nurmdrafi/02f2dc857bef0943d5821932a1a0da25 to your computer and use it in GitHub Desktop.
Save nurmdrafi/02f2dc857bef0943d5821932a1a0da25 to your computer and use it in GitHub Desktop.
How to Build and Publish NPM Package Using Rollup, Babel, TypeScript, ESLint, and Jest

🚀 How to Build and Publish NPM Package Using Rollup, Babel, TypeScript, ESLint, and Jest

1. Setup the Project

Create a new directory for your project and initialize it with npm:

mkdir my-package
cd my-package
npm init -y

2. Install Dependencies

We will need a variety of development dependencies for our project. Here's a breakdown of what we need:

  • Rollup: Bundler to compile our code into different module formats.
  • TypeScript: To write strongly-typed JavaScript.
  • Babel: To transpile modern JavaScript for compatibility with older environments.
  • Rollup Plugins: To handle TypeScript, Babel, resolving node modules, and cleaning up dist directories.

Install them by running:

npm install --save-dev @babel/core @babel/plugin-transform-runtime @babel/preset-env @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve @types/jest @typescript-eslint/parser eslint jest rollup rollup-plugin-clear rollup-plugin-copy rollup-plugin-terser rollup-plugin-typescript2 ts-jest typescript

3. Rollup Configuration (rollup.config.js)

We will bundle our code in three formats: IIFE (for browsers), CJS (for Node.js), and ESM (for modern JavaScript bundlers). Here’s the configuration:

// [Why use babel with rollup?](https://rollupjs.org/tools/#babel)

import babel from '@rollup/plugin-babel' // Transpiles modern JavaScript code
import { nodeResolve } from '@rollup/plugin-node-resolve' // Allows Rollup to resolve modules from node_modules
import commonjs from '@rollup/plugin-commonjs' // Converts CommonJS modules to ES6, allowing them to be included in the bundle.
import { terser } from 'rollup-plugin-terser' // Minifies the final output 
import clear from 'rollup-plugin-clear' // Cleans the `dist` folder before each build
import typescript from 'rollup-plugin-typescript2' // Compiles TypeScript files
import copy from 'rollup-plugin-copy' // Copy essential files
import path from 'path'; // Path declaration

export default [{
  // IIFE Bundle Configuration
  input: 'src/index.ts',  // Entry point of the library
  output: [
    {
      file: 'dist/iife/geo-polyline-tools.js', // Output bundle file
      format: 'iife', // Format for browser global usage (Immediately Invoked Function Expression)
      name: 'myPackage', // Global variable name for browser usage
      sourcemap: true // Enable sourcemap for debugging
    }
  ],
  plugins: [
    clear({ targets: ['dist/iife'] }), // Clear previous output in the dist/iife folder
    nodeResolve(), // Allow bundling third-party modules
    commonjs(), // Convert CommonJS modules to ES6
    typescript({
      tsconfig: path.resolve(__dirname, './tsconfig.json'), // Use TypeScript configuration
    }),
    babel({
      exclude: 'node_modules/**', // Don't transpile node_modules
      babelHelpers: 'bundled', // Use bundled babel helpers for efficiency
    }),
    terser(), // Minify the bundle for the browser
    copy({
      targets: [
        { src: 'types/index.d.ts', dest: 'dist/iife' } // Copy TypeScript types to output folder
      ]
    })
  ],
},
{
  // CJS & ESM Bundle Configuration
  input: 'src/index.ts', // Entry point for CommonJS and ESM builds
  output: [
    {
      dir: 'dist/cjs', // Output directory for CommonJS format
      format: 'cjs', // CommonJS format (for Node.js)
      preserveModules: true, // Keep the original module structure
      exports: 'auto', // Auto-detect export style
      sourcemap: true // Enable sourcemap
    },
    {
      dir: 'dist/esm', // Output directory for ESM format
      format: 'es', // ES Module format
      preserveModules: true, // Keep the original module structure
      exports: 'auto', // Auto-detect export style
      sourcemap: true // Enable sourcemap
    }
  ],
  plugins: [
    clear({ targets: ['dist/cjs', 'dist/esm'] }),
    nodeResolve(),
    commonjs(),
    typescript({
      tsconfig: path.resolve(__dirname, './tsconfig.json'),
    }),
    babel({
      exclude: 'node_modules/**',
      babelHelpers: 'runtime',
      plugins: ['@babel/plugin-transform-runtime']
    }),
    terser(),
    copy({
      targets: [
        { src: 'types/index.d.ts', dest: 'dist/cjs' }, // Copy TypeScript types to CJS
        { src: 'types/index.d.ts', dest: 'dist/esm' } // Copy TypeScript types to ESM
      ]
    })
  ]
}
];

4. TypeScript Configuration (tsconfig.json)

{
  "compilerOptions": {
    "target": "ES5", // Target ECMAScript 5 for compatibility
    "module": "ESNext", // Use the latest ECMAScript module system
    "declaration": true, // Generate TypeScript declaration files (.d.ts)
    "declarationDir": "types/index.d.ts", // Specify where to output type declarations
    "rootDir": "src", // Root directory for source files
    "strict": true, // Enable strict type checking
    "esModuleInterop": true, // Compatibility for ES module imports
    "moduleResolution": "node", // Resolve modules like Node.js does
    "sourceMap": true, // Generate sourcemap files for debugging
    "skipLibCheck": true, // Skip type checking for libraries
    "forceConsistentCasingInFileNames": true, // Enforce consistent file name casing
    "allowSyntheticDefaultImports": true, // Allow default imports from modules without default exports
    "typeRoots": [
      "./node_modules/@types", // Look for types in node_modules/@types
      "types/index.d.ts" // Look for custom types in the specified folder
    ]
  },
  "include": [
    "src/**/*.ts", // Include all TypeScript files in the src directory
    "types/**/*" // Include custom types from the types folder
  ],
  "exclude": [
    "node_modules", // Exclude node_modules from compilation
    "dist" // Exclude the dist folder from compilation
  ]
}

Create index.d.ts file which contains the TypeScript type definitions for your my-package library

5. Babel Configuration (.babelrc)

{
  "presets": [
    "@babel/preset-env"
  ]
}

6. ESLint Configuration (.eslintrc.json)

{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint"
  ],
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "rules": {
    "@typescript-eslint/ban-ts-comment": "off",
    "indent": [
      "error",
      4
    ],
    "linebreak-style": [
      "error",
      "unix"
    ],
    "quotes": [
      "error",
      "single"
    ],
    "semi": [
      "error",
      "always"
    ],
    "@typescript-eslint/no-unnecessary-type-constraint": "off"
  },
  "overrides": [
    {
      "files": "*.ts",
      "rules": {
        "@typescript-eslint/no-unused-vars": "warn"
      }
    }
  ]
}

7. Jest Configuration (jest.config.js)

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
};

Create index.test.ts file inside __test__ directory in your root and write test cases

8. Package Configuration (package.json)

{
  "name": "my-package",
  "version": "1.0.0",
  "description": "",
  "author": "username",
  "license": "MIT",
  "main": "dist/cjs/index.js", // Entry point for CommonJS modules
  "typings": "dist/cjs/index.d.ts", // TypeScript type definitions for CommonJS modules
  "module": "dist/esm/index.js", // Entry point for ES modules
  "types": "dist/esm/index.d.ts", // TypeScript type definitions for ES modules
  "files": [
    "src",
    "dist",
    "README.md"
  ],
  "keywords": ["my-package"],
  "repository": {
    "type": "git",
    "url": "https://github.com/username/my-package.git"
  },
  "scripts": {
    "test": "jest",
    "build": "rollup -c",
    "lint": "eslint 'src/**/*.{ts}'" 
    "lint:fix": "eslint 'src/**/*.{ts}' --fix"
  },
  "devDependencies": {
    // List of development dependencies...
  }
}

8. Running Tests

Run the tests using:

npm test

9. Building the Package

npm run build

This will generate your package in the dist/ folder with the iife, cjs, and esm formats including index.d.ts file

10. Publishing the Package

Finally, to publish your package to npm:

  1. Log in to your npm account (if not already logged in):
npm login
  1. Publish the package:
npm publish

Note: Make sure the version in your package.json is updated for each new release.

@svkhrobbeck
Copy link

Thank you, this worked

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment