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 mon 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 plement 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 mand line that allows imports of both, external libraries and relative paths (that can be Typescript aswell)?
Thank you!
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 mon 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 plement 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 mand line that allows imports of both, external libraries and relative paths (that can be Typescript aswell)?
Thank you!
Share Improve this question edited Apr 27, 2023 at 9:15 Unapedra asked Apr 27, 2023 at 6:49 UnapedraUnapedra 2,4035 gold badges32 silver badges52 bronze badges2 Answers
Reset to default 4Actually 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 thescripts
folder and set the"type": "module"
. - Add the following configuration to your
tsconfig.json
:
"ts-node": {
"transpileOnly": true,
"pilerOptions": {
"module": "ESNext",
"esModuleInterop": true
},
"esm": true // <--- this is the most important part
},
"pilerOptions": {...}
- Add the file extension (.ts) to your relative imports. Eslint-TS will plain, 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 yourpackage.json
:
"run-my-script": "ts-node ./scripts/myScript"
Install the necessary dependencies by running the mand npm install typescript ts-node @types/node.
Create a new tsconfig.json file in the root of your project with the following contents:
{
"pilerOptions": {
"target": "es6",
"module": "monjs",
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./src",
"resolveJsonModule": true,
"sourceMap": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
This configuration file tells TypeScript how to pile your source code into JavaScript.
Create a new src directory in the root of your project and create your TypeScript files in this directory. For example, create a file called main.ts.
Write your TypeScript code in main.ts. You can use import statements to import external libraries and relative paths.
To run your script from the mand line, you can use the ts-node mand. For example, to run main.ts, you can use the mand ts-node src/main.ts.
You can also add a script to your package.json file to make it easier to run your script. For example:
{
"scripts": {
"start": "ts-node src/main.ts"
}
}