📦

Node.js SEAで単一実行可能.exeファイルを生成する

に公開

Node.jsのプログラムをWindows上で実行できるように.exeファイルに変換する方法は色々ありますが、開発終了しているものが多いです。Node.jsには、SEA(Single Executable Applictions)という機能があり、Node.jsがインストールされていない環境でもNode.js上で動くアプリケーションを簡単に配布できるようになります。

ただし、macOS上で.exeファイルを生成するには仮想環境のWindows上で実行する必要があります。

ビルドの手順

$
npm i -D esbuild postject

まずは、必要なnpmモジュールをインストールしましょう。esbuildはES modulesをCommonJS形式に変換するバンドラーで、postjectは.exeにBlobを注入するために使われます。

src/index.js
import process from 'node:process'
import {setTimeout} from 'node:timers/promises'

// .exeファイル実行時に自動で閉じてしまうのを防ぐ
process.stdin.resume()

!(async() => {
  await setTimeout(1000)
  console.log(process.version)
})()

簡単にNode.jsのバージョンを出力するJavaScriptファイルを作成してみます。Node.js v24時点でCommonJS形式のみの対応なので、esbuildで変換する際、Top Level Awaitでエラーが起きないようにasync IIFE(asyncの即時関数)で囲んでおきます。

$
npx esbuild src/index.js --outfile=dist/index.cjs --bundle --platform=node --format=cjs --minify

esbuildコマンドでES modulesからCommonJS形式に変換します。

sea-config.json
{
  "main": "dist/index.cjs",
  "output": "dist/index.blob",
  "disableExperimentalSEAWarning": true
}

Node.jsプログラムからBlobを生成するための設定ファイルを作成します。mainフィールドにはCommonJS形式に変換されたファイルのパス、outputフィールドには生成先のファイルパスを書いておきます。disableExperimentalSEAWarningは.exeファイル実行の際の警告を非表示にしてくれます。

$
node --experimental-sea-config sea-config.json

コマンドを実行して、注入するBlobを生成します。

$
node -e "fs.copyFileSync(process.execPath, 'dist/index.exe')"

Node.jsが含まれた.exeファイルを生成します。この.exeファイルを実行しても、Node.jsがバンドルされているだけなのでまだ何も実行されません。

$
npx postject dist/index.exe NODE_SEA_BLOB dist/index.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2

.exeファイルにBlobを注入します。これで.exeファイル起動時にNode.jsプログラムが実行されるようになります。

npm-scriptsで一元化

npm-scriptsを使って1つのコマンドで生成できるようにしていきます。

$
npm i -D npm-run-all2

npm-run-all2をインストールして、コマンドを直列や並列で実行できるようにします。

package.json
 {
   ...
+  "scripts": {
+    "build": "npm-run-all build:*",
+    "build:bundle": "esbuild src/index.js --outfile=dist/index.cjs --bundle --platform=node --format=cjs --minify",
+    "build:blob": "node --experimental-sea-config sea-config.json",
+    "build:exe": "node -e \"fs.copyFileSync(process.execPath, 'dist/index.exe')\"",
+    "build:inject": "postject dist/index.exe NODE_SEA_BLOB dist/index.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2",
+    "build:clean": "run-p build:clean:*",
+    "build:clean:blob": "node -e \"fs.rmSync('dist/index.blob', {recursive: true})\"",
+    "build:clean:cjs": "node -e \"fs.rmSync('dist/index.cjs', {recursive: true})\""
+  }
}

package.jsonにnpm-scriptsを追加します。これで、npm run buildとコマンドを打つだけで.exeファイルが生成されるようになります。

https://github.com/takamoso/node-sea-boilerplate

GitHubに今回の最小構成のプロジェクトを公開しているので参考にしてみてください。

Discussion