Правила лінтингу React 19 у ESLint 10, коли eslint-plugin-react відстає
Чому eslint-plugin-react перестає працювати в ESLint 10, як налаштування з акцентом на Biome обмежує перевірку коду React до 11 правил, та як під’єднати цей форк до flat config та Next.js.
Підвищення версії кодової бази React до ESLint 10 часто зупиняється через одну залежність — eslint-plugin-react. На момент написання цього тексту його остання версія не підтримує ESLint 10, а виправлення у вихідному проекті все ще чекає на об’єднання. У цій статті пояснюється причина цієї проблеми, описується, як незалежний форк @ternaus/eslint-plugin-react скоротив набір правил до тих, що ще мають значення для React 19, коли Biome виконує більшу частину перевірок, а також показано, як його встановити у звичайній конфігурації та в Next.js.
Чому правила лінтингу стають важливішими, коли код пишуть агенти
Чим більше роботи команда передає агентам для кодування, тим більше вимог її репозиторію має бути виконуваними. Перевірка людиною — це дорогий спосіб виявити передбачувану помилку. Функції pre-commit hooks, тести, перевірки за стандартами та невеликі комітти, які підлягають перевірці, перетворюють ці вимоги на сигнали «пройшло/не пройшло», за якими може діяти агент, та забезпечують те, щоб кожна помилка залишалася достатньо незначною для діагностики. Linting є одним із таких захисних механізмів, тому втрата рівня linting для React під час оновлення інструментальної сумки є більш ніж просто незручністю.
Що ламається під ESLint 10
Серед критичних змін у версії ESLint 10 — видалення давно знецінених методів з об’єкта контексту правил. Найновіша опублікована версія плагіна React, eslint-plugin-react@7.37.5, вказує ESLint 9 як свою найвищу підтримувану версію, і деякі з його правил все ще використовують ці методи. У ESLint 10 це призводить до такої помилки:
TypeError: contextOrFilename.getFilename is not a function
Згаданий метод context.getFilename() був замінений на властивість context.filename у сучасному API правил, тому код плагіна потребує змін; жоден флаг конфігурації не дозволяє його відновити.
Upstream відстежує цю проблему у заявці на GitHub, поданій 7 лютого 2026 року; кандидатське рішення надійшло у вигляді pull request 30 липня. Жодна з цих заявок на той час, наприкінці серпня 2026 року, не була закрита. Перевірте їхній статус перед тим, як діяти: якщо до моменту читання цього тексту Upstream вже випустив підтримку ESLint 10, найпростішим рішенням може бути оновлення початкового плагіну.
Форк, описаний тут, орієнтований на певну матрицю підтримки:
- React 19 та новіші версії
- ESLint 10 та новіші версії, лише flat config
- Biome 2.5.8 та новіші версії
- Node.js 22.13, 24 та 26
Налаштування, засноване на Biome, яке передбачає цей форк
Вибір правил має сенс лише для певної стек-структури. Biome є основним форматувальником та лінтером, який охоплює загальні перевірки JavaScript, TypeScript, JSX, DOM та більшість функцій React. ESLint залишається частиною інструментарію лише для того, що не надає Biome: плагінів фреймворку та кількох специфікацій React 19.
У референтних проектах використовується:
- React 19 на Next.js 16, написаний на TypeScript
- Biome налаштований з попереднім налаштуванням
all - ESLint 10 із плоским конфігураційним файлом
- Yarn 4 як менеджер пакетів
- Node.js 22, 24 та 26
У такій схемі ви не хочете другого, перекриваючого лінтера. Ви потребуєте лише перевірок, специфічних для React, які додають додаткову інформацію після виконання Biome.
Ця версія починається з вихідного репозиторію та зберігає його історію Git, ліцензію MIT та вказівки щодо авторства; вона підтримується незалежно від нього. Під час коміту, коли відбулося розгалуження, вихідний проект експортував 104 модулі правил. Попередньо налаштований варіант all увімкнув 102 з них (два інші були видалені), а варіант recommended вказав 22 правила, з яких react/no-unsafe було прямо вимкнено, залишивши 21 у силі.
Якби все було перенесено, пакет залишився б великим без чіткої мети. Натомість кожне правило було розсортоване за типом рішення, яке воно забезпечує:
- Якщо Biome вже повідомляє про ту саму проблему, правило відкидається.
- Якщо це стосується форматування, найменувань, структури файлів чи політики команди, це належить до Biome або до власних налаштувань додатку.
.eslintrc, обхідних заходів для парсера чи застарілих API React, вона не входить до специфікації React 19.Рівно 20 правил потрапили до першої категорії, серед них jsx-key, no-danger, no-unknown-property та self-closing-comp. У документаційній папці форку є документ зі зв’язками, який прив’язує кожне з них до відповідного еквівалента у Biome.
Правила, які описують стиль чи політику, наприклад prefer-stateless-function, jsx-sort-props або function-component-definition, були вилучені, оскільки вони не мають жодного стосунку до коректності роботи React 19. no-unused-prop-types та no-unused-state також були видалені, адже перевірка AST у одному файлі не може надійно відповісти на запитання, що стосуються всього проекту; такі «шумні» правила змушують людей та агентів ігнорувати результати перевірки. Усе інше, що було виключено, — це код для сумісності зі старими версіями, який не входить до заданої матриці підтримки.
Пройшли перевірку лише чотири ідентифікатори правил з вихідних проектів: no-deprecated, no-invalid-html-attribute, no-direct-mutation-state та jsx-no-constructed-context-values.
Нові правила та одне, яке було навмисно вилучено
Три пропозиції з вихідних проектів, які ще не були об’єднані, мали відношення до переходу на React 19:
- флаги компонентів, які генерують
undefined(проблема у верхньому рівні #3020) - заборона використання
defaultPropsу функційних компонентах (проблема #3911) - використання ледачого ініціалізатора для
useState(PR #3579)
Усі три пропозиції були реалізовані, а потім no-render-return-undefined знову було видалено. React 19 дозволяє компоненту повертати undefined, тож його заборона була б лише внутрішнім правилом під виглядом правила фреймворку. Інші два правила були впроваджені як no-function-default-props, яке позначає API, що ігнорується React 19 у функційних компонентах, та prefer-use-state-lazy-initialization — попередження про зайві операції під час кожного рендерування, наприклад використання expensive() замість () => expensive().
Ще п’ять правил стосуються конкретної поведінки React 19 — нової або посиленої порівняно з попередніми версіями: no-prop-types, no-misspelled-lifecycle-methods, jsx-no-key-after-spread, controlled-form-requires-handler та no-implicit-ref-callback-return. Правило щодо ref-callback є гарним прикладом того, чому це зараз має значення: оскільки React 19 дозволяє функції-колбеку ref повертати функцію для очищення, стрілкова функція, яка неявно повертає значення з колбеку ref, більше не є безневинною.
Тому версія 8.0.0 містить 11 правил, усі з яких налаштовані як recommended. Дев’ять правил, пов’язаних із коректністю, вважаються помилками; два правила щодо продуктивності — попередженнями. Налаштування all або було б дублікатом recommended, або відрізнялося б лише ступенем серйозності, тому пакет його не пропонує.
Які реальні проекти виявили те, що не змогли виявити тести
З версією 8.0.0-rc.3 тестування модулів та перевірка пакетів пройшли успішно. Саме під час встановлення плагіна у реальних додатках почали з’являтися корисні помилки.
Першою проблемою були метадані атрибутів HTML. Функція no-invalid-html-attribute відхиляла абсолютно коректні атрибути, зокрема alt, accept, name, loading, form та value у елементах <select>, <option> та <textarea>. Щоб вирішити цю проблему, знадобилось три раунди обговорень у системі відстеження змін форка (#21/#22, #25/#26 та #29/#31). Остаточним рішенням стало розглядати атрибути вмісту HTML за стандартом WHATWG та властивості React DOM як два окремі джерела інформації, замість того щоб припускати, що одна таблиця метаданих описує обидва.
Друга проблема виникла у Next.js. eslint-config-next@16 створює просту конфігурацію, проте читає правила з поля у форматі старої версії — react.configs.recommended.rules. Форк оприлюднив лише react.configs.flat.recommended, тому конфігурація зламалась ще до того, як було перевірено хоча б один файл. У наступній зміні (issue #24, PR #27) було додано це поле лише для читання, не повертаючи підтримку .eslintrc. Оскільки Next.js імпортує цей плагін за його неназваним іменем, потрібне також вирішення проблеми через Yarn, яке показано нижче.
Ці інтеграції дозволили створити кандидати на випуск 4–6 та змінили стратегію тестування. Перед остаточним випуском архів npm перевіряється за допомогою publint, імпортується тестовими засобами, написаними на ESM, CommonJS та TypeScript, а також пропускається через конфігурацію Next.js, яку пакет стверджує про підтримку, з використанням CI на Node.js 22.13, 24 та 26. Цей урок можна застосувати до будь-якого інструментального пакету: тестуйте сам продукт, який ви публікуєте, з точки зору засобів, які ви підтримуєте, а не лише джерельну структуру.
Отриманий пакет є нативним ESM, використовує лише flat-config та зберігає знайомий простір імен правил react/*.
Встановлення та налаштування
У командах використовується Yarn 4. Спочатку додайте Biome, ESLint 10 та плагін як залежності типу dev:
yarn add --dev @biomejs/biome@'>=2.5.8' eslint@^10 @ternaus/eslint-plugin-react@^8.0.0
Увімкніть повний стабільний набір правил Biome та його сферу дії React у файлі biome.json, щоб Biome покривав усе, що навмисно було пропущено в форку:
{
"linter": {
"domains": {
"react": "all"
},
"rules": {
"preset": "all"
}
}
}
Потім додайте решту правил React у файл eslint.config.js. Оператор spread об’єднує реєстрацію плагінів та правила з готових налаштувань у об’єкт конфігурації, який діє лише для файлів, вказаних у параметрі files:
import react from '@ternaus/eslint-plugin-react';
export default [
{
files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
...react.configs.flat.recommended,
},
];
Якщо цей параметр files включає файли з розширеннями .ts або .tsx, необхідно зареєструвати парсер, сумісний із TypeScript, у попередньому об’єкті конфігурації; готові налаштування не створюють такого парсера автоматично.
Запускайте обидві програми одночасно, зазвичай як окремі кроки у процесі CI або через один скрипт:
yarn biome check .
yarn eslint .
Ідентифікатори правил зберігають префікс react, тому зміни, внесені для початкового плагіна, залишаються чинними для правил, які все ще існують:
{
rules: {
'react/no-deprecated': 'error',
'react/no-implicit-ref-callback-return': 'error',
},
}
Інтеграція з Next.js
eslint-config-next імпортує цей плагін як eslint-plugin-react. За допомогою Yarn можна перенаправити це ім’я на форк через параметр resolutions:
{
"devDependencies": {
"@ternaus/eslint-plugin-react": "8.0.0"
},
"resolutions": {
"eslint-plugin-react": "npm:@ternaus/eslint-plugin-react@8.0.0"
}
}
Треба підтримувати однакові номери версій. Цей механізм запобігає тому, щоб eslint-config-next завантажував оригінальну версію для ESLint 9 разом із форком для ESLint 10. Оскільки Next.js реєструє плагін під ім’ям react, існуючі ідентифікатори правил react/* продовжують працювати.
Коли цей форк є неправильним вибором
- Він не містить усіх правил з оригіналу. Конфігурації, які залежать від
react/prop-types,react/display-nameабоreact/jsx-sort-props, слід порівняти список підтримуваних правил у репозиторії форку перед тим, як переходити на нього.
key.Основні висновки
- Збій ESLint 10 виникає через видалення API контексту правил, тому його може виправити лише оновлення плагіну.
- Стек, що ґрунтується на Biome, потребує значно меншої кількості правил ESLint для React; сортування правил за типом прийнятих рішень — це метод, який можна використовувати для скорочення будь-якої дублюючоїся налаштування лінтера.
- Правила, які потребують доказів з усього проекту, створюють «шум» у лінтері для окремих файлів, тож краще їх видалити, ніж терпіти.
- Тестування інструментів у тому вигляді, у якому їх бачать користувачі: стиснутий архів, кожен формат модуля та справжні конфігурації фреймворку, такі як
eslint-config-next. - З Next.js псевдонім Yarn
resolutionsдозволяє форку використовуватися замість пакета без обмежень, не змінюючи ідентифікаторів правил.
Вихідні та примітки до випуску знаходяться у репозиторії форку.