33
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ts-jestでSyntaxError: Cannot use import statement outside a moduleが出た時の対処法

Last updated at Posted at 2023-01-19

先に結論だけ

例として、このTypeScriptファイルのテスト実行時に

▼問題となるimport

import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js";

以下のシンタックスエラーが発生しているなら

SyntaxError: Cannot use import statement outside a module

jest.config.jsでtransformIgnorePatternstransformフィールドを追加して

▼jest.config.js

module.exports = {
  preset: "ts-jest",
  transformIgnorePatterns: ["/node_modules/(?!three/examples/)"],
  transform: {
    "node_modules/three/examples/.+.(j|t)sx?$": "ts-jest",
  },
  testEnvironment: "node", // or jest-environment-jsdom
};

tsconfig.jsonにallowJs : trueを追加します。rootDirは指定してはいけません。

▼tsconfig.json

{
  "compilerOptions": {
-    "rootDir": "./src",
+    "allowJs": true,
   }
}

はじめに

この記事は、Jestの実行時に発生する変換エラーについての情報を解説、共有するためのものです。

対象とする環境

この記事では、以下の環境を前提としています。

% sw_vers
ProductName:    macOS
ProductVersion: 12.6.1
BuildVersion:   21G217
% node --version
v18.12.1
% npm --version
8.19.2

▼package.json

  "devDependencies": {
    "jest": "^29.3.1",
    "three": "^0.148.0",
    "ts-jest": "^29.0.3"
  }

対象となるパッケージのメジャーバージョンが変わると、記事の内容がそのまま適用できないかもしれません。記事を読む前に、お手元の環境をご確認ください。

対象とする読者

  • Jestを使ったことがある
  • Jestを使っているプロジェクトが、パッケージの更新でテストに失敗した

Jestを使っているユーザーを対象としているため、この記事ではツール自体の解説やインストールガイドは扱いません。ご承知ください。

問題

Jestでテストしているプロジェクトのパッケージを更新すると、以下のエラーが発生してテストが失敗しました。

SyntaxError: Cannot use import statement outside a module
ログの抜粋
  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • 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/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

package.jsonを巻き戻すとテストが成功するため、パッケージ更新がエラーの原因のようです。

three.jsのCHANGELOGを読んでいくと、importしているクラスがES6に移行していました。このES6への移行と、Jestの設定の不整合が問題の原因でした。

原因

この問題はテストにimportされるファイルが、CommonJS形式に変換されないため起きます。どのように問題が発生するのか、順を追って解説します。

Jestとトランスフォーマー

Jestはテスト時にnode.jsでサポートされていない言語をJavaScriptに変換します。対象となる言語はJSX、TypeScript、Vueテンプレートなどです。

この変換処理を担当するのがトランスフォーマーです。Jestはデフォルトbabel-jestを使用します。ts-jestはTypeScriptトランスフォーマーです。

Jestはnode_modules以下のファイルを変換しない

Jestの標準設定ではnode_modules.pnp以下のモジュールを変換せず、配布された形式のまま実行します。

node_modulesにES Moduleのみ対応のパッケージがある場合

node_modulesにトランスパイルされていないコードが含まれている場合、Jestはコードを理解できずに構文エラーが発生します。

Sometimes it happens (especially in React Native or TypeScript projects) that 3rd party modules are published as untranspiled code. Since all files inside node_modules are not transformed by default, Jest will not understand the code in these modules, resulting in syntax errors. To overcome this, you may use transformIgnorePatterns to allow transpiling such modules. You'll find a good example of this use case in React Native Guide.
時々、サードパーティモジュールがトランスパイルされていないコードとして公開されていることがあります(特にReact NativeやTypeScriptプロジェクトにおいて)。node_modules 内のすべてのファイルはデフォルトでトランスフォームされないため、Jestはこれらのモジュール内のコードを理解できず、結果として構文エラーが発生します。これを克服するために、transformIgnorePatternsを使用して、このようなモジュールのトランスパイルを許可できます。React Native Guideに、この使用例の良い例があります。

この問題は頻繁に発生するようで、公式ドキュメントにも記載がありました。

対処

この問題はnode_modules以下のファイルが変換されないため発生します。解決のため、Jestに問題となるファイルを変換するように指示します。

jest.config.js

jest.config.jsに2つのフィールドを追加します。

  • transformIgnorePatternsフィールド
  • transformフィールド

transformIgnorePatterns

transformIgnorePatternsフィールドのデフォルト値は以下の通りです。

Default: ["/node_modules/", "\\.pnp\\.[^\\\/]+$"]

これはnpmのnode_modulesかyarnのpnpディレクトリを すべて変換しない という指定です。transformIgnorePatternsフィールドを追加し、値を以下のように書き換えます。

▼jest.config.js

module.exports = {
  transformIgnorePatterns: ["/node_modules/(?!three/examples/)"],
};

この正規表現パターンは

  • node_modulesディレクトリ以下は変換しない
  • しかしnode_modules/three/examplesディレクトリは 除外から除外する → 変換する

という意味になります。

?!否定先読みアサーションという正規表現です。 条件に当てはまる場合はマッチしない という否定表現です。

二重の否定になっているので直感的に理解しにくいですが、この否定表現でnode_modules/three/examplesディレクトリを変換対象に指定します。

transform

つぎにnode_modules/three/examplesディレクトリを変換するトランスフォーマーを指定します。

▼jest.config.js

module.exports = {
  preset: "ts-jest",
  transform: {
    "node_modules/three/examples/.+.(j|t)sx?$": "ts-jest",
  },
};

jest.config.jsにtransformフィールドを追加します。フィールドはkey-valueが対となるオブジェクトで、それぞれ

  • key : 変換対象ファイルの正規表現
  • value : トランスフォーマーの名前

を指定します。今回はts-jestでnode_modules/three/examples以下のファイルを変換します。

tsconfig.js

ts-jestはtsconfig.jsonの設定に従ってファイルを変換します。JavaScriptファイルを変換できるよう、tsconfig.jsにallowJsフィールドを追加します。また、rootDirは指定してはいけません。node_modulesがコンパイル対象外になるためです。

▼tsconfig.json

{
  "compilerOptions": {
-    "rootDir": "./src",
+    "allowJs": true,
   }
}

これでnode_modules/three/examples以下のファイルがTypeScriptのトランスパイル設定にしたがって変換され、テストが正常に稼働します。

今後の予定

Jestは、現在ES modulesへの実験的サポートを始めています

Jest 30以降では、トランスフォーマーを使用せずES modulesをimportしたテストが実行できるようになるかもしれません。Jestのメジャーバージョンが更新されたら、ドキュメントを再読してください。

以上、ありがとうございました。

参考記事

33
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?