Electronでアプリ作成2　Node.jsのモジュールを活用

# 概要

　いまさらかもしれませんが、ElectronはNode.js環境でブラウザベースのデスクトップアプリケーションを作成できるフレームワークです。[前回](https://qiita.com/watmot/items/e79a3f3f8fe27fa54a33)は基本的なアプリ作成方法について、ご説明しました。
　今回は、もう一歩踏み込んで、Node.jsのモジュールを活用する方法を学びます。サンプルとしファイル操作を行うfsモジュールを利用したElectronアプリを紹介します。
　htmlファイルにJavascriptでコーディングしても、ブラウザからファイル操作は基本的にできません。Electronはブラウザベースのアプリですが、nodeベースのアプリでもあります。nodeのfsモジュールはファイル操作がでます。とういうことは、ブラウザベースのアプリもElectronなら、nodeの力をかりてファイル操作が、できるはずではありませんか！
　そうなんだ、じゃあ簡単に出来るんだ！と思っていたら、自分のようなJavascript知識レベルが浅はかな人間では、実行できるまで、まぁまぁ時間がかかってしまいました。自分のやろうとしていることの解説サイトも少ないようでした（検索力がないだけかも）。
　というわけで、ElectronがNode.jsのモジュールを活用するためのサンプルコードをできるだけ、簡略した形で書いております。今回も公式サイトを存分に活用しております。

https://www.electronjs.org/ja/docs/latest/tutorial/tutorial-preload

https://www.electronjs.org/ja/docs/latest/tutorial/ipc

# 準備1
ファイルの書き込みを行う、Electronアプリを作成します。以下の環境にて動作を確認しております。
 Windows11
 node.js v18.18.1
 electron v27.0.0
 
Promise型について理解があった方がよさげです。わかりやすい解説ありがとうございます。 

https://qiita.com/cheez921/items/41b744e4e002b966391a

https://qiita.com/soarflat/items/1a9613e023200bbebcb3

# 準備2
　まずは、前回と同様にElectronアプリの作成を行います。

 https://qiita.com/watmot/items/e79a3f3f8fe27fa54a33

この先、
・preload.js
・main.js
・index.html
を修正していきます。
# preload.js 
ざっくり言えば、以下の通りです。
・main.js　メインプロセス(Node.js)
・index.html　レンダラープロセス（ブラウザ表示）
・preload.js　メインとレンダラーの橋渡し
セキュリティ面からレンダラープロセスとメインプロセスを分離する必要があるので、その仲介役としてpreload.jsが存在しています。以下の例の場合、レンダラープロセスからwindow.myObj.appendTxt()にてメインプロセスにアプローチします。メインプロセスではチャンネル'appendTxt'を拾って、処理を実行します。

```preload.js
// preload.jsはレンダラーからNode.jsにアクセスするためのファイルです
const { contextBridge, ipcRenderer } = require('electron');

// メインプロセスから分離した処理をレンダラーに'myObj'として公開します
// preload.jsのinvokeとmain.jsのhandleがセットになっており
// invokeされたをチャンネル'appendTxt'をメインプロセスのhandleで拾う
contextBridge.exposeInMainWorld('myObj', {
    appendTxt: async (myText) => ipcRenderer.invoke('appendTxt', myText),
});
```
# main.js
app.whenReady内のipcMain.handleにてチャンネル'appendTxt'を拾って処理をします。第2引数がPromise型のコールバック関数として、処理を記述しています。プロジェクト内に「text.txt」ファイルをなければ作成し、fsモジュールを使って文字列を追記しています。コールバック関数はPromise型でなくてもよいようですが、asyncを外しても、なぜかPromise型で返るのだけど、、、理由はわかりません。サンプルコードもPromise型になってるので、第2引数はPromise型と思いこんどきます。

```main.js
// main.jsはNode.jsにて動作する requireにてelectronモジュールを読み込んでいる
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
//fsモジュールの読み込み
const fs = require('fs');

// ウィンドウの作成準備
const createWindow = () => {
    const win = new BrowserWindow({
        width: 800,   //ウィンドウサイズ
        height: 600,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js'), //プレローダーの指定
        }
    });
    // 読み込むファイル
    win.loadFile('index.html');
    // デベロッパーツールを開く　不要ならコメントアウト
    win.webContents.openDevTools();
}

//ウィンドウの初期化時
app.whenReady().then(() => {
    // preload.jsにてinvokeされたチャンネル'appendTxt'を拾い呼び出される
    // 第2引数がPromise型のコールバック関数になっています。
    ipcMain.handle('appendTxt', async (eve, myText) => {
        const writeString = new Date().toLocaleString() + ':' + myText + '\n';
        fs.appendFileSync('./text.txt', writeString, 'utf-8', (err) => {
            if (err) throw err;
        });
        return "書き込み文字列:" + writeString;
    });
    
    // ウィンドウの表示
    createWindow();
    // macOS用
    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) createWindow();
    });
})

// ウィンドウのクローズ
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') app.quit();
})

```
# index.html
window.myObj.appendTxt()を呼び出し、文字列をファイルに書き込みます。プロジェクトフォルダ内に「text.txt」が作成され、ボタンを押すたびに文字列が追記されます。

```index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>Electron基本2</title>
</head>

<body>
  <h1>Electron基本2</h1>
  <input type="text" id="text">
  <input type="button" id="button" value="書込">
  <script>
    const write = document.getElementById("button");
    const text = document.getElementById("text");
    write.onclick = () => {
      // preload.js経由にてメインプロセスの処理を呼び出します。
      window.myObj.appendTxt(text.value)
        .then((value) => { console.log(value) })
        .catch((reason) => { console.log(reason) });
    }
  </script>
</body>

</html>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536745/d4459897-d853-f1ad-d55e-f44c89f87f01.png)

できる限り、簡素なコーディングにしたつもりですが、いかがでしょうか。Electronでnodeの機能がフルに使えるとなると、やれることも広がるのではないでしょうか。つたない説明でしたが、読んでもらって感謝です。ありがとうございました。