Różnice z Prettier
W niektórych przypadkach Biome celowo zdecydowało się formatować kod w sposób, który nie odpowiada wynikom Prettier. Te rozbieżności są wyjaśnione poniżej.
Prettier nie usuwa cudzysłowów z niektórych właściwości obiektów, które są poprawnymi identyfikatorami JavaScript.
Dział zatytułowany „Prettier nie usuwa cudzysłowów z niektórych właściwości obiektów, które są poprawnymi identyfikatorami JavaScript.”Prettier i Biome usuwają cudzysłowy z właściwości obiektów i klas, które są poprawnymi identyfikatorami JavaScript. Prettier usuwa cudzysłowy tylko z poprawnych identyfikatorów ES5.
To jest starsze ograniczenie w ekosystemie, gdzie ES2015 jest teraz powszechnie stosowany. Dlatego zdecydowaliśmy się tutaj rozejść, usuwając cudzysłowy ze wszystkich poprawnych identyfikatorów JavaScript w ES2015+.
Możliwym obejściem byłoby wprowadzenie konfiguracji do ustawienia wersji ECMAScript używanej przez projekt.
Moglibyśmy wtedy dostosować zachowanie usuwania cudzysłowów na podstawie tej wersji.
Ustawienie wersji ECMAScript na ES5 mogłoby odpowiadać zachowaniu Prettier.
const obj = { 'a': true, b: true, "𐊧": true,}Różnica
const obj = { a: true, b: true, "𐊧": true, 𐊧: true,};Prettier ma niespójne zachowanie dla przypisań w obliczanych kluczach.
Dział zatytułowany „Prettier ma niespójne zachowanie dla przypisań w obliczanych kluczach.”Prettier i Biome otaczają niektóre wyrażenia przypisania nawiasami, szczególnie w warunkach. Pozwala to Biome zidentyfikować wyrażenie, które powinno być porównaniem.
Prettier ma niespójne zachowanie, ponieważ dodaje nawiasy dla przypisania w obliczanym kluczu właściwości obiektu, ale nie robi tego dla obliczanego klucza właściwości klasy, jak pokazuje poniższy przykład:
Dane wejściowe
a = { [x = 0]: 1,}
class C { [x = 0] = 1}Różnica
a = { [(x = 0)]: 1, [x = 0]: 1,};
class C { [x = 0] = 1;}Aby być spójnymi, zdecydowaliśmy się rozejść i pominąć nawiasy. Alternatywnie moglibyśmy otoczyć dowolne przypisanie w obliczanym kluczu obiektu lub klasy.
Prettier dodaje końcowy przecinek do parametrów typu funkcji strzałkowych, nawet gdy nie jest to wymagane.
Dział zatytułowany „Prettier dodaje końcowy przecinek do parametrów typu funkcji strzałkowych, nawet gdy nie jest to wymagane.”W niektórych szczególnych przypadkach lista parametrów typu funkcji strzałkowej wymaga końcowego przecinka, aby odróżnić ją od elementu JSX. Gdy podany jest domyślny typ, ten końcowy przecinek nie jest wymagany. Tutaj rozbiegamy się z Prettier, ponieważ uważamy, że lepiej respektuje to pierwotny zamiar Prettier, który polegał na dodawaniu końcowego przecinka tylko wtedy, gdy jest to wymagane.
Dane wejściowe
<T = unknown>() => {};Różnica
<T = unknown,>() => {};<T = unknown>() => {};Prettier ma niespójne zachowanie dla nawiasowanych łańcuchów opcjonalnych z asercją nie-null
Dział zatytułowany „Prettier ma niespójne zachowanie dla nawiasowanych łańcuchów opcjonalnych z asercją nie-null”W TypeScript operator asercji nie-null ! pozwala na stwierdzenie, że wartość nie jest null.
Gdy jest stosowany na łańcuchu opcjonalnym, asercja dotyczy całego łańcucha niezależnie od obecności nawiasów,
czyniąc równoważnymi (a.?.b)! i a.?.b!.
Poprzednie przykłady kodu są już dobrze sformatowane, według Prettier. Prettier jest używany do wymuszenia obecności lub braku nawiasów. Wygląda to na utraconą okazję do normalizacji kodu.
Ponadto Prettier nie usuwa nawiasów nawet wtedy, gdy otaczają one asercję nie-null. Zamiast tego przenosi operator na zewnątrz nawiasów.
Dane wejściowe:
a.?.b!(a.?.b)!(a.?.b!)Różnica
a.?.b!(a.?.b)!a.?.b!(a.?.b!)a.?.b!Prettier formatuje nieprawidłowe składnie
Dział zatytułowany „Prettier formatuje nieprawidłowe składnie”Parsowanie oparte na Babel w Prettier dla JavaScript i TypeScript jest bardzo luźne i pozwala ignorować wiele błędów. Parser Biome jest celowo bardziej restrykcyjny niż parser Prettier. Poprawnie identyfikuje następujące błędy składniowe:
- Funkcja nie może mieć duplikatów modyfikatorów
- nieprawidłowa kolejność modyfikatorów właściwości
- Deklaracje funkcji nie mogą mieć ciał
- klasy nie-abstrakcyjne nie mogą mieć właściwości abstrakcyjnych
- Łańcuch opcjonalny nie może być przypisany
- Modyfikator
constnie może być ustawiony na parametrze typu interfejsu - return na najwyższym poziomie
- itp.
W Prettier te błędy nie są uważane za błędy parsowania, a AST jest nadal budowane “poprawnie” z odpowiednimi węzłami. Podczas formatowania Prettier traktuje te węzły jako normalne i formatuje je odpowiednio.
W Biome błędy parsowania skutkują węzłami Bogus, które mogą zawierać dowolną liczbę poprawnych węzłów, nieprawidłowych węzłów i/lub surowych znaków.
Podczas formatowania Biome traktuje węzły bogus jako faktycznie zwykły tekst, drukując je dosłownie do wynikowego kodu bez żadnego formatowania, ponieważ próba ich sformatowania mogłaby być nieprawidłowa i spowodować zmiany semantyczne.
Dla właściwości klas obecna strategia parsowania Prettier również używa pól logicznych dla modyfikatorów, co oznacza, że tylko jeden z każdego rodzaju modyfikatora może być kiedykolwiek obecny (modyfikatory dostępności są przechowywane jako pojedynczy ciąg znaków). Podczas drukowania Prettier patrzy na listę wartości logicznych i decyduje, które modyfikatory wydrukować ponownie. Biome zamiast tego przechowuje listę modyfikatorów, co oznacza, że duplikaty są zachowywane i mogą być analizowane (stąd komunikaty o błędach parsowania dotyczące duplikatów modyfikatorów i kolejności). Podczas drukowania węzłów bogus ta lista jest zachowywana nienaruszona, a drukowanie niesformatowanego tekstu powoduje, że te modyfikatory nadal istnieją.
Istnieją sposoby, w jakie Biome może to rozwiązać. Jedną z możliwości jest próba interpretacji węzłów Bogus podczas formatowania i konstruowania z nich poprawnych węzłów. Jeśli można zbudować poprawny węzeł, to po prostu sformatowałby ten węzeł jak normalnie, w przeciwnym razie drukuje tekst bogus dosłownie, jak robi to obecnie. Jednak jest to nieuporządkowane i wprowadza formę logiki parsowania do formatera, która nie jest znacząca.
Inną opcją jest wprowadzenie jakiejś formy “składniowo poprawnego węzła bogus” do parsera, który akceptuje tego rodzaju czysto semantyczne błędy (duplikaty modyfikatorów, właściwości abstrakcyjne w klasach nie-abstrakcyjnych).
Kontynuowałby budowanie węzłów jak normalnie (efektywnie dopasowując zachowanie w Prettier), ale przechowywałby je wewnątrz nowego rodzaju węzła bogus, wraz z diagnostyką.
Podczas formatowania te szczególne węzły bogus po prostu próbowałyby sformatować wewnętrzny węzeł, a następnie wrócić do poprzedniego stanu, jeśli wystąpi błąd (istniejące narzędzie format_or_verbatim już by to robiło).
To utrzymuje logikę parsowania i formatowania oddzielnie od siebie, ale wprowadza więcej złożoności do parsera, pozwalając na uznanie nieprawidłowych stanów za częściowo prawidłowe.
Duplikaty modyfikatorów we właściwościach klas
Dział zatytułowany „Duplikaty modyfikatorów we właściwościach klas”Dane wejściowe
// Multiple accessibility modifiersclass Foo { private public a = 1;}
// Declare function with bodydeclare function foo ( ) { }
// Invalid use of abstractclass Bar { abstract foo ;}
// Duplicate Readonlyclass Read { readonly readonly x: number;}Różnica
// Wiele modyfikatorów dostępnościclass Foo { private public a = 1; private a = 1;}
// Deklaracja funkcji z ciałemdeclare function foo ( ) { }declare function foo() {};
// Nieprawidłowe użycie abstractclass Bar { abstract foo ; abstract foo;}
// Duplikat Readonlyclass Read { readonly readonly x: number; readonly x: number;}Przypisanie do łańcucha opcjonalnego
Dział zatytułowany „Przypisanie do łańcucha opcjonalnego”Dane wejściowe
(a?.b) = c;Różnica
a?.b = c;(a?.b) = c;Nieprawidłowy modyfikator dla parametrów typu interfejsu
Dział zatytułowany „Nieprawidłowy modyfikator dla parametrów typu interfejsu”Dane wejściowe
interface L<in const T> {}Różnica
interface L<const in T> {}interface L<in const T> {}Return na najwyższym poziomie
Dział zatytułowany „Return na najwyższym poziomie”return someVeryLongStringA && someVeryLongStringB && someVeryLongStringC && someVeryLongStringDreturn someVeryLongStringA && someVeryLongStringB && someVeryLongStringC && someVeryLongStringDreturn ( someVeryLongStringA && someVeryLongStringB && someVeryLongStringC && someVeryLongStringD);Błędne auto-inkrementacja i auto-dekrementacja
Dział zatytułowany „Błędne auto-inkrementacja i auto-dekrementacja”Dane wejściowe
(1)++;1++;(1)++;Użycie modyfikatora abstract w klasach nie-abstrakcyjnych
Dział zatytułowany „Użycie modyfikatora abstract w klasach nie-abstrakcyjnych”Dane wejściowe
class C { abstract f() : number;}Różnica
class C { abstract f(): number; abstract f() : number;}Prettier ma niespójności między parsowaniem TypeScript i Babel
Dział zatytułowany „Prettier ma niespójności między parsowaniem TypeScript i Babel”Prettier wspiera wiele różnych parserów dla kodu JavaScript i TypeScript, z których wszystkie mają być kompatybilne ze specyfikacją estree. W większości przypadków Prettier używa Babel jako domyślnego parsera dla kodu JavaScript, ale podczas parsowania TypeScript najpierw próbuje użyć własnego parsera TypeScript i dopiero potem wraca do Babel z włączonym TypeScript. Chociaż parser TypeScript jest ogólnie kompatybilny z estree, nie jest dokładny, i może to prowadzić do pewnych niespójności, które wpływają na wynik tworzony przez Prettier. Ogólnie rzecz biorąc, są to uważane za błędy w samym Prettier, ponieważ wynik powinien być taki sam niezależnie od użytego parsera.
Biome implementuje własne parsowanie, które obsługuje wszystkie formy kodu JavaScript i TypeScript, co oznacza, że nie powinno być żadnych niespójności między nimi. Jednak podczas migracji bazy kodu TypeScript z Prettier do Biome możliwe jest, że niektóre formatowanie będzie wydawać się zmienione z powodu tych rozbieżności między parserami z Prettier.
Te przypadki nie są uważane za błędy lub niezgodności w Biome. Jeśli sformatowany kod wygląda inaczej tylko przy użyciu ustawienia parsera typescript w Prettier, ale pasuje przy użyciu babel i/lub babel-ts, to Biome uważa wynik za kompatybilny.
Jako przykład, rozważ ten przypadek, sformatowany przy użyciu Biome i Prettier 3.1.0 z parserem typescript:
Dane wejściowe
function someFunctionName( someLongBreakingParameterName, anotherLongParameterName,) { return isEqual(a?.map(([t, _]) => t?.id), b?.map(([t, _]) => t?.id));}Różnica
function someFunctionName( someLongBreakingParameterName, anotherLongParameterName,) { return isEqual(a?.map(([t, _]) => t?.id), b?.map(([t, _]) => t?.id)); return isEqual( a?.map(([t, _]) => t?.id), b?.map(([t, _]) => t?.id), );}Prettier z parserem TypeScript wybiera zapisanie wywołania isEqual w jednej linii, podczas gdy Biome dopasowuje wynik Prettier z parserami babel i babel-ts. W związku z tym nie jest to uważane za niezgodność z Biome, lecz za błąd w Prettier.
Copyright (c) 2023-present Biome Developers and Contributors.