Node.jsの新しいモジュール方式の実験的導入
💁♀️ ブログが移管されたので,新しい方へ移動します。
- 💁♀️ ブログが移管されたので,新しい方へ移動します。
- 内容まとめ
- ESM事前知識
- PR
- --es-module-specifier-resolution
- リゾルバアルゴリズム
- 使用法
- 特定ファイルのモジュール形式をロックしたい場合
Node.jsのCoreへESMとCJSの新しい方式が実験的フェイズ(stability: 1)として入ります。
ESM対応は安定化までのプランとしてステージを4つ(0 -3)用意しており、現在が2です。
2019年の10月に実験的から安定的へ移行するのが最終目標となります。(stage:3)
内容まとめ
https://github.com/nodejs/modules/blob/announcement/doc/announcement.mdgithub.com
--es-module-specifier-resolution=node|explicit
で処理解決方法を決定するexplicit
がデフォルト
--entry-type=commonjs|module
でCJSかESMかを決定する- デフォルトは近しい親にあるpackage.jsonの
type
フィールドを参照する
- デフォルトは近しい親にあるpackage.jsonの
- ESMではデフォルトでjsonは読み込めない
--experimental-json-modules
を付ける必要がある
- CJSとESMの違い
- ESMの場合、拡張子が必須
NODE_PATH
がないrequire
,exports
,module.exports
,__filename
,__dirname
がないmodule.createRequireFromPath
、及びimport.meta.url
を使いましょう
require.extensions
,require.cache
の使用不可- URL-basedのパス指定
とりあえず、package.jsonにtype
フィールド追加すると、そのスコープ内の.js
ファイルはそのモジュールタイプになるよって覚えておけばいいです。
ESM事前知識
以下の記事を読んでください。
PR
CoreへのPR
初期提案実装
--es-module-specifier-resolution
explicit
と node
が存在し、デフォルトはexplicit
です。
違いは以下の通りとなります。
- 拡張子を省略することができない
index
を許容しない
まだ、変更される可能性が高いため注意が必要です。
今までどおりの挙動を望むのであれば、node
を指定する必要があります。
リゾルバアルゴリズム
type
フラグがmodule
の場合、package.jsonを軸に次の package.json までにネストされたフォルダとサブフォルダをすべて ESM とみなす仕様(そしてつぎpackage.jsonのフラグがmodule
の場合は続く)
もしpackage.jsonがない場合は、デフォルトでcommonjsとなります。
使用法
実験的なフェイズなため、実行時に--experimental-modules
フラグが必要です。
.mjs
がエントリーポイントの場合
この場合は、デフォルトでESMとして読み込みます。
node --experimental-modules index.mjs
また、上記の実行の場合、エントリーポイントからESM形式でimportするファイルは.mjs
である必要があります。
しかし、--entry-type
フラグ及び、package.jsonのtype
にmodule
を指定すると、.mjs
という拡張子を使うことなく、ESMとして読み込むことが可能となります。
// package.json { "type": "module" }
--entry-type
このフラグには、commonjs
とmodule
の2つの設定が存在します。
module
が指定された場合、.js
, .mjs
, 拡張子がないファイルはESMとして呼び出されます。
この指定がない場合、デフォルトはcjs
です。
$ node --experimental-modules --entry-type=module --eval \ "import { sep } from 'path'; console.log(sep);" /
$ node --experimental-modules --entry-type=commonjs --eval \ "import { sep } from 'path'; console.log(sep);" import { sep } from 'path'; console.log(sep); ^ SyntaxError: Unexpected token {
package.jsonのtypeフィールド
--entry-type
のpackage.jsonに書く版です。
最も近い親のpackage.jsonのtype
フィールドを参照し、モジュール方式を決定していきます。
一般的に今までのnode_modulesのpackage.jsonはtype
フィールドを持たないため、commonjsで読み込まれ互換性を保つことが期待されます。
// sample/package.json { "type": "module" }
// ./sample/index.js // 近しいpackage.jsonのtypeがmoduleなので、このファイルはESMで読み込まれる import './sample/setup/init.js'; // ./node_modules/foo/package.jsonにはtypeが書いてないため、CJSで読み込まれる import 'foo';
特定ファイルのモジュール形式をロックしたい場合
ユーザーが表現できる拡張子は、.js
, .mjs
, .cjs
となります。
type: module|commonjs
以下では、.js
はそれに従います。
つまり、特定のファイルに対して拡張子で操作することになります。
type:module
以下でcommonjsとして扱いたいファイルに対しては、.cjs
の拡張子にするtype:commonjs
以下でesmとして扱いたいファイルに対しては、.mjs
の拡張子にする
// 常にcommonjsとして読み込む import './legacy-file.cjs'; // 常にesmとして読み込む import 'commonjs-package/src/index.mjs';