ESLint и esbuild

ESLint выполняет статический анализ JavaScript-кода, выявляя ошибки, нарушения стиля и потенциально проблемные конструкции ещё до запуска приложения. В связке с современными сборщиками особое значение приобретает корректная организация этапа линтинга, поскольку инструменты вроде esbuild ориентированы на скорость компиляции и минимальный overhead.

ESLint функционирует как независимый слой анализа исходного кода и не требует выполнения программы. Это позволяет интегрировать его практически в любую сборочную систему, включая esbuild, Rollup, Webpack и Vite.

В архитектуре фронтенд-сборки ESLint обычно размещается в отдельном этапе:

  • до сборки (pre-build lint)
  • параллельно в режиме watch
  • в CI-пайплайне
  • как часть pre-commit хуков

esbuild при этом выполняет исключительно трансформацию и бандлинг, не включая встроенный линтинг.

Особенности esbuild как среды сборки

esbuild ориентирован на предельную производительность за счёт реализации на Go. Основные характеристики:

  • отсутствие встроенного линтера
  • минималистичная система плагинов
  • акцент на скорость трансформации
  • поддержка incremental rebuild

Такое разделение ответственности делает ESLint внешним инструментом, интегрируемым через CLI или API.

Базовая схема интеграции ESLint и esbuild

Наиболее распространённая архитектура включает два независимых процесса:

  1. ESLint анализирует исходники
  2. esbuild выполняет сборку

Пример разделения скриптов:

{
  "scripts": {
    "lint": "eslint src",
    "build": "esbuild src/index.js --bundle --outfile=dist/app.js",
    "dev": "npm run lint && npm run build"
  }
}

Подход обеспечивает детерминированность: ошибки линтинга блокируют сборку.

Использование ESLint CLI в связке с esbuild

ESLint CLI поддерживает кэширование, что критично при частых пересборках:

eslint "src/**/*.{js,ts}" --cache

Кэш уменьшает время повторного анализа, что особенно важно при watch-режиме esbuild.

Типовая конфигурация .eslintrc:

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": [
    "eslint:recommended"
  ],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "rules": {
    "no-unused-vars": "warn",
    "no-console": "off"
  }
}

Интеграция через esbuild plugin API

esbuild поддерживает плагины, позволяющие подключать сторонние инструменты анализа. ESLint может быть встроен через пользовательский плагин, вызывающий его Node.js API.

Пример плагина:

import { ESLint } from "eslint";

export function eslintPlugin() {
  return {
    name: "eslint-plugin",
    setup(build) {
      const eslint = new ESLint({ cache: true });

      build.onEnd(async () => {
        const results = await eslint.lintFiles(["src/**/*.js"]);

        const formatter = await eslint.loadFormatter("stylish");
        const resultText = formatter.format(results);

        if (resultText) {
          console.log(resultText);
        }

        const hasErrors = results.some(r =>
          r.errorCount > 0
        );

        if (hasErrors) {
          throw new Error("ESLint validation failed");
        }
      });
    }
  };
}

Использование в конфигурации esbuild:

import esbuild from "esbuild";
import { eslintPlugin } from "./eslint-plugin.js";

esbuild.build({
  entryPoints: ["src/index.js"],
  bundle: true,
  outfile: "dist/app.js",
  plugins: [eslintPlugin()]
});

Такой подход переводит ESLint в фазу post-build анализа, сохраняя скорость основной компиляции.

Разделение ответственности в архитектуре сборки

Комбинация ESLint и esbuild обычно строится по принципу разделения потоков:

  • ESLint — анализ качества кода
  • esbuild — трансформация и упаковка

Это разделение предотвращает деградацию производительности сборщика.

В больших проектах применяется параллельный запуск:

npm run lint & npm run build --watch

или через инструменты управления процессами:

{
  "scripts": {
    "dev": "concurrently \"eslint src --cache --watch\" \"esbuild src/index.js --bundle --watch\""
  }
}

Инкрементальная проверка и watch-режим

esbuild обеспечивает мгновенные пересборки, тогда как ESLint может стать узким местом при полном прогоне анализа.

Оптимизация достигается через:

  • --cache
  • ограничение файлов через glob patterns
  • запуск lint только изменённых файлов (lint-staged подход)
  • использование файловой системы watch

Пример ограниченного анализа:

eslint src/components/Button.js --cache

Интеграция через pre-commit хуки

В системах контроля качества ESLint часто выполняется до сборки:

{
  "husky": {
    "hooks": {
      "pre-commit": "eslint src --cache"
    }
  }
}

Такой механизм предотвращает попадание некорректного кода в сборочный процесс esbuild.

ESLint в CI/CD пайплайне с esbuild

В CI ESLint выполняется как отдельный stage:

steps:
  - run: npm ci
  - run: npm run lint
  - run: npm run build

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

Потенциальные конфликты и их устранение

При совместном использовании возникают типовые проблемы:

Дублирование обработки файлов

  • ESLint и esbuild анализируют одни и те же пути
  • решение: исключение dist, node_modules через ignorePatterns

Различие парсеров

  • esbuild не интерпретирует TypeScript как ESLint
  • решение: синхронизация parserOptions и tsconfig

Несовпадение модульных систем

  • esbuild трансформирует ESM/CommonJS
  • ESLint может требовать настройки sourceType: module

Работа с TypeScript

При использовании TypeScript ESLint требует дополнительного конфигурационного слоя:

import tsParser from "@typescript-eslint/parser";
import tsPlugin from "@typescript-eslint/eslint-plugin";

export default {
  parser: tsParser,
  plugins: {
    "@typescript-eslint": tsPlugin
  },
  extends: [
    "plugin:@typescript-eslint/recommended"
  ]
};

esbuild при этом выполняет трансляцию без типовой проверки, что усиливает роль ESLint как статического анализатора.

Производственные паттерны

В зрелых проектах применяется комбинация:

  • esbuild — dev server и production bundle
  • ESLint — отдельный quality gate
  • lint-staged — проверка изменённых файлов
  • CI — полный прогон анализа

Типовая структура:

lint → typecheck → build → test

esbuild занимает исключительно этап build, не вмешиваясь в анализ качества кода.

Архитектурное разделение инструментов

Связка ESLint и esbuild демонстрирует принцип разделения инструментов по ответственности:

  • анализ синтаксиса и семантики — ESLint
  • генерация бандла — esbuild
  • контроль типов (при наличии TypeScript) — tsc

Такое разделение позволяет масштабировать проект без деградации производительности сборки и без усложнения пайплайна трансформации кода.