かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

node.js モジュール(ES Module)のimport/exportにハマる。

node.jsでimport/exportで試そうとしてハマったのでメモ。

$ node -v
v11.4.0

import / export を使っているファイルの拡張子は.mjsでないとエラー

// moduleA.js
export const hi = (name) => `Hi ${name}. `;

// main.js
import {hi} from './moduleA.js';
console.log( hi('星宮いちご') );
$ node main.js
SyntaxError: Unexpected token

ファイルが.mjsでも--experimental-modulesオプションがないとエラー

// moduleA.mjs
export const hi = (name) => `Hi ${name}. `;

// main.mjs
import {hi} from './moduleA.mjs';
console.log( hi('星宮いちご') );
$ node main.mjs
SyntaxError: Unexpected identifier

👇 --experimental-modulesをつける

$ node --experimental-modules main.mjs
ExperimentalWarning: The ESM module loader is experimental.
Hi 星宮いちご

ExperimentalWarning: The ESM module loader is experimental.ってワーニングが表示されるけどimport/exportできています。

--experimental-modulesオプションは実行するファイルより前にないとエラー

$ node main.mjs --experimental-modules
ExperimentalWarning: The ESM module loader is experimental.
SyntaxError: Unexpected identifier

module.exportsしているファイルは.jsでもOK

// moduleB.js
module.exports = { name: 'ジョニー別府' };

// main.mjs
import foo from './moduleB.js';
console.log(foo.name);
$ node --experimental-modules main.mjs
ExperimentalWarning: The ESM module loader is experimental.
ジョニー別府

module.exportsなファイルだけをインポートしてる場合でもインポートしてるファイルは.mjsでないとエラー

// moduleB.js
module.exports = { name: 'ジョニー別府' };

// main.js
import foo from './moduleB.js';
console.log(foo.name);
$ node --experimental-modules main.js
ExperimentalWarning: The ESM module loader is experimental.
SyntaxError: Unexpected identifier

import する時拡張子は省略してもOK

// moduleA.mjs
export const hi = (name) => `Hi ${name}. `;

// moduleB.js
module.exports = { name: 'ジョニー別府' };

// main.mjs
import {hi} from './moduleA';
import foo from './moduleB';

console.log( hi('星宮いちご') );
console.log(foo.name);
$ node --experimental-modules main.mjs
ExperimentalWarning: The ESM module loader is experimental.
Hi 星宮いちご
ジョニー別府

import するファイルの拡張子を省略した場合は.mjs の方が優先される

// moduleA.mjs
export const hi = (name) => `Hi ${name}. `;

// moduleB.js
module.exports = { name: 'ジョニー別府' };

// moduleB.mjs
export default { name: '涼川直人' };

// main.mjs
import {hi} from './moduleA';
import foo from './moduleB';

console.log( hi('星宮いちご') );
console.log(foo.name);
$ node --experimental-modules main.mjs
ExperimentalWarning: The ESM module loader is experimental.
Hi 星宮いちご
涼川直人

まとめ

node.jsv11.4.0現在でimport/exportを使う時は

  1. importしてるメインのファイルは.mjsにする
  2. モジュール側もexport 〜を使っている場合は拡張子を.mjsにする
  3. --experimental-modulesオプションが必要
  4. importは拡張子を省略してもOK。mjs > js の順にファイルを探す

ということのようです。

The --experimental-modules flag can be used to enable features for loading ESM modules.
Once this has been set, files ending with .mjs will be able to be loaded as ES Modules.

node --experimental-modules my-app.mjs
出典: ECMAScript Modules | Node.js v11.4.0 Documentation

 
お手軽にnodeでコマンドラインからimport/exportを試せると思ったら罠だった...
まだ言語仕様が決まりきってないのが理由っぽいですが、node.js側とJavaScript(ECMAScript)側とで同じ機能なのに違いが生まれるのあまり幸せにならなそう…というお気持ち


[参考]