単一実行可能アプリケーション
[履歴]
バージョン | 変更点 |
---|---|
v20.6.0 | "useSnapshot" のサポートを追加 |
v20.6.0 | "useCodeCache" のサポートを追加 |
v19.7.0, v18.16.0 | 追加: v19.7.0, v18.16.0 |
ソースコード: src/node_sea.cc
この機能により、Node.js がインストールされていないシステムに Node.js アプリケーションを簡単に配布できます。
Node.js は、バンドルされたスクリプトを含むことができる Node.js によって準備された BLOB をnode
バイナリに挿入することで、単一実行可能アプリケーションの作成をサポートしています。起動時に、プログラムは何かが挿入されているかどうかをチェックします。BLOB が見つかった場合、BLOB 内のスクリプトを実行します。それ以外の場合は、Node.js は通常どおり動作します。
単一実行可能アプリケーション機能は現在、CommonJSモジュールシステムを使用した単一の埋め込みスクリプトの実行のみをサポートしています。
ユーザーは、バンドルされたスクリプトとnode
バイナリ自体、およびバイナリにリソースを挿入できるツールを使用して、単一実行可能アプリケーションを作成できます。
このようなツールの一つであるpostjectを使用した単一実行可能アプリケーションの作成手順を以下に示します。
単一実行可能準備 BLOB の生成
アプリケーションに挿入される単一実行可能準備 BLOB は、単一実行可能ファイルのビルドに使用される Node.js バイナリの--experimental-sea-config
フラグを使用して生成できます。これは、JSON 形式の構成ファイルへのパスを取ります。渡されたパスが絶対パスでない場合、Node.js はカレントワーキングディレクトリを基準とした相対パスを使用します。
構成は現在、以下のトップレベルフィールドを読み取ります。
{
"main": "/path/to/bundled/script.js",
"output": "/path/to/write/the/generated/blob.blob",
"disableExperimentalSEAWarning": true, // デフォルト: false
"useSnapshot": false, // デフォルト: false
"useCodeCache": true, // デフォルト: false
"assets": {
// オプション
"a.dat": "/path/to/a.dat",
"b.txt": "/path/to/b.txt"
}
}
パスが絶対パスでない場合、Node.js はカレントワーキングディレクトリを基準とした相対パスを使用します。BLOB を生成するために使用される Node.js バイナリのバージョンは、BLOB が挿入されるバージョンと同じでなければなりません。
注: クロスプラットフォーム SEA(例: darwin-arm64
でlinux-x64
の SEA を生成)を生成する場合、互換性のない実行可能ファイルの生成を避けるため、useCodeCache
とuseSnapshot
を false に設定する必要があります。コードキャッシュとスナップショットは、コンパイルされたのと同じプラットフォームでのみロードできるため、異なるプラットフォームでビルドされたコードキャッシュまたはスナップショットのロードを試行すると、生成された実行可能ファイルが起動時にクラッシュする可能性があります。
アセット
assets
フィールドとしてキーパス辞書を構成に追加することで、ユーザーはアセットを含めることができます。ビルド時に、Node.js は指定されたパスからアセットを読み取り、準備用 BLOB にバンドルします。生成された実行可能ファイルでは、ユーザーはsea.getAsset()
とsea.getAssetAsBlob()
API を使用してアセットを取得できます。
{
"main": "/path/to/bundled/script.js",
"output": "/path/to/write/the/generated/blob.blob",
"assets": {
"a.jpg": "/path/to/a.jpg",
"b.txt": "/path/to/b.txt"
}
}
シングル実行可能アプリケーションは、以下のようにアセットにアクセスできます。
const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea')
// ArrayBuffer内のデータのコピーを返します。
const image = getAsset('a.jpg')
// アセットからUTF8としてデコードされた文字列を返します。
const text = getAsset('b.txt', 'utf8')
// アセットを含むBlobを返します。
const blob = getAssetAsBlob('a.jpg')
// コピーせずに生の資産を含むArrayBufferを返します。
const raw = getRawAsset('a.jpg')
詳細については、sea.getAsset()
、sea.getAssetAsBlob()
、およびsea.getRawAsset()
API のドキュメントを参照してください。
スタートアップスナップショットサポート
useSnapshot
フィールドを使用して、スタートアップスナップショットサポートを有効にすることができます。この場合、最終実行可能ファイルが起動されたときにmain
スクリプトは実行されません。代わりに、ビルドマシンでシングル実行可能アプリケーションの準備用 BLOB が生成されたときに実行されます。生成された準備用 BLOB には、main
スクリプトによって初期化された状態をキャプチャするスナップショットが含まれます。準備用 BLOB が挿入された最終実行可能ファイルは、実行時にスナップショットをデシリアライズします。
useSnapshot
が true の場合、main
スクリプトは、v8.startupSnapshot.setDeserializeMainFunction()
API を呼び出して、ユーザーによって最終実行可能ファイルが起動されたときに実行する必要があるコードを構成する必要があります。
シングル実行可能アプリケーションでスナップショットを使用するアプリケーションの典型的なパターンは次のとおりです。
スタートアップスナップショットスクリプトの一般的な制約は、シングル実行可能アプリケーションのスナップショットの構築に使用されるmain
スクリプトにも適用され、main
スクリプトはv8.startupSnapshot
APIを使用してこれらの制約に適応できます。Node.js のスタートアップスナップショットサポートに関するドキュメントを参照してください。
V8 コードキャッシュサポート
設定でuseCodeCache
をtrue
に設定すると、単一実行ファイルの準備ブロブ生成中に、Node.js はmain
スクリプトをコンパイルして V8 コードキャッシュを生成します。生成されたコードキャッシュは準備ブロブの一部となり、最終実行ファイルに挿入されます。単一実行ファイルアプリケーションが起動されると、Node.js はmain
スクリプトを最初からコンパイルする代わりに、コードキャッシュを使用してコンパイルを高速化してからスクリプトを実行するため、起動パフォーマンスが向上します。
注記: useCodeCache
がtrue
の場合、import()
は機能しません。
挿入されたメインスクリプト内
単一実行ファイルアプリケーション API
node:sea
組み込みモジュールを使用すると、実行ファイルに埋め込まれた JavaScript メインスクリプトから単一実行ファイルアプリケーションと対話できます。
sea.isSea()
追加されたバージョン: v21.7.0, v20.12.0
- 戻り値: <boolean> このスクリプトが単一実行ファイルアプリケーション内で実行されているかどうか。
sea.getAsset(key[, encoding])
追加されたバージョン: v21.7.0, v20.12.0
このメソッドは、ビルド時に単一実行ファイルアプリケーションにバンドルするように設定されたアセットを取得するために使用できます。一致するアセットが見つからない場合は、エラーがスローされます。
key
<string> 単一実行ファイルアプリケーション設定のassets
フィールドで指定されたディクショナリ内にあるアセットのキー。encoding
<string> 指定されている場合、アセットは文字列としてデコードされます。TextDecoder
でサポートされているエンコーディングであればどれでも使用できます。指定されていない場合、アセットのコピーを含むArrayBuffer
が代わりに返されます。- 戻り値: <string> | <ArrayBuffer>
sea.getAssetAsBlob(key[, options])
追加日時: v21.7.0, v20.12.0
sea.getAsset()
と似ていますが、結果を Blob
として返します。一致するアセットが見つからない場合、エラーがスローされます。
key
<string> シングル実行アプリケーション設定のassets
フィールドで指定された辞書内にあるアセットのキー。options
<Object>type
<string> Blob のオプションの MIME タイプ。
戻り値: <Blob>
sea.getRawAsset(key)
追加日時: v21.7.0, v20.12.0
このメソッドは、ビルド時にシングル実行アプリケーションにバンドルされるように設定されたアセットを取得するために使用できます。一致するアセットが見つからない場合、エラーがスローされます。
sea.getAsset()
や sea.getAssetAsBlob()
とは異なり、このメソッドはコピーを返しません。代わりに、実行可能ファイル内にバンドルされた生の資産を返します。
現時点では、返された配列バッファへの書き込みは避けるべきです。挿入されたセクションが書き込み可能としてマークされていない場合、または適切にアラインされていない場合、返された配列バッファへの書き込みはクラッシュを引き起こす可能性があります。
key
<string> シングル実行アプリケーション設定のassets
フィールドで指定された辞書内にあるアセットのキー。- 戻り値: <ArrayBuffer>
挿入されたメインスクリプト内の require(id)
はファイルベースではありません
挿入されたメインスクリプト内の require()
は、挿入されていないモジュールで使用可能な require()
とは異なります。また、挿入されていない require()
が持つプロパティrequire.main
を除いて、どのプロパティも持ちません。組み込みモジュールをロードするためだけに使用できます。ファイルシステム内でのみ見つかるモジュールをロードしようとすると、エラーがスローされます。
ファイルベースの require()
に依存する代わりに、アプリケーションをスタンドアロンの JavaScript ファイルにバンドルして実行可能ファイルに挿入できます。これにより、より決定論的な依存関係グラフが保証されます。
ただし、ファイルベースの require()
がまだ必要な場合は、次のように実現することもできます。
const { createRequire } = require('node:module')
require = createRequire(__filename)
注入されたメインスクリプトにおける__filename
とmodule.filename
注入されたメインスクリプトにおける__filename
とmodule.filename
の値は、process.execPath
と等しくなります。
注入されたメインスクリプトにおける__dirname
注入されたメインスクリプトにおける__dirname
の値は、process.execPath
のディレクトリ名と等しくなります。
注意点
単一実行ファイルアプリケーション作成プロセス
単一実行ファイル Node.js アプリケーションを作成することを目的としたツールは、--experimental-sea-config"
で準備された blob の内容を以下に注入する必要があります。
node
バイナリがPEファイルの場合、NODE_SEA_BLOB
という名前のリソースにnode
バイナリがMach-Oファイルの場合、NODE_SEA
セグメント内のNODE_SEA_BLOB
という名前のセクションにnode
バイナリがELFファイルの場合、NODE_SEA_BLOB
という名前のノートに
バイナリ内でNODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0
fuse文字列を検索し、最後の文字を1
に反転させて、リソースが注入されたことを示します。
プラットフォームサポート
単一実行ファイルのサポートは、以下のプラットフォームでのみ CI で定期的にテストされています。
- Windows
- macOS
- Linux(Node.js でサポートされているすべてのディストリビューション(Alpine を除く)およびNode.js でサポートされているすべてのアーキテクチャ(s390x を除く))
これは、他のプラットフォームでこの機能をテストするために使用できる、より優れた単一実行ファイル生成ツールの不足が原因です。
他のリソース注入ツール/ワークフローに関するご提案は大歓迎です。それらを文書化するために、https://github.com/nodejs/single-executable/discussionsでディスカッションを開始してください。