How To Use an ESM dependency in a library that exports ESM+CJS

Ryan LeeRyan Lee
2 min read

A common problem Typescript library developers face is when importing libraries that only output ESM. For example [the standard library (@std) from the JSR repo](https://jsr.io/@std) only outputs ESM as according to Deno’s mandate. However for most Typescript library developers, this poses a problem because ESM cannot be directly imported into CJS.

This article provides a solution using [TSUP](https://github.com/egoist/tsup), a bundler for Typescript.

Repro: https://github.com/substrate-sdk/substrate-sdk/tree/master/packages/%40std

Solution

The solution to this problem is to re-export the library under a new package and then bundle it with TSUP but declare two entries in your `tsup.config.ts` file; one for ESM and one for CJS. Here is an example for re-exporting @std/bytes.

// tsup.config.ts
import { defineConfig } from 'tsup'

export default defineConfig([
  {
    entry: {
      index: 'src/mod.ts',
    },
    outDir: 'dist/esm',
    format: ['esm'],
    dts: true,
    sourcemap: true,
    clean: true,
  },
  {
    entry: {
      index: 'src/mod.ts',
    },
    outDir: 'dist/commonjs',
    format: ['cjs'],
    dts: true,
    sourcemap: true,
    clean: true,
  },
])

Our source code simply looks like this

// src/mod.ts
export * from '@std/bytes'

And our package.json would look something like this. Pay extra attention to the subpath exports where we define import and require.

{
  "name": "@substrate-sdk/std__bytes",
  "version": "0.0.0",
  "author": "Ryan Lee <ryanleecode@gmail.com>",
  "type": "module",
  "main": "./dist/commonjs/index.cjs",
  "types": "./dist/commonjs/index.d.cts",
  "module": "./dist/esm/index.js",
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "import": {
        "@substrate-sdk/source": "./src/mod.ts",
        "types": "./dist/esm/index.d.ts",
        "default": "./dist/esm/index.js"
      },
      "require": {
        "types": "./dist/commonjs/index.d.cts",
        "default": "./dist/commonjs/index.cjs"
      }
    }
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/substrate-sdk/substrate-sdk.git"
  },
  "keywords": [
    "bytes"
  ],
  "files": [
    "dist"
  ],
  "scripts": {
    "prepare": "pnpm turbo build",
    "clean": "rimraf dist",
    "build": "pnpm run clean && tsup"
  },
  "dependencies": {
    "@std/bytes": "npm:@jsr/std__bytes@^1.0.4"
  }
}

The full example can be found on github here: https://github.com/substrate-sdk/substrate-sdk/tree/master/packages/%40std

0
Subscribe to my newsletter

Read articles from Ryan Lee directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ryan Lee
Ryan Lee

Full Stack Developer writing about functional programming, AI, and blockchain.