8

There's dozens of other questions with basically the same title, but none of the answers seem to be relevant and just add to the confusion.

This is my tsconfig.json:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "lib": ["es2017", "es7", "es6", "dom"],
    "declaration": true,
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true
  },
  "exclude": [
    "node_modules",
    "dist"
  ]
}

This is what I typed:

import md5 from 'js-md5';
import got from 'got';
import { Design } from './Design';
...

This is my error:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: C:\Users\...\node_modules\got\dist\source\index.js require() of ES modules is not supported.

What. I'm not using require, I'm using import. I haven't had this error on any other modules, so why is Got different (and why is nothing ever simple)?

Why is this happening and how do I fix it?


As per @jsejcksn's answer, I tried changing my tsconfig.json to the following:

{
  "compilerOptions": {
    "target": "es6",
    "module": "esnext",
    "lib": ["es2017", "es7", "es6", "dom"],
    "declaration": true,
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true
  },
  "exclude": [
    "node_modules",
    "dist"
  ]
}

... and added "type": "module" to my package.json:

{
    "dependencies": {
        "body-parser": "^1.19.0",
        "express": "^4.17.1",
        "fs-extra": "^10.0.0",
        "got": "^12.0.1",
        "js-md5": "^0.7.3",
        "moment": "^2.29.1",
        "semver": "^7.3.5",
        "typescript": "^4.4.3",
        "uuid": "^8.3.2",
        "@types/node": "^16.9.2",
        "@types/express": "^4.17.13",
        "@types/fs-extra": "^9.0.13",
        "@types/semver": "^7.3.9",
        "@types/uuid": "^8.3.1",
        "@types/js-md5": "^0.4.3"
    },
    "type": "module"
}

But now I just get different errors :|

src/Logging.ts:1:20 - error TS2792: Cannot find module 'moment'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?

1 import moment from 'moment';

src/SyncAPI.ts:2:17 - error TS2792: Cannot find module 'got'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?

2 import got from 'got';

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Clonkex
  • 3,373
  • 7
  • 38
  • 55

3 Answers3

9

In my case this very error was caused by ts-node-dev package.

If you use the same package (it usually sits in dev dependencies in package.json) you may check the resolution steps I posted in a parallel thread. Hope this helps.

Denis P
  • 497
  • 6
  • 11
8

You have configured TypeScript to compile your modules to CommonJS format (your TSConfig option compilerOptions.module is set to "commonjs"). You'll need to set that option to an ESM-compatible setting (such as "esnext") so that the compiled JavaScript emitted by the TS compiler is in ESM format.

In your package.json, you'll probably also need to set the "type" field to "module".


Edit: To use npm packages by name as bare specifiers in your import statements, you'll also need to update your TSConfig compilerOptions.moduleResolution to "node". In your current config, you have included "dom" in the compilerOptions.lib array. "dom" is for environments with a DOM (which Node does not natively have), so that shouldn't be there. Instead, setting that array to ["es2017"] will include the others you have specified.


Edit: In attempt to provide more direct guidance:

  • Make sure you're not using an outdated version of Node (the current LTS is ^16.x.x)
  • Make sure you're not using an outdated version of TypeScript (currently at ^4.5.x)
  • Move all your source files to ./src (if they're not already in that directory), and
  • Use this as your ./tsconfig.json:
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "declaration": true,
    "esModuleInterop": true,
    "lib": [
      "es2021"
    ],
    "module": "esnext",
    "moduleResolution": "node",
    "outDir": "dist",
    "strict": true,
    "target": "es2021"
  },
  "include": [
    "src/**/*"
  ]
}
  • and make sure that the "type" field in your ./package.json is set to "module":
{
  // ...
  "type": "module"
  // ...
}

You shouldn't have any trouble with import statement module system compatibility after that.

jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • Thanks for the answer, though I don't have a clue what it means :| Changing `compilerOptions.module` to `esnext` makes new errors appear, and adding `"type": "module"` doesn't change anything. `Cannot find module 'moment'. Did you mean to set the 'moduleResolution' option to 'node'` and the same thing for Got. I don't know anything about how importing modules actually works but this seems beyond excessively complicated. – Clonkex Feb 22 '22 at 04:31
  • Yes, setting `compilerOptions.moduleResolution` to `"node"` is required to use bare module specifiers (npm package imports). You'll want to spend some time learning about different JS module systems (ESM and CJS specifically), about how import specifiers work, about how TypeScript compiles TypeScript syntax to JavaScript syntax according to your configuration, about how Node interprets different syntax, etc. before you can expect to be productive. – jsejcksn Feb 22 '22 at 04:52
  • If you are looking for a simpler tooling experience for your out-of-browser TypeScript programming, you might find [Deno](https://deno.land) of interest. – jsejcksn Feb 22 '22 at 04:59
  • 17
    _"You'll want to spend some time learning..."_ sigh. My issue with this is I shouldn't have to. It should just work. Typescript, NodeJS, even Got... these have all been around for AGES and are massively popular. There's zero reason that I should have to understand how a compiler works to use it. I should just be able to follow the instructions and it should just work. I get really fed up with people saying "nah you have to know 13 years of history to understand how to use this". That's insane. `import got from 'got'` should work exactly the same as `import moment from 'moment'`. – Clonkex Feb 22 '22 at 05:05
  • Thank you for your answer though. Eventually I discovered that Got v11 works as expected. For clarification, my problem isn't just that it doesn't work, but also that it doesn't work and _doesn't tell you why_. The error is meaningless since I'm already using `import` rather than `require`. Suddenly I go from being nearly finished to wasting two hours trying to understand why it doesn't work when I'm doing exactly what it says to do and exactly what I have done for the past year with literally every other module. Makes it feel like Got is different for the sake of being different. – Clonkex Feb 22 '22 at 05:06
  • "_My issue with this is I shouldn't have to [spend time learning how to use the toolchain I'm trying to use]_" That mentality might be part of the reason you're having trouble. – jsejcksn Feb 22 '22 at 21:16
  • Perhaps, though I would argue very few people fully understand their toolchains. They just learn enough to get it working and then expect it to keep working. My complaint is more about how hidden the problem is. I've been using it for over a year with dozens of instances `import x from 'x'` and this is the first time I've seen this issue. That's really frustrating. I don't even remember why I set tsconfig the way I did let alone how it works. I just want it to continue functioning as it has. (Now I vaguely know the issue: there's a giant nightmare of different module systems under the hood.) – Clonkex Feb 22 '22 at 21:30
  • @Clonkex I updated my answer to provide some very direct guidance. Does that now answer your question? – jsejcksn Feb 22 '22 at 21:51
  • Well, I tried. I used nvm-windows to update node to 16.14.0. I set my tsconfig and package.json as you said. I specified the latest 4.5 version of ts. But something about node 16 or npm 8 just doesn't play nice. Like, at all. I can't even do npm install. All it ever does is say `npm ERR! Unexpected token '.'`. So I tried reverting to node 14.15.4 and using that latest ts, but I just get `Cannot find module '...\dist\Design' imported from ...\dist\Server.js` for any of my own code. Unfortunately this is production software and I need it to just work, so I can't really mess with updates. – Clonkex Feb 22 '22 at 22:29
  • Ooo apparently it's a [bug in npm](https://github.com/npm/cli/issues/4234), huh. Well I'll come back to this when I have more time. Really under pressure to just get the software done right now. – Clonkex Feb 22 '22 at 22:32
  • 1
    Re: `nvm-windows`: I suggest looking at [`volta`](https://volta.sh/) when you get a chance. – jsejcksn Feb 22 '22 at 22:34
  • The JS world is completely confusing. No normal person knows when/why to use which module type in which case. It's just so fragmented. – Josh M. May 16 '23 at 19:10
1

You can also use tsc-node-esm instead of tsc-node to run your script after adding "type": "module" to your package.json

Vic
  • 41
  • 2