Механизм “suggest”-фиксов в ESLint предназначен для случаев, когда автоматическое исправление возможно, но не должно применяться без явного подтверждения. Это промежуточный уровень между жёстким autofix и простым сообщением о проблеме. Он особенно важен в правилах, где существует несколько допустимых вариантов исправления или где изменение кода может повлиять на поведение программы.
Система исправлений в ESLint строится вокруг двух независимых механизмов:
eslint --fixКаждое правило может комбинировать оба подхода, но поведение должно быть строго разделено: autofix обязан быть безопасным и однозначным, тогда как suggest допускает неоднозначность.
Чтобы ESLint начал учитывать исправления, правило должно явно
объявлять возможности в meta:
meta: {
fixable: "code",
hasSuggestions: true,
}
Поля имеют строгую семантику:
fixable: "code" — правило поддерживает автоматическое
исправление через fixer APIhasSuggestions: true — правило возвращает список
альтернативных исправленийЕсли hasSuggestions не указано, массив
suggest будет игнорироваться ESLint-движком.
Suggest-исправления формируются внутри
context.report:
context.report({
node,
message: "Использование устаревшего метода",
suggest: [
{
desc: "Заменить на newMethod()",
fix(fixer) {
return fixer.replaceText(node, "newMethod()");
}
}
]
});
Ключевые элементы:
desc — описание действия, отображается в IDEfix — функция, возвращающая набор операций fixer
APIОбе конструкции используют fixer, но различаются по
семантике применения:
fix(fixer) {
return fixer.replaceText(node, "const");
}
suggest: [
{
desc: "Заменить на const",
fix(fixer) {
return fixer.replaceText(node, "const");
}
}
]
Suggest-API позволяет возвращать несколько альтернативных стратегий:
suggest: [
{
desc: "Удалить выражение",
fix(fixer) {
return fixer.remove(node);
}
},
{
desc: "Заменить на null",
fix(fixer) {
return fixer.replaceText(node, "null");
}
}
]
Такая модель используется в случаях:
Fixer предоставляет минимальный набор операций:
replaceText(node, text)replaceTextRange(range, text)insertTextBefore(node, text)insertTextAfter(node, text)remove(node)Внутри suggest-фиксов используются те же операции, но с более осторожной логикой.
Suggest-фиксы почти всегда зависят от AST-структуры:
const sourceCode = context.getSourceCode();
const text = sourceCode.getText(node);
Часто используются:
node.range — точные позиции в кодеsourceCode.getText(node) — получение исходного
текстаsourceCode.getCommentsBefore(node) — работа с
комментариямиВажно избегать ручных вычислений строк, если доступен AST-узел.
Suggest-исправления должны соблюдать несколько технических требований:
Каждое исправление должно быть применимо независимо:
suggest: [
{
desc: "Удалить переменную",
fix(fixer) {
return fixer.remove(node);
}
}
]
Нельзя строить suggest, зависящий от других suggestions.
Если два фикса изменяют пересекающиеся диапазоны, ESLint может отклонить применение.
Пример проблемного случая:
fixer.replaceText(node.left, "a");
fixer.replaceText(node, "b");
Такие операции должны быть объединены или разделены по альтернативам.
Fix должен всегда давать одинаковый результат для одного AST:
Правило может одновременно иметь автофикс и suggestions:
meta: {
fixable: "code",
hasSuggestions: true
}
context.report({
node,
message: "Устаревший синтаксис",
fix(fixer) {
return fixer.replaceText(node, "newSyntax()");
},
suggest: [
{
desc: "Использовать альтернативный вариант",
fix(fixer) {
return fixer.replaceText(node, "alternativeSyntax()");
}
}
]
});
Поведение:
fix применяется при --fixsuggest показывается в редактореВ сложных правилах применяется messageId:
meta: {
messages: {
replaceWithConst: "Используйте const",
replaceWithLet: "Используйте let"
}
}
context.report({
node,
messageId: "replaceWithConst",
suggest: [
{
desc: "Заменить на const",
fix(fixer) {
return fixer.replaceText(node, "const");
}
}
]
});
Преимущества:
suggest: [
{
desc: "Заменить на новый API",
fix(fixer) {
return fixer.replaceText(node, "newApi()");
}
}
]
Используется при изменении библиотек и депрекейте функций.
suggest: [
{
desc: "Удалить небезопасный вызов",
fix(fixer) {
return fixer.remove(node);
}
}
]
suggest: [
{
desc: "Обернуть в try/catch",
fix(fixer) {
return fixer.insertTextBefore(node, "try { ");
}
}
]
(в реальных правилах обычно комбинируется несколько операций)
Хотя suggest-фиксы не выполняются автоматически, их создание влияет на производительность анализа:
fix замедляют lintingРекомендуется:
fixgetText для одного узлаSuggest-фиксы активно используются редакторами:
IDE отображает:
descПри тестировании через RuleTester необходимо учитывать, что suggestions проверяются отдельно:
{
code: "var a = 1",
errors: [
{
messageId: "replaceWithConst",
suggestions: [
{
messageId: "replaceWithConst",
output: "const a = 1"
}
]
}
]
}
Важно:
outputfix вместо массива объектов в
suggestТипичная структура зрелого правила:
error — фиксируется как нарушениеfix — безопасное автоматическое исправлениеsuggest — альтернативные трансформацииТакая модель позволяет разделить: