Support exports and imports subpath (package.json) pointing to generated .js files
Desired Behavior
Consider a source file src/package-a/index.ts that will be transpiled by tsc to build/package-a/index.js, and a package.json that looks like this:
{
"type": "module",
"imports": {
"#package-a": "./build/package-a/index.js"
}
}
Now TypeScript allows me to import import {...} from #package-a in any other file. Even when I am importing the generated .js file (that maybe don't exists yet), TypeScript knows that file will be generated from the .ts in src and correctly type-check that file, uses it for editor support, etc.
ts-node, however, doesn't know how to resolve the file and fails with
node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:366
throw new ERR_MODULE_NOT_FOUND(
[...]
The same occurs for subpath exports: TypeScript resolves to the source file that will generate that .js but ts-node doesn't.
I would expect ts-node to follow TypeScript semantics here and resolve to the corresponding .ts file as the language server does.
Is this request related to a problem?
Using subpath imports (since v14, v12 in experimental) is a common practice to alleviate the burden of nested, relative paths, as well to provide "internal packages" and many other cases. TypeScript, with a well configured tsconfig.json and package.json, just works.
The issue is, you can't point to the .ts file in the "imports" field of the package.json to make ts-node works since that field will also be read by node at runtime to resolve the import, so it must point to the generated .js. This issue makes it really difficult to correctly use this Node feature.
Alternatives you've considered
My first idea was to use conditional imports so the package.json could look something like this:
{
"type": "module",
"imports": {
"#package-a": {
"ts-node": "./src/package-a/index.ts",
"default": "./build/package-a/index.js"
}
}
}
However I can't figure out how to use a custom condition for ts-node (I'm using ts-node-esm but don't think that's relevant). I have tested with NODE_OPTIONS="-C ts-node" npx ts-node-esm thing but got the same error. A possible workaround is to allow custom conditions and the last pattern should work, but I still think the original example should be supported by ts-node out of the box (or at least via some config).
Additional context
I think the issue was commented on #1007, but that's a very long thread to follow a possible resolution there.
Ran into this issue myself when working in a multi package repo. Only solution I could find was transpile all packages in watch mode, in which case there's no longer a benefit to using ts-node.
Switched to @esbuild-kit/esm-loader and that seems to handle it out of the box.
This https://github.com/dividab/tsconfig-paths/issues/243 seems related for my setup, since I do have paths setup between the packages to match their package.json exports.
Ran into this issue myself when working in a multi package repo. Only solution I could find was transpile all packages in watch mode, in which case there's no longer a benefit to using ts-node.
Switched to @esbuild-kit/esm-loader and that seems to handle it out of the box.
This dividab/tsconfig-paths#243 seems related for my setup, since I do have paths setup between the packages to match their package.json exports.
Are u developing a package or app? If this is a published package - how did u get around this comment in the TS docs? "While it’s ok for bundled apps to set up paths, it’s very important that published libraries do not, since the emitted JavaScript will not work for consumers of the library without those users setting up the same aliases for both TypeScript and their bundler. Both libraries and apps can consider package.json "imports" as a standard replacement for convenience paths aliases." ?
@mihaa1 developing multiple packages in a monorepo. There's no issue with the paths I have setup because they only serve to mimic nodes actual behavior. If I wasn't in a monorepo I wouldn't need them, or if typescript had built in support for monorepo setups.