これまでも個別に調べたりしてるけど、総まとめ的なものを書いておく。
バージョンは3.8.3時点。(ちなみにもうすぐ3.9.0が出るらしい)
xxx-in-yyy
JSの中にCSSが埋め込まれてるケースを、CSS-in-JSと呼ぶ。
それぞれ言語別に見ていく。
JS / TS
- scss-in-js / scss-in-ts
- styled-components,
css/styled,<style jsx>, css prop,@Component({ styles })
- styled-components,
- graphql-in-js / graphql-in-ts
gql/graphqltag,graphql.experimentaltag,graphql()call,/* GraphQL */comment
- html-in-js / html-in-ts
htmltag,/* HTML */comment
- angular-in-js / angular-in-ts
@Component({ template })
- markdown-in-js / markdown-in-ts
md/markdowntag(${}があると対象外)
ちなみに/* XXX */系のコメントはスペース込みの完全一致で判定される。
JSON / JSONC / JSON5 / JSONStringify
- (no op)
GraphQL
- (no op)
CSS / LESS / SCSS
- yaml-in-css / yaml-in-less / yaml-in-scss
- front matter
- toml-in-css / toml-in-less / toml-in-scss
- front matter
誰がそんな使い方するんやって思ったら、Jekyll系か・・・。
---で囲われてたらYAML、+++で囲われてたらTOMLになる。
ただ、TOMLはデフォルトでサポートされていないので、そのまま出力される。(TOMLプラグインがあったりして、inferできたらフォーマットされる)
ちなみに、このfront matterの挙動は、言語ごとに実装されてるというより、いくつかの言語向けに根本で入ってる。
YAML
- json-in-yaml
.prettierrc/.stylelintrc/.lintstagedrcのみ
そもそもYAMLって、JSONそのまま書いても合法って知らんかった。
この3ファイルだけは特別なフローで処理される。
- まずは、ファイル名からYAMLとして分類されてパースされる
- しかし、print時にembedの仕組みで全文を
parser:jsonでフォーマットし直そうとする - JSONとして解釈できたらJSON、できなかったら、
parser:yamlの結果として出力される
なぜならこのファイルたちは、JSON/YAMLどっちで書いてもいいとされてる厄介なやつだから・・・。
Markdown
- ANYTHING-in-markdown
- fenced code block
- yaml-in-markdown / toml-in-markdown
- front matter
Markdownも、inferできるものならコードブロックになんでも書ける。
front matterの挙動はCSSに書いた先述のとおり。
MDX
- ANYTHING-in-mdx
- fenced code block
- yaml-in-mdx / toml-in-mdx
- front matter
- js-in-mdx
- import / export statement
- js-in-mdx
- JSX expr
基本はMarkdownと同じで、import/exportがありJSXのコンポーネントがあるくらい。
HTML
- css-in-html
<style>/style=""attr
- less-in-html / scss-in-html
<style lang=...>
- js-in-html
<script>(src属性があると対象外),on*=""event handler attr(既知のイベント属性のみ)
- ts-in-html
<script lang=ts>/type=application/x-typescript
- markdown-in-html
<script type=text/markdown>
- json-in-html
<script type=*json | importmap | speculationrules>
- glimmer-in-html
<script type=text/x-handlebars-template>
- html-in-html
<script type=text/html>
- ANYTHING-in-html
<script lang>/<style lang>
- yaml-in-html / toml-in-html
- front matter
わけがわからん。
なお、このHTML系の埋め込み(<script>のtype/lang、<style>のlang、front matter)は、html / vue / angular / lwc / mjml の5パーサーで実装が共有されてる。なので、ここに挙げたものは以降のHTML系でも全部使える。
ちなみに、言語の埋め込みではないけど、同じembedの仕組みでこれらもフォーマットされる。
<img srcset>: 行ごとに分割して右揃えclass属性: 不要な空白を削る<iframe allow>Permissions Policyを;区切りで整形
ただembed全体はembeddedLanguageFormatting: "off"で一括無効になるので、これらの属性整形やfront matterのフォーマットも一蓮托生になってる。
Angular
- ANYTHING-in-angular
基本的にHTMLと同上だが、expr部で使われる独自のやつがある。
__ng_interpolation{{ expr }}
__ng_action(click)=""/on-*=""
__ng_binding[target]=""/bind-*=""/[(target)]=""/bindon-*=""- AngularJS 1.x風の
ng-if/ng-show/ng-hide/ng-class/ng-style、@letRHS
__ng_directive*directive=""- 新しめのcontrol flow block(
@if/@else if/@for/@switch/@case)のパラメータ部も
微調整されたJS/TSって感じ。
あとi18n属性は、言語じゃなくただの長文テキストとして整形される。
Vue
- ANYTHING-in-vue
- HTMLと同上
- 加えてSFCトップレベルのcustom blockの
<xxx lang>
- js-in-vue / ts-in-vue
v-forLHS,v-slotbindings,<script setup>attr,<style vars>attr
- ts-in-vue
<script generic="">attr
一部のexpr(v-for leftとか)は、function _(...) {}みたいなプレースホルダーに埋めて通常のJS/TSパイプラインでフォーマットされるが、それとは別の独自のやつもある。
__vue_expression/__vue_ts_expression:prop=""/.prop=""/v-bind:prop=""、{{ expr }}
__vue_event_binding/__vue_ts_event_binding@click=""/v-on:click=""(まず普通のexprとして試して、構文エラーならこっちにフォールバック)
__js_expression/__ts_expression- その他の
v-*ディレクティブ、v-forのright
- その他の
JS/TSどっちのパーサー系統になるかは、SFC内に<script lang="ts">があるかどうかで一括で決まる。
LWC
- ANYTHING-in-lwc
- HTMLと同上
MJML
- ANYTHING-in-mjml
- HTMLと同上
- css-in-mjml
<mj-style>
使ってる人いるんか?
Glimmer (Handlebars)
- css-in-glimmer
<style>tag, lang=css/empty
- yaml-in-glimmer / toml-in-glimmer
- front matter
xxx-in-yyy-in-zzz
無段階にネストできるが、実用的には数段階で歯止めがかかるはず。 エスケープしまくってまで書きたくないと思うし。
ただ明示的にガードが入ってるものもある。
たとえばcss-in-html-in-jsは、<style>はフォーマットされるけど、style=""はフォーマットされない。
実装的には、style="" / on*="" / class / iframeのallowといった属性系には、!options.parentParserならっていうガードが入ってる。つまりHTML属性系は、親パーサーがいないトップレベルの時(xxx-in-htmlまで)だけフォーマットされる。
あと、これらの属性は値に{{を含む場合もスキップされる。テンプレートエンジンのinterpolationを壊さないためっぽい。
まとめ
つまり、Prettier互換ですって言うのはすごく大変ってこと。