Nodeのバージョン18以上のLambdaでは、デフォルトファイルの拡張子がmjs
に変更されています。LambdaのNodeのバージョンを更新するにあたり、mjs
ファイルとjs
ファイルとの違いについて調査しました。結論として、拡張子の違いはJavaScriptのモジュールシステムの違いに由来しています。以下、詳細を解説します。
拡張子が.mjsと.jsのファイルの違い
前提
JavaScriptには2つの主要なモジュールシステムが存在します。
- CommonJs
- ECMAScript(ES Module, ES2015)
モジュールシステムと拡張子
- CommonJs: cjs(あまり一般的ではありません)
- ES Module: mjs
拡張子がjs
の場合は、package.json
内のtype
フィールドで指定されたモジュールシステムに従います。type
は"commonjs"
または"module"
のいずれかを指定できます。指定がない場合、デフォルトはCommonJSとなります。
実験
拡張子が.mjsのファイルでCommonJS形式で記述した場合
// lib.mjs
const sayHello = () => {
console.log("Hello")
}
module.exports = { sayHello }
// index.mjs
const { sayhello } = require("./lib")
exports.handler = async (event) => {
sayHello()
}
Lambdaを実行してみると、以下のエラーが表示されました。
{
"errorType": "ReferenceError",
"errorMessage": "require is not defined in ES module scope, you can use import instead",
"trace": [
"ReferenceError: require is not defined in ES module scope, you can use import instead",
" at file:///var/task/index.mjs:1:22",
" at ModuleJob.run (node:internal/modules/esm/module_job:218:25)",
" at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)",
" at async _tryAwaitImport (file:///var/runtime/index.mjs:1008:16)",
" at async _tryRequire (file:///var/runtime/index.mjs:1057:86)",
" at async _loadUserApp (file:///var/runtime/index.mjs:1081:16)",
" at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)",
" at async start (file:///var/runtime/index.mjs:1282:23)",
" at async file:///var/runtime/index.mjs:1288:1"
]
}
上記のLambdaを実行すると、require
がESモジュールスコープで定義されていないというエラーが表示されました。これは、拡張子が.mjs
のファイルがESモジュールとして解釈されるため、CommonJS形式での記述がサポートされていないことを意味します。同じコードを.jsの拡張子で実行すると、問題なく動作します。
結論
- 拡張子の違いは、JavaScriptのモジュールシステムの違いに基づく。
- Nodeのバージョン18以降のLambdaではデフォルトのファイル拡張子が
.mjs
になっているが、CommonJS形式を引き続き使用したい場合は、.js
(または.cjs
)の拡張子を持つファイルを使用する必要がある。
参照