UNPKG

load-esm

Version:

Utility to dynamically load ESM modules in TypeScript CommonJS projects

141 lines (90 loc) 3.9 kB
[![Node.js CI](https://github.com/Borewit/load-esm/actions/workflows/nodejs-ci.yml/badge.svg)](https://github.com/Borewit/load-esm/actions/workflows/nodejs-ci.yml) [![NPM version](https://img.shields.io/npm/v/load-esm.svg)](https://npmjs.org/package/load-esm) [![npm downloads](http://img.shields.io/npm/dm/load-esm.svg)](https://npmcharts.com/compare/load-esm?start=365) # load-esm **load-esm** is a tiny utility that lets CommonJS (CJS) TypeScript projects **dynamically import pure ESM packages** at runtime—without hacks like `eval()`. It helps avoid errors like: * `Error [ERR_REQUIRE_ESM]: require() of ES Module` * `Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in ...` --- ## Installation ```bash npm install load-esm # or yarn add load-esm # or pnpm add load-esm ``` > Works in CJS TypeScript projects. No config changes required. --- ## Quick start ```ts // TypeScript (CJS project) import { loadEsm } from "load-esm"; (async () => { const esmModule = await loadEsm("esm-module"); // use esmModule... })(); ``` ### With typings ```ts import { loadEsm } from "load-esm"; (async () => { const esmModule = await loadEsm<typeof import("esm-module")>("esm-module"); // esmModule is fully typed })(); ``` ### Concrete example (pure ESM package) ```ts import { loadEsm } from "load-esm"; (async () => { try { // Import a pure ESM package from a CommonJS TS project const { fileTypeFromFile } = await loadEsm<typeof import("file-type")>( "file-type" ); const type = await fileTypeFromFile("fixture.gif"); console.log(type); } catch (error) { console.error("Error importing module:", error); } })(); ``` > Note: Because top‑level `await` isn’t available in CommonJS, examples use an async IIFE. --- ## API ```ts function loadEsm<T = unknown>(name: string): Promise<T> ``` **Parameters** * `name` Package name or file path to import. **Returns** * `Promise<T>` resolving to the imported module namespace. --- ## How it works In CJS TypeScript projects (`"module": "commonjs"`), the TS compiler transpiles dynamic `import()` to `require()`, which **breaks** when the target is a pure ESM package. `load-esm` executes the `import()` **outside of TypeScript’s transpilation scope**, preserving native dynamic `import()` semantics at runtime. This keeps your code type‑safe while avoiding brittle workarounds (e.g., wrapping `import()` in `eval()`). ### What about Node.js ≥ 22.12? Since Node.js 22.12, `require` can load **some** ESM modules, but there are [documented constraints](https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require). If your dependencies are compatible with that path, you might not need this utility. `load-esm` remains useful when: * You’re on older Node.js versions that support `import()` (see Compatibility) but not the newer `require()` behavior. * You want a single, consistent pattern that works across environments and avoids edge cases. > If Node’s built‑in `require(esm)` works for your packages and version, feel free to use it. --- ## Compatibility * **Node.js**: 13.2.0 (first version with native `import()` support) * **TypeScript**: Fully typed; works in CJS projects. --- ## Troubleshooting * **`ERR_REQUIRE_ESM`**: Ensure you’re using `load-esm(...)` to import the ESM dependency from CJS code. * **`No "exports" main defined`**: Some packages only expose ESM entry points. Import them via `load-esm`. * **Type declarations**: Use the generic form `loadEsm<typeof import("pkg")>("pkg")` for typed access. * **Top‑level await**: Wrap usage in an async IIFE in CJS. --- ## License [MIT](./LICENSE.txt) --- ### Changelog See [Releases](https://github.com/Borewit/load-esm/releases). --- ### Acknowledgements Inspired by common pain points when mixing CJS projects with modern ESM‑only libraries.