LoginSignup
41
33

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-02-14

どうやら最近の 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 にあるように、まだまだ時期尚早という気がする。すでに半年以上経過しているというのに、だ! 

41
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
33