Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES Module error: Cannot use import statement outside a module #265

Open
puchm opened this issue May 23, 2021 · 28 comments
Open

ES Module error: Cannot use import statement outside a module #265

puchm opened this issue May 23, 2021 · 28 comments

Comments

@puchm
Copy link

puchm commented May 23, 2021

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

@quadratz
Copy link

quadratz commented Jul 12, 2021

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 TypeStrong/ts-node#922 (comment)

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".

@octet-stream
Copy link

octet-stream commented Jul 12, 2021

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

@Cretezy
Copy link

Cretezy commented Jul 14, 2021

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

@ghost
Copy link

ghost commented Jul 24, 2021

Same issue here. This needs to be updated.

@ivawzh
Copy link

ivawzh commented Aug 1, 2021

Same issue

@elliotsaha
Copy link

elliotsaha commented Aug 6, 2021

Any update to this? The issue is still persistent in the package.

@jianhao
Copy link

jianhao commented Aug 25, 2021

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

@ghost
Copy link

ghost commented Aug 26, 2021

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

@johanolandero
Copy link

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)

@lukeberry99
Copy link

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?

@SrBrahma
Copy link

SrBrahma commented Nov 19, 2021

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.

@trickstival
Copy link

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

@damianobarbati
Copy link

Did anybody find a solution to this mess?

@spiffytech
Copy link

spiffytech commented Feb 8, 2022

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",
}

@ghost
Copy link

ghost commented Apr 1, 2022

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.

@angelhdzdev
Copy link

angelhdzdev commented Jul 28, 2022

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.

I managed to get everything working with Nodemon + Babel + Babel TypeScript plugins/presets. And I think the solution also involves ts-node, can't remember now.

Yeah confirmed it, it uses ts-node: nodemon -I --exec node --experimental-specifier-resolution=node --loader ts-node/esm ./src/main.ts.

I hope this gets solved soon as I want to keep using ts-node-dev but I need ESM and top level await and all the good stuff from EcmaScript Next.

Update

I've been lately using the following approach which is fast and smooth:
nodemon --exec node --loader ts-node/esm src/main.ts.

@pravesa
Copy link

pravesa commented Oct 14, 2022

Solution

To use the native ESM support of the node within our typescript project and to transpile with ts-node, we have to reconfigure the package.json and tsconfig.json accordingly.

In package.json,

{
-  "type": "commonjs"
+  "type": "module"
}

In tsconfig.json,

{
  "compilerOptions": {
-    "module": "CommonJS"
+    "module": "ESNext" // or ES2015, ES2020
  },
  "ts-node": {
+    "esm": true
  }
}

Note:

  • without "type": "module" in package.json, we get SyntaxError: Cannot use import statement outside a module error
  • without "esm": true in tsconfig.json but package.json reconfigured, we get TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" error

@carlocorradini
Copy link

@pravesa Unfortunately throws an error: ERR_UNSUPPORTED_DIR_IMPORT 😥

@pravesa
Copy link

pravesa commented Oct 14, 2022

@carlocorradini This error might be caused while resolving your imported module path. So, could you provide your exact error stack

@angelhdzdev
Copy link

Solution

To use the native ESM support of the node within our typescript project and to transpile with ts-node, we have to reconfigure the package.json and tsconfig.json accordingly.

In package.json,

{
-  "type": "commonjs"
+  "type": "module"
}

In tsconfig.json,

{
  "compilerOptions": {
-    "module": "CommonJS"
+    "module": "ESNext" // or ES2015, ES2020
  },
  "ts-node": {
+    "esm": true
  }
}

Note:

  • without "type": "module" in package.json, we get SyntaxError: Cannot use import statement outside a module error
  • without "esm": true in tsconfig.json but package.json reconfigured, we get TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" error

Sadly this doesn't work for me.

I have everything configured correctly, type module in package.json, target and module to ESNext in tsconfig, moduleResolution to node, esModuleInterop true, allowSyntheticImports true, and "ts-node": { esm: true }

Then in my scripts:
"dev": "ts-node-dev --respawn src/main.ts"

And I get the error that I posted many times in this repository:

Must use import to load ES Module.

With nodemon and ts-node I got it working this way:
nodemon --exec node --loader ts-node/esm src/main.ts

No issues so far.

@carlocorradini
Copy link

Same error as @angelhdzmultimedia
@pravesa I obtain ERR_UNSUPPORTED_DIR_IMPORT error when simply running ts-node ....
Supporting top-level-await could be super cool and very (very) useful.

@angelhdzdev
Copy link

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

@pravesa
Copy link

pravesa commented Oct 14, 2022

Same error as @angelhdzmultimedia @pravesa I obtain ERR_UNSUPPORTED_DIR_IMPORT error when simply running ts-node .... Supporting top-level-await could be super cool and very (very) useful.

@carlocorradini I hope that the error message you get in your terminal is like this,
CustomError: ERR_UNSUPPORTED_DIR_IMPORT <file_path_in_your_project_that_is_imported> <file_that_has_import_statement>

So, to resolve this I need your exact error stack

@carlocorradini
Copy link

@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

@pravesa
Copy link

pravesa commented Oct 15, 2022

@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.

@warcayac
Copy link

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?

@angelhdzdev
Copy link

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.

@hyoretsu
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests