🌟

jest SyntaxError: Cannot use import statement outside a module エラーメモ

2022/11/17に公開

SyntaxError: Cannot use import statement outside a module

出ていたエラーはこれで、

 SyntaxError: Cannot use import statement outside a module

エラー文は、CommonJSとES Modulesの違いによるエラーっぽいなって感じ。

module.exports = {
...
-  preset: "ts-jest",
+  preset: "ts-jest/presets/js-with-babel-esm",
+  transformIgnorePatterns: ["/node_modules/(?!lodash-es)"],
}

transformIgnorePatternsだけでなく、jest.config.jsのpresetも変更したら通りました。
ts-jestではうまいことコンパイルしてくれてないのかな?

jest.config.jsのpresetについて調べてみた

jest.config.jsのpreset変更したけど、これが何かよくわからない。

公式にはこう書いてある

A preset that is used as a base for Jest's configuration. A preset should point to an npm module that has a jest-preset.json, jest-preset.js, jest-preset.cjs or jest-preset.mjs file at the root.

要は、ベースとなるconfigfileをどうするのかということらしい。
defaultはundefined
ネットでよく見かけるのは、ts-jestだが、それを使っていたらエラーが出て、
ts-jest/presets/js-with-babel-esmを使ったら解消された。

違いがよくわからなかったので、nodemoduleを覗いてみた。

ts-jest/presets/index.jsのファイルの中身はこれ

const { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } = require('../dist/constants')
const { createJestPreset } = require('../dist/presets/create-jest-preset')

module.exports = {
  get defaults() {
    return createJestPreset()
  },
  get defaultsLegacy() {
    return createJestPreset(true, false)
  },
  get defaultsESM() {
    return createJestPreset(false, false, { extensionsToTreatAsEsm: TS_EXT_TO_TREAT_AS_ESM })
  },
  get defaultsESMLegacy() {
    return createJestPreset(true, false, { extensionsToTreatAsEsm: TS_EXT_TO_TREAT_AS_ESM })
  },
  get jsWithTs() {
    return createJestPreset(false, true)
  },
  get jsWithTsLegacy() {
    return createJestPreset(true, true)
  },
  get jsWithTsESM() {
    return createJestPreset(false, true, {
      extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
    })
  },
  get jsWithTsESMLegacy() {
    return createJestPreset(true, true, {
      extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
    })
  },
  get jsWithBabel() {
    return createJestPreset(false, false, {
      transform: {
        '^.+\\.jsx?$': 'babel-jest',
      },
    })
  },
  get jsWithBabelLegacy() {
    return createJestPreset(true, false, {
      transform: {
        '^.+\\.jsx?$': 'babel-jest',
      },
    })
  },
  get jsWithBabelESM() {
    return createJestPreset(false, false, {
      extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
      transform: {
        '^.+\\.m?[j]sx?$': 'babel-jest',
      },
    })
  },
  get jsWithBabelESMLegacy() {
    return createJestPreset(true, false, {
      extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
      transform: {
        '^.+\\.m?[j]sx?$': 'babel-jest',
      },
    })
  },
}

ts-jest/presets/js-with-babel-esmの設定がこれ

module.exports = require('..').jsWithBabelESM

indexに全ての関数が集約されてて、他のファイルからそれを読んでくるだけになってるのね。

presetにts-jestと書くだけなら多分、この設定が使われている。

  get defaults() {
    return createJestPreset()
  },

presetにts-jest/presets/js-with-babel-esmと書くとこれが使われるっぽい

  get jsWithBabelESM() {
    return createJestPreset(false, false, {
      extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
      transform: {
        '^.+\\.m?[j]sx?$': 'babel-jest',
      },
    })
  },

transformってオプションがついてるし、この設定使ったらbabelってくれそう。

余談

jestのテストでSyntaxError: Cannot use import statement outside a moduleが出た時に
このサイトのように解決策

module.exports = {
...
transformIgnorePatterns: ['node_modules/(?!@mui/material)/'],
}

を書いてくれているのが多かったが、それ通りやってもエラーが変わらなかった
結局、presetをしっかり理解してたら、変にハマらんかったってことか。

参考
https://jestjs.io/docs/tutorial-react-native#transformignorepatterns-customization

Discussion