Skip to content

Instantly share code, notes, and snippets.

@nurmdrafi
Last active February 18, 2025 19:48
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.

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