Joel'-'
Joel'-'

Reputation: 734

Why does import of an ESM module in a CommonJS TypeScript project, result in ERR_REQUIRE_ESM?

I am trying to import the package p-limit into my typescript project. When trying to run the project using tsc && node serve.js, I run into the error below.

Im stuck at this for a few hours now...

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /project/node_modules/p-limit/index.js
require() of ES modules is not supported.
require() of /project/node_modules/p-limit/index.js from /project/dist/services/file.ts is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /project/node_modules/p-limit/package.json.

This piece of code in file.ts is causing the issue:

import pLimit from 'p-limit';
const limit = pLimit(1);

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "rootDir": "src",
    "outDir": "dist",
    "sourceMap": true,
    "experimentalDecorators": true,
    "types": [
      "node",
      "express"
    ],
    "strictNullChecks": true
  },
  "files": [
    "./node_modules/@types/node/index.d.ts",
    "./node_modules/p-limit/index.d.ts"
  ],
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

Node version: v14.18.0

Upvotes: 16

Views: 24986

Answers (4)

Borewit
Borewit

Reputation: 195

Problem

  1. You cannot declare ESM modules in a CommonJS project, you need to use dynamic import for that.
  2. When using TypeScript also using dynamic import maybe problematic, as the compiler transpiles import() to require() which may not able to load the ESM module.

Explanation

You cannot directly import a pure ESM module in a CommonJS project, in the same way you import other dependencies.

Although you are using import, the TypeScript compiler does compile that to require, and therefor you get the error you should be using import.

You can import an ESM module in CommonJS project using dynamic import, using JavaScript, this does not work for TypeScript.

(async () => {
    // This is the way to load an ESM module in a JavaScript / CommonJS project
    const {default: pLimit} = await import('p-limit'); // dynamic import
    const limit = pLimit(1);
})();

When you work in TypeScript, and you try the above code, you will directly run into the next problem, as the TypeScript compiler also transpiles the dynamic import (import()) to require(). That problem is described here: Dynamically load ESM module in CJS package in TypeScript.

Proposed Solution

In line with the recommendation I gave in that problem, this a way to resolve your issue:

You need to add load-esm to your project (a utility I wrote).

npm add load-esm

Now you can dynamically load the pure ESM plimit in your TypeScript code:

import {loadEsm} from 'load-esm';

(async () => {
  const { default: pLimit } = await loadEsm<typeof import('p-limit')>('p-limit'); // dynamic-import of pure ESM module
  const limit = pLimit(1);
})();

Upvotes: 0

mateus
mateus

Reputation: 11

Starting from version 22.12.0, nodejs is able to require() ecmascript modules (ESM) with some constraints, you can check the documentation and constraints here.

In this case, p-limit can be loaded using this feature

Example

Using [email protected] (current latest LTS version)

npm init && npm i -DE [email protected] [email protected] @types/node
// test.ts
import pLimit from "p-limit"

const throttle = pLimit(2)

const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms))

const run = async () => {
    console.log("== START ==")

    const promises = Array(20).fill(0).map((_, idx) => throttle(async () => {
        await delay(1000)
        console.log("done", idx)
    }))
    
    await Promise.all(promises)

    console.log("== FINISH ==")
}

run()
# transpile to js
tsc --module commonjs test.ts

# run
node test.js

Upvotes: 1

SNyamathi
SNyamathi

Reputation: 776

p-limit 4.0.0 and above are now ESM only. You can downgrade p-limit to 3.1.0 which is commonjs and it should work fine.

This package is now pure ESM. Please read this.

https://github.com/sindresorhus/p-limit/releases/tag/v4.0.0

Alternatively you can switch your project from CJS to ESM, but that's a larger issue.

https://nodejs.org/api/esm.html

Upvotes: 21

Brian Ogden
Brian Ogden

Reputation: 19212

add

"lib": [
    "es2017"
]

to your tsconfig.json

Upvotes: -5

Related Questions