ts-node с tsconfig-paths не работает при использовании ESM модулей

Рейтинг: 0Ответов: 1Опубликовано: 19.02.2023

При переходе на ESM modules в Node JS перестают работать алиасы, пакет tsconfig-paths не помогает.

import { delay } from '@helpers/libs.js';
// import { delay } from './helpers/sub1/libs.js';

console.log('Hello world, start delay');
await delay(2_000);
console.log('Delay end');

Если указать стандартный относительный путь - то код работает. В tsconfig.json сделана стандартная запись:

    "baseUrl": "./src",
    "paths": {
      "@helpers/*": ["helpers/sub1/*"]
    }

Если ESM модули убрать (перейти на стандартный commonjs) - то все работает.

Как использовать алиасы с ts-node при применении ESM modules ?

Ответы

▲ 0

Ответ на данный вопрос находится в этом issue:

https://github.com/TypeStrong/ts-node/discussions/1450

Для того, чтобы алиасы работали нужно загружать файлы через custom loader:

// this not work in Node 19

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 lastIndexOfIndex = specifier.lastIndexOf('/index.js');
  if (lastIndexOfIndex !== -1) {
    // Handle index.js
    const trimmed = specifier.substring(0, lastIndexOfIndex);
    const match = matchPath(trimmed);
    if (match) return resolveTs(pathToFileURL(`${match}/index.js`).href, ctx, defaultResolve);
  } else if (specifier.endsWith('.js')) {
    // Handle *.js
    const trimmed = specifier.substring(0, specifier.length - 3);
    const match = matchPath(trimmed);
    if (match) return resolveTs(pathToFileURL(`${match}.js`).href, ctx, defaultResolve);
  }
  return resolveTs(specifier, ctx, defaultResolve);
}

export { load, transformSource } from 'ts-node/esm';

Запускать ts-node надо командой:

node --loader ./src/loader.js  src/main"

файл tsconfig.json

{
  "compilerOptions": {
    "module": "Node16",
    "target": "ESNext",
    "outDir": "./build",
    "baseUrl": "./src",
    "paths": {
      "@helpers/*": ["helpers/sub1/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
  ],

  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node",
    "transpileOnly": true,
    "files": true,
    "require": ["tsconfig-paths/register"]
  }
}

Работающий пример положил сюда: github branch 03_all_work

В ветке main содержится работающий пример на commonjs, ветка 03_all_work - работающий пример на ESM.

Следует обратить внимание - что как написано в issue - код не будет работать в Node 19.

Возможно, как бы это ни было печально, следует избегать использования алиасов.