ASAR アーカイブ
アプリケーションの頒布形式 を作成した後、大抵はアプリのソースコードを ASAR アーカイブ へバンドルします。これは Electron アプリ向けに設計されたシンプルで広範なアーカイブフォーマットです。 アプリをバンドルすることで、Windows の長いパス名に関する問題を軽減し、require
を高速化し、ソースコードを簡単に覗けないようにできます。
バンドルされているアプリは仮想ファイルシステム上で動作し、ほとんどの API は通常通り動作しますが、いくつかの注意点があるほか、明示的に ASAR アーカイブを操作したいケースもあるでしょう。
ASAR アーカイブを使用する
Electron には、2 組の API があります。Node.js により提供される Node API、そして Chromium により提供されるウェブ API です。 どちらの API も ASAR アーカイブからのファイル読み込みに対応しています。
Node API
Electron では Node.js に特別なパッチを当てており、fs.readFile
や require
のような Node API は ASAR アーカイブを仮想ディレクトリのように扱い、そのファイルをファイルシステム上の通常のファイルのように扱います。
例えば、/path/to
配下に、example.asar
アーカイブがあると仮定します:
$ asar list /path/to/example.asar
/app.js
/file.txt
/dir/module.js
/static/index.html
/static/main.css
/static/jquery.min.js
以下で ASAR アーカイブ内のファイルを読み込みます。
const fs = require('node:fs')
fs.readFileSync('/path/to/example.asar/file.txt')
アーカイブのルート配下にあるすべてのファイルの一覧を取得する:
const fs = require('node:fs')
fs.readdirSync('/path/to/example.asar')
アーカイブからモジュールを使用する:
require('./path/to/example.asar/dir/module.js')
以下のように BrowserWindow
で ASAR アーカイブ内のウェブページを表示できます。
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
win.loadURL('file:///path/to/example.asar/static/index.html')
Web API
ウェブページで、アーカイブ内のファイルを file:
プロトコルでリクエストできます。 Node API と同様に、ASAR アーカイブはディレクトリのように扱われます。
例えば、$.get
でファイルを取得するには:
<script>
let $ = require('./jquery.min.js')
$.get('file:///path/to/example.asar/file.txt', (data) => {
console.log(data)
})
</script>
ASAR アーカイブを通常のファイルのように扱う
ASAR アーカイブそのもののチェックサムを検証する等のいくつかのケースでは、ASAR アーカイブをファイルとして読み込む必要があります。 この目的のために、 asar
サポートしないオリジナルの fs
API を提供するビルトインの original-fs
モジュールを使用できます。
const originalFs = require('original-fs')
originalFs.readFileSync('/path/to/example.asar')
もしくは、process.noAssar
に true
をセットして fs
モジュールの asar
サポートを無効にすることができます:
const fs = require('node:fs')
process.noAsar = true
fs.readFileSync('/path/to/example.asar')
Node API の制限
Node API では ASAR アーカイブがディレクトリのように動作するよう可能な限り懸命に工夫していますが、低レベル環境での Node API に起因した制限がいくつかあります。
アーカイブは読み取り専用
ASAR アーカイブは変更できないため、ファイルを変更できる全ての Node API は ASAR アーカイブに対して動作しません。
作業ディレクトリは、アーカイブ内のディレクトリに設定できない
ASAR アーカイブはディレクトリのように扱われるにも関わらず、ファイルシステム上には実際のディレクトリが存在しないため、ASAR アーカイブ内のディレクトリを作業ディレクトリとして設定することはできません。 いくつかの API の cwd
の引数としてアーカイブ内のディレクトリを渡すのも同様にエラーの原因になります。
いくつかの API で余分な展開がされる
たいていの fs
API は、展開せずに ASAR アーカイブからファイルを読み込んだり、ファイル情報を取得できます。しかし、システムコールに実際のファイルパスを渡すようになっているいくつかの API では、Electron は必要なファイルを一時ファイルとして展開し、API に一時ファイルのパスを渡して、API が動作するようにします。 このため、当該 API には多少のオーバーヘッドがあります。
追加の展開が必要なAPIです:
child_process.execFile
child_process.execFileSync
fs.open
fs.openSync
process.dlopen
- ネイティブモジュールのrequire
で使用されます。
fs.stat
の偽の統計情報
asar
アーカイブ内のファイルはファイルシステム上に存在しないので、fs.stat
および asar
アーカイブ内のファイルへの関連情報によって返されるStats
オブジェクトは、推測して生成されます。 ファイルサイズの取得とファイルタイプのチェックを除いて、 Stats
オブジェクトを信頼すべきではありません。
ASAR アーカイブ内のバイナリを実行する
child_process.exec
、child_process.spawn
、child_process.execFile
のようなバイナリを実行できる Node API があります。しかし ASAR アーカイブ内のバイナリの実行をサポートしているのは execFile
のみです。
なぜならば、exec
と spawn
は入力として file
の代わりに command
を受け取り、command
はシェル配下で実行されるからです。 コマンドが asar アーカイブ内のファイルを使うかどうかを決定するための信頼できる方法はありませんし、そうするとしてもコマンドで使うファイルパスを副作用なしに置き換えることができるかどうかを確認することはできません。
ASAR アーカイブへパックされていないファイルを追加する
上で述べたように、いくつかの Node API は、呼び出されたときにファイルをファイルシステムに解凍します。 パフォーマンスの問題とは別に、この動作によってさまざまなウイルス対策スキャナが起動される可能性があります。
回避策として、--unpack
オプションを使用して様々なファイルを解凍したままにできます。 以下の例では、ネイティブ Node.js モジュールの共有ライブラリはパッケージされません。
$ asar pack app app.asar --unpack *.node
コマンドを実行すると、app.asar.unpacked
という名前のフォルダが app.asar
ファイルとともに作成されていることがわかります。 それには解凍されたファイルが含まれており、app.asar
アーカイブと共に送られる必要があります。