2
votes

I have been trying to import an ESM module written in typescript in nodejs. But I am getting the following error:

An import path cannot end with a '.ts' extension.

Util.ts

 export class Util {
    constructor ( ) {
       
    }
      log(msg) {
        console.log(msg) 
    }
  }

index.ts

import {log} from './Util.ts'
log(task.query.criteria, payload.parameters)

I have also added "type":"module" inside package.json

I changes .ts to .js just to see if it works and then I got :

Object.defineProperty(exports, "__esModule", { value: true });                         ^

ReferenceError: exports is not defined
at file:///C:/Users/abc/NestJsPOC/NestPOC/dist/main.js:2:23

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true
  }
}

EDIT

I have also tried:

 var log = require('../utility/util.js');

Util.js

    function log(msg) {
      console.log(msg)
     
  }
    module.exports= { log}

index.ts

    log('hello')

Error:

TypeError: log is not a function
1
Simply remove the .js from import. If you want the extension see this. You can change from Node JS to Deno that support Typescript by default and use the .ts import path. Use Deno only if you know what are you doing since is relative new. - Carlo Corradini
@CarloCorradini sorry that was a mistake while copy pasting.. I have tried all. ".js", ".ts" and also removing all extensions. I believe extension is mandatory while importing ESM mdoules. - SamuraiJack
In tsconfig.json (create one if not present) in compilerOptions add the following line: "module": "commonjs". Typescript info page here. Tsconfig info page here. - Carlo Corradini
That's how it already is. I have added tsconfig in my post. - SamuraiJack
I created a simple example here. You are trying to call the function log without using the class Util. More documentation here. - Carlo Corradini

1 Answers

4
votes

Update May 23st, 2021

Keep in mind that you need to use ts-node 10+ and NodeJS 12+ in order to use the setup below.

Answer

It seems you are looking to use ESM with Node and TS.

The updated settings to make it by the time I'm answering your question is:

tsconfig.json

On your tsconfig.json file make sure to have the following settings:

{
    "compilerOptions": {
        "lib": ["es2020"],
        "module": "ESNext",
        "moduleResolution": "node",
        "target": "ES2020",
        "esModuleInterop": true,
        ...
    },
    ... 
}

package.json

Make sure to have the following attribute on your package.json file.

{ 
    ...
    "type":"module",
    ...
}

Running it with transpiled files

Don't forget that you can't import a TS file direct in NodeJS, you need to first transpile it using tsc to then import it in the NodeJS in runtime, so in the end you will be importing the js and not the ts file. To run it as TS please make sure to read the next section of my answer.

Also when running it make sure to use the flag --experimental-specifier-resolution=node as it will enable you to have imports with no extensions as it happens in TypeScript:

node --experimental-specifier-resolution=node index.js

For more details for the flag please read https://nodejs.org/api/esm.html#esm_customizing_esm_specifier_resolution_algorithm

Running it with ts-node

TS Node is a runtime transpiler so it allow you to import typescript files in runtime when using node.

Currently there is an experimental feature in TS Node that allows you to run it using ESM, to use it you will need to install TS Node 9.1+. For more details on the implementation and possible issues check: https://github.com/TypeStrong/ts-node/issues/1007

To install it you will need to run:

npm i -D ts-node 

And to run the service supporting the new resolution mode and the TSNode loader you will need to run:

node --experimental-specifier-resolution=node --loader ts-node/esm index.ts

After this you will be able to use TS and ESM in your project in runtime and this statement will be possible:

import {log} from './Util'

Where log is an exported member from Util.ts

Important! I don't recommend using ts-node in production, so this is very handy and useful for a development environment but make sure to transpile and use the resulting code in production to avoid possible memory issues with TS Node.