Структура правила

Правило ESLint представляет собой модуль, экспортирующий объект с двумя ключевыми частями: meta и create. Именно эта структура определяет поведение линтера, способ анализа AST и механизм генерации сообщений об ошибках.

Каждое правило оформляется как JavaScript-модуль:

export default {
  meta: {
    type: "problem",
    docs: {
      description: "описание правила",
      recommended: true
    },
    schema: [],
    fixable: "code",
    messages: {
      unexpected: "Найдено нарушение правила"
    }
  },

  create(context) {
    return {
      Identifier(node) {
        if (node.name === "var") {
          context.report({
            node,
            messageId: "unexpected"
          });
        }
      }
    };
  }
};

Эта структура делится на декларативную часть (meta) и исполнительную (create), где происходит анализ кода.


meta: метаинформация правила

Объект meta описывает свойства правила, не связанные напрямую с анализом AST, но влияющие на его поведение в экосистеме ESLint.

type

Поле type определяет категорию:

  • problem — потенциальные ошибки в коде
  • suggestion — рекомендации по улучшению стиля
  • layout — форматирование и внешний вид кода

Это поле используется для группировки правил и их семантической классификации.


docs

Секция docs содержит документационные данные:

  • description — краткое описание логики правила
  • recommended — флаг, указывающий, включено ли правило в рекомендованный набор
  • url — ссылка на внешнюю документацию (в конфигурациях ESLint)

Пример:

docs: {
  description: "Запрещает использование var",
  recommended: true,
  url: "https://eslint.org/docs/rules/no-var"
}

schema

schema определяет структуру опций правила в формате JSON Schema. ESLint использует её для валидации конфигурации.

Простейший вариант:

schema: []

Правило без опций.

Более сложный вариант:

schema: [
  {
    type: "object",
    properties: {
      allowGlobals: {
        type: "boolean"
      }
    },
    additionalProperties: false
  }
]

Schema напрямую влияет на context.options, обеспечивая предсказуемую структуру входных данных.


messages

Объект messages содержит шаблоны сообщений об ошибках. Вместо “сырого текста” ESLint рекомендует использовать идентификаторы:

messages: {
  unexpectedVar: "Использование var запрещено",
  missingSemicolon: "Отсутствует точка с запятой"
}

Использование messageId повышает читаемость и облегчает локализацию.


fixable

Поле fixable указывает, поддерживает ли правило автоматическое исправление:

  • "code" — изменение кода
  • "whitespace" — изменение только пробельных символов

Это важно для интеграции с --fix.


deprecated

Флаг deprecated: true помечает правило как устаревшее. Он используется для миграции конфигураций и предупреждений.


create: логика анализа

Функция create(context) является центральной частью правила. Она возвращает объект, содержащий обработчики узлов AST.

create(context) {
  return {
    Identifier(node) {
      // обработка узла
    }
  };
}

context: объект окружения

context передается в каждое правило и предоставляет API для взаимодействия с ESLint.

Основные возможности:

context.report

Основной метод для регистрации нарушений:

context.report({
  node,
  messageId: "unexpectedVar"
});

Также поддерживается динамическое сообщение:

context.report({
  node,
  message: "Ошибка в коде"
});

context.options

Содержит пользовательские параметры правила, прошедшие через schema.

const [options] = context.options;
if (options.allowGlobals) {
  return;
}

context.getSourceCode()

Возвращает объект SourceCode, предоставляющий доступ к исходному тексту и утилитам анализа:

const sourceCode = context.getSourceCode();
const text = sourceCode.getText(node);

AST и обработчики

Правила ESLint работают на основе AST (Abstract Syntax Tree). Каждый узел дерева может быть обработан через соответствующий селектор.

Селекторы узлов

return {
  VariableDeclaration(node) {},
  FunctionDeclaration(node) {},
  "Identifier[name='eval']"(node) {}
};

Селекторы позволяют точно фильтровать интересующие конструкции.


Пример анализа

create(context) {
  return {
    VariableDeclaration(node) {
      if (node.kind === "var") {
        context.report({
          node,
          message: "Используйте let или const"
        });
      }
    }
  };
}

Работа с фиксаторами

Если правило поддерживает автоматическое исправление, используется fix:

context.report({
  node,
  message: "Замена var на let",
  fix(fixer) {
    return fixer.replaceText(node, "let");
  }
});

fixer API

  • replaceText(node, text) — замена текста узла
  • remove(node) — удаление
  • insertTextBefore(node, text)
  • insertTextAfter(node, text)

Фиксеры должны быть детерминированными и не зависеть от внешнего состояния.


Поток выполнения правила

Внутренний механизм ESLint последовательно:

  1. Парсит код в AST
  2. Создает context для каждого правила
  3. Вызывает create
  4. Регистрирует обработчики
  5. Обходит AST и вызывает соответствующие функции
  6. Собирает результаты report

Использование дополнительных служб контекста

sourceCode API

const sourceCode = context.getSourceCode();
sourceCode.getAllComments();
sourceCode.getTokens(node);
sourceCode.getFirstToken(node);

Это позволяет анализировать не только структуру, но и текстовую форму кода.


Работа с комментариями

Комментарии обрабатываются как отдельные сущности:

const comments = sourceCode.getCommentsBefore(node);

Это используется в правилах, связанных с аннотациями и директивами.


Локальные состояния в правилах

Каждое правило может хранить состояние внутри create:

create(context) {
  let count = 0;

  return {
    Identifier(node) {
      count++;
    },

    "Program:exit"() {
      if (count > 10) {
        context.report({
          message: "Слишком много идентификаторов"
        });
      }
    }
  };
}

Событие Program:exit вызывается после завершения обхода дерева.


Обработка жизненного цикла AST

ESLint поддерживает специальные хуки:

  • Program
  • Program:exit
  • :exit для любого узла

Пример:

return {
  FunctionDeclaration(node) {
    // вход в узел
  },
  "FunctionDeclaration:exit"(node) {
    // выход из узла
  }
};

Расширенные возможности meta

messages и интерполяция

Сообщения могут использовать параметры:

messages: {
  unexpected: "Недопустимое значение {{name}}"
}

Использование:

context.report({
  node,
  messageId: "unexpected",
  data: {
    name: node.name
  }
});

Флаг recommended используется конфигурациями ESLint для автоматического включения правил в preset’ы. Это влияет на:

  • eslint:recommended
  • сторонние конфигурации

Совместимость и версионирование правил

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

  • версии ESLint
  • уровня стабильности
  • статуса поддержки

Эти данные обычно добавляются в meta.docs или meta расширенные поля.


Типовые ошибки в структуре правила

  • отсутствие meta приводит к невозможности документирования
  • отсутствие schema усложняет проверку конфигурации
  • неправильные селекторы могут приводить к пропуску узлов
  • использование некорректных фиксеров вызывает конфликт с --fix
  • мутация AST недопустима, только чтение

Организация сложных правил

При усложнении логики структура может включать:

  • вспомогательные функции внутри create
  • кеширование узлов
  • контекстные флаги
  • несколько уровней анализа
create(context) {
  const visited = new Set();

  function checkNode(node) {
    if (visited.has(node)) return;
    visited.add(node);
  }

  return {
    Identifier(node) {
      checkNode(node);
    }
  };
}