ESMに依存するモジュール(CJS/ESM)をts-mochaに入力すると、何故かエラーが出てくる現象について色々実験した
from ts-mochaを使うとECMAScript moduleに依存したファイルでTypeError ERR_UNKNOWN_FILE_EXTENSION: Unknown file extension ".ts"が出てくる。
ESMに依存するモジュール(CJS/ESM)をts-mochaに入力すると、何故かエラーが出てくる現象について色々実験した
結論
ts-node側の問題だった。
ts-node-esmを使えば問題なかった...。
neatCsv: ESMに分類されるモジュール
code:neat-csv-tester.ts
import neatCsv from "neat-csv"
neatCsv('Header1,Header2\ncell1, cell2\ncell3,cell4')
.then(data => console.log(data))
.catch(e => console.error(e))
この記事の目的
ESMであるneatCSVを読み込むモジュールneat-csv-tester.tsを、
CJSあるいはESMと扱った上で、ts-mochaに渡すとどうなるかを実験する。
追記:
ts-nodeでも同様の問題が発生することを確認した。
ということは、これはts-node側の問題?
CJSからESMを読み込む
import文での書き方は使えない。(コンパイル後がESMへのrequireになるため。)
現在のファイルは CommonJS モジュールであり、このインポートでは 'require' 呼び出しが生成されますが、参照ファイルは ECMAScript モジュールであるため、'require' ではインポートできません。代わりに動的な 'import("neat-csv")' 呼び出しを記述することを検討してください。 (エラーメッセージ)
moduleオプションを使えばコンパイル後のコードもimportにできそうなもんだけど、CJSだからそれは無効になるのか...?
わからん。
それをそのまま実行するとTypeError: Unknown file extension ".cts" for /src/neat-csv-tester.ctsが出てくる。
このエラーが出てくるのもなんか変なような...。
ESMからESMを読み込む。
TypeError: Unknown file extension ".mts" for /Users/AppleBird/Downloads/timetable/src/neat-csv-tester.mtsが出力される。
CJSからESMの読み込みはともかく、こちらのESMからESMの読み込みはimportによる呼び出しコードが生成されるはず。
だから、モジュールの読み込みの制約に引っかかることはないはず....。
#疑問 どうして同じ問題が出てきているのかが理解できない。
単体でコンパイルした後のコードがCJS方式になってる?
でもこれって関係あるのかな...?
ts-nodeが内部で直接tscを単体ファイルに対して呼んでいるなら可能性は十二分にある...。
でもts-mochaのざっくりとしたしくみをみるに、単体でコンパイルはしていないはず...。
まあ一応確かめてみる
なっとるやんけ!(ESMをそのままコンパイルした結果)
Code generation for modules -- module typescript docs
code:neat-csv-tester.mjs
"use strict";
exports.__esModule = true;
var neat_csv_1 = require("neat-csv");
(0, neat_csv_1"default")('Header1,Header2\ncell1, cell2\ncell3,cell4')
.then(function (data) { return console.log(data); })"catch"(function (e) { return console.error(e); });
でも、全体でコンパイルするとそうでもなく、ESM方式になっている。
code:build/neat-csv-tester.mjs
import neatCsv from "neat-csv";
neatCsv('Header1,Header2\ncell1, cell2\ncell3,cell4')
.then(data => console.log(data))
.catch(e => console.error(e));
//# sourceMappingURL=neat-csv-tester.mjs.map
...もしかしてこれ、単体でコンパイルするとプロジェクトtsconfig.jsonの設定が無効になる?
sourceMappingURLも貼られてないし...。
そうらしい。
ファイルを具体的に指定してでtscを動かした場合tsconfig.jsonは無視される。
When input files are specified on the command line, tsconfig.json files are ignored.
単体ファイルに対してもtsconfig.jsonを取り入れるかどうかについてはIssuesで議論されている。
Allow tsconfig.json when input files are specified -- TypeScript GitHub Issues
今なお議論が続いてるみたいだ....。
動的なimportへ書き換えたらいずれも正しく実行される。
code:neat-csv-tester.ts
// 実際にはctsファイル
import("neat-csv").then(neatCsv => {
neatCsv.default('Header1,Header2\ncell1, cell2\ncell3,cell4')
.then(data => console.log(data))
.catch(e => console.error(e))
})