Prettier와의 차이점
일부 경우에서, Biome는 정해진 방식으로 코드를 형식화하여 Prettier의 출력과 일치하지 않도록 의도적으로 결정했습니다. 이러한 차이점은 아래에 설명되어 있습니다.
Prettier는 유효한 자바스크립트 식별자인 객체의 일부 속성에서 따옴표를 제거하지 않습니다
섹션 제목: “Prettier는 유효한 자바스크립트 식별자인 객체의 일부 속성에서 따옴표를 제거하지 않습니다”Prettier와 Biome 모두 유효한 자바스크립트 식별자인 객체 및 클래스의 속성에서 따옴표를 제거합니다.
다만, Prettier는 단지 ES5 유효 식별자에 대한 따옴표만 제거합니다.
이는 이미 광범위하게 사용되고 있는 ES2015 기반 환경에서 오래된 제약 조건입니다. 따라서 우리는 이 점에서 벗어나, 모든 유효한 자바스크립트 식별자(ES2015 이상)에 대해 따옴표를 제거하기로 결정했습니다.
하나의 해결 방법은 프로젝트가 사용하는 ECMAScript 버전을 설정할 수 있는 옵션을 도입하는 것입니다. 이를 통해 따옴표 제거 동작을 해당 버전에 따라 조정할 수 있습니다. ES5로 설정하면, Prettier의 동작과 일치시킬 수 있습니다.
const obj = { 'a': true, b: true, "𐊧": true,}Diff
const obj = { a: true, b: true, "𐊧": true, 𐊧: true,};Prettier는 계산된 키에서 할당 연산자의 동작이 일관되지 않습니다
섹션 제목: “Prettier는 계산된 키에서 할당 연산자의 동작이 일관되지 않습니다”Prettier와 Biome는 조건문 내에서 일부 할당 표현식을 괄호로 둘러싸며, 이는 Biome가 반드시 비교 연산임을 식별하도록 해줍니다.
그러나 Prettier는 일관성이 부족합니다. 객체 속성의 계산된 키에서 할당 연산 시 괄호를 추가하지만, 클래스 속성의 계산된 키에서는 그렇지 않으며, 다음 예제에서 이를 확인할 수 있습니다:
입력
a = { [x = 0]: 1,}
class C { [x = 0] = 1}Diff
a = { [(x = 0)]: 1, [x = 0]: 1,};
class C { [x = 0] = 1;}일관성을 위해, 우리는 괄호를 생략하는 방향으로 차이를 두기로 결정했습니다. 대안적으로, 객체나 클래스의 계산된 키 내 모든 할당을 괄호로 둘러싸는 것도 가능합니다.
Prettier는 화살표 함수의 타입 매개변수에 필요하지 않은 경우에도 마지막 쉼표를 추가합니다
섹션 제목: “Prettier는 화살표 함수의 타입 매개변수에 필요하지 않은 경우에도 마지막 쉼표를 추가합니다”특정 경우에, 화살표 함수의 타입 매개변수 목록은 JSX 요소와 구분하기 위해 마지막 쉼표가 필요할 수 있습니다. 기본 타입이 제공될 경우, 이 마지막 쉼표는 불필요합니다.
이 경우, 우리는 비록 필요 없지만 더 잘 설계된 원래의 의도를 존중하기 위해, Prettier와 차이를 둡니다.
입력
<T = unknown>() => {};Diff
<T = unknown,>() => {};<T = unknown>() => {};Prettier는 선언되지 않은 null 값을 가진 선택적 체이닝 연산자에서 일관되지 않은 동작을 보입니다
섹션 제목: “Prettier는 선언되지 않은 null 값을 가진 선택적 체이닝 연산자에서 일관되지 않은 동작을 보입니다”_타입스크립트_에서, !는 non-null assertion 연산자로, 값이 null이 아니라고 주장합니다.
선택적 체이닝에 적용될 때, 이 주장은 괄호 여부와 무관하게 전체 체이닝에 적용되므로 (a.?.b)!와 a.?.b!는 동등합니다.
이전 예제는 모두 이미 잘 포맷팅되었으며, 이것은 모두 Prettier의 규칙에 의해 강제되는 괄호의 존재 또는 누락입니다.
이런 상황은 코드 정규화의 좋은 기회를 놓치고 있다고 볼 수 있습니다.
또한, ! 연산자가 괄호 안에 있어도, Prettier는 괄호를 삭제하고 대신 연산자를 괄호 외부로 이동시킵니다.
입력:
a.?.b!(a.?.b)!(a.?.b!)Diff
a.?.b!(a.?.b)!a.?.b!(a.?.b!)a.?.b!Prettier는 잘못된 구문을 형식화합니다
섹션 제목: “Prettier는 잘못된 구문을 형식화합니다”Prettier는 자바스크립트와 타입스크립트에 대해 벨트 기반의 구문 분석이 매우 관대하며, 여러 가지 오류를 무시하도록 허용합니다. 여기에서 확인할 수 있습니다.
Biome의 구문 분석기는 의도적으로 Prettier보다 더 엄격합니다. 다음과 같은 구문 오류를 정확히 식별합니다:
- 함수에는 중복된 수정자가 될 수 없습니다
- 속성 수정자들의 순서가 잘못되었습니다
- 함수 선언에는 본문이 있을 수 없습니다
- 추상 클래스가 아닌 클래스에는 추상 속성이 있을 수 없습니다
- 선택적 문자열에 할당할 수 없습니다
- 인터페이스의 타입 매개변수에
const수정자를 설정할 수 없습니다 - 최상위 반환값
- 기타 등등
Prettier에서는 이러한 오류가 구문 분석 오류로 간주되지 않으며, 적절한 노드로 정상적인 구문 트리가 생성됩니다.
형식화 과정에서 Prettier는 이러한 노드들을 일반 노드처럼 처리하고, 정상적으로 형식화합니다.
반면, Biome에서는 구문 분석 오류로 인해 Bogus 노드가 생성되며, 이 노드는 유효한 노드, 유효하지 않은 노드, 그리고 처리되지 않은 문자를 포함할 수 있습니다.
형식화 시 Biome는 이러한 Bogus 노드를 평문 텍스트처럼 취급하여, 형식화 없이 그대로 코드에 삽입합니다. 왜냐하면 형식화하려 시도하면 의미론적 변화를 초래할 수 있기 때문입니다.
클래스 속성의 경우, 현재 Prettier의 구문 분석 전략은 수정자에 부울 필드를 사용하기 때문에, 각 유형의 수정자는 하나씩만 존재할 수 있습니다 (접근성 수정자는 하나의 문자열로 저장됩니다). 출력 시, Prettier는 부울 리스트를 확인하고 어떤 수정자를 다시 출력할지 결정합니다. 반면, Biome는 수정자 리스트를 유지하므로, 중복 항목이 보존되며 이를 분석할 수 있습니다 (결과적으로 중복 수정자 및 순서 오류에 대한 분석 오류 메시지가 발생합니다).
Bogus 노드를 출력할 때, 이 리스트는 그대로 유지되며, 평문 텍스트로 출력 시에도 해당 수정자가 여전히 존재합니다.
Biome는 이 문제를 여러 가지 방식으로 다룰 수 있습니다. 하나의 가능성은 Bogus 노드를 형식화할 때 해석을 시도하고, 이들로부터 유효한 노드를 구성하는 것입니다. 만약 유효한 노드를 생성할 수 있다면, 정상적으로 형식화하고, 그렇지 않다면 현재와 같이 텍스트를 그대로 출력합니다. 그러나 이는 혼란스럽고, 형식화기 내부에 분석 로직을 도입하는 것이 의미가 없습니다.
다른 옵션은 구문 분석기에 “구문적으로 유효한 잘못된 노드”라는 특정 타입을 도입하는 것입니다. 이는 순수한 의미적 오류(중복 수정자, 추상 클래스에 추상 속성 등)를 수용하도록 합니다.
여전히 정상적인 방식으로 노드를 생성하되, 이 오류들을 새로운 종류의 Bogus 노드 안에 포함시키며, 함께 진단 정보도 포함합니다.
형식화 시, 이 특수 Bogus 노드는 내부 노드만 형식화하려 하며, 오류가 발생하면 되돌아갑니다 (기존의 format_or_verbatim 유틸리티가 이미 이를 수행합니다).
이 방식은 구문 분석 로직과 형식화 로직을 분리하지만, 구문 분석기 내에서 유효하지 않은 상태를 부분적으로 유효한 것으로 간주하도록 복잡성을 더합니다.
클래스 속성의 중복 수정자
섹션 제목: “클래스 속성의 중복 수정자”입력
// 접근성 수정자가 중복됨class Foo { private public a = 1;}
// 본문이 있는 함수 선언declare function foo ( ) { }
// abstract의 잘못된 사용class Bar { abstract foo ;}
// readonly 중복class Read { readonly readonly x: number;}Diff
// 접근성 수정자가 중복됨class Foo { private public a = 1; private a = 1;}
// 본문이 있는 함수 선언declare function foo ( ) { }declare function foo() {};
// abstract의 잘못된 사용class Bar { abstract foo ; abstract foo;}
// readonly 중복class Read { readonly readonly x: number; readonly x: number;}선택적 문자열에 할당
섹션 제목: “선택적 문자열에 할당”입력
(a?.b) = c;Diff
a?.b = c;(a?.b) = c;인터페이스의 타입 매개변수에 잘못된 수정자
섹션 제목: “인터페이스의 타입 매개변수에 잘못된 수정자”입력
interface L<in const T> {}Diff
interface L<const in T> {}interface L<in const T> {}최상위 반환
섹션 제목: “최상위 반환”return someVeryLongStringA && someVeryLongStringB && someVeryLongStringC && someVeryLongStringDreturn someVeryLongStringA && someVeryLongStringB && someVeryLongStringC && someVeryLongStringDreturn ( someVeryLongStringA && someVeryLongStringB && someVeryLongStringC && someVeryLongStringD);잘못된 증감 연산자
섹션 제목: “잘못된 증감 연산자”입력
(1)++;1++;(1)++;비추상 클래스에서 abstract 수정자 사용
섹션 제목: “비추상 클래스에서 abstract 수정자 사용”입력
class C { abstract f() : number;}Diff
class C { abstract f(): number; abstract f() : number;}Prettier는 타입스크립트 구문 분석기와 벨트 구문 분석기 사이에 일관성이 부족합니다
섹션 제목: “Prettier는 타입스크립트 구문 분석기와 벨트 구문 분석기 사이에 일관성이 부족합니다”Prettier는 자바스크립트 및 타입스크립트 코드에 대해 서로 다른 구문 분석기를 지원하며, 모두 estree spec 언어 사양에 호환됩니다. 대부분의 경우, Prettier는 자바스크립트 코드에 벨트를 기본 구문 분석기로 사용하지만, 타입스크립트 코드를 분석할 때는 먼저 자체 타입스크립트 분석기를 시도하고, 실패하면 타입스크립트 활성화된 벨트로 돌아갑니다.
비록 타입스크립트 구문 분석기가 일반적으로 estree에 호환되지만, 정확하지는 않으며, 이로 인해 일부 일관성 없는 결과가 발생할 수 있습니다. 이는 일반적으로 Prettier 자체의 결함으로 간주되며, 사용하는 구문 분석기와 무관하게 출력은 동일해야 합니다.
Biome는 자체 구문 분석기를 구현하여 자바스크립트 및 타입스크립트 코드의 모든 형태를 처리하므로, 두 시스템 간 일관성 문제가 발생하지 않아야 합니다. 그러나 Prettier에서 Biome로 타입스크립트 코드베이스를 마이그레이션할 때, Prettier의 구문 분석기 간 차이로 인해 일부 형식이 바뀐 것처럼 보일 수 있습니다.
이러한 경우는 Biome의 오류나 호환성 문제로 간주하지 않습니다. 만약 Prettier의 typescript 분석기 설정을 사용했을 때만 포맷이 다르게 보이고, babel 및/또는 babel-ts를 사용하면 일치한다면, Biome는 이 출력이 호환된다고 판단합니다.
예를 들어, 다음과 같은 경우를 고려해보세요. Biome와 Prettier 3.1.0을 사용해 typescript 분석기를 이용하여 포맷팅한 결과입니다:
입력
function someFunctionName( someLongBreakingParameterName, anotherLongParameterName,) { return isEqual(a?.map(([t, _]) => t?.id), b?.map(([t, _]) => t?.id));}Diff
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), );}TypeScript 분석기를 사용할 때, Prettier는 isEqual 호출을 한 줄로 작성하는 것을 선택하는 반면, Biome는 babel 및 babel-ts 분석기와 일치하는 출력을 만들어냅니다. 따라서 이는 Biome의 호환성 문제로 간주되지 않으며, 오히려 Prettier의 문제입니다.
Copyright (c) 2023-present Biome Developers and Contributors.