Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

sosukesuzuki
September 06, 2023
1.1k

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

VSCode 拡張で dynamic import が動かない

sosukesuzuki

September 06, 2023
Tweet

Transcript

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  4. Prettier 3.0 リリース

    View full-size slide

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

    View full-size slide

  6. 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選

    View full-size slide

  7. Prettier 3.0 はすべてが
    ECMAScript Modules

    View full-size slide

  8. 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を使っている

    View full-size slide

  9. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  12. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. 【余談】問題の原因を推測する
    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開発中のデバッグモードではこの問題は起こらなかった
    ○ 多分、デバッグモードではこの隔離が有効になっていない

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  25. 挫折...
    言語サーバーの開発と挫折
    ● イチから作れというのならまだ適当にやればよかった
    ● が、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
    ● 言語サーバーを導入する方法は諦めた

    View full-size slide

  26. worker_threads による
    回避策

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  29. これしかない 🔥

    View full-size slide

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

    フォーマットした

    View full-size slide

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

    View full-size slide

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

    View full-size slide