Node v9 で ES Module import を使ってみる

どうやら最近の JavaScript はモジュール分割が可能になってきているようだ。
つまり別ファイルに定義したオブジェクトを `import` や `export` というキーワードで参照できるようだぞ、すげえ！

というので、フハフハしながら趣味のコードを `import`/`export` 使いながら書いていた。
のだが。

# 世の中はまだ require によるモジュール参照
Node で紹介されているコードの断片をみても `require` 使ったものしかないじゃない[^commonjs]？　たとえばこんなの。

[^commonjs]: `require` を使う由緒正しい Node JS 形式のモジュール参照スタイルを CommonJS、略して CJS って言うのですか？

```javascript
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});
```
(引用元 https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options)

# Node JS サンプルを import で書き直して Node JS で実行
そうじゃないんだ、わたしは `import`/`export` を使いたいんだよ！　そういうサンプルと、それをどう動かすかを知りたいんだよ！

というので、書き直したコードがこちら。

```javascript
import child_process from 'child_process';
const { spawn } = child_process;

const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', data => console.log(`stdout: ${data}`));
ls.stderr.on('data', data => console.log(`stderr: ${data}`));
ls.on('close', code => code && console.log(`child process exited with code ${code}.`));
```

これを `main.mjs` と名前をつけて保存。（`main.js` とすると、実行できない[^ext]ので注意！）

[^ext]: `import`/`export` キーワードを使ったスクリプトを、拡張子 `.js` で `node` インタープリターにわたすと解釈実行してくれない。

そして `node` インタープリターで実行[^option]。

[^option]: https://qiita.com/euxn23/items/c75c0ba08709ab446faf

```bash
node --experimental-modules main.mjs
```
すると確かに `/usr` 配下のファイルリスティングが得られる[^version]。

[^version]: Node v8.9.4 および v9.5.0 で動作を確認。

```text
(node:31690) ExperimentalWarning: The ESM module loader is experimental.
合計 124K
drwxr-xr-x   2 root root  64K  2月 13 14:37 bin
drwxr-xr-x   2 root root 4.0K 10月 25 20:56 games
drwxr-xr-x  34 root root  16K  1月 19 06:19 include
drwxr-xr-x 137 root root 4.0K  1月 23 11:04 lib
drwxr-xr-x  10 root root 4.0K  4月 12  2017 local
drwxr-xr-x   3 root root 4.0K  4月 12  2017 locale
drwxr-xr-x   2 root root  12K  2月 13 14:37 sbin
drwxr-xr-x 291 root root  12K  1月 17 18:39 share
drwxr-xr-x  15 root root 4.0K  1月 28 06:46 src
```

めでたし[^2]。
[^2]: あまりめでたくもない。 http://teppeis.hatenablog.com/entry/2017/08/es-modules-in-nodejs にあるように、まだまだ時期尚早という気がする。すでに半年以上経過しているというのに、だ！

#[参考1] 拡張子 .js での実行時エラーメッセージ
なお、拡張子 `.js` で先のスクリプトをインタープリターに渡すと、以下のような無情なエラーメッセージが出力される。
「`import` なんて構文、ボク知らないです」だと、白々しい！

```text
(function (exports, require, module, __filename, __dirname) { import child_process from 'child_process';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:607:28)
    at Object.Module._extensions..js (module.js:654:10)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at createDynamicModule (internal/loader/ModuleRequest.js:52:15)
    at setExecutor (internal/loader/ModuleWrap.js:40:23)
    at file:///home/ryohji/workspace/node-test/main.js:8:36
```

#[参考2] Common JS モジュールの参照
先の書き直し例の冒頭、こう書いた。

```javascript
import child_process from 'child_process';
const { spawn } = child_process;
```

ES Module の `import` なら、本来、以下のように一行で書く。

```javascript
import { child_process } from 'child_process';
```

残念ながら 2018 年 2 月中旬現在、世の中の JavaScript モジュールは Common JS スタイルで書かれたものがほとんど。
つまり `module.exports = ...` みたいにオブジェクトをエクスポートして、これを `const mod = require('module');` としてインポートするパターン。

このような CJS スタイルで定義されたモジュールは、デフォルト import で受け取って、その中から利用するオブジェクトを抽出する、ことで参照するようだ。

あるいは以下のようにする。

```javascript
import child_process from 'child_process';
const spawn = child_process.spawn;

...
```

しかし、こうするなら、むしろこう書いたほうが通りがいい？

```javascript
import child_process from 'child_process';

const ls = child_process.spawn('ls', ['-lh', '/usr']);
...
```