jestのmoduleNameMapperは,順序が大事


問題

moduleNameMapper: {
    "^~/(.+)": "<rootDir>/src/$1",
    "\\.(css|less|sass|scss)$": "identity-obj-proxy",
  }

という定義をしていて,JSXファイル内で,

// components/foo.tsx
import styles from "~/styles/Foo.module.css"

と書いていると,

   Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /work/src/styles/Layout.module.css:1
    .flexContainer {
    ^

    SyntaxError: Unexpected token '.'
        at compileFunction (<anonymous>)

というエラーが出て,「あれ,ちゃんとCSSはtestでは読み込まないようにしているはずなのに?」と首をかしげることになる.

原因

公式ドキュメント をちゃんと読め,ということなのだが,moduleNameMapperに定義されたパターンは,上から順番にひとつずつチェックされていき,最初にマッチしたパターンだけが適用される

上記の場合,最初に~の解決を行ってしまうと,cssなファイルのときに適用してほしかったidentity-obj-proxyが適用されないのだ.

The most specific rule should be listed first.

とドキュメントにあるように,上から順に,詳細なパターン→適用範囲の広いパターン(例えば今回のようなalias設定)となるように設定を書いていく必要があることに注意してくださいね.

Note

2020年初頭における Next.js をベースとしたフロントエンドの環境構築#8-1. Jest を設定を参照して,Next.jsでjestを設定した人は,気をつけたし.