お知らせ

現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。
投稿日:
言語::MarkdownNode.js::Prettier

PrettierというとTypeScriptやJavaScriptのコードを整形するツールであって、Markdownを整形するツールという意識のある人は少ないかもしれない。

それが実はPrettierはMarkdownの整形に使えるのだ。例えば次の図はPrettierを使ってMarkdownを整形しているシーンを撮ったものだ。具体的にはnpx prettier -w hoge.mdした時の前後を撮っている。

prettier-md.gif

これは別の文書からコピペしたなどで書式が崩れることや単純なtypoでズレたりしたときに地味に効果を発揮してくれる。Prettier v2以前だと全角と半角文字の間の自動スペース挿入が嬉しかった人もいるだろうが、v3でこの機能は廃止されている。

他にもチーム開発だとMarkdownの細かい仕様に熟知した人は多くないので、最低限の書式を合わせるのにも役立つだろう。Markdownのインデント幅を変えたいが、コードフェンスの中のインデントは変えたくないみたいな用途みたいなカスタマイズもできるし、個人用途でも結構使える。

しかしフォーマッタの挙動がアップデートでころころ変わるのは割と不快なので、この辺りは何とかなってほしい気もする。

prettier-vscodeをPrettier v3に対応させる試みは既にあるのだが、PRがRejectされている。レビューを見た感じtypoが原因っぽいのでtypoを直してみたのだがテストが落ちる

まずテストが通らない原因だが、これはアサーション結果とテストコード、落ちた時の結果を三点比較したときに落ちた時の結果がプラグインを反映せずprettier -wされたものであることに気が付いた。次にこの解決法だが、原因が複数あるため、その切り分けが必要だと考えている。これはテスト走行時に指定されたプラグインが正しく読み込まれていればテストを通すことができるはずだが、package.jsonの指定通りインストールされているのかや、prettier側が正常に読み込めているのかなどが解らないと、真の原因が突き止められないためだ。

見た感じ割と面倒な仕組みなテストを実行してそうだったので一旦私は諦めることにした。これはprettier v3を個別にインストールすることで基本的に解決できるためだ。とはいえ、インストールできない条件だとその回避に苦労するのでネイティブに対応してほしい思いはあるが、検証するにも本家リポジトリにPRを向けないとテスト走行すらままならず、ローカルでの検証が極めて厳しいので、正直やる気が起きない。ぶっちゃけDockerで個別テストケースごとにビルドしてやるのが一番仕組みが単純で安全に思うが、敷くほど遅くなりそうだし、イメージのメンテも大変そうだし、アホほどイメージができるはずなので、容量も食いそうでなかなか微妙そうである。

そもそも論としてmainブランチを引っ張ってきてビルドすると、この時点で型エラーを吐くので、ここからどうにかしていく必要があり、割としんどい作業になりそうだ。

投稿日:
言語::MarkdownNode.js::Prettier

2023-07-05にPrettier 3.0がリリースされて大分経ったので個人的に気になったところのまとめ。Highlights~Breaking Changesの部分だけ書いてます。Other Changesまで書くと長すぎるので…(Other Changesはざっと見た感じ概ね可読性が向上する感じの内容だと思いましたが、行が動くので書き方によってはコードや、行ベースで動作しているCIの検査が壊れる可能性が場合によってはありそうです。)

変更点

気になったところしか書いてないのでちゃんと見たい人は本家のリリースノートを見てください

日本語と西洋文字の間に半角スペースを挿入しない様に

Stop inserting spaces between Chinese or Japanese and Western characters

恐らく日本人的にはこれが今回最大の変更点になると思います。かなり賛否両論があると思いますが、私は肯定派です。むしろ半角スペースが入るようになって以来、これが来る日をずっと待ちわびていた。

今までは全角文字と半角文字の間に半角スペースが挿入されていましたが、これがなくなります。これは可読性の観点では悪くない処理だとは思うのですが、ドキュメントとしてみた場合に検索性が非常に悪く個人的に好きではありませんでした。

<!-- 入力値 -->
漢字
Alphabetsひらがな12345カタカナ67890

<!-- Prettier 2.8 -->
漢字 Alphabets ひらがな 12345 カタカナ 67890

<!-- Prettier 3.0 -->
漢字Alphabetsひらがな12345カタカナ67890

なお、この変更には後方互換性がなく、今までのように半角スペースを挿入することはできなくなります。

また、新しい方式に既存のドキュメントを合わせる場合、Prettierが挿入したこの半角スペースを自動で綺麗に除去するのは難しいと思います。探せばあるかもしれませんが、恐らくtextlintの除去ルールでは不完全だと思います。私が試した限りでは見出しやリンクタイトル辺りでは上手く機能してくれませんでした。ただ、ある程度はtextlintが解決してくれるので、大まかにはtextlintを使い、細かい部分は手修正していくのが無難かなと思います。

設定ファイルでESM形式をサポート

https://prettier.io/blog/2023/07/05/3.0.0.html#support-config-files-in-esm-13130httpsgithubcomprettierprettierpull13130-by-fiskerhttpsgithubcomfisker

今まではCJS形式で書く必要があったconfigファイルでESM形式がサポートされたようです。但し拡張子が.jsである場合はpackage.json{"type": "module"}を指定する必要があります。

<!-- Prettier 2.8 -->
module.exports = {
  // ...
}

<!-- Prettier 3.0 -->
export default {
  // ...
};

trailingComma設定の初期値をes5からallに変更

https://prettier.io/blog/2023/07/05/3.0.0.html#change-the-default-value-for-trailingcomma-to-all-11479httpsgithubcomprettierprettierpull11479-by-fiskerhttpsgithubcomfisker-13143httpsgithubcomprettierprettierpull13143-by-sosukesuzukihttpsgithubcomsosukesuzuki

これは正直あまり良く解っていないのですが、Trailing Commasを読んだ感じ、恐らく関数の仮引数の最後にカンマが入るかどうかだと思われます。つまり以下のような感じです。

<!-- 入力値 -->
function HelloWorld(greeting = "hello", greeted = '"World"', silent = false, onMouseOver) {
}

<!-- trailingComma: es5 -->
function HelloWorld(
  greeting = "hello",
  greeted = '"World"',
  silent = false,
  onMouseOver // カンマが入らない
) {}

<!-- trailingComma: all -->
function HelloWorld(
  greeting = "hello",
  greeted = '"World"',
  silent = false,
  onMouseOver, // カンマが入る
) {}

上記の結果はこちらのPlaygroundで確認できます。(--trailing-commaの設定値をいじると比較できます)

個人的にはこの修正は余り歓迎できないですね…。というのも不要なカンマが入っていると引数の数を見間違えたり、ケアレスミスの原因になると思いますし、いたずらにコードが長くなるからです。

今まで深く考えたことがありませんでしたが、trailingCommanoneが例外がなく、統一感があり、コードが短くなり、ケアレスミスが減ると思うため無難だと思いました。noneにしている場合、単純に一番最後の要素にカンマが入らなくなります。

BabelパーサーからFlow構文のサポートを削除

https://prettier.io/blog/2023/07/05/3.0.0.html#remove-flow-syntax-support-from-babel-parser-14314httpsgithubcomprettierprettierpull14314-by-fiskerhttpsgithubcomfisker-thorn0httpsgithubcomthorn0

これによりPrettierの動作パフォーマンスが上がったようです。

デフォルトで.gitignoreのファイルを無視するように変更

https://prettier.io/blog/2023/07/05/3.0.0.html#ignore-gitignored-files-by-default-14731httpsgithubcomprettierprettierpull14731-by-fiskerhttpsgithubcomfisker

.prettierignoreではなく.gitignoreを見るようになったようです。これもなんとも微妙な変更だと思います…。どっちかといえば.eslintignoreを見てもらいたいような気もするような…。従前のようにしたい場合はCLIのオプションに--ignore-path=.prettierignoreを渡してやると行けるようです。

関連記事

投稿日:
Node.js::Prettier言語::Markdown

Prettier の次期 Major release である v3 がそろそろ出そうなので、今回含まれる大きな変更の一つについての記事です。

内容としてはMarkdown文書の全角文字と半角英数の間にスペースが挿入されなくなります。個人的にこれは相当賛否が分かれると思うので、v3が出た後の対処方法も併せて書いておきます。

変更点

以下は漢字Alphabetsひらがな12345カタカナ67890という文字列が渡された時の変化例です。

v3 より前 v3 以降
漢字 Alphabets ひらがな 12345 カタカナ 67890 漢字Alphabetsひらがな12345カタカナ67890

多分スペースが空いたときのほうが好みといった人が一定数居ると思います。個人的には検索性が破滅的に低下するためこの機能には否定的でしたが、今回のアップデートで遂にスペースが入らなくなったので、割と喜んでいます。ぶっちゃけこのブログの過去記事ににある余計なスペースも現在全て除去する作業をしてるくらいです。

発端としては次のIssueにあり、これをずっと追っていた人もきっといると思います。私もそのうちの一人です。

この問題は次のPullRequestで対応され、現在v3 用のブランチに取り込まれています。

  • [Markdown[next branch]: Do not insert spaces between Chinese/Japanese & latin letters #11597](https://github.com/prettier/prettier/pull/11597)

この機能を試したい場合、以下のようにインストールすることで試すことが出来ます。

npm i -D https://github.com/prettier/prettier.git#next

なお、PullRequestにある通り既にあるものには影響しないので、既にあるスペースを詰めることは出来ません。

これまで通りスペースを入れたい場合

引き続き漢字Alphabetsひらがな12345カタカナ67890漢字 Alphabets ひらがな 12345 カタカナ 67890にしたいケース

前述のPullRequestの中でも言及されていますが、textlintを利用することで対応できます。ただPrettierと併用するのは難しいと思います。

導入方法

textlintにtextlint-rule-preset-ja-spacingを組み合わせて対応します。

npm i -D textlint textlint-rule-preset-ja-spacing
使用方法

まず.textlintrcを作成し、次の設定を追加します。

{
    "rules": {
        "preset-ja-spacing": {
            "ja-space-between-half-and-full-width": {
                "space": "always"
            }
        }
    }
}

CLIを用いて修正する場合

npx textlint --fix <path>

VSCodeを用いる場合、vscode-textlintをインストールし、settings.jsonに以下の設定を追加することで利用することが出来ます。Prettierと併用する場合、Prettierと競合し、textlintが負けるため、Markdownの整形に関してはtextlintに任せるのが無難だと思います。

{
    "textlint.autoFixOnSave": true
}

一応Custom Local Formattersを使えば近いことは出来なくもないのですが、ファイルへの書き込み同期がズレてるのか上手く動かない気がします。一応以下の設定で動くことには動きますが、あんま期待しないほうがいいかも…。拡張からコマンドを蹴ること自体セキュリティ的に微妙な気がするので、この方法は使わないのが無難な気がします。

{
    "[markdown]": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "jkillian.custom-local-formatters"
    },
    "customLocalFormatters.formatters": [
        {
            "command": "npx prettier ${file} | npx textlint --stdin --stdin-filename ${file} --fix --format fixed-result",
            "languages": ["markdown"]
        }
    ]
}

これはPrettierとtextlintを呼ぶ時点で二重処理になって重いので、Markdownのフォーマットはどっちか片方にやらせたほうが良いでしょう。ただtextlintはデフォルトだと表の整形とかいい感じにしてくれないようなのが困りものです。textlintにmarkdownプラグインを入れると改善されるかもしれませんが試してないです。

これまでのスペースを消したい場合

既にある漢字 Alphabets ひらがな 12345 カタカナ 67890漢字Alphabetsひらがな12345カタカナ67890にしたいケース

前項の「これまで通りスペースを入れたい場合」で紹介した環境を導入し、.textlintrcを以下のように作成すればある程度は対応できます。但し完全には除去しきれません。

{
    "rules": {
        "preset-ja-spacing": {
            "ja-space-between-half-and-full-width": {
                "space": "never"
            }
        }
    }
}

この設定によるtextlintの整形結果は以下のとおりです。記号の前後の半角スペースや見出しやリンク文章にある半角スペースは除去しきれないようです。

 ## 123 あああ abc

-ユーザー ID は Slack アプリから
+ユーザー IDはSlackアプリから
 やよいの ○ 色申告
 [Web サイトの example](https://example.com)

余談

多分大抵の人はPrettierをv3にしてからこの事に気が付くと思います。当然です。一々 OSSのアップデートを追っている人は恐らくメンテナか物好きだからです。

現在Prettierのリポジトリでは似たような議論として、デフォルトでインデントにスペースを使っているところをタブに置き換えようという動きがあります。幸いv3では採用に至っていませんが、今後採用される可能性があり、メンテナであるSosuke Suzuki氏もこの事について以下の記事で憂慮されています。

ある日突然インデントのスペースがタブに変われば、苦情が出るのは明白です。設定一つ直すだけとは言え、影響は大きいでしょう。CIで全体にPrettierを掛けていないプロジェクトでは一時的にタブインデントのファイルとスペースインデントのファイルが混在するケースもあると思います。空白文字の差分表示や検出を無効化していると気づかないまま、そのような状態になるケースもあると思います。

個人的にはMarkdownに落とした時にどの環境でも同じように見えるという理由でスペースインデントが好みですが、A11yの観点からタブにせよという考えも理解は出来ます。でもブラウザで.mdファイル見たときとかに見づらいんですよね…。PythonPHPYAML の様に標準がスペースインデントの言語もありますし、個人的には標準はスペースで構わないのではと思ったりもします。(その上で必要に応じてタブを使うという選択肢は出来ると思います。Google JavaScript Style Guideでも、これが標準です。Prettierは代表的なフォーマッタで事実上のコーディング標準であるとも言えるため、社会的責務でそうする必要があるかもしれませんが、個人的な思いとしては標準はスペースのままであってほしいなぁと思います。どうしても標準が変わると、世間はそこに引きずられざれ得ないと思いますから。

多分今回の変更も、上記のuseTab同様に一部で非難が上がる気がしていて、余り大事にならなければいいなぁ…とか考えています。(タブと比べると影響範囲が狭いので、ひょっとしたら今回はあまり何も起きないかもですが…)