ts-node: ts-node seems to fail to resolve dependencies in referenced projects

Expected Behavior

I would expect code that doesn’t error when trying to compile it with tsc to compile when run through ts-node.

Actual Behavior

I get a TSError warning me about missing dependencies (the README linked below) when ts-node tries to load a referenced project.

Reproduction

I’ve created a minimal reproduction repo for my scenario here with the behaviour/stack trace and command to reproduce.

Specifications

  • ts-node version: 10.0.0
  • node version: 16.3.0
  • TypeScript version: 4.3.4
  • tsconfig.json, if you’re using one: please see the reproduction repo for the tsconfig structure, here is the effective tsconfig:
{
  "compilerOptions": {
    "noEmit": false,
    "paths": {
      "@org/liba": [
        "a/liba"
      ]
    },
    "esModuleInterop": true,
    "target": "es2017",
    "resolveJsonModule": true,
    "moduleResolution": "node",
    "baseUrl": "../..",
    "rootDir": "../..",
    "outDir": "./.ts-node",
    "sourceMap": true,
    "inlineSourceMap": false,
    "inlineSources": true,
    "declaration": false,
    "module": "commonjs"
  },
  "references": [
    {
      "path": "../../a/liba"
    }
  ],
  "ts-node": {
    "cwd": "[SHORTENED]\\ts-node-repro\\b\\btest",
    "projectSearchDir": "[SHORTENED]\\ts-node-repro\\b\\btest",
    "require": [],
    "project": "[SHORTENED]/ts-node-repro/b/btest/tsconfig.json"
  }
}
  • Operating system and version: Windows 10 Pro 19043.1052
  • If Windows, are you using WSL or WSL2?: no

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 6
  • Comments: 15 (2 by maintainers)

Most upvoted comments

tsx is the clear winner! Thx @gund

@fourpastmidnight you understand the core issue pretty well. As you said, there are 2 possibilities when @myworkspace/mylib tries to load @myworkspace/mycomponent:

it is precompiled

@myworkspace/mycomponent has been pre-compiled and ./lib/index.js and ./lib/index.d.ts exist. This may technically work, but is non-ideal: TS will typecheck against .d.ts and node will resolve to .js. ts-node will not be executing the source .ts, which is what we probably want.

it is not precompiled

@myworkspace/mycomponent has not been compiled yet. #1111 teach ts-node to do the necessary configuration, so that TS understands ./lib/index.js corresponds to ./src/index.ts. It understands because the referenced tsconfig has rootDir and outDir, which map between src and lib. This is halfway there: the typechecker understands to typecheck against index.ts, but node does not understand which file to load. Node will attempt to require ./lib/index.js which does not exist. We need to teach node to do the same mappings that TypeScript does. I talk about this in #1111 under “Missing is a runtime resolver”

Hmm, my issue seems to also be related to #897, as I’m working in a monorepo (not lerna, just a yarn PnP monorepo) and the module not being found is being referenced as @myworkspace/mycomponent, and in package.json, the dependency entry for it is @myworkspace/mycomponent: workspace:packages/mycomponent. However, given that ts-node is transpiling on the fly, I would expect it to be able to find the references in tsconfig.json and build the required dependencies. (Perhaps I expected too much—as I read in another issue, I hadn’t fully appreciated the implications of “supporting project references” with respect to ts-node. 😛 )

So on second-thought, maybe these two issues are actually related—and it all comes down to ts-node’s support for TS project references?

One way that I had this working in the past is by updating the package.json so that the main field points to the typescript entry point, e.g. "main": "./src/index.ts", instead of "main": "./lib/index.js". Pointing to the typescript file, ts-node works. However, this breaks actual builds of the packages, then, because during a build, the referenced dependencies are expecting to find a TypeScript file, but instead, the transpiled JavaScript should be used.

And now that I think through everything, this makes sense why that change worked. ts-node isn’t building the referenced projects, but when the package.json’s main field points to a dependendent package’s main entry point TS file, ts-node will happily transpile and everything just works–well, everything except actual builds of the component packages.