TL;DR
electron-builder
を使用しているElectronアプリをmacOSの「このアプリケーションで開く」に対応させます。
さらに、アプリ内でファイルパスを受け取る方法も取り上げます。

行儀のいいエディターの挙動
特定の形式のファイルを編集するためのエディターをElectronで作る時など、特定のファイル形式と自作アプリケーションを関連づけたい時があると思います。
Macで動作する他のエディターの代表としてVisual Studio Codeを観察してみました。
以下のような挙動があることがわかります。
「このアプリケーションで開く」に表示される

「推奨アプリケーション」で明るく表示される

ファイルにアイコンが設定されている
JSONファイルに専用のアイコンが設定されています。
(余談ですが、上の画像にてJSONファイルに設定されているアイコンはVSCodeのものではなく、Adobe製品によるものだと思われます。)
ダブルクリックすると開く
当たり前ですが、ファイルアイコンをダブルクリックしたり、「このアプリケーションで開く」にてVSCodeを選択すると、VSCodeでそのファイルが開かれます。
これらの挙動はInfo.plistによるもの
世の多くのエディターが上にあげたものと同様の挙動をするにもかかわらず、あなたの作ったエディターは「このアプリケーションで開く」に現れませんし、「推奨アプリケーション」にてグレーアウトされると思います。
それは、Info.plist
を正しく設定していないからです。
試しにVisual Studio CodeのInfo.plistを覗いてみましょう。
Finderからアプリケーション→Visual Studio Codeを右クリック→パッケージの内容を表示 です。

plist
はプロパティリストの略だそうで、いろんな値が設定されています。
その中にDocument types
という項目があり、しっかりとJSONファイルも定義されています。
このDocument typesを適切に設定してあげることで、自作のアプリケーションも「このアプリケーションで開く」に対応させ、「推奨アプリケーション」に表示させることができます。
ビルド時にInfo.plistに項目を追記する
electron-builderには、このInfo.plistに項目を追記する機能が備わっています。
package.jsonに以下のように extendInfo
の項目を追加します。
{
"build": {
"productName": "My Awesome Application",
"appId": "com.wararyo.myawesomeapplication",
"mac": {
"icon": "build/icons/icon.icns",
"extendInfo": {
"CFBundleDocumentTypes":[
{
"CFBundleTypeExtensions": ["myextention"],
"CFBundleTypeIconFile": "My Awesome Application.icns"
}
]
}
}
}
}
この例では、このアプリケーションが拡張子.myextention
のファイルに対応していると認識されるように記述します。ついでにアプリアイコンを割り当てています。
あとはいつものようにbuild、dmgファイルを開きインストールすれば、「このアプリケーションで開く」に現れています。

ちなみに私は.eclairconversation
という拡張子を編集するためのアプリケーション Eclair Conversation Editor
を作成しました。
第2引数にファイルパスは渡らない
Windowsにてファイルを開くときはファイルパスが第2引数に渡るので、以下のようなコードでパスを取得することができます。
//引数でファイル指定があったらそれを開く
if(process.argv.length > 1) {
var p = process.argv[1];
if(p.match(/\.[a-zA-Z]+$/)) load(p);
}
この方法はMacでは使えません。
Mac版のElectronにおいては起動時にopen-file
というイベントがapp
に対して渡るので、そこでファイルパスを取得します。
ファイルを開くとともにアプリケーションが起動する場合の他に、すでに起動しているアプリケーションでファイルを開く場合にもこのイベントが発行されます。
import { app } from 'electron'
app.on('open-file', (event,path) => {
event.preventDefault();
load(path);
});
ただし、ファイルを開くとともにアプリケーションが起動する場合、open-file
が発行されるタイミングではまだmainWindowが立ち上がっていないと思います。
そのときは一旦別の変数に退避させ、また別のタイミングでファイルを開く処理を行います。
これに関してはアプリケーションによって実装が異なってくるかと思います。参考に私のアプリケーションでの実装を貼っておきます。electron-vue
を使用しています。
var mainWindow;
app.on('open-file', (event,path) => {
event.preventDefault();
if(mainWindow === void 0) process.openFile = path;
else mainWindow.webContents.send("Load",path);
});
app.on('ready', () => {
mainWindow = new BrowserWindow({
height: 560,
useContentSize: true,
width: 680,
webPreferences: {
nodeIntegration: true
}
})
mainWindow.loadURL(winURL)
mainWindow.on('closed', () => {
mainWindow = null
})
});
<script>
const electron = require('electron');
const { ipcRenderer } = require('electron');
const remote = electron.remote;
export default {
mounted(){
//引数でファイル指定があったらそれを開く
if(remote.process.argv.length > 1) {
var p = remote.process.argv[1];
if(p.match(/\.[a-zA-Z]+$/)) this.load(p);
}
//Macでファイル指定があったらそれを開く
if(remote.process.openFile !== void 0) {
if(remote.process.openFile.match(/\.[a-zA-Z]+$/)) this.load(remote.process.openFile);
}
ipcRenderer.on('Load',(event,path) => this.load(path));
}
}
</script>