シングル実行可能アプリケーション
[履歴]
バージョン | 変更点 |
---|---|
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
バイナリ自体、およびリソースをバイナリに注入できるツールを使用して、バンドルされたスクリプトからシングル実行可能アプリケーションを作成できます。
そのようなツールの 1 つである 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 の場合、メインスクリプトは v8.startupSnapshot.setDeserializeMainFunction()
API を呼び出して、ユーザーによって最終的な実行可能ファイルが起動されたときに実行する必要のあるコードを設定する必要があります。
シングル実行可能アプリケーションでスナップショットを使用するアプリケーションの典型的なパターンは次のとおりです。
スタートアップスナップショットスクリプトの一般的な制約は、シングル実行可能アプリケーションのスナップショットを構築するために使用されるメインスクリプトにも適用され、メインスクリプトは v8.startupSnapshot
API を使用してこれらの制約に適応できます。Node.js でのスタートアップスナップショットサポートに関するドキュメントを参照してください。
V8 コードキャッシュのサポート
構成で useCodeCache
が true
に設定されている場合、シングル実行可能ファイル準備 blob の生成中に、Node.js は main
スクリプトをコンパイルして V8 コードキャッシュを生成します。生成されたコードキャッシュは準備 blob の一部となり、最終的な実行可能ファイルに注入されます。シングル実行可能アプリケーションが起動されると、Node.js は main
スクリプトを最初からコンパイルする代わりに、コードキャッシュを使用してコンパイルを高速化し、スクリプトを実行します。これにより、起動パフォーマンスが向上します。
注記: useCodeCache
が true
の場合、import()
は動作しません。
注入された main スクリプト内
シングル実行可能アプリケーション 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])
Added in: v21.7.0, v20.12.0
sea.getAsset()
と同様ですが、結果を Blob
で返します。一致するアセットが見つからない場合はエラーがスローされます。
key
<string> シングル実行可能アプリケーション構成のassets
フィールドで指定されたディクショナリ内のアセットのキー。options
<Object>type
<string> Blob のオプションの mime タイプ。
戻り値: <Blob>
sea.getRawAsset(key)
Added in: 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 (Alpine を除く Node.js でサポートされているすべてのディストリビューション と、s390x を除く Node.js でサポートされているすべてのアーキテクチャ)
これは、他のプラットフォームでこの機能をテストするために使用できるシングル実行可能ファイルを生成するためのより良いツールがないためです。
他のリソースインジェクションツール/ワークフローに関する提案を歓迎します。 それらを文書化するために、https://github.com/nodejs/single-executable/discussions で議論を開始してください。