Skip to main content

Command Palette

Search for a command to run...

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

Updated
2 min read
R

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

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