ts-node-dev: ES Module error: Cannot use import statement outside a module

Issue description

I am looking at porting some projects to ESM and I don’t know what I am doing wrong. This is the full error:

(node:10732) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
C:\Users\Moritz\Documents\Programming\esmtest\main.ts:1
import cryptoRandomString from 'crypto-random-string';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Object.compileFunction (node:vm:355:18)
    at wrapSafe (node:internal/modules/cjs/loader:1039:15)
    at Module._compile (node:internal/modules/cjs/loader:1073:27)
    at Module._compile (C:\Users\Moritz\Documents\Programming\esmtest\node_modules\source-map-support\source-map-support.js:547:25)
    at Module.m._compile (C:\Users\Moritz\AppData\Local\Temp\ts-node-dev-hook-06833675296542485.js:69:33)
    at Module._extensions..js (node:internal/modules/cjs/loader:1138:10)
    at require.extensions.<computed> (C:\Users\Moritz\AppData\Local\Temp\ts-node-dev-hook-06833675296542485.js:71:20)
    at Object.nodeDevHook [as .ts] (C:\Users\Moritz\Documents\Programming\esmtest\node_modules\ts-node-dev\lib\hook.js:63:13)
    at Module.load (node:internal/modules/cjs/loader:989:32)
    at Function.Module._load (node:internal/modules/cjs/loader:829:14)
[ERROR] 20:21:04 SyntaxError: Cannot use import statement outside a module

Context

OS version (is it docker or host?), ts-node-dev version Windows 10 Home Node version 16.2.0

Did you try to run with ts-node? Yes, this is what I tried: npx ts-node main.ts returned:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for C:\Users\Moritz\Documents\Programming\esmtest\main.ts
    at new NodeError (node:internal/errors:363:5)
    at Loader.defaultGetFormat [as _getFormat] (node:internal/modules/esm/get_format:71:15)
    at Loader.getFormat (node:internal/modules/esm/loader:105:42)
    at Loader.getModuleJob (node:internal/modules/esm/loader:243:31)
    at Loader.import (node:internal/modules/esm/loader:177:17)
    at Object.loadESM (node:internal/process/esm_loader:68:5)

However node --loader ts-node/esm main.ts worked.

Did you try to run with --files option enabled? Yes, no changes

Did you try to run with --debug option enabled? Yes, nothing special

Do you have a repro example (git repo) with simple steps to reproduce your problem? Yes, here: https://github.com/puchm/ts-node-dev-esm-test

Just run npm start

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 58
  • Comments: 28

Most upvoted comments

I got the same error. In my case, the issue has been resolved by changing the option in tsconfig.json

from: { "compilerOptions": { "module": "es2020" } } to: { "compilerOptions": { "module": "commonjs" } }

As mentioned in https://github.com/TypeStrong/ts-node/issues/922#issuecomment-765008102

So all in all this can be summarized as: Using TypeScript, node.js and the TypeScript compiler option “module”: “esnext” together is next to impossible. So if you’re building code that should be ran with node.js, in 2020, you should still choose “module”: “commonjs”.

A workaround is to override compiler options for ts-node and keep TS compiling to module type of choice

Can be done within tsconfig.json

// tsconfig.json

{
  "compilerOptions": {
    "module": "es2020",
    // ... rest of config
  },
  "ts-node": {
    "compilerOptions": {
      "module": "commonjs"
    }
  }
}

ts-node-dev ver. 1.1.8 (using ts-node ver. 9.1.1, typescript ver. 4.4.2)

This is a major blocking issue for us, and we’re considering switching away from ts-node-dev (which worked perfectly in the past) as many dependencies are switching to ESM.

We are using:

In package.json: "type": "module"

In tsconfig.json: "module": "ES2020"

And we are getting: SyntaxError: Cannot use import statement outside a module

Yep, same issue. I’m giving up on react / typescript. It’s a nightmare.

Did anybody find a solution to this mess?

When you set the module option to commonjs, TS will compile your ESM code down to CJS, which is not the solution when you want to have native ESM support. I ran into the same problem with ts-node-dev, while with ts-node it works fine when I’m using the ESM loader as mentioned in original post (like node --loader ts-node/esm src/index.ts).


Node.js version: 16.4.0 ts-node version: 10.0.0 ts-node-dev version: 1.1.6 OS version: macOS 11.4

I still get issues when using ESM only modules with the above config from @johanolandero. Is there any progress on ts-node-dev supporting ES2020 and type: module yet? Or is there a suggested project to migrate to?

I also encounter this problem, so when will ESM be supported?

If your compile time is quick you can instruct nodemon to restart your app using the ts-node ESM loader:

nodemon --exec 'node --loader ts-node/esm' src/index.ts

Otherwise, you can use tsc --watch and have something watch the compiled output. My preferred way to do this is to combine npm-run-all with watchexec (you can do this with nodemon, you’ll just have to do a full build before you start nodemon)

"scripts": {
    "server:build:dev": "tsc -w",
    "server:watch": "watchexec --postpone --restart --debounce 250 --no-vcs-ignore --filter 'dist/**/*' 'node dist/index.js'",
    "server:dev": "npm-run-all --parallel server:build:dev server:watch",
}

ESM is being a nightmare to me. The solution of @johanolandero doesn’t work if you are using import [...] .js to suit ESM in the project as some libs require it.

Same Must use import to load ES Module. error, please fix.

Same issue here. This needs to be updated.

A workaround is to override compiler options for ts-node and keep TS compiling to module type of choice

Can be done within tsconfig.json

// tsconfig.json

{
  "compilerOptions": {
    "module": "es2020",
    // ... rest of config
  },
  "ts-node": {
    "compilerOptions": {
      "module": "commonjs"
    }
  }
}

ts-node-dev ver. 1.1.8 (using ts-node ver. 9.1.1, typescript ver. 4.4.2)

This doesn’t help since that’s for ts-node options not for ts-node-dev, the issue still persists even today for me, can’t get top-level to work properly sadly (works perfectly with ts-node, the fact is that ts-node-dev doesn’t do that), waiting for an update that will allow us the save behaviour in tsconfig.json as ts-node provides already.

Will probably try to figure this out more or simply change to something like nodemon that’s already mentioned.

Same issue

nodemon --exec node --loader ts-node/esm src/main.ts

Install nodemon and ts-node while the ts-node-dev team solves this issue. You’ll enjoy top level await and other goodies. Then in your script: "dev": "nodemon --exec node --loader ts-node/esm src/main.ts" image

Hi @angelhdzmultimedia, I like your theme, is it applied to IDE only or desktop in general? what theme are you using (for VSCode and OS)? is it on a Linux distro?

One Dark Theme and the GlassIt extension to make the window transparent.

@pravesa Running with ts-node-dev I obtain the same error as @angelhdzmultimedia Running with “pure” ts-node I obtain the following error:

immagine_2022-10-15_113235325

PS: the project is available here

@carlocorradini nodejs resolves module path in ESM type with some additional rules. So, refactor imports and exports in typescript project as follows to solve your issue,

example directory

my-app
├── package.json
│ 
└── src
    ├── logger
    │   ├── logger.ts
    │   └──  index.ts
    ├── somefile.ts
    └── main.ts

NOTE --> import and export statements should have full path and .js extensions for files,

example.

// exporting a file inside `/logger/index.ts`
export somefeature from 'logger.js';

 // importing (from) file
import 'somefile.js'; 
import somefeature from 'logger.js';

// importing a subdirectory (full path should be specified)
import { loggerfeature } from './logger/index.js';

If the file doesn’t have .js extension in export or import statement you will get ERR_MODULE_NOT_FOUND error, while for subdirectory import without full path (incl. index.js) you will get ERR_UNSUPPORTED_DIR_IMPORT error.

Hope, this will resolves your problem.

I have the same problem when trying to run mocha tests with ts-node