Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node script with Typescript and ts-node: can't get to import anything, no matter the configuration and package type

I have a React Typescript project that has multiple files that share a structure. The code is similar, but quite different between them at some points, and it's pretty common to have to copy-paste files and change bits of information to create some of those new files.

I wanted to create a Node script that, asking the user for some input, would do that automatically. As I don't want to create a package for this (as it's a simple script inside my current project), I just created a folder called scripts and put it there.

So, the project structure is as follows:

- node_modules/
- scripts/
  - myScript/
    - helpers/
    - index.ts
    - package.json
- ...
- src/
- tsconfig.json
- package.json

To be able to execute Typescript, I've installed the ts-node library, and I'm executing this script through an npm script in the main project's package.json:

"run-script": "ts-node ./scripts/myScript"

This project already has code inside its src folder, and this scripts folder is a complement that does not belong to the main application's code, and that's why I keep it separated in an external folder, as it acts more as a helping tool for developers in the team.

The problem

Whenever I try to make an import, I get this error:

throw new ERR_MODULE_NOT_FOUND(
          ^
CustomError: Cannot find module ...

If I use dynamic imports for packages and set esModuleInterop in my tsconfig file, everything works fine. But as soon as I try to import a method with a relative path, it breaks:

const inquirer = (await import('inquirer')).default;
const fs = (await import('fs-extra')).default;
const { getCommonPath } = await import('./helpers/getCommonPath');
Error [ERR_MODULE_NOT_FOUND]: Cannot find module ...

I thought that could have to do with the directory path, but if I use __dirname in the import, it tells me it's not allowed because of the format of the path.

If I try to use non-dynamic imports in stead, I get the following error:

(node:106220) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.

But if I do that, then I get this error:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for ...

Then I thought that would have to do with ts-node configuration, and I read to use ts-node-esm. But if I do that, then the first error appears again. I've also tried multiple configurations for ts-node (esModuleInterop: true, esm: true, module: CommonJS|ES2020|ESNext...) without luck.

The question

What do I have to do to be able to write a Node script with Typescript that I can execute from a command line that allows imports of both, external libraries and relative paths (that can be Typescript aswell)?

Thank you!

like image 368
Unapedra Avatar asked Oct 29 '25 22:10

Unapedra


1 Answers

Actually I've been able to make it work finally with normal imports. Steps I've followed. I've followed the documentation of ts-node esm:

  • Install ts-node package.
  • Create a package.json inside the scripts folder and set the "type": "module".
  • Add the following configuration to your tsconfig.json:
  "ts-node": {
    "transpileOnly": true,
    "compilerOptions": {
      "module": "ESNext",
      "esModuleInterop": true
    },
    "esm": true // <--- this is the most important part
  },
  "compilerOptions": {...}
  • Add the file extension (.ts) to your relative imports. Eslint-TS will complain, but it's the only way to make it work (otherwise it won't find the module):
import fs from 'fs-extra';
import { getSrcPath } from './helpers/getSrcPath.ts';
  • Run your node script with ts-node in your package.json:
"run-my-script": "ts-node ./scripts/myScript"
like image 178
Unapedra Avatar answered Oct 31 '25 11:10

Unapedra