Slide 1

Slide 1 text

Prettier 3.0 の VSCode 拡張対応 における技術的な意思決定 ~ VSCode 拡張で dynamic import が動かない~ 2023/09/06 nihonbashi.js

Slide 2

Slide 2 text

自己紹介 SUZUKI Sosuke ● Ubie 株式会社プロダクト開発エンジニア ● サイボウズ フロントエンドエキスパートチーム ● 筑波大学 情報学群 情報科学類 B3 ● Prettier メンテナー ● Babel コミッター @__sosukesuzuki @sosukesuzuki

Slide 3

Slide 3 text

目次 1. Prettier 3.0 リリース 2. Prettier 3.0 はすべてが ECMAScript Modules 3. prettier-vscode における ECMAScript Modulesの問題 4. 言語サーバーの開発と挫折 5. worker_threads による回避策 5. まとめ

Slide 4

Slide 4 text

Prettier 3.0 リリース

Slide 5

Slide 5 text

2023年7月5日、実に3年以上ぶりとなるPrettierのメジャーリリース Prettier 3.0 リリース

Slide 6

Slide 6 text

Prettier 3.0 リリース trailingCommaのデフォルト値がes5からallに Internet Explorer のサポート終了により、主流のウェブブラウ ザのすべてがtrailingComma: allのJavaScriptを解釈できる ようになった。 日本語とラテン文字の間にスペースを入れない Markdownのフォーマットで、日本語 /中国語とラテン文字の 間にスペースを挿入しないようになった。今回のリリースの中 で最も賛否が分かれるであろう変更。 .gitignoreのファイルを無視する .prettierignoreだけでなく.gitignoreのファイルもデフォルトで 無視するように。 --ignore-path=.prettierignoreをつければ今 まで通り。 *.d.tsファイルが本体パッケージに含まれる ライブラリとして使うときの型定義が本体パッケージに含まれ るようになった。 @types/prettier を別途インストールする必要 はもうない。 https://prettier.io/blog/2023/07/05/3.0.0.html Prettier 3.0 におけるユーザー影響が大きい変更4選

Slide 7

Slide 7 text

Prettier 3.0 はすべてが ECMAScript Modules

Slide 8

Slide 8 text

CommonJS から ECMAScript Modules へ Prettier 3.0 はすべてが ECMAScript Modules ● Prettier 2.x まではソースコードは CommonJS で書かれていた ● Prettier 3.0 からはソースコードがすべて ESM になった ○ 実行されるときもNode.jsのネイティブESM ○ メリット ■ (Prettierチームにとって)開発者体験が向上する ■ Tree Shakingの精度が高くなる ■ ビルドスクリプトの複雑さが軽減される ■ Pure ESM パッケージのために使っていた workaround を除去できる ○ dynamic import を使ってプラグインや設定ファイル (.prettierrc)を読み込む ● 2.x ではrequireを使っている

Slide 9

Slide 9 text

CommonJS から ECMAScript Modules へ Prettier 3.0 はすべてが ECMAScript Modules ./node_modules/.bin/prettier ./bin/index.cjs ./internal/cli.mjs ./internal/internal.mjs 1. エントリポイントはCJSファイル 2. CLIの本体ファイル(ESM)を読み込む 3. コア実装(ESM)を読み込む 4. プラグインや設定ファイルも import で読み込む import(“./internal/cli.mjs”) import(“./internal/internal.mjs”) plugins .prettierrc import import

Slide 10

Slide 10 text

prettier-vscode における ECMAScript Modulesの問題

Slide 11

Slide 11 text

prettier-vscode prettier-vscode における ECMAScript Modulesの問題 ● Prettierの公式VSCode拡張 ● 正式な名前は「Prettier - Code formatter」 ○ 紛らわしいのでリポジトリ名から、 prettier-vscodeと呼ぶことにする ● 開いているファイルをフォーマットしてくれる ○ format on save を設定しておくと便利 ○ Range formatting もサポートしている ● なんとブラウザでも動く (= codespacesでも動く?)

Slide 12

Slide 12 text

prettier-vscodeのPrettier3.0対応 prettier-vscode における ECMAScript Modulesの問題 ● Prettier 3.0からJavaScript APIはすべてPromiseを返す ● prettier-vscodeはPrettierのJavaScript APIを使うので、全部にawaitをつけて回る必要がある const formatted = prettier.format(text, options); const formatted = await prettier.format(text, options); Prettier 2.x Prettier 3.x

Slide 13

Slide 13 text

prettier-vscodeのPrettier3.0対応 prettier-vscode における ECMAScript Modulesの問題 ● これくらいなら普通にサクッとやれば良い ● シュッとやってBeta版を出してみた https://github.com/prettier/prettier-vscode/pull/2947

Slide 14

Slide 14 text

prettier-vscodeのPrettier3.0対応 prettier-vscode における ECMAScript Modulesの問題 ● 本番ビルドした結果全然動いてなかった (debug モードだと動いていた ) ● 調査の結果、dynamic importの実行が全部エラーになることがわかった ローカルで試したら 全然動かないです。多分 dynamic import が動いてな い...。 Prettierのビルドいじって頑張って みたけど動かない ...。 マジか

Slide 15

Slide 15 text

prettier-vscodeのPrettier3.0対応 prettier-vscode における ECMAScript Modulesの問題 ESLint の flat config も設定ファイ ルを dynamic import してるはずだ けど、VSCode 拡張ちゃんと動いて いるよね。なんでだろう? たしかに 調べてみます

Slide 16

Slide 16 text

言語サーバーを使ったアーキテクチャ prettier-vscode における ECMAScript Modulesの問題 ● vscode-eslintなどの一部のVSCode拡張は言語サーバーを使っている ● VSCode拡張の本体は、その言語サーバーにとってはクライアントである ● クライアントと、別に立ち上がる Language Serverプロセスとが通信しあう ○ LSP(Language Server Protocol) 言語サーバー vscode-eslint ESLint を実行してくれ foo.jsの3行目に ◯◯エラーが出てるよ

Slide 17

Slide 17 text

言語サーバーを使わないアーキテクチャ prettier-vscode における ECMAScript Modulesの問題 ● prettier-vscode は言語サーバーを使わない ● 全てを VSCode 拡張本体の中で処理する prettier-vscode 今開いてるファイルに Prettierを実行して上書きす るぞ〜

Slide 18

Slide 18 text

言語サーバーからは dynamic import ができる prettier-vscode における ECMAScript Modulesの問題 ● クライアント(拡張の本体)では dynamic import ができない ● しかし、言語サーバーからなら dynamic import ができる 言語サーバー VSCode拡張本体 foo.js dynamic import

Slide 19

Slide 19 text

prettier-vscode も言語サーバーがあれば dynamic import が使えるのでは? 😎

Slide 20

Slide 20 text

【余談】問題の原因を推測する prettier-vscode における ECMAScript Modules対応とその問題 ● VSCode拡張クライアントでdynamic importしたときに起こるエラーは ○ TypeError: A dynamic import callback was not specified. ● これは、Node.js の vm.Script 環境で dynamic import を呼び出したときに発生する ○ つまり、VSCodeでは拡張を実行するときに vm.Script環境に隔離していそう ● prettier-vscode開発中のデバッグモードではこの問題は起こらなかった ○ 多分、デバッグモードではこの隔離が有効になっていない

Slide 21

Slide 21 text

言語サーバーの開発と挫折

Slide 22

Slide 22 text

prettier-vscodeにも言語サーバーを実装する計画 言語サーバーの開発と挫折 ● dynamic importを使うために、eslint-vscodeと同じアーキテクチャに変更しようした ● つまり ○ 言語サーバーを導入し、 Prettierの実行はすべてサーバーで行う ○ クライアントはそれを VSCodeに反映させるだけにする ● しかし...

Slide 23

Slide 23 text

prettier-vscodeのメンテナが不在 😢

Slide 24

Slide 24 text

VSCode 拡張の経験者も 言語サーバーの経験者もいない 😢

Slide 25

Slide 25 text

挫折... 言語サーバーの開発と挫折 ● イチから作れというのならまだ適当にやればよかった ● が、prettier-vscodeの挙動を維持しながら言語サーバーを使ったアーキテクチャに移行するというのが 本当に困難だった ○ 実装や仕様がわからないソフトウェアの挙動を維持しながらアーキテクチャを大幅に変えるのは一 般に困難な気がする ● 努力の残骸たち(マジで苦しかった) ○ https://github.com/sosukesuzuki/vsce-doc-formatting-sample ○ https://github.com/sosukesuzuki/prettier-language-server-vscode-client ○ https://github.com/sosukesuzuki/prettier-language-server-deprecated ● 言語サーバーを導入する方法は諦めた

Slide 26

Slide 26 text

worker_threads による 回避策

Slide 27

Slide 27 text

言語サーバーを使わずにdynamic importを実行する術 worker_threads による回避策 何も思いつかねえ ... VSCode拡張から新し いプロセス生やせばい けるらしいよ  え? child_processとかでプ ロセス作れば、そこから dynamic importできるら しい

Slide 28

Slide 28 text

worker_threads による回避策 worker_threads による回避策 ● vm.Script環境ではdynamic importができない ● が、vm.Script環境で新たにスレッドを立てれば、そのスレッド内からは dynamic importができる ○ (わざわざプロセスを作る必要はなかったので worker_threads を使うことにした) VSCode拡張 メインスレッド worker_threadで作った新しいスレッド foo.js dynamic import

Slide 29

Slide 29 text

これしかない 🔥

Slide 30

Slide 30 text

worker_threads による回避策 worker_threads による回避策 ● worker_threads のスレッド内でのみ Prettier 3.0 を実行するアーキテクチャへ ○ (Prettier 2.0 を使っている場合はメインスレッドで実行する ) prettier-vscode メインスレッド worker_threadで作った新しいスレッド .pretterrc プラグイン Prettier本体 dynamic import フォーマットした い フォーマットした よ

Slide 31

Slide 31 text

worker_threads による回避策 worker_threads による回避策 https://github.com/prettier/prettier-vscode/pull/3016

Slide 32

Slide 32 text

まとめ

Slide 33

Slide 33 text

まとめ まとめ ● VSCode拡張でdynamic import が動かない ● 言語サーバー導入しようとしたけど難しかった ● だからworker_threadsでスレッド立てて、そこから dynamic importするようにした ● 今のところ重大なバグ報告は来ていないが、実装者的には普通にバグってそうな気がする ● 怪しい挙動があったら教えて下さい https://opencollective.com/prettier か https://github.com/sponsors/sosukesuzuki から寄付をくれると、 メンテを継続しやすくなります。