lenec ru

← все посты

Node.js + TypeScript: настройка проекта с нуля в 2026 — tsx, paths, ESM

19K

Настроить TypeScript-проект на Node.js в 2026 — задача на 15 минут, если знаешь правильные опции. Но выбор между ESM и CJS, tsx и ts-node, tsc и tsup может запутать. Разберём актуальный стек: ESM по умолчанию, tsx для разработки, tsup для сборки и flat config ESLint.

ESM vs CJS в 2026

В 2026 ESM — стандарт. Большинство новых пакетов публикуются только как ESM. Настройка:

{
  "name": "my-service",
  "type": "module",
  "engines": { "node": ">=22" },
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsup",
    "start": "node dist/index.js"
  }
}

Ключевые моменты:

  • "type": "module" — все .js файлы трактуются как ESM. Для CJS используйте расширение .cjs.
  • exports — точка входа для потребителей пакета. Заменяет устаревшее поле main.
  • В ESM нет require, __dirname, __filename. Замены: import.meta.url и import.meta.dirname (Node 22+).

tsx vs ts-node vs swc-node

Для запуска TypeScript без предварительной компиляции:

  • tsx — обёртка над esbuild. Мгновенный старт (~50 мс), поддержка ESM и CJS, watch-режим из коробки. Рекомендация для 2026.
  • ts-node — классика, но медленнее (~300 мс старт). Нужен для проектов с кастомными трансформерами.
  • swc-node — быстрый, но менее стабильный с edge-case ESM-импортами.
# Установка tsx
npm i -D tsx

# Запуск
npx tsx src/index.ts

# Watch-режим с перезапуском
npx tsx watch src/index.ts

tsx не проверяет типы — только транспилирует. Проверку типов делает tsc --noEmit отдельно (в CI или pre-commit hook).

tsconfig.json: strict, paths, moduleResolution

{
  "compilerOptions": {
    "target": "ES2023",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "paths": {
      "@/*": ["./src/*"],
      "@config/*": ["./src/config/*"],
      "@services/*": ["./src/services/*"]
    },
    "resolveJsonModule": true,
    "isolatedModules": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Важные опции:

  • moduleResolution: "NodeNext" — правильное разрешение ESM-импортов (требует расширений в import).
  • verbatimModuleSyntax — заменяет устаревшие importsNotUsedAsValues. Заставляет писать import type явно.
  • isolatedModules — совместимость с esbuild/swc (они компилируют файлы по одному).

Path aliases в рантайме

tsc понимает paths, но в скомпилированном JS пути остаются как @/services/db. Node.js их не разрешит. Решения:

1. tsup/esbuild — резолвят при сборке (рекомендуется):

// tsup.config.ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm'],
  dts: true,
  clean: true,
  // paths из tsconfig резолвятся автоматически
});

2. tsx — поддерживает paths из tsconfig без дополнительной настройки.

3. Node.js subpath imports (без внешних зависимостей):

{
  "imports": {
    "#config/*": "./src/config/*",
    "#services/*": "./src/services/*"
  }
}

Subpath imports (#-префикс) работают нативно в Node.js без транспиляции. Это самый надёжный вариант для production.

Сборка для прода: tsc vs tsup vs esbuild

  • tsc — генерирует чистый JS 1:1 с исходниками. Медленный (~5 с на 500 файлов), но предсказуемый. Подходит для библиотек.
  • tsup — обёртка над esbuild. Бандлит в один файл, tree-shaking, ~200 мс. Рекомендация для сервисов.
  • esbuild напрямую — максимальный контроль, но больше конфигурации.
# tsup — сборка за 200 мс
npx tsup src/index.ts --format esm --dts

# tsc — классическая компиляция
npx tsc --project tsconfig.build.json

ESLint flat config + Prettier

В 2026 ESLint использует flat config (eslint.config.js). Старый .eslintrc deprecated:

// eslint.config.js
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.strictTypeChecked,
  prettier,
  {
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
    rules: {
      '@typescript-eslint/no-unused-vars': ['error', {
        argsIgnorePattern: '^_',
      }],
    },
  },
  { ignores: ['dist/', 'node_modules/'] },
);

Итоговый стек для нового Node.js проекта в 2026: ESM + tsx (dev) + tsup (build) + strict tsconfig + flat ESLint. Настраивается за 10 минут, работает быстро, типы проверяются строго.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.