I couldn't figure out why ts-node
isn't resolving the alias when esm is enabled
I made a tiny project trying to isolate the issue as much as possible
package.json
{
"type": "module"
}
tsconfig.json
{
"compilerOptions": {
"module": "es2020",
"baseUrl": "./",
"paths": {
"$lib/*": [
"src/lib/*"
]
},
},
"ts-node": {
"esm": true
}
}
test.ts
import { testFn } from "$lib/module"
testFn()
lib/module.ts
export function testFn () {
console.log("Test function")
}
command
ts-node -r tsconfig-paths/register src/test.ts
Solution from: https://github.com/TypeStrong/ts-node/discussions/1450#discussion-3563207
At the moment, the ESM loader does not handle TypeScript path mappings. To make it work you can use the following custom loader:
// loader.js
import {
resolve as resolveTs,
getFormat,
transformSource,
load,
} from "ts-node/esm";
import * as tsConfigPaths from "tsconfig-paths"
export { getFormat, transformSource, load };
const { absoluteBaseUrl, paths } = tsConfigPaths.loadConfig()
const matchPath = tsConfigPaths.createMatchPath(absoluteBaseUrl, paths)
export function resolve(specifier, context, defaultResolver) {
const mappedSpecifier = matchPath(specifier)
if (mappedSpecifier) {
specifier = `${mappedSpecifier}.js`
}
return resolveTs(specifier, context, defaultResolver);
}
Then use the loader with:
node --loader loader.js index.ts
Caveat: This only works for module specifiers without an extension. For example, import /foo/bar
works, but import /foo/bar.js
and import /foo/bar.ts
do not.
Remember to install these packages as well:
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.1.2",
The new loader will automatically resolve the index.(js|ts):
import { resolve as resolveTs } from 'ts-node/esm'
import * as tsConfigPaths from 'tsconfig-paths'
import { pathToFileURL } from 'url'
const { absoluteBaseUrl, paths } = tsConfigPaths.loadConfig()
const matchPath = tsConfigPaths.createMatchPath(absoluteBaseUrl, paths)
export function resolve (specifier, ctx, defaultResolve) {
const match = matchPath(specifier)
return match
? resolveTs(pathToFileURL(`${match}`).href, ctx, defaultResolve)
: resolveTs(specifier, ctx, defaultResolve)
}
export { load, transformSource } from 'ts-node/esm'
Example:
Path: /src/modules/order/index.ts
Resolve: import orderModule from '@/modules/order';
For the node version 20, we need to set the arg: --experimental-specifier-resolution=node
node --experimental-specifier-resolution=node --loader ./loader.js src/main.ts