ts-node: ts-node cannot find module

I have two files:

  • src/app/index.ts
  • src/config.ts

Within index.ts:

import { getConfig } from "config";  
getConfig();

Within config.ts:

export const getConfig = () => console.log("hello world!");

I have a tsconfig.json file:

{
  "compilerOptions": {
    "outDir": "./ts-build/",
    "module": "commonjs",
    "moduleResolution": "node",
    "baseUrl": "src",
    "target": "es5"
  }
}

Running ts-node ./src/app/index.ts throws me: Error: Cannot find module 'config'. Setting moduleResolution to classic doesn’t change anything.

Versions:

"ts-node": "^3.3.0",
"typescript": "^2.5.2"

Any ideas if this is a ts-node issue, typescript issue or did I do something wrong?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 7
  • Comments: 33 (6 by maintainers)

Most upvoted comments

Thanks for the reply @blakeembrey. I’ve run this with tsc --traceResolution and the resolution works fine as seen here:

======== Resolving module 'config' from 'C:/Dev/test-ts-node/src/app/index.ts'. ========
Explicitly specified module resolution kind: 'NodeJs'.
'baseUrl' option is set to 'C:/Dev/test-ts-node/src', using this value to resolve non-relative module name 'config'.
Resolving module name 'config' relative to base url 'C:/Dev/test-ts-node/src' - 'C:/Dev/test-ts-node/src/config'.
Loading module as file / folder, candidate module location 'C:/Dev/test-ts-node/src/config', target file type 'TypeScript'.
File 'C:/Dev/test-ts-node/src/config.ts' exist - use it as a name resolution result.
======== Module name 'config' was successfully resolved to 'C:/Dev/test-ts-node/src/config.ts'. ========

What I actually discovered was that I have to have the environment variable NODE_PATH set to match the baseUrl also. I ended up running the following: NODE_PATH=./src ts-node ./src/app/index.ts.

Now I know we can’t set environment variables in tsconfig, this would be convenient and isn’t a ts-node concern. However, do you have any thoughts if environment variables could be somehow settable on the ts-node side? I’m running nodemon in development and there it’s easy to set within nodemon.json.

I also ran into this issue in my NestJS project when I tried enabling debugging in VSCode. I managed to solve this by changing my nodemon.json to:

{
  "watch": ["src"],
  "ext": "ts",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "node -r tsconfig-paths/register -r ts-node/register src/main.ts"
}

It was the part -r tsconfig-paths/register that fixed it. This parameter will convert tsconfig defined paths into absolute paths.

I had pretty much the same issue as the OP, but using esm. I didn’t get anywhere with the solutions in the comments here but this blog post did help.

Specifically, adding "experimentalSpecifierResolution": "node" in my ts-config under ts-node resolved my issue.

Full ts-config:

{
  "compilerOptions": {
    "target": "ES2019",
    "lib": [
      "es2019",
      "DOM"
    ],
    "module": "ESNext",
    "declaration": true,
    "strict": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": false,
    "experimentalDecorators": true,
    "strictPropertyInitialization": false,
    "isolatedModules": true,
    "typeRoots": [
      "./node_modules/@types"
    ],
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "moduleResolution": "node"
  },
  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node",
    "transpileOnly": true
  }
}

Actually, it looks like you expect it to find config.ts from app/index.ts? You should use relative paths import {} from '../config'.

Removing module and target fields from tsconfig.json worked for me.

I met the problem: Error: Cannot find module '@app/lib/prisma_client', and I add the following code in the tsconfig.json:

{
  "ts-node": {
    // Do not forget to `npm i -D tsconfig-paths`
    "require": ["tsconfig-paths/register"]
  }
}

then re-run ts-node src/index.ts, everything is good now! reference: paths and baseUrl and the full tsconfig.json is here:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "baseUrl": "./src",
    "paths": {
      "@app/*": ["*"]
    },
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"],
  "ts-node": {
    "require": ["tsconfig-paths/register"]
  }
}

A year later, dealing with this again. This thing needs to throw a more directive error.

Am I suppose to install tslib? Maybe put some typings somewhere… what was it again? Why isn’t fs already there…

Does ts-node not handle non-relative paths? I’m trying to use this strategy to import modules: https://goenning.net/2017/07/21/how-to-avoid-relative-path-hell-javascript-typescript-projects/. Basically I don’t want to use relative paths, but ts-node fails saying it cannot find the module. Is this not supported?

Have you tried compiling with regular TypeScript? Otherwise, I’d guess you haven’t written a definition file - this is the standard error from TypeScript when it can’t find the definition for a module.

@tedcurrent solution did the trick for me. I also hade NODE_PATH=src in my .env file, so adding it before the ts-node call solved it. I’m using ts-node together with nodemon, so my code is:

nodemon.json

{
  "ignore": ["**/*.test.ts", "**/*.spec.ts", ".git", "node_modules"],
  "watch": ["./src"],
  "exec": "set NODE_PATH=./src node --inspect -r ./node_modules/.bin/ts-node/register ./src/index.ts",
  "ext": "ts"
}

Note that Windows users need to do set NODE_PATH.

Thank you dwil618

It works in windows node v18

“ts-node”: { “esm”: true, “experimentalSpecifierResolution”: “node”, “transpileOnly”: true }

@ashok-sc See the note in the README under https://github.com/TypeStrong/ts-node#loading-tsconfigjson. It’s not provided directly by ts-node, but it’s easily supported.

For anyone struggling with ts-node or ts-node-dev modify your package.json with ts-node-dev -r tsconfig-paths/register --poll src/app.ts

Thank you @abinici, you are my hero. No idea how you figured that out.

i know this is an old issue, but i recently stumbled onto it myself. I believe i found a workaround:

Relevant configuration:

directory structure:

/

/dist/src/foo/...
/dist/src/foo.js

/dist/test/foo/integration/...
/dist/test/foo/unit/...

/src/foo/...
/src/foo.ts

/test/foo/integration/...
/test/foo/unit/...

THE MAIN OFFENDER: package.json:

{
    "imports": {
        "#src/*": "./dist/src/*",
        "#test/*": "./dist/test/*"
    },
}

tsconfig.json:

{
    "include": [
        "src/**/*",
        "test/**/*"
    ],
    "exclude": [
        "node_modules/**/*",
        "dist/**/*"
    ],
    "compilerOptions": {
        "rootDir": ".",
        "outDir": "./dist",
        "baseUrl": ".",
        "paths": {
            "#src/*": [ "src/*" ],
            "#test/*": [ "test/*" ]
        },
    }
}

Problem synopsis:

  • tsconfig-paths/register can’t handle this particular case.
  • ts-node uses imports: { ... } from package.json rather then paths from tsconfig.json.
  • if "imports": { "#src/*": "./dist/src/*" } then NODE_ENV=dev code stops working. aka ts-node, nodemon, etc stops working, because /dist/ dir by definition doesn’t exist in NODE_ENV=dev mode.
  • if "imports": { "#src/*": "./src/*" } then NODE_ENB=prod code stops working. aka node . bc *.js files reside in /dist/src/ dir. node simply doesn’t see /src/dist/**/*.js code with path aliases directed to /src/ dir. Not to mention /src/ dir is populated with *.ts files.

Workaround:

  1. Create a 2nd package.json file in /dist/package.json:
{
    "imports": {
        "#src/*": "./src/*",
        "#test/*": "./test/*"
    },
}
  1. Rewrite original package.json in /package.json:
{
    "imports": {
        "#src/*": "./src/*",
        "#test/*": "./test/*"
    },
}

Explanation:

  • NODE_ENB=dev code resides in /src/ dir as *.ts files.
  • NODE_ENB=prod code resides in /dist/src/ dir as *.js files.
  • import: { ... } in /package.json configures how ts-node will resolve path aliases in /src dir, aka the NODE_ENB=dev code.
  • import: { ... } in /dist/package.json configures how node will resolve path aliases in /src/dist dir, aka the NODE_ENB=prod code.

I do not know if this workaround is foolproof. I can imagine problems when trying to publish a package with multiple package.json files. But haven’t tested it yet.

Please leave a like if you found this workaround helpful 🙇🙇🙇🙇

Stuck here, too.

% npx ts-node src/queue.bench.ts
⨯ Unable to compile TypeScript:
src/queue.bench.ts:5:20 - error TS2307: Cannot find module 'benny'.

5 import * as b from "benny";
                     ~~~~~~~

Loading the module use plain node works:

% node
> require('benny')
{ add: { [Function: add] only: [Function], skip: [Function] },
  complete: [Function: complete],
  cycle: [Function: cycle],
  save: [Function: save],
  suite: [Function: suite],
  default:
   { add: { [Function: add] only: [Function], skip: [Function] },
     complete: [Function: complete],
     cycle: [Function: cycle],
     save: [Function: save],
     suite: [Function: suite] } }

Adding --files doesn’t help. Using node -t ts-node/register instead of npc ts-node gives same error. Not sure why ts-node doesn’t want to load the module.

Types are available under node_modules:

% find node_modules -path '*benny*' -name '*.d.ts'
node_modules/benny/lib/internal/common-types.d.ts
node_modules/benny/lib/internal/format.d.ts
node_modules/benny/lib/internal/getCaseResult.d.ts
node_modules/benny/lib/internal/getSummary.d.ts
node_modules/benny/lib/internal/prepareFileContent.d.ts
node_modules/benny/lib/add.d.ts
node_modules/benny/lib/complete.d.ts
node_modules/benny/lib/cycle.d.ts
node_modules/benny/lib/index.d.ts
node_modules/benny/lib/save.d.ts
node_modules/benny/lib/suite.d.ts

@blakeembrey Before I open another issue I want to be sure I’m not going insane because I currently cannot get ts-node to do anything worthwhile. I’ve been beating my head bloody for days searching, reading and experimenting with every config I can think of.

If it’s not one thing it’s another. But most of my woes seem to be related to importing other modules. In the case of gulp for example, I’m loading other modules and registering tasks with gulp but it can’t even get past the imports:

TSError: ⨯ Unable to compile TypeScript
_gulp\tests.ts (1,43): Cannot find module 'gulp-typescript-helper'. (2307)
_gulp\tests.ts (2,23): Cannot find module './constants/TaskNames'. (2307)
_gulp\tests.ts (3,23): Cannot find module 'gulp'. (2307)

This is the branch I’m currently working on: https://github.com/electricessence/TypeScript.NET/tree/next

I can’t get mocha to work either. 😦

Environment variables don’t really have anything to do with ts-node. You can set it just like you are, or before nodemon if you wanted. You can also modify the require function in node.js using https://github.com/TypeStrong/ts-node#loading-tsconfigjson if you wanted to. I don’t intend to have ts-node have the ability to alter the runtime require behaviour of node.js in any way directly.