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

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

世の中はまだ require によるモジュール参照

Node で紹介されているコードの断片をみても require 使ったものしかないじゃない1? たとえばこんなの。

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 を使いたいんだよ! そういうサンプルと、それをどう動かすかを知りたいんだよ!

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

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 とすると、実行できない2ので注意!)

そして node インタープリターで実行3

node --experimental-modules main.mjs

すると確かに /usr 配下のファイルリスティングが得られる4

(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

めでたし5

[参考1] 拡張子 .js での実行時エラーメッセージ

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

(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 モジュールの参照

先の書き直し例の冒頭、こう書いた。

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

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

import { child_process } from 'child_process';

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

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

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

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

...

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

import child_process from 'child_process';

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

  1. require を使う由緒正しい Node JS 形式のモジュール参照スタイルを CommonJS、略して CJS って言うのですか? 

  2. import/export キーワードを使ったスクリプトを、拡張子 .jsnode インタープリターにわたすと解釈実行してくれない。 

  3. https://qiita.com/euxn23/items/c75c0ba08709ab446faf 

  4. Node v8.9.4 および v9.5.0 で動作を確認。 

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

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.