tsconfigのmoduleResolution

Recent posts

参考

Node.jsのモジュール解決

Node.jsにはcommonJSとESMの2通りのモジュール解決が存在する。 ESMはNode12からサポートされた。

ESMとCJSの相互読み込み

ESMとCJSを混在させる場合は以下の通り。

読み込み Static import
import {} from "foo"
Dynamic import
import(path).then
require()
ESM から ESM を読み込む OK OK NG
CJS から CJSを読み込む NG NG OK
ESM から CJS を読み込む OK NG NG
CJS から ESM を読み込む NG OK NG

package.json type フィールド

Node>=12で使える。プロジェクト内のモジュール解決方法を指定する。 package.jsonはディレクトリごとに配置できるので、ディレクトリ単位での解決も可能。

module Node.jsでモジュール解決する時、importされたファイルにもっとも近いESMファイルが使われる。

commonjs Node.jsでモジュール解決する時、importされたファイルにもっとも近いCommonJSファイルが使われる。

tsconfig.json module フィールド

吐き出したJSのモジュール読み込みを変更するオプション。

import { foo } from "./foo.js";
foo();
// module: CommonJS

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const foo_js_1 = require("./foo.js");
(0, foo_js_1.foo)();
// module: UMD

(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./foo.js"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    const foo_js_1 = require("./foo.js");
    (0, foo_js_1.foo)();
});
// module: Node16

import { foo } from "./foo.js";
foo();
// module: ES2022

import { foo } from "./foo.js";
foo();

tsconfig.json moduleResolution

Node/Node10

mainがエントリポイントになる。 外部パッケージのtypeやexportsは無視される。

Node16

package.jsonのtypeによってCommonJSかESMが使用され、exportsがエントリポイントになる。

"exports": {
    ".": {
      "require": "./commonjs/index.cjs",
      "import": "./esm/index.mjs"
    }
  }
// type:CommonJSなら: index.cjsが読み込まれる
// type:ES2022なら: index.mjsが読み込まれる
import { foo } from "package";

Bundler

インポート文の拡張子補完が行われる。つまり以下の文はfoo.tsが読み込まれる。 exportsがエントリポイントになる。

import { value } from "./foo";

拡張子の出力

トランスパイル後のコードのimportに拡張子を含めたい場合は、以下のように書く。TSのモジュール解決上は拡張子を無視していずれもfoo.tsを探しに行く。(foo.jsがあっても無視される)

import { foo } from "./foo.js";
import { foo } from "./foo.cjs";
import { foo } from "./foo.mjs";

トリプルスラッシュコメントの型参照はresolution-modeオプションで区別する。

/// <reference types="foo" resolution-mode="require" />
/// <reference types="bar" resolution-mode="import" />