LoginSignup
1
1
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

JestでCannot find module が出たら依存モジュールのexportsフィールドにdefaultフォールバックを書け

Last updated at Posted at 2024-01-10

先に結論だけ

Jestで、依存先のPure ESMモジュールを読み込んでテストしたら

Log
 FAIL  __test__/SphericalRotor.spec.ts
  ● Test suite failed to run

    Cannot find module '@masatomakino/threejs-spherical-controls' from '__test__/SphericalRotor.spec.ts'

    > 1 | import {
        | ^
      2 |   SphericalController,
      3 |   SphericalParamType,
      4 | } from "@masatomakino/threejs-spherical-controls";

      at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/resolver.js:427:11)
      at Object.<anonymous> (__test__/SphericalRotor.spec.ts:1:1)

Resolverでエラーが出ている場合

Log
at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/resolver.js:427:11)

依存パッケージのPackage.jsonを開いて

package.json
{
  "type": "module",
  "exports": {
    ".": {
      "import": {
        "types": "./esm/index.d.ts",
        "default": "./esm/index.js"
      },
+     "default": {
+       "types": "./esm/index.d.ts",
+       "default": "./esm/index.js"
+     }
    }
  }
}

exportsフィールドに"default"フィールドを追加してください。

はじめに

この記事は、Jest29におけるモジュール解決について記録、共有するためのものです。

想定する環境

この記事は、Jest v29.7およびts-jest v29.1を想定して書かれています。Jestのバージョンが異なる場合は、記事の内容がそのまま適用できないかもしれません。記事を読む前に、お手元の環境をご確認ください。

想定する読者

この記事は、すでにJestを利用しているユーザーに向けて書かれています。Jestの基本的な使い方については、公式ドキュメントをご参照ください。

JestにおけるESM対応

JestにおけるESM対応は実験段階に位置付けられています。Jest29でESMをテストするには以下の2つの方法があります。

  • --experimental-vm-modulesオプションを指定してESMを変換せずに実行する
  • transformerを利用して、ESMをCommonJSに変換して実行する

この記事では、後者の方法を紹介します。

CommonJSにトランスパイルする

ESMをCommonJSに変換する方法はts-jestのドキュメントで紹介されています。

jest.config.ts
import type { JestConfigWithTsJest } from 'ts-jest'

const jestConfig: JestConfigWithTsJest = {
  extensionsToTreatAsEsm: ['.ts'],
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
  },
  transform: {
    '^.+\\.tsx?$': [
      'ts-jest',
      {
        useESM: true,
      },
    ],
  },
  transformIgnorePatterns: [`node_modules/(?!(@masatomakino/threejs-spherical-controls)/)`],
}

export default jestConfig

ts-jestをトランスフォーマーとして、TypeScriptやESMのJavaScriptをCommonJSに変換します。transformIgnorePatternsには、依存しているPure ESモジュールを指定します。?!(モジュールのパス)のように指定すると、指定されたモジュールは変換の対象となります。

Jestのモジュール解決

Jestは独自のモジュール解決処理を実装しています。この処理は、Package.jsonのexportsフィールドを参照し、defaultフィールドを順に解決していくようです。

package.json
{
  "type": "module",
  "exports": {
    ".": {
      "import": {
        "types": "./esm/index.d.ts",
        "default": "./esm/index.js"
      }
    }
  }
}

したがって、このようにdefaultフォールバックがないexportsフィールドを持つモジュールは、解決に失敗します。

defaultフォールバックを追加すれば、この問題は解決します。

package.json
{
  "type": "module",
  "exports": {
    ".": {
      "import": {
        "types": "./esm/index.d.ts",
        "default": "./esm/index.js"
      },
+     "default": {
+       "types": "./esm/index.d.ts",
+       "default": "./esm/index.js"
+     }
    }
  }
}

importとdefaultフォールバックをフラットな階層においても解決に成功します。

package.json
{
  "type": "module",
  "exports": {
    ".": {
      "types": "./esm/index.d.ts",
      "import": "./esm/index.js",
      "default": "./esm/index.js"
    }
  }
}

個人的な感想

node.jsの周辺ツールにおける、ESMサポートはまだ成熟していません。ツール同士で処理に差があります。どのようなツールで扱われても問題が出ないように、exportsフィールドのdefaultフォールバックは念入りに書き込んでおいた方が安全です。

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

1
1
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
1
1