Механизм автоматических исправлений в пользовательских правилах
ESLint опирается на функцию fix, возвращаемую из
context.report. Именно через неё правило получает
возможность трансформировать исходный текст программы, устраняя
обнаруженные нарушения без участия разработчика. Ключевая особенность
этого механизма заключается в том, что фикс работает не с абстрактной
моделью кода, а с конкретными диапазонами исходного текста, полученными
через SourceCode.
Основной контракт фикса в пользовательском правиле строится вокруг объекта отчёта:
context.report({
node,
messageId: 'unexpectedSemicolon',
fix(fixer) {
return fixer.remove(node);
}
});
Функция fix принимает объект fixer,
предоставляемый ESLint, и обязана вернуть одно или несколько изменений
текста либо null. Любое исправление трактуется как операция
над исходной строкой, а не над AST-узлом как структурой.
Автоисправление в ESLint считается корректным только в случае, если трансформация:
Функция fix должна быть чистой: вызовы побочных
эффектов, асинхронность, использование внешних переменных приводят к
неопределённому поведению механизма --fix.
Объект fixer предоставляет набор атомарных операций над
текстом.
fixer.remove(node)
Используется для удаления узла целиком, включая его текстовое представление. Применимо, например, для удаления лишних выражений, пустых инструкций или устаревших конструкций.
fixer.replaceText(node, 'newValue')
Операция заменяет текстовое содержимое узла на новую строку. AST при этом не перестраивается вручную — ESLint повторно анализирует итоговый код.
Типичный пример:
context.report({
node,
messageId: 'useConst',
fix(fixer) {
return fixer.replaceText(node.kind, 'const');
}
});
fixer.insertTextBefore(node, 'debugger;\n')
Используется для добавления кода перед текущим узлом. Важно учитывать форматирование и контекст токенов, поскольку вставка не нормализует пробелы автоматически.
fixer.insertTextAfter(node, ';')
Применяется для добавления завершающих символов или дополнительных выражений после текущего фрагмента.
fixer.replaceTextRange([start, end], 'replacement')
Наиболее низкоуровневая операция. Позволяет точно управлять
изменениями, опираясь на индексы символов, полученные через
sourceCode.getIndexFromLoc.
Использование диапазонов особенно важно при работе с частями узлов или форматированием, где AST-границы не совпадают с необходимым изменяемым фрагментом.
Для точного определения границ исправлений используется
SourceCode:
const sourceCode = context.getSourceCode();
Через него доступны:
getText(node)getFirstToken(node)getLastToken(node)getTokenAfter(node)getIndexFromLoc(loc)Эти методы позволяют строить корректные диапазоны для
replaceTextRange и избегать разрушения синтаксиса.
Функция fix может возвращать массив фиксов:
fix(fixer) {
return [
fixer.remove(node.left),
fixer.insertTextAfter(node.right, ';')
];
}
Каждый элемент массива рассматривается как независимая операция. ESLint применяет их последовательно, но порядок выполнения имеет значение: пересекающиеся диапазоны могут конфликтовать.
Система ESLint запрещает применение перекрывающихся изменений в рамках одного прохода. Если два фикса затрагивают одинаковые диапазоны текста, результат считается конфликтным и автоисправление для данного правила может быть проигнорировано.
Типичная проблема возникает при:
replaceTextRange на пересекающихся
интервалах.Для включения автоисправлений правило должно явно объявлять поддержку фиксов:
meta: {
fixable: 'code'
}
Возможные значения:
code — исправления, изменяющие логику кода;whitespace — изменения, касающиеся только
форматирования.Это влияет на режимы работы ESLint и интеграцию с форматирующими инструментами.
Автоисправления должны учитывать синтаксическую целостность. Ошибки часто возникают при:
Например, удаление аргумента функции требует корректного управления запятыми:
fixer.remove(argNode)
в чистом виде может оставить висячую запятую, поэтому часто требуется комбинированный фикс:
fix(fixer) {
return [
fixer.remove(argNode),
fixer.remove(commaToken)
];
}
Во многих случаях надёжнее работать не с узлами, а с токенами:
const token = sourceCode.getTokenAfter(node);
Токены позволяют учитывать реальные границы текста, включая пробелы, комментарии и форматирование, которые не представлены в AST-структуре.
Корректный фикс должен быть идемпотентным: повторное применение не должно изменять результат после первого запуска.
Пример некорректного подхода:
fix(fixer) {
return fixer.replaceText(node, node.name + ' ');
}
Такое исправление может добавлять пробелы при каждом запуске.
В случаях многошаговых изменений используется стратегия композиции:
Иногда применяется несколько правил вместо одного, чтобы избежать сложной логики фиксации.
Помимо fix, ESLint поддерживает механизм
предложений:
context.report({
node,
messageId: 'preferConst',
suggest: [
{
messageId: 'convertToConst',
fix(fixer) {
return fixer.replaceText(varToken, 'const');
}
}
]
});
Разделение на fix и suggest позволяет
разграничивать автоматические исправления и опциональные изменения,
требующие подтверждения.
Устойчивые автоисправления обычно следуют нескольким принципам:
SourceCode вместо строковых эвристик;Фикс, опирающийся только на AST-узлы без учёта текстового контекста, часто оказывается нестабильным при изменении стиля кода.
Автоисправления ESLint не заменяют форматтеры. При совместной работе с инструментами форматирования изменения должны быть либо:
Иначе форматтер может перезаписать результат, нивелируя эффект фикса.
При использовании replaceTextRange критически важно
учитывать:
Ошибки в диапазонах приводят к синтаксическим сбоям, которые ESLint не всегда способен диагностировать до повторного парсинга.
Фиксеры в пользовательских правилах формируют слой преобразования между диагностикой и изменением кода. Они функционируют как декларативный интерфейс над текстовыми операциями, где каждое изменение должно быть точно локализовано, предсказуемо и безопасно с точки зрения повторного анализа программы.