
해당 시리즈는 prettier plugin 을 개발하는 과정을 담습니다.
이번 글은 공식문서를 시작으로 앞서 언급한 글인 How to write a plugin for Prettier 를 분석하며, JS 기반의 prettier plugin을 개발하기 위한 근거를 찾는 과정을 담을 예정입니다.
prettier 공식문서 plugins 파트를 방문해보면 다음과 같은 섹션으로 나누어져 있습니다.

Prettier 플러그인은 다음과 같은 다섯 가지 export 또는 다음 속성을 가진 기본 export를 가진 일반적인 자바스크립트 모듈입니다:
해당 플러그인이 export 하는 요소들에 대해 각각의 깊은 이해가 필요하지만,
요약하자면 다음과 같습니다.
여기까지 설명했으면, 대략적으로 prettier의 plugin이 어떻게 동작하는지 확인할 수 있으리라 생각합니다.
요약하자면 아래와 같습니다.
parser를 통해서 파일을 읽고 AST로 변환합니다. -> printer를 통해 "적절한 규칙"을 바탕으로 출력합니다.
대략적으로 plugin 의 동작방식에 대해서 읽어보았으니, 눈으로 확인해볼 차례입니다.
prettier 공식문서에서는 해당 과정이 별도의 tutorial 과정이 제공되어 있지는 않지만, plugin 을 개발하기를 원하는 개발자에게 아래와 같은 링크를 제공합니다.

그렇다면 해당 링크로 이동해서 좀 더 탐구해보겠습니다.
해당 게시글에 대해서 궁금하신분은 제가 미리 번역한 글을 참고하시길 바랍니다.
해당 글을 정말정말 짧게 요약하자면 다음과 같습니다.
.toml 확장자를 가진 파일은 "TOML" (Tom's Obvious, Minimal Language) 언어로 작성된 설정 파일입니다. TOML은 구성 파일을 위해 만들어진 데이터 직렬화 언어로, 이는 JSON이나 YAML과 유사한 역할을 하지만, 더 읽기 쉽고 명확하게 작성될 수 있도록 설계되었습니다.
정말 짧게 요약해서 위와 같은 3줄로 작성할 수 있었습니다.
하지만 글을 읽는것에서 끝내면 안됩니다. 당연히 해당 원작자의 프로젝트를 clone해서 테스트를 해보며 구조를 파악해야만 합니다.
해당 프로젝트는 여기서 클론을 받아서 진행하실 수 있습니다.
저는 보통 프로젝트를 처음으로 확인할때, 특히나 plugin 과 같이 npm 에 올라간 프로젝트를 볼때는 package.json부터 읽어보는 편입니다.

prettier-plugin-toml/package.json
해당 프로젝트의 package.json의 main을 확인해보면, index.js가 눈에 보입니다.
바로 먼저 확인해봅니다!

조금 긴 부분은 제가 간략하게 접어두었습니다.
여기서는 prettier plugin의 module이 export하는 속성들에 집중하면 됩니다.
그러핟면 저희가 실행시킬 파일을 확인해보면,

일부 정렬이 되어 있지않고 indent가 들어가 있거나 배열의 규칙이 뒤죽박죽으로 보입니다.
하지만 본문의 parsing 과정을 거쳐, printer에서 정렬을 해준다면...?!

아래와 같이 정말 이쁘게 나오는것을 확인할 수 있습니다.
여기서 잠깐!! 코드를 변경해 error를 발생시켜 보겠습니다. 역으로 prettier 오픈소스를 추적해보며 어떤 에러인지 확인해보며 plugin의 구조를 이해해보겠습니다.
우선 해당 로직의 error를 일으킵니다.

그럼 다음과 같은 error를 cli에서 반환합니다.
아주 좋습니다 흔적을 찾았으니, 공식문서에서 따라가 보겠습니다.
바로 "No parser could be infeered for file:"을 검색합니다.
그럼 다음과 같은 함수를 찾을 수 있습니다.

아하 normalizeFormatOption 은 rawOptions를 확인해서 parser가 없다면 다음과 같은 에러를 반환하는 군요!
아주 흥미롭습니다.

저는 사실 module export 되는 속성들중 languages를 주석처리해 두었습니다
왜 languages를 주석처리했는데 parser가 없다는 에러가 반환되었을까요? 그건 공식문서에도 적혀있든, languages에서 입력받은 key를 이용해 parser와 연결되기 때문입니다.


parser의 키는 languages와 정확히 match 되어야만 합니다.
이정도면, 해당 plugin tutorial에 대해서 대략적인 이해는 충분히 했으리라 생각합니다.
그렇다면, JS로 변환하여 해당 로직을 동일하게 수행해보겠습니다.
우선 방금 진행한것과 같이, JS 파일을 해당 plugin 코드를 동작시키며 오류를 반환해보겠습니다.


방금과 동일하지만, 루트에 있는 example.js를 동작해봅니다.

어라라 꽤나 흥미롭습니다. 이번에는 language에 대해서 별도의 셋팅을 해주지 않아서 오류를 반환해야한다고 예상했지만 오류를 반환하지 않음을 확인했습니다.
공식문서에서도 language 작성에 대한 언급이 있었는데도 말이죠.
이는 극히 특이합니다. 이 경우에는 보통 멘붕이 오곤 합니다🤣

심지어 원래 코드에 세미콜론이 들어가며 아주 이쁘게? 포매팅됨을 확인할 수 있습니다.
순조롭게 진행되던 학습에 살짝 제동이 걸렸습니다.
이때는 주저하지 말고 이상하다 싶으면, 오픈소스로 떠나는 것이 극히 현명합니다.
이번에는 prettier 오픈소스의 구조를 탐색해 봅시다
앞서 prettier-plugin-toml 에 대한 예제를 분석할때도 마찬가지로 이번에도 package.json 부터 분석에 들어갑니다.

다시 module을 반환하는 src/index.js로 들어가봅시다.

코드의 하단의 export를 보면 어디서부터 찾아볼지 대충 시작점을 짐작할 수 있습니다.
우선 여러가지 요소가 반환되고 있지만, 유력해보이는 format에 대해서 분석해보겠습니다.

format은 formatWithCursor의 반환값인것을 확인할 수 있었습니다.
다른 예시지만 check라는 함수로 wrapping 되기도 하는군요 이는 module의 export값 내부에 있었습니다.
이번엔 formatWithCursor을 탐색해보겠습니다.

아하 formatWithCursor은 withPlugins에 대한 반환값이습니다. 해당 withPlugins는 Promise 객체를 resoluve 시킨 후 반환하고 있으니
loadBuiltinPlugins와 loadPlugins를 중심으로 분석하다보면, prettier의 동작원리를 확인할 수 있을것 같습니다.

loadBuiltinPlugins를 export 하는 파일을 찾아보았습니다만, 한번더 이동을 해보아야겠군요 "../../plugins/builtin-plugins-proxy.js" 로 이동해보겠습니다.
https://github.com/prettier/prettier/blob/main/src/plugins/builtin-plugins-proxy.js

아주 흥미롭습니다. 이제 문제가 어느정도 해결된듯 보입니다. prettier에는 내장된 기본 plugin 이 존재했습니다. 이는 수많은 종류의 함수로서 구현되어 있었으며, 우리가 JS 파일에 대해서 별도의 parser를 연결하지 않았음에도 동작할 수 있었던 것은, 다음과 같이 이미 정의된 plugin의 laguages 들이 parser들과 연결되어 있었기 때문입니다.
물론 해당 파일의 상단에서 plugin의 parser와 print를 연결시키는 createParserAndPrinters 함수를 확인할 수 있었습니다.

여기까지 prettier의 핵심적인 로직에 대해서 이해할 수 있었습니다.
그렇다면, 이런 결과를 도출할 수 있습니다.
- JS를 중심으로 prettier plugin을 개발할 때는 별도의 languages 설정을 하지 않는다ㅈ
- 기존에 진행된 toml언어는 prettier에 저의된 languages 속성이 없었으므로 직접 정의해 연결해주어야합니다.
- 다만, 이미 작성된 babel, babel-flow 등의 key에 대해서 별도의 parser를 연결해주어야 내가 원하는 print 로직을 수행시킬 수 있을것이다.
this contents is very helpful!