Prettier を呼び出している自作スクリプトや lint タスクが、依存を更新したあと次のエラーで止まります。
TypeError: prettier.resolveConfig.sync is not a function
落ちているのは node_modules に入った Prettier で、自分のソースの差分は要りません。
まず、.sync を呼んでいるのが誰かをトレースバックで確かめてください。 eslint-plugin-prettier を指しているなら、呼んでいるのはそのプラグインで、直し方も別になります(下の切り分け)。
直し方の要点を先に
Prettier を戻さず、呼び出し側を await に変えます。
// 変更前
const config = prettier.resolveConfig.sync(filePath);
// 変更後
const config = await prettier.resolveConfig(filePath);
await を使うので、呼び出している関数を async にします。CommonJS のトップレベルで呼んでいるなら、即時実行の async 関数で包みます。
const prettier = require("prettier");
(async () => {
console.log(await prettier.resolveConfig("missing-prettier-config.json"));
})();
実測では、Prettier 3.0.0 のまま呼び出しを await prettier.resolveConfig(...) に変えると、同じ「存在しない設定ファイル」への問い合わせが null を返して終了コード 0 で終わりました。動かしたのは呼び出し側だけで、Prettier の版は下げていません。
戻り値が null になるのは設定ファイルが見つからなかったときで、これは同期版と同じ挙動です。 移行で変わるのは値ではなく、値の受け取り方です。
なぜ Prettier 3 で出るのか:public API が非同期に統一された
Prettier 3.0.0 は、公開 API を非同期へ統一し、同期版を削除しました。3.0.0 のリリース告知は「Change public APIs to asynchronous」として、prettier.resolveConfig.sync / prettier.resolveConfigFile.sync / prettier.getFileInfo.sync の3つを名指しで削除と挙げています。本記事のシグネチャはその1つ目です。API ドキュメントも「public API は非同期である」と述べ、同期呼び出しが必要な利用者を @prettier/sync へ案内しています。
削除なので、prettier.resolveConfig.sync は undefined になります。undefined を関数として呼んだ結果が、冒頭の TypeError です。警告や非推奨メッセージを経由せずに TypeError へ飛ぶのはこのためで、resolveConfig という名前自体は 3 系にも残っています。消えたのは .sync の側だけです。
| Prettier | 呼び出し | 終了コード | 結果 |
|---|---|---|---|
| 2.8.8 | resolveConfig.sync("missing-prettier-config.json") | 0 | null を出力 |
| 3.0.0 | 同じ | 1 | TypeError: prettier.resolveConfig.sync is not a function |
| 3.0.0 | await resolveConfig("missing-prettier-config.json") | 0 | null を出力 |
測ったのは 2.8.8 と 3.0.0 の2点で、境界がこの2点の間にあることまでを確認しています。同期 API の削除が 3.0.0 の変更であることは、上記のリリース告知と API ドキュメントによります。
なぜ依存を更新した日に出るのか
package.json に "prettier": "^2.8.8" と書いてあれば、^ は 3.0.0 を含みません。この形のままなら 3 系は入らないので、境界はその範囲指定を動かしたときに来ます。
踏み方は主に2つです。
prettierの範囲を^3.0.0やlatestへ上げた(意図した更新)- Prettier を直接の依存に持たず、他のパッケージ経由で入っている——この場合、自分の
package.jsonを触っていなくても、そちらの更新で 3 系に入れ替わります
npm ls prettier で、いま解決されている版と、それを要求しているパッケージを確認できます。
同期呼び出しを残す必要があるとき
呼び出し元を async にできない事情があるなら、Prettier のドキュメントが案内しているのは @prettier/sync です。Prettier 3 と組み合わせて同期 API を提供する別パッケージで、Prettier 2 へ戻す方法ではありません。
const prettierSync = require("@prettier/sync");
const config = prettierSync.resolveConfig(filePath);
この経路は errfix では実測していません。 ドキュメントが示す選択肢として挙げています。上の await への移行は、同じ Prettier 3.0.0 で終了コード 0 まで確認したものです。
版を 2 系へ戻す前に
package.json を "prettier": "2.8.8" に固定すればエラーは消えます。ただしそれは、Prettier 3 の整形結果(Markdown やトレーリングカンマの既定など)を採らない選択と同じです。呼び出し側の変更は await の追加が主なので、戻す前に影響範囲を数えると比べられます。対象は resolveConfig.sync / resolveConfigFile.sync / getFileInfo.sync と、format の戻り値を文字列として扱っている箇所です。
切り分け
- 【最初に見る】トレースバックが
eslint-plugin-prettierを指している → 呼んでいるのはそのプラグインです。eslint-plugin-prettier@4は内部でprettier.resolveConfig.syncを呼ぶので、Prettier 3 と組み合わせると lint の実行だけで同じTypeErrorが出ます。この場合、自分のコードに.syncは 1 つもありません。 直すのはプラグインを Prettier 3 対応版(5 系)へ上げることで、下のawaitへの移行ではありません。errfix で実測したのはeslint-plugin-prettier@4がresolveConfig.syncを呼んでいることまでで、5 系へ上げて直るところは検証していません。 TypeErrorではなく、整形結果が[object Promise]になっている →prettier.formatの戻り値をそのまま文字列として使っています。3 系のformatは Promise を返すので、こちらもawaitが要ります。例外が出ないぶん、ファイルに書き出したあとで気づく形になります。resolveConfigFile.syncとgetFileInfo.syncも 3 系では消えています。prettierコマンド(CLI)は動くのに、スクリプトだけ落ちる → CLI は 3 系でも同期・非同期を意識せずに使えます。境界は Node の API を直接呼んでいる箇所だけに出ます。package.jsonのprettierを触っていないのに 3 系が入っている → 他の依存が Prettier を運んでいます。npm ls prettierで要求元を確認してください。resolveConfigそのものがundefined→ それは Prettier が読めていない形です。require("prettier")の解決先と、node_modules/prettier/package.jsonのversionを確認してください。この記事の境界は.syncプロパティだけが消えている状態です。- エディタの保存時整形は効いているのに、スクリプトが落ちる → エディタの拡張は自前で Prettier を抱えていることがあり、プロジェクトの
node_modulesとは別の版を使っている場合があります。
検証環境
- image:
node(digest 固定sha256:d32cdf61...)、ネットワーク有り(npm registry から Prettier を取得するため) - 再現:
[email protected]を入れ、prettier.resolveConfig.sync("missing-prettier-config.json")を呼ぶ CommonJS スクリプトを実行 →TypeError: prettier.resolveConfig.sync is not a functionで終了コード 1 - 修正:同じ
[email protected]のまま、呼び出しをawait prettier.resolveConfig("missing-prettier-config.json")に変えて実行 →nullを出力して終了コード 0 - 版の境界表の 2.8.8 の行は、同じイメージ・同じスクリプトで Prettier だけを入れ替えて実行したもの
再現から修正までは errfix の検証ハーネスが機械的に確認しています。reproduce と fix の差は呼び出し側のスクリプトだけで、Node のイメージ・Prettier の版・問い合わせる設定ファイル名はすべて同一です。同期 API の削除と @prettier/sync の位置づけは、Prettier 3.0.0 のリリース告知および API ドキュメントで確認しました。@prettier/sync を使う経路そのものは実行していません。