Oxlint, szybki linter JS i TS napisany w Rust
Oxlint to linter dla JavaScriptu i TypeScriptu napisany w Rust, wchodzący w skład projektu Oxc. Wersja 1.79.0 ukazała się 18 sierpnia 2026 roku na licencji MIT. Na katalogu src tego repozytorium, czyli 643 plikach, przebieg z domyślną konfiguracją zajmuje na moim laptopie z ośmioma rdzeniami około 0,04 sekundy.
Oxc to zestaw narzędzi, oxlint to jedno z nich
Nazwy Oxc i oxlint bywają używane zamiennie i to jest pierwsze źródło pomyłek przy instalacji. Oxc, czyli Oxidation Compiler, to parasol nad kilkoma niezależnymi programami: parserem, transformerem, minifikatorem, resolverem modułów, formaterem i właśnie linterem. Wszystkie mieszkają w jednym repozytorium oxc-project/oxc, ale do rejestru npm trafiają jako osobne pakiety z własną numeracją.
Stan na dzień pisania wygląda tak. Linter to pakiet oxlint w wersji 1.79.0. Formater to oxfmt w wersji 0.64.0, pierwszy raz opublikowany 10 września 2025 roku, czyli wciąż przed jedynką. Parser, transformer i minifikator to oxc-parser, oxc-transform i oxc-minify, wszystkie w wersji 0.146.0. Resolver to oxc-resolver w wersji 11.24.2. Do tego dochodzą @oxc-project/types z definicjami węzłów drzewa składniowego oraz @oxc-project/runtime z pomocnikami dla transformera, oba w wersji 0.146.0. Nie istnieje jedna wersja całego zestawu, więc pytanie „jaką masz wersję Oxc” nie ma sensownej odpowiedzi.
Najgorsza pułapka nazewnicza czai się w rejestrze npm. Pakiet o nazwie oxc istnieje, ma wersję 1.0.1 i pochodzi z 5 maja 2016 roku, a jego repozytorium to jasonmccreary/oxc. To narzędzie wiersza poleceń do otwierania projektów w Xcode i nie ma żadnego związku z Oxidation Compiler. Wpisanie npm install oxc zainstaluje coś zupełnie innego, niż się spodziewasz, i nie dostaniesz przy tym ostrzeżenia.
Sam plik wykonywalny lintera również nie leży tam, gdzie mógłbyś zakładać. Pakiet oxlint zawiera wyłącznie kod JavaScript, a binarium przychodzi przez dziewiętnaście zależności opcjonalnych o nazwach @oxlint/binding-darwin-arm64, @oxlint/binding-linux-x64-gnu, @oxlint/binding-win32-x64-msvc i tak dalej. Instalacja z pominięciem zależności opcjonalnych daje pakiet, który nie uruchomi się wcale.
Wersja, licencja i sposób dystrybucji
Licencję sprawdziłem w trzech miejscach, bo w tej kolekcji zdarzało się już, że rozjeżdżały się między sobą.
Pierwsze źródło to plik LICENSE w gałęzi głównej repozytorium. Zawiera tekst licencji MIT z dwiema notami: „Copyright (c) 2024-present VoidZero Inc. & Contributors” oraz „Copyright (c) 2023 Boshen”. Druga nota to ślad po okresie, gdy projekt prowadził pojedynczy autor, zanim powstała firma.
Drugie źródło to pole license w rejestrze npm dla pakietu oxlint, które ma wartość MIT. Trzecie i najważniejsze to zawartość opublikowanej paczki. Archiwum oxlint-1.79.0.tgz waży 371 kilobajtów w postaci spakowanej i około 2,38 megabajta po rozpakowaniu, zawiera siedemnaście plików, w tym LICENSE z tekstem MIT, katalog dist z realnym kodem, plik startowy bin/oxlint oraz configuration_schema.json o wielkości około 758 kilobajtów. To nie jest atrapa ani pusty metapakiet, kod tam jest, a plik licencyjny również. Wyrywkowo sprawdziłem także pakiet z binarium, @oxlint/binding-linux-x64-gnu w wersji 1.79.0, i on też deklaruje MIT. Wszystkie trzy źródła mówią to samo, co w tym zestawieniu jest raczej wyjątkiem niż regułą.
Rytm wydań jest gęsty. Pakiet istnieje w rejestrze od 27 czerwca 2023 roku, wersję 1.0.0 dostał 10 czerwca 2025 roku, a do dziś opublikowano 206 wersji. Ostatnie pięć wydań przypadło na 21 lipca, 27 lipca, 3 sierpnia, 10 sierpnia i 18 sierpnia 2026 roku, czyli mniej więcej co tydzień. Numer środkowy rośnie przy każdym wydaniu, więc zależność zapisana jako ^1.79.0 przyjmie kolejne setki reguł bez pytania.
Pole engines wymaga Node w wersji ^20.19.0 || >=22.12.0. To realne ograniczenie, nie formalność, i wywróci instalację na starszych obrazach kontenerów, które nadal krążą po serwerach budujących.
Warto spojrzeć też na deklarowane zależności rówieśnicze, bo mówią coś o kierunku projektu. Są dwie, obie oznaczone jako opcjonalne: oxlint-tsgolint w wersji co najmniej 7.0.2001 oraz vite-plus bez ograniczenia wersji. Pierwsza to składnik odpowiadający za reguły korzystające z typów. Druga to vite-plus, pakiet w wersji 0.2.9 opisany jako zunifikowany łańcuch narzędzi, produkt firmy VoidZero, tej samej, która stoi za Vite i Vitest. Dokumentacja migracji wprost kieruje część czytelników w tamtą stronę: oxlint dla samego lintowania, Vite+ dla zintegrowanego przepływu pracy. Sam linter jest na MIT i nikt Ci go nie odbierze, ale kierunek rozwoju wyznacza firma, która sprzedaje szerszy produkt, i to jest ryzyko, które przy wyborze narzędzia trzeba nazwać.
Instalacja i pierwsze uruchomienie
Oxlint nie wymaga konfiguracji, żeby cokolwiek zrobić. Uruchomiony bez pliku ustawień włącza kategorię correctness i trzy domyślne wtyczki.
npm install --save-dev oxlint@1.79.0
npx oxlint src
npx oxlint --init
npx oxlint --print-config
npx oxlint src --fix
npx oxlint src --format=github --deny-warningsPolecenie --init tworzy plik .oxlintrc.json z wartościami domyślnymi. Polecenie --print-config wypisuje pełną, rozwiniętą konfigurację w formacie JSON i jest najlepszym sposobem, żeby sprawdzić, co naprawdę jest włączone, zamiast zgadywać z dokumentacji.
Flagi naprawiania są trzy i różnią się poziomem ryzyka. --fix stosuje poprawki uznane za bezpieczne, --fix-suggestions dokłada sugestie, które mogą zmienić zachowanie programu, a --fix-dangerously obejmuje jeszcze te oznaczone jako niebezpieczne. Na serwerze budującym używa się zwykle wyłącznie pierwszej albo żadnej.
Formaty wyjścia to checkstyle, default, agent, github, gitlab, json, junit, sarif, stylish i unix. Format agent jest przeznaczony dla asystentów piszących kod, github dla adnotacji przy zmianach w repozytorium.
Konfiguracja i pola, które naprawdę istnieją
Plik .oxlintrc.json przyjmuje dokładnie dwanaście pól najwyższego poziomu: $schema, categories, env, extends, globals, ignorePatterns, jsPlugins, options, overrides, plugins, rules oraz settings. Wszystko poza tą listą zostanie odrzucone.
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "react", "vitest"],
"categories": {
"correctness": "error",
"suspicious": "warn",
"pedantic": "off"
},
"env": { "browser": true, "es2024": true },
"globals": { "__DEV__": "readonly" },
"ignorePatterns": ["dist", "coverage", "**/*.generated.ts"],
"options": {
"denyWarnings": false,
"maxWarnings": 50,
"reportUnusedDisableDirectives": true,
"respectEslintDisableDirectives": true,
"typeAware": false,
"typeCheck": false
},
"rules": {
"eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"typescript/no-explicit-any": "warn",
"import/no-cycle": "error"
},
"overrides": [
{
"files": ["**/*.test.ts", "**/*.test.tsx"],
"excludeFiles": ["**/fixtures/**"],
"plugins": ["vitest"],
"rules": { "vitest/expect-expect": "error" }
}
]
}Kategorii jest siedem i każda przyjmuje wartość allow, off, warn, error albo deny: correctness, suspicious, pedantic, perf, style, restriction oraz nursery. Skrót all dostępny w wierszu poleceń obejmuje wszystkie poza nursery.
Pole plugins przyjmuje wartości z zamkniętej listy piętnastu nazw: eslint, react, unicorn, typescript, oxc, import, jsdoc, jest, vitest, jsx-a11y, nextjs, react-perf, promise, node, vue. Ustawienie tego pola nadpisuje zestaw domyślny, którym są unicorn, typescript i oxc, a nie dokłada się do niego. To najczęstsza przyczyna sytuacji, w której po dodaniu jednej wtyczki część reguł nagle przestaje raportować.
Obiekt overrides przyjmuje pola files, excludeFiles, env, globals, jsPlugins, plugins oraz rules. Pola typeAware i typeCheck z sekcji options działają wyłącznie w konfiguracji korzeniowej i są ignorowane w konfiguracjach zagnieżdżonych.
Alternatywą jest plik oxlint.config.ts, oznaczony w dokumentacji jako eksperymentalny i wymagający uruchomienia przez Node.
import { defineConfig } from "oxlint";
export default defineConfig({
plugins: ["typescript", "unicorn", "oxc"],
options: {
typeAware: true,
typeCheck: true
},
rules: {
"typescript/no-floating-promises": ["error", { ignoreVoid: true }],
"typescript/no-unsafe-assignment": "warn"
}
});Różnica dotyczy też pola extends. W .oxlintrc.json jest to lista ścieżek w postaci napisów, rozwiązywanych względem pliku, który je zawiera. W oxlint.config.ts jest to lista zaimportowanych obiektów konfiguracji. Skopiowanie jednego wprost do drugiego nie zadziała.
Ile reguł oxlint naprawdę ma
Zamiast powtarzać liczbę z materiałów projektu, policzyłem ją poleceniem --print-config na wersji 1.79.0.
npx oxlint --print-config | node -e "
let s=''; process.stdin.on('data', d => s += d).on('end', () => {
console.log(Object.keys(JSON.parse(s).rules).length)
})"
npx oxlint --print-config -D all -D nursery \
--import-plugin --react-plugin --jsdoc-plugin --jest-plugin \
--vitest-plugin --jsx-a11y-plugin --nextjs-plugin --react-perf-plugin \
--promise-plugin --node-plugin --vue-plugin > full.jsonBez konfiguracji włączonych jest 111 reguł. Po włączeniu wszystkich kategorii razem z nursery oraz wszystkich wtyczek wychodzi 870 reguł, rozłożonych następująco.
| Przestrzeń nazw | Liczba reguł | Odpowiednik w świecie ESLinta |
|---|---|---|
| eslint | 187 | reguły rdzenia ESLinta |
| unicorn | 138 | eslint-plugin-unicorn |
| typescript | 110 | typescript-eslint |
| react | 85 | react, react-hooks, react-refresh, React Compiler |
| vitest | 73 | @vitest/eslint-plugin |
| jest | 60 | eslint-plugin-jest |
| vue | 46 | eslint-plugin-vue, tylko sekcja skryptu |
| jsx_a11y | 36 | eslint-plugin-jsx-a11y |
| import | 33 | eslint-plugin-import |
| oxc | 27 | reguły własne oraz porty z deepscan |
| jsdoc | 23 | eslint-plugin-jsdoc |
| nextjs | 21 | @next/eslint-plugin-next |
| promise | 16 | eslint-plugin-promise |
| node | 11 | eslint-plugin-n |
| react_perf | 4 | eslint-plugin-react-perf |
Ta tabela pokazuje zarówno zasięg, jak i jego granice. Czternaście wtyczek wbudowanych pokrywa zestawy, które spotyka się najczęściej, a poza nimi nie ma nic natywnego. Reguł dotyczących TypeScriptu jest 110 i nie pokrywają one całego typescript-eslint, a wtyczka Vue obsługuje wyłącznie reguły działające na sekcji skryptu, więc analiza szablonu odpada.
Reguły korzystające z informacji o typach to osobna historia. Obsługuje je składnik oxlint-tsgolint, wersja 7.0.2001 z 21 lipca 2026 roku, licencja MIT, napisany w Go i oparty na typescript-go. Dokumentacja podaje pokrycie 59 z 61 reguł typowanych z typescript-eslint. Cena jest konkretna: wymagany jest TypeScript w wersji 7.0 lub nowszej, część starszych opcji tsconfig.json nie działa, w tym baseUrl, a przy bardzo dużych repozytoriach dokumentacja sama ostrzega przed wysokim zużyciem pamięci.
npm install --save-dev oxlint-tsgolint@7.0.2001
npx oxlint --type-aware
npx oxlint --type-aware --type-check
npx oxlint --type-aware --debug timings
OXC_LOG=debug npx oxlint --type-awareTryb --type-check zgłasza błędy typów obok wyników lintowania i może zastąpić osobny krok tsc --noEmit na serwerze budującym. To niepozorna, ale duża zmiana wobec Biome, które sprawdzania typów nie robi wcale.
Dla wtyczek, których nie ma natywnie, istnieje mechanizm jsPlugins zgodny z interfejsem ESLinta w wersji dziewiątej. Jest oznaczony jako alfa i ma dwa wyraźne braki: nie obsługuje własnych parserów, czyli plików Svelte, Vue i Angulara, oraz nie obsługuje reguł korzystających z typów. Reguły z takich wtyczek działają wolniej, bo przechodzą przez warstwę JavaScriptu, czyli dokładnie to, czego oxlint miał unikać.
Współistnienie z ESLintem
Oxlint nie jest zamiennikiem ESLinta w sensie „usuń jedno, wstaw drugie”. Dokumentacja projektu mówi to zresztą wprost i zaleca układ dwuetapowy.
npx @oxlint/migrate
npx @oxlint/migrate --type-aware
npx @oxlint/migrate --js-plugins=false
npm install --save-dev eslint-plugin-oxlint@1.79.0
npx oxlint && npx eslint .Narzędzie @oxlint/migrate w wersji 1.79.0 czyta płaską konfigurację ESLinta z wersji dziewiątej lub dziesiątej i generuje .oxlintrc.json, zachowując poziomy zgłoszeń, opcje reguł, nadpisania dla ścieżek oraz zmienne globalne. Stare pliki .eslintrc.js nie przejdą bezpośrednio, trzeba je najpierw przepuścić przez @eslint/migrate-config. Lokalne wtyczki z własnego repozytorium nie są przenoszone automatycznie i trzeba je dopisać ręcznie do pola jsPlugins.
Pakiet eslint-plugin-oxlint, również w wersji 1.79.0, robi rzecz odwrotną: wyłącza w ESLincie te reguły, które oxlint już sprawdza. Bez tego dostaniesz każdy problem zgłoszony dwa razy, a łączny czas przebiegu wcale nie spadnie.
Kolejność ma znaczenie. Oxlint uruchamiany pierwszy odrzuca zmianę w kilkadziesiąt milisekund, więc ESLint włącza się dopiero na kodzie, który przeszedł tani etap. Odwrotna kolejność nie daje nic. Jeśli budujesz w Turborepo, oba przebiegi opłaca się trzymać jako osobne zadania, żeby pamięć podręczna liczyła je niezależnie.
Oxlint a Biome
Biome jest najbliższym konkurentem i wybór między nimi sprowadza się do jednej decyzji: czy chcesz jedno narzędzie do formatowania i lintowania, czy dwa osobne.
| Cecha | Oxlint 1.79.0 | Biome 2.5.9 |
|---|---|---|
| Zakres | tylko lintowanie | formatowanie, lintowanie, akcje porządkowe |
| Formatowanie | osobny pakiet oxfmt 0.64.0 | wbudowane, dojrzałe |
| Licencja | MIT | MIT albo Apache 2.0 do wyboru |
| Reguły własne | wtyczki w JavaScripcie zgodne z ESLint v9, alfa | wyłącznie GritQL w plikach .grit |
| Reguły typowane | 59 z 61 przez oxlint-tsgolint, wymaga TypeScript 7 | własne wnioskowanie, bez tsc |
| Sprawdzanie typów | --type-check zastępuje tsc --noEmit | brak, tsc zostaje w potoku |
| Języki poza JS i TS | brak | CSS, GraphQL, JSON, wstępnie Vue i Svelte |
| Plik konfiguracji | .oxlintrc.json lub oxlint.config.ts | biome.json |
Praktyczny wniosek jest taki. Jeśli masz już Prettiera i jesteś z niego zadowolony, a chcesz wyłącznie skrócić czas lintowania, oxlint pasuje lepiej, bo nie próbuje przy okazji przepisać całego repozytorium. Jeśli chcesz wyrzucić parę ESLint plus Prettier za jednym zamachem, Biome robi to dziś w sposób bardziej kompletny, bo jego formater jest po wersji drugiej, a oxfmt dopiero po zerowej. Jeśli zależy Ci na regułach korzystających z typów, oxlint w trybie --type-aware jest bliżej celu, o ile możesz podnieść TypeScript do wersji siódmej.
Oba narzędzia mają ten sam obszar, którego nie dotykają: nie budują aplikacji, od czego jest esbuild albo Vite, i nie mają nic wspólnego z warstwą danych, którą zajmuje się na przykład Prisma.
Warto rozróżnić dwie sąsiadujące kategorie, bo granica bywa płynna. Linter pilnuje stylu i typowych błędów, a Semgrep dopasowuje wzorce do drzewa składniowego i szuka podatności, więc reguła przetrwa zmianę formatowania czy nazwy zmiennej. Przed wdrożeniem sprawdź jednak, gdzie przebiega granica wersji płatnej: analiza międzyplikowa i międzyfunkcyjna nie jest w otwartym kodzie, tylko w zamkniętej binarce pobieranej po zalogowaniu, a reguły ze zbioru społecznościowego mają własną licencję zakazującą redystrybucji. Bezpłatne i otwarte to tam dwie różne rzeczy.
Typowe błędy przy wdrożeniu
Pierwszy to instalacja pakietu oxc zamiast oxlint. Dostaniesz wtedy niepowiązany program z 2016 roku i żadnego ostrzeżenia.
Drugi to ustawienie pola plugins z myślą, że dokłada się ono do domyślnych. Nadpisuje. Lista musi zawierać wszystkie wtyczki, których chcesz używać, razem z unicorn, typescript i oxc, jeśli mają zostać.
Trzeci to przeniesienie reguły, której oxlint nie implementuje. Konfiguracja nie zgłasza wtedy ostrzeżenia, tylko odmawia wczytania: komunikat brzmi „Failed to parse oxlint configuration file” i wskazuje regułę nieznaną w danej wtyczce. Przepisywanie konfiguracji ręcznie, linijka po linijce, kończy się serią takich niespodzianek, dlatego lepiej puścić @oxlint/migrate, który nieobsługiwane reguły po prostu pomija.
Czwarty to instalacja z flagą pomijającą zależności opcjonalne. Bez pakietu @oxlint/binding-* odpowiedniego dla systemu nie ma czego uruchomić.
Piąty to Node starszy niż 20.19. Pole engines jest tu realnym ograniczeniem, a nie zapisem na wyrost.
Szósty to ustawianie typeAware albo typeCheck w konfiguracji zagnieżdżonej. Oba pola działają wyłącznie w pliku korzeniowym i w podkatalogu zostaną zignorowane bez słowa wyjaśnienia.
Siódmy to uruchomienie trybu --type-aware na repozytorium, którego korzeniowy tsconfig.json ma include ustawione na **/*. Powstaje wtedy jeden ogromny program obejmujący także wyniki budowania, a przebieg rozciąga się na minuty. Diagnoza jest prosta: przy OXC_LOG=debug widać w dzienniku linię z liczbą plików źródłowych w programie.
Ósmy to oczekiwanie, że oxlint sformatuje kod. Nie sformatuje, bo to zadanie oxfmt, który jest osobnym pakietem i osobną decyzją.
Dziewiąty to włączanie kategorii nursery bez przypięcia dokładnej wersji. Reguły z tej grupy zmieniają nazwy i zachowanie między wydaniami, a wydania wychodzą co tydzień.
FAQ
Czy oxlint zastąpi ESLinta w moim projekcie?
Zależy od konfiguracji. Jeśli opiera się na regułach rdzenia, typescript-eslint, unicorn, import, react albo jsx-a11y, pokrycie jest wysokie i oxlint sam wystarczy. Jeśli używasz wtyczek spoza listy czternastu wbudowanych, zostaje mechanizm jsPlugins w wersji alfa albo utrzymywanie obu narzędzi obok siebie.
Ile reguł ma oxlint w wersji 1.79.0?
Po włączeniu wszystkich kategorii i wszystkich wtyczek polecenie --print-config wypisuje 870 reguł. Bez żadnej konfiguracji aktywnych jest 111, bo domyślnie działa kategoria correctness oraz wtyczki unicorn, typescript i oxc.
Czy reguły wymagające informacji o typach działają?
Tak, po doinstalowaniu oxlint-tsgolint i uruchomieniu z flagą --type-aware. Pokrycie wynosi 59 z 61 reguł typowanych z typescript-eslint. Wymagany jest TypeScript 7.0 lub nowszy, a baseUrl w tsconfig.json nie jest obsługiwane.
Czym różni się oxlint od oxfmt?
Oxlint sprawdza kod i zgłasza problemy, oxfmt przepisuje go według reguł formatowania. To dwa osobne pakiety npm z osobną numeracją: 1.79.0 dla lintera, 0.64.0 dla formatera. Instalacja jednego nie daje drugiego.
Czy oxlint jest płatny?
Nie. Pakiet, wszystkie binaria i składnik oxlint-tsgolint są na licencji MIT, bez wariantu płatnego i bez ograniczeń komercyjnych. Firma VoidZero sprzedaje osobny produkt o nazwie Vite+, do którego dokumentacja migracji kieruje osoby szukające zintegrowanego łańcucha narzędzi.
Dokumentacja mieszka na stronie Oxc, przewodnik migracji w sekcji poświęconej ESLintowi, a kod źródłowy w repozytorium na GitHubie.