なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

ts-node で ESM Native な感じに実行したい

TypeScript を直接実行できる ts-node 便利ですよね。
ところで、最近 (ってほどでもないですが) Node.js 界隈 ESM へ移行する流れがあります。

Node.js 界隈で多くの OSS をリリースしておられる Sindre さんのパッケージも、多くが ESM Native になっており、この流れは止められないでしょう。
ところで、 ESM Native で実行するとなると、 TypeScript で困るのが、拡張子問題です。

TypeScript は基本的に .ts もしくは .tsx な拡張子を解決するのを望むわけで、それをコンパイルしても .js に直してくれるわけではありません。
これの何が困るか、というと、 ts-node で ESM として実行する場合、 import 分にトランスパイル後の拡張子 .js を含めたパスを記述する必要があり、直感的ではありません。
ということで、なんとかしましょうというのが、今回の記事。

目標は、以下のようなファイルを ts-node src/index.ts みたいな感じで直接実行することです。

import { sayHello } from "./hello";

sayHello();
const sayHello = () => console.log("Hello");

export { sayHello };

といっても、やり方は簡単で、コンパイル時に .js を差し込んでしまえばいいわけです。
ということで、やっていきましょう。

まず、 ts-node で直接 ESM を実行することは出来ないので、以下のように呼び出します。

$ yarn run ts-node --esm src/index.ts

ただ、これだけだと拡張子が解決されないので、書き換えてもらいましょう。
都合が良いことに、そういうことをしてくれるものがあるので、導入します。

$ yarn add ttypescript typescript-transform-extensions --dev

次に、 tsconfig.json に以下を追加します。

{
  "compilerOptions": {
    // ...
    "plugins": [
      {
        "transform": "typescript-transform-extensions",
        "extensions": {".ts": ".js"}
      }
    ]
  }
}

最後に、 ts-nodettypescript を使うように設定を変更します。

$ ts-node --esm --compiler ttypescript ./src/index.ts

これで完成です。
あとは、これを package.jsonscripts に追加してあげれば、汎用性が上がって良いですね。

{
  "name": "example-package",
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "ts:esm": "ts-node --esm --compiler ttypescript",
    "hello": "npm run ts:esm src/index.ts"
  }
}

ということで、新年はじめの記事でした。