Механизм автоисправлений в ESLint строится вокруг функции
fixer, которая передаётся в объект контекста правила и
используется внутри context.report. Фиксер формирует набор
операций над исходным кодом, которые ESLint способен применить без
участия разработчика. Каждая такая операция описывает минимальное и
точное изменение текстового представления программы, сохраняя её
синтаксическую корректность и поведение.
Фиксеры являются частью контрактной модели правила: правило не только обнаруживает проблему, но и формирует корректное преобразование кода, если оно возможно. Важным аспектом является строгая локальность изменений — фиксер не выполняет произвольную трансформацию AST, а оперирует диапазонами текста и узлами.
Автоисправление определяется внутри вызова
context.report через поле fix:
context.report({
node,
message: "Использование var запрещено",
fix(fixer) {
return fixer.replaceText(node, "let");
}
});
Функция fixer предоставляет API для генерации операций
изменения исходного кода. Возвращаемое значение может быть:
ESLint применяет их последовательно, если они не конфликтуют по диапазонам.
Ключевым моментом является то, что фиксер работает с текстом исходного файла, а не с AST-структурой напрямую. AST используется только для определения границ изменений.
Наиболее распространённая операция:
fixer.replaceText(node, "newText");
Замещает текст узла полностью. Используется для простых замен идентификаторов, литералов и выражений, когда структура кода сохраняется.
Типичный пример:
fixer.replaceText(node, "const");
Применяется только если замена не нарушает синтаксис и семантику окружающего кода.
fixer.replaceTextRange([start, end], "value");
Позволяет работать с произвольными диапазонами символов. Используется, когда узел нельзя использовать напрямую или требуется частичное изменение.
Пример применения — удаление части выражения или замена оператора:
fixer.replaceTextRange([node.start, node.end], "0");
Эта операция требует высокой точности, так как неверный диапазон может привести к повреждению структуры файла.
fixer.insertTextBefore(node, "text");
fixer.insertTextAfter(node, "text");
Используются для добавления кода без удаления существующего содержимого.
Частые сценарии:
Пример:
fixer.insertTextBefore(node, "/* eslint-disable */\n");
Удаление элементов кода:
fixer.remove(node);
fixer.removeRange([start, end]);
Применяется для удаления лишних выражений, импортов, параметров.
Удаление требует особой осторожности, поскольку может изменить синтаксическую структуру.
Для построения надёжных фиксов используется объект
sourceCode, доступный через контекст:
const sourceCode = context.getSourceCode();
const text = sourceCode.getText(node);
Этот объект позволяет:
Фиксеры часто комбинируются с sourceCode.getFirstToken и
getLastToken для точной привязки к синтаксису.
Пример:
const firstToken = sourceCode.getFirstToken(node);
const lastToken = sourceCode.getLastToken(node);
return fixer.replaceTextRange(
[firstToken.range[0], lastToken.range[1]],
"replacement"
);
Фиксеры должны быть безопасными, то есть гарантировать, что после применения код остаётся:
ESLint не проверяет семантику кода после фикса автоматически, поэтому ответственность за корректность лежит на разработчике правила.
Запрет на перекрывающиеся диапазоны Два фикса не могут изменять один и тот же участок кода.
Недопустимость зависимых изменений Фиксы должны быть независимыми, если они возвращаются массивом.
Стабильность результата Повторное применение фикса не должно менять код дальше (идемпотентность).
Сохранение форматирования (по возможности) Не всегда обязательно, но желательно избегать разрушения читаемости.
Полная структура кастомного правила включает meta и
create:
export default {
meta: {
type: "problem",
fixable: "code",
messages: {
noVar: "Использование var запрещено"
}
},
create(context) {
return {
VariableDeclaration(node) {
if (node.kind === "var") {
context.report({
node,
messageId: "noVar",
fix(fixer) {
return fixer.replaceTextRange(
[node.start, node.start + 3],
"let"
);
}
});
}
}
};
}
};
Поле fixable в meta обязательно указывает,
что правило поддерживает автоисправления. Возможные значения:
"code" — исправление кода"whitespace" — только форматированиеТокены позволяют избегать ошибок, связанных с комментариями и форматированием.
const token = sourceCode.getTokenAfter(node);
Это особенно важно, когда фиксер зависит от точного положения символов, например при вставке запятых или скобок.
Пример безопасного добавления запятой:
fixer.insertTextAfter(node, ",");
Однако более надёжный подход — проверка следующего токена:
const nextToken = sourceCode.getTokenAfter(node);
if (nextToken.value !== ",") {
return fixer.insertTextAfter(node, ",");
}
Фиксер может возвращать массив операций:
fix(fixer) {
return [
fixer.remove(node),
fixer.insertTextAfter(parent, ";")
];
}
Такая модель применяется для сложных преобразований, когда одна операция не может выразить изменение.
Важно учитывать порядок применения: ESLint применяет фиксы в последовательности, но конфликтующие диапазоны приведут к отказу применения.
Ошибки в range приводят к повреждению кода:
AST-узлы не включают комментарии, поэтому:
fixer.replaceText(node, "x");
может удалить комментарии вокруг. Для сохранения комментариев требуется работа через токены.
Фикс может создать некорректный код:
fixer.replaceText(node, "return;");
если узел находится внутри выражения.
Фикс должен быть одинаковым при повторном запуске. Если результат зависит от внешнего состояния, автоисправление становится непредсказуемым.
Иногда фиксер зависит от окружения:
const scope = context.getScope();
Это позволяет проверять, не конфликтует ли изменение с другими переменными.
В сложных случаях применяется частичная реконструкция кода:
fixer.replaceText(node, `${left} + ${right}`);
Лучшие фиксы изменяют минимально возможный участок кода. Это снижает риск конфликтов с форматированием и другими правилами.
Фиксеры ESLint часто работают вместе с форматерами (Prettier или встроенные правила стилизации). Важно учитывать:
Наиболее устойчивые подходы:
replaceText вместо диапазоновМенее надёжные:
Если несколько правил предлагают пересекающиеся изменения:
Поэтому проектирование фиксеров требует учёта возможных взаимодействий между правилами.