npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

esm-and-other-format-libraries-starter

v1.1.0

Published

A starter kit for building and publishing multi-format (ESM, CommonJS, UMD) npm packages with TypeScript + Rollup.

Readme

esm-and-other-format-libraries-starter

npm version License: MIT TypeScript Rollup

Write once in TypeScript, ship everywhere — ESM, CommonJS, and UMD from a single source.

A batteries-included starter for building and publishing multi-format npm packages with TypeScript + Rollup. Clone it, swap in your code, and publish.

This repository includes a string utility library (capitalize, slugify, truncate, camelCase) as a working sample. It demonstrates the full workflow — writing modules, testing, building, and publishing — so you can see how everything fits together before replacing it with your own code.

Table of Contents

Build Outputs

The included sample library (mylib) produces the following files:

| Format | File | Use Case | |--------|------|----------| | ES Modules | dist/mylib.esm.js | Bundlers (Vite, webpack, Rollup, etc.) | | CommonJS | dist/mylib.common.js | Node.js require() | | UMD | dist/mylib.js | <script> tag / global window.mylib |

The Getting Started guide below uses your-library as a placeholder name. Replace it with your own library name when creating a new project.

Getting Started

1. Create a Project

mkdir your-library && cd $_
npm init -y

2. Configure package.json

Add the following entry points:

{
  "main": "dist/your-library.common.js",   // CommonJS
  "module": "dist/your-library.esm.js",     // ES Modules
  "browser": "dist/your-library.js",        // UMD
  "types": "types/your-library.d.ts"        // TypeScript declarations
}

3. Install Dependencies

npm i -D typescript ts-node tsconfig-paths \
  rollup rollup-plugin-typescript2 @rollup/plugin-terser \
  jest @types/jest ts-jest

| Package | Role | |---------|------| | typescript | TypeScript compiler | | ts-node | Run TypeScript directly on Node.js | | tsconfig-paths | Resolve tsconfig paths aliases at runtime | | rollup | Module bundler | | rollup-plugin-typescript2 | TypeScript plugin for Rollup | | @rollup/plugin-terser | Minify bundles | | jest | Testing framework | | @types/jest | Type definitions for Jest | | ts-jest | TypeScript preprocessor for Jest |

4. Configure TypeScript

Create tsconfig.json:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "declaration": true,
    "declarationDir": "./types",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "noImplicitAny": true,
    "baseUrl": "./",
    "paths": { "~/*": ["src/*"] },
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.test.ts"]
}

| Option | Value | Description | |--------|-------|-------------| | target | ESNext | Emit latest ECMAScript features | | module | ESNext | Preserve import / export as-is | | declaration | true | Generate .d.ts type definition files | | declarationDir | ./types | Output directory for type definitions | | paths | {"~/*": ["src/*"]} | Use ~/ as an alias for src/ |

5. Write Your Library

mkdir src
// src/capitalize.ts
/** Capitalize the first letter of a string. */
export default function capitalize(str: string): string {
  if (str.length === 0) return str;
  return str.charAt(0).toUpperCase() + str.slice(1);
}
// src/slugify.ts
/** Convert a string into a URL-friendly slug. */
export default function slugify(str: string): string {
  return str
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/[\s-]+/g, '-')
    .replace(/^-+|-+$/g, '');
}
// src/truncate.ts
/** Truncate a string to the given length and append a suffix. */
export default function truncate(str: string, length: number, suffix: string = '...'): string {
  if (str.length <= length) return str;
  return str.slice(0, length) + suffix;
}
// src/camelCase.ts
/** Convert a string to camelCase. */
export default function camelCase(str: string): string {
  return str
    .replace(/(?:^\w|[A-Z]|\b\w)/g, (ch, index) =>
      index === 0 ? ch.toLowerCase() : ch.toUpperCase()
    )
    .replace(/[\s\-_]+/g, '');
}
// src/your-library.ts — Entry point that re-exports all modules
import capitalize from '~/capitalize';
import slugify from '~/slugify';
import truncate from '~/truncate';
import camelCase from '~/camelCase';
export { capitalize, slugify, truncate, camelCase };

6. Run on Node.js

Replace your-library with the actual entry point file name (e.g. mylib in this starter).

npx ts-node -r tsconfig-paths/register -P tsconfig.json \
  -O '{"module":"commonjs"}' \
  -e "import { capitalize, slugify } from '~/your-library'; \
      console.log(capitalize('hello'));  \
      console.log(slugify('Hello World'));"
# => Hello
# => hello-world

7. Set Up & Run Tests

Create jest.config.js:

const { pathsToModuleNameMapper } = require('ts-jest');
const { compilerOptions } = require('./tsconfig.json');

module.exports = {
  roots: ['<rootDir>/src', '<rootDir>/tests/'],
  transform: { '^.+\\.tsx?$': 'ts-jest' },
  testRegex: '(/tests/.*|(\\.|/)(test|spec))\\.tsx?$',
  moduleFileExtensions: ['ts', 'js'],
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
    prefix: '<rootDir>/',
  }),
};

Write test files (showing two examples below — the sample library includes tests for all four functions):

mkdir tests
// tests/capitalize.test.ts
import { capitalize } from '~/your-library';

test('capitalizes the first letter', () => {
  expect(capitalize('hello')).toBe('Hello');
});

test('handles empty string', () => {
  expect(capitalize('')).toBe('');
});
// tests/slugify.test.ts
import { slugify } from '~/your-library';

test('converts spaces to hyphens', () => {
  expect(slugify('Hello World')).toBe('hello-world');
});

test('removes special characters', () => {
  expect(slugify('Hello, World!')).toBe('hello-world');
});
npm test
PASS  tests/capitalize.test.ts
PASS  tests/slugify.test.ts
PASS  tests/truncate.test.ts
PASS  tests/camelCase.test.ts

Test Suites: 4 passed, 4 total
Tests:       16 passed, 16 total

8. Build

Create rollup.config.mjs:

Note: The UMD global name is derived from the browser field in package.json (e.g. dist/mylib.jswindow.mylib). If the name contains hyphens or underscores, it will be automatically converted to camelCase (e.g. your-librarywindow.yourLibrary).

import { createRequire } from 'node:module';
import typescript from 'rollup-plugin-typescript2';
import terser from '@rollup/plugin-terser';

// Load package.json (Rollup v4 no longer supports direct JSON imports)
const require = createRequire(import.meta.url);
const pkg = require('./package.json');

export default {
  external: Object.keys(pkg.dependencies || {}),
  input: './src/your-library.ts',
  plugins: [
    typescript({
      tsconfigDefaults: { compilerOptions: {} },
      tsconfig: 'tsconfig.json',
      tsconfigOverride: { compilerOptions: {} },
      useTsconfigDeclarationDir: true,
    }),
    terser(),
  ],
  output: [
    { format: 'esm', file: pkg.module },
    { format: 'cjs', file: pkg.main },
    {
      format: 'umd',
      file: pkg.browser,
      name: pkg.browser
        .replace(/^.*\/|\.js$/g, '')
        .replace(/([-_][a-z])/g, (g) =>
          g.toUpperCase().replace('-', '').replace('_', '')
        ),
    },
  ],
};
npm run build

Build output:

dist/
  your-library.esm.js       # ES Modules
  your-library.common.js     # CommonJS
  your-library.js            # UMD
types/
  your-library.d.ts          # Type definitions
  capitalize.d.ts
  slugify.d.ts
  truncate.d.ts
  camelCase.d.ts

Publishing to npm

Initial Setup

# Set local npm user info
npm set init.author.name 'Your name'
npm set init.author.email '[email protected]'
npm set init.author.url 'https://your-url.com'
npm set init.license 'MIT'
npm set init.version '1.0.0'

# Log in to npm (or create a new account)
npm adduser

Publish

# 1. Create a GitHub repo & clone
git clone https://github.com/your-user/your-repository.git

# 2. Exclude unnecessary files from the package
printf 'node_modules/\npackage-lock.json\n' > .npmignore

# 3. Tag and publish
git tag -a v1.0.0 -m 'My first version v1.0.0'
git push origin v1.0.0
npm publish

Version Upgrade

git commit -am 'Update something'
git push
npm version patch -m "Update something"
git push --tags
npm publish

Usage

The examples below use the sample library included in this repository.

npm i esm-and-other-format-libraries-starter

ES Modules

Works directly in the browser — no compilation needed.

<script type="module">
  import { capitalize, slugify } from './node_modules/esm-and-other-format-libraries-starter/dist/mylib.esm.js';
  console.log(capitalize('hello'));      // => 'Hello'
  console.log(slugify('Hello World'));   // => 'hello-world'
</script>

CommonJS

Requires a bundler to run in the browser.

npm i -D webpack webpack-cli
// app.js
import { capitalize, slugify } from 'esm-and-other-format-libraries-starter';
console.log(capitalize('hello'));      // => 'Hello'
console.log(slugify('Hello World'));   // => 'hello-world'
npx webpack app.js -o bundle.js
<script src="bundle.js"></script>

UMD

Available as a global variable — just drop in a <script> tag.

<script src="node_modules/esm-and-other-format-libraries-starter/dist/mylib.js"></script>
<script>
  console.log(mylib.capitalize('hello'));      // => 'Hello'
  console.log(mylib.slugify('Hello World'));   // => 'hello-world'
</script>

Appendix: tsconfig module Output Comparison

This appendix uses a minimal example to illustrate how the module setting in tsconfig affects compiled output.

// src/app.ts
import capitalize from './capitalize';
const result = capitalize('hello');
// src/capitalize.ts
export default function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

module: "es2015" / "ESNext"

import / export statements are preserved as-is.

// dist/app.js
import capitalize from './capitalize';
const result = capitalize('hello');
// dist/capitalize.js
export default function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

module: "commonjs"

Converted to require() / exports.

// dist/app.js
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const capitalize_1 = __importDefault(require("./capitalize"));
const result = capitalize_1.default('hello');
// dist/capitalize.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function default_1(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
}
exports.default = default_1;

module: "amd"

Wrapped in AMD define().

// dist/app.js
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
define(["require", "exports", "./capitalize"], function (require, exports, capitalize_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    capitalize_1 = __importDefault(capitalize_1);
    const result = capitalize_1.default('hello');
});
// dist/capitalize.js
define(["require", "exports"], function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    function default_1(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }
    exports.default = default_1;
});

module: "system"

Wrapped in SystemJS System.register().

// dist/app.js
System.register(["./capitalize"], function (exports_1, context_1) {
    "use strict";
    var capitalize_1, result;
    var __moduleName = context_1 && context_1.id;
    return {
        setters: [
            function (capitalize_1_1) {
                capitalize_1 = capitalize_1_1;
            }
        ],
        execute: function () {
            result = capitalize_1.default('hello');
        }
    };
});
// dist/capitalize.js
System.register([], function (exports_1, context_1) {
    "use strict";
    var __moduleName = context_1 && context_1.id;
    function default_1(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }
    exports_1("default", default_1);
    return {
        setters: [],
        execute: function () {
        }
    };
});

module: "umd"

Wrapped in a universal loader that supports both CommonJS and AMD.

// dist/app.js
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./capitalize"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    const capitalize_1 = __importDefault(require("./capitalize"));
    const result = capitalize_1.default('hello');
});
// dist/capitalize.js
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    function default_1(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }
    exports.default = default_1;
});

References

License

MIT

Author