どうやら最近の JavaScript はモジュール分割が可能になってきているようだ。
つまり別ファイルに定義したオブジェクトを import
や export
というキーワードで参照できるようだぞ、すげえ!
というので、フハフハしながら趣味のコードを 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']);
...
-
require
を使う由緒正しい Node JS 形式のモジュール参照スタイルを CommonJS、略して CJS って言うのですか? ↩ -
import
/export
キーワードを使ったスクリプトを、拡張子.js
でnode
インタープリターにわたすと解釈実行してくれない。 ↩ -
Node v8.9.4 および v9.5.0 で動作を確認。 ↩
-
あまりめでたくもない。 http://teppeis.hatenablog.com/entry/2017/08/es-modules-in-nodejs にあるように、まだまだ時期尚早という気がする。すでに半年以上経過しているというのに、だ! ↩