Closed6

Vitest を導入しただけで tsc がコケるようになった

shingo.sasakishingo.sasaki

本スクラップについて

以下のエラーについて追求する。

$ tsc --noEmit
node_modules/vite/dist/node/index.d.ts:6:41 - error TS2307: Cannot find module 'rollup/parseAst' or its corresponding type declarations.

6 export { parseAst, parseAstAsync } from 'rollup/parseAst';
                                          ~~~~~~~~~~~~~~~~~


Found 1 error in node_modules/vite/dist/node/index.d.ts:6

error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

結論から言うと、以下で解決した。

tsconfig.json
{
  "compilerOptions": {
    // 一部抜粋
    "moduleResolution": "bundler",
  },
}

のだが、どういう理屈で発生してなぜ治ったのかよくわからなかったので、順に明らかにしていく。

shingo.sasakishingo.sasaki

背景

テストフレームワークに Jest を使用しているプロダクトで、Vitest への移行を行った。
その際、vitest の global API を宣言無く呼び出せるようにするため、その型定義を読み込むように tsconfig.json を追加した。

tsconfig.json
{
  "compilerOptions": {
    // 一部抜粋
    "moduleResolution": "node",
    "types": ["vitest/globals"],
  },
}

vitest 移行後のテストは全て通るようにまで一気に修正したが、tsc による型チェックが通らなくなった。

$ tsc --noEmit
node_modules/vite/dist/node/index.d.ts:6:41 - error TS2307: Cannot find module 'rollup/parseAst' or its corresponding type declarations.

6 export { parseAst, parseAstAsync } from 'rollup/parseAst';
                                          ~~~~~~~~~~~~~~~~~


Found 1 error in node_modules/vite/dist/node/index.d.ts:6

error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
shingo.sasakishingo.sasaki

手っ取り早い解決

エラーメッセージで雑にググると、以下の Discussion にて、まったく同じ報告があり、そこですぐに解決されていた。

https://github.com/vitejs/vite/discussions/12466#discussioncomment-7596971

Vite リポジトリであることや、Rollup でのエラーであることから、vitest でなく vite, それも Vite 5 からであることがうかがえる。

You need to use moduleResolution: 'bundler' (or node16/nodenext) to fix it. Rollup only export those types from the "exports" field which those resolution settings respect.

とのことなので、 moduleResolution についておさらいすれば良さそう。 import をどのように解決するかのアプローチの設定だった気がするが細かくは理解してない。

shingo.sasakishingo.sasaki

moduleResolution について

この記事が最高に良くまとまってる。というか半年前に読んでブコメを残してた。のでこういう記事があったことは覚えてたんだけど、結局 Node16 や bundler を設定した時にどうなるかは覚えてなかった。
https://blog.s2n.tech/articles/dont-use-moduleresolution-node

package.json における、モジュールシステムの宣言

  • type: "module" の場合は ESM で提供することを宣言できる
  • `type: "commonjs" の場合は CJS で提供することを宣言できる-
  • main フィールドにはインポートした際のエントリポイントを指定できる (CJS, ESM 共通)
  • exports フィールドには、インポートするパスごとに、CJS, ESM それぞれのエントリーポイントを指定できる
    • main が定義されていてもこちらが優先される

TypeScript 側のモジュール解決

tsconfig.jsonmoduleResolution フィールドで設定する

  • Node / Node10
    • CJS 専用
    • main フィールドのみ使用される
  • Node16 / NodeNext
    • type の値によって CJS か ESM か決まる
    • exports フィールドからエントリーポイントを決定する
    • 拡張子や index の補完を行うの自動補完は行われない
  • Bundler
    • 拡張子や index の補完を行うの自動補完を行う
    • exports フィールドによってエントリーポイントが決定する
shingo.sasakishingo.sasaki

Rollup について

今回エラーが発生したのは rollup/parseAst でした。

6 export { parseAst, parseAstAsync } from 'rollup/parseAst';

Vite 5 を使用しているので、Rollup も v4 が使用されます。

$ yarn why vite
=> Found "vite@5.0.6"
$ yarn why rollup
=> Found "rollup@4.6.1"

rollup の package.json から rollup/parseAst を確認する。

https://github.com/rollup/rollup/blob/098e29ca3e0643006870f9ed94710fd3004a9043/package.json#L220-L224

cjs あるくね?

あー違う。現状だと

"moduleResolution": "Node",

が使われてたんだ。だから昔ながらの main フィールドしか見えてない。

https://github.com/rollup/rollup/blob/098e29ca3e0643006870f9ed94710fd3004a9043/package.json#L5

だから require('rollup') だったら、dist/rollup.js を見つけられるけど、 require('rollup/parseAst') だと解決できないんだ。

このスクラップは5ヶ月前にクローズされました