using esm modules with ts-node

13.3k Views Asked by At

I successfully ran ts-node that transpiles to CommonJS modules. I used the official: docs

I also wanted to use esm modules following the official esm docs, but was unfortunately unsuccessful. The error I keep getting is: CustomError: Cannot find module '/module/next/to/index/ts/ModuleName' imported from /Users/mainuser/angular_apps/typescript_test/index.ts

This is tsconfig.json

{
    "compilerOptions": {
      "target": "ES2015",                                 
      "module": "ES2020",                               
      "esModuleInterop": true,                             
      "forceConsistentCasingInFileNames": true,            
      "strict": true,                                      
         "skipLibCheck": true                                
    },
    "ts-node": {
      "esm": true
    }
  }
  

and this is the contest of package.json:

{
  "name": "typescript_test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {},
  "devDependencies": {
    "ts-node": "^10.7.0",
    "typescript": "^4.6.3"
  },
  "type": "module"

}

I run ts-node with: ./node_modules/.bin/ts-node index.ts while being in the project root.

What am I missing?

This is my index.ts code:

import { SmokeTest } from "./SmokeTest";
SmokeTest.Log();

And this is the SmokeTest.ts file contents. Both are at the same fs level:

export module SmokeTest{
    export function Log(){
        console.log("Smoke test running");  
    }
}

I am running node v14.19.1 and ts-node v10.7.0

3

There are 3 best solutions below

2
On BEST ANSWER

Adding ts-node to the tsconfig.json proved ineffective for me. I updated the tsconfig slightly (see node14 base as a starting point) :

{
  "compilerOptions": {
    "target": "ES2020",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "build",
    "module": "es2020",
    "moduleResolution": "node"
  },
  "include": ["./*.ts"],
  "exclude": ["node_modules"]
}

I used the --loader ts-node/esm option:

$ node --loader ts-node/esm ./index.ts

I also updated my smoke-test.ts module to look like this:

function Log() {
  console.log('Smoke test running')
}

export const SmokeTest = { Log }

When importing with native esm modules, the extension must always be provided as .js for a .ts file:

import { SmokeTest } from './smoke-test.js'
SmokeTest.Log()
0
On

I believe with ts-node v10.9.1 you can use the --esm flag

yarn ts-node --esm path/to/file.js
4
On

Switching to vite-node fixed the error immediately

  • Install vite-node
  • Run npx vite-node script.ts

Why?

  • Using .js imports doesn't always work, and it's kinda uncomfortable (you import typescript files but you use .js ).

    I was trying to use ts-node for hours, following https://stackoverflow.com/a/71823326/7365866. I tried renaming all my imports to .js, and that worked briefly. However, when I wanted to add a dependency, e.g. drizzle-orm, imports failed (e.g. import { eq } from 'drizzle-orm/expressions';) with the same error.

  • vite-node is sometimes faster. For a small script, vite-node took 0.53 seconds, where as ts-node took 3.06 seconds. For a NodeJS application used at work, ts-node took <5 seconds to launch the app, but vite-node took ~8 seconds.


Some more thoughts since I wrote this answer: I don't recommend it in production

  • I don't use it in production since it seems designed more for fast iteration, but has features like HMR, fast start up time.
  • I don't think it respects tsconfig.json. I've found issues with dependency resolution (I still use it for my build scripts though!)
  • It's relatively new compared to just transpiling your Node application into typescript and running that, so you'll find some bugs.