リマインダー開発Ⅱエレクトロンを
召喚する
エレクトロンをインストールし
開発環境を構築。
予定を保存可能にする。

image: リマインダー開発Ⅱエレクトロンを召喚する
Toplabo
Developing Reminder II

この記事は『リマインダーを開発する』の続きになります。先にそちらをご覧ください。

WEBアプリとして開発したカレンダーをエレクトロンへ移植する

 | 2025/09/21

開発
環境の
構築

エレクトロンの開発環境を構築しアプリを開発する
  • エレクトロンを導入する前に再び問う。なぜアプリ化?

    前章でも解説しましたが、WEBブラウザで展開するカレンダーでは、ブラウザを閉じると通知が来ない。
    デスクトップへ通知がこないと、気が付かないで予定時刻が過ぎ去ってしまう。
    これでは予定表をつくる意味がない。
    OSへインストールできるアプリにすると通知でサウンドも鳴らせ、さらにタスクバーへアイコンを表示させ、PC起動と同時にバックグランドで常時予定を監視し時間がきたら通知してくれる。
    つまり使えるリマインダーの条件はOSへインストールできることが重要になるのです。

    アプリ化はわかった、ではなぜエレクトロンか?

    ElectronはHTML / CSS / JavaScript で作ったWebアプリを、そのまま Windows / Mac / Linux のアプリに変換できる仕組みを持っている。つまり移植が楽にできる。開発コストが非常に少なくすむ。
    基盤がNode.js + Chromium(googleクロム)なので、他のアプリと違いHTML、CSS、Javascriptがそのまま使え、WEB技術がそのまま使える。アプリ専用のネイティブ言語を覚える必要がない。
    しかもOS専用のインストーラーを作成できる。windows の場合、EXEファイルのインストーラを書き出せる。
    余談ですが、VSCode、Slack、Discord もElectronで作られているそうです。

エレクトロンの開発環境を構築する
  • 構成内容

    ファイル構成は下記の様に構築します。
    第一章までで制作したindex.htmlはJS,CSSひとまとめにしてこの段落の下で確認できます。
    また、PHPは今後不要なので削除します。

    D:\xampp8\htdocs\shv-reminder
    ├index.html ← カレンダー 兼リマインダー
    ├main.js ← Electron起動用 
    └node_modules/ ← npm install electron したら自動でできる 

    リマインダーを開発する第1章迄で制作したindex.html:https://studio-happyvalley.com/wp/data/index.html.txt

Powershellを使いコマンドでインストール
  • まずはNode.js とElectronをインストール

    エレクトロンはNode.js上に展開されますので、まずはNode.jsをインストールします。
    ここからの作業はコマンド入力がメインになります。
    数行で済むので、コマンド入力が苦手な人でもなんとかなるかと思います。
    windowsはデフォルトのコマンドプロンプトもいいですが、私はpowershellを使ってます。
    さて、インストールする場所はプロジェクト用のフォルダを専用に作り、そこへインストールします。
    私は『shv-reminder』としました。
    ちなみに私の場合、毎回プロジェクトディレクトリへ行くのが面倒なのでpowerShellのショートカットに作業フォルダの場所を設定して使ってます。

    https://studio-happyvalley.com/wp/wp-content/uploads/fig1-23.png
    electronまでインストールする

    インストールサイズは5~600MBとかなりでかいですが、まあ、そういうもののようで気にせず進めましょう。(-_-;)
    ※Node.jsのインストールは割と環境に左右されず、楽にインストールできます。
    Electronのインストールまで完了したらルートに node_modulesフォルダ ができます。

    mkdir my-calendar
    cd my-calendar
    npm init -y
    npm install electron --save-dev
    Node.js の簡単な説明

    Node.js はElectron以外にもいろいろに使われます。Net上のJS関連プロジェクトはほとんどNode.jsでインストールできるものがほとんどです。私の場合は他に、WebPackで使ってます。
    さて、Node.js はシステム全体にインストールされる(例:C:\Program Files\nodejs\)のでプロジェクトのフォルダで npm install すると、そのフォルダに node_modules が作られます。
    このnode_modulesはプロジェクト別に必ず自動で作られます。名前は同じでも中身はまったく別、今回はelectron専用のモジュールになります。
    つまり、Electronは 「そのプロジェクトのフォルダ」 にインストールされるのが普通です
    そのためプロジェクトのサイズがかなり膨大になります。
    私の場合でいうと600MB以上になりました。
    なんとかしたいなと思ってます(-_-;)

必要ファイルの作成
  • そのほかの必要ファイルの作成:main.js

    これまでの作業でNode.jsとElectronが入りました。
    この後はpowershellから離れていくつかJSONファイルなど必要なファイルを作成します。
    まずはmain.jsを作成します。

    const { app, BrowserWindow, Notification } = require('electron');
    
    function createWindow() {
      const win = new BrowserWindow({
        width: 1000,
        height: 800,
        webPreferences: {
          nodeIntegration: true
        }
      });
    
      win.loadFile('index.html'); // ←これまで作ったカレンダーがそのまま使える!
    }
    
    app.whenReady().then(createWindow);
    
    app.on('window-all-closed', () => {
      if (process.platform !== 'darwin') app.quit();
    });
    
    そのほかの必要ファイルの作成:package.json

    package.json にスタートスクリプトを追記
    今後使うコマンドを設定します。
    これで Electronアプリとしてカレンダーが開くようになります。
    普通のブラウザじゃなく、アプリのウィンドウとして出てきます。

    "scripts": {
      "start": "electron ."
    }
    package.json にmain.jsを設定

    最初の方に'description'というプロパティがあるので
    "index.js"を"main.js"と修正。

    //package.jsonサンプル↓
    
      "name": "shv-reminder",
      "version": "1.0.0",
      "description": "Simple Reminder and Calendar",
      "main": "main.js",
    https://studio-happyvalley.com/wp/wp-content/uploads/fig2-17.png

アプリの
起動確認

いよいよアプリとして起動
  • powershellで実行

    これで、これまでブラウザで表示していたindex.htmlをアプリとして表示させる準備が整いました。
    powershellに戻って下記を入力

    npm start
    まずはカレンダーをアプリとして表示させました。

    ここまででブラウザで表示させていたカレンダーをElectronのアプリとして表示できました。
    ただし、現在はデータの保存をPHPで処理する設定のままなので、予定もローカルストレージへ簡易保存している状態です。
    今後はそれらをElectronで保存できるようにjavascriptを変更していきます。
    また、検証はshift+cntrl+iで開発ツールが開きます。
    今後、JSなどはそれを使って開発・検証していきます。

データ
の保存

ElectronはサーバーではないのでNode.jsを使ってデータを保存する
  • PHPをやめて fs.writeFileSync / fs.readFileSync で JSON ファイルを直に扱う

    これまでブラウザ版では localStorage や PHP を使って保存していましたが、Electron ではサーバーがありません。
    代わりに Node.js が提供するファイル操作機能(fs モジュール)を使って、JSON ファイルに直接書き込み/読み込みをする方法に切り替えます。
    つまり、サーバーサイドの仕組みを完全に削除して「アプリ内だけでデータが完結する」形になります。
    変更点はこの3つです。

    1. localStorage は完全削除
    2. fetch("save_notes.php") も削除
    3. データの保存と読み込みをfs.writeFileSync / fs.readFileSync に差し替え

Electron
で開発

javascriptでファイルを保存できる『fs』とは?
  • Electron は Node.js がベースになっているので、Node.js の標準ライブラリがそのまま使えます。

    その中でもファイル操作を担当するのが fs (File System) モジュールです。
    fs は「ファイルを読む/書く」ためのモジュールです。
    ブラウザでは localStorage に JSON を保存していましたが、Electron では fs を使って直接 JSON ファイルを読み書きします。
    ファイルを読み込む → fs.readFileSync("path", "utf-8")
    ファイルに書き込む → fs.writeFileSync("path", data, "utf-8")
    つまり、ブラウザの localStorage.setItem/getItem と同じ役割を 実ファイルに対して行うのが fs です。

ただし、このままだとセキュリティエラー
  • Electron の「レンダラープロセスでは require がそのまま使えない

    Electron はメインプロセス(Node.js が動く部分)レンダラープロセス(ブラウザ環境っぽい部分)の二つで動いています。
    現状、fsをブラウザパート(レンダラー)で読み込んでいますが、セキュリティの都合で require が無効になっているので、
    このままでは、エラー『Uncaught ReferenceError: require is not defined』 が出てしまいます。

    解決方法は セキュリティを無視か、preload.js を使う方法か

    本来はセキュリティ上の仕様でエラーになりますが、いったん開発用ということでレンダラー側でrequireを有効にする方法があります。
    もう一つは、preload.js を作ります。つまりメインプロセス側でデータの保存と読み込みをやってしまおうという方法です。
    ただ、この方法はかなり工程を踏み、electron側の構文を使いますので今回は、簡単なレンダラー側でrequireを有効にする方法で進めます。

    下記のようにmain.jsを書き換えます。

    function createWindow() {
      const win = new BrowserWindow({
        width: 1000,
        height: 800,
        webPreferences: {
          nodeIntegration: true,    // ←これでレンダラで require が使える
          contextIsolation: false,  // ←合わせて false に
          enableRemoteModule: false
        }
      });
prompt()が使えないため、保存ファイル名を固定で進める
  • 保存先はいったん固定で作業を進める

    Electronではこれまでユーザー名の登録に使用していた、手軽なprompt()が使えません。
    となると、登録フォームを作ったり、JSをモジュール化し小分けするなど、今回の最重要のゴールの『electronでリマインダー通知』の趣旨から大きく外れますので、保存先は、ひとまずプロジェクトのルートに data/cal-default.json を置くようにします。
    (実際にインストールアプリにした時の保存先については、次回の記事で解説します。)

現状コードにどう組み込むか?
  • 1. currentUserKey廻りを置き換える

    現状jsのトップに let currentUserKey = null;がありますがそのあとにディレクトリ関連のコードを追加します。
    仮にユーザーキーを『cal-default'』で進めます。

    let currentUserKey = 'cal-default';
    const fs = require("fs");
    const path = require("path");
    const dataDir = path.join(__dirname, "data");
    const dataFile = path.join(dataDir, `${currentUserKey}.json`);
    
    2. getAllNotes を置き換える

    次に読み込み箇所を置き換えます。
    localStorage 部分を fs に置き換えるのがポイントです。
    async function initUser() {//このブロックは不要になったのですべて削除します。

    // 旧: localStorage
    function getAllNotes() {
      const notes = localStorage.getItem(currentUserKey);
      return notes ? JSON.parse(notes) : {};
    }
    // 新: fs版
    
    // ディレクトリがなければ作る
    function ensureDir() {
      if (!fs.existsSync(dataDir)) {
    	fs.mkdirSync(dataDir, { recursive: true });
      }
    }
    
    function loadNotes() {
    	ensureDir();
    	if (!fs.existsSync(dataFile)) return {};
    	const raw = fs.readFileSync(dataFile, "utf-8");
    	return JSON.parse(raw || "{}");
    }
    
    function saveNotes(notes) {
      fs.writeFileSync(dataFile, JSON.stringify(notes, null, 2), "utf-8");
    }
    
    let notes = loadNotes();
    
    // --- ノートの操作関数は現状のままでOK ---
    function getAllNotes() {
    	  return loadNotes();
    }
    3.saveNoteToStorageを置き換える

    function saveNoteToStorage() {のスコープ内すべてを書き換え

    // 旧: localStorage + fetch(PHP)
    function saveNoteToStorage() {
      let notes = getAllNotes();
      notes[date] = { title, body };
      localStorage.setItem(currentUserKey, JSON.stringify(notes));
    
      fetch("save_notes.php", { ... }); 
    }
    

    これを fs に置き換えると:

    // 新: fsのみ
    function saveNoteToStorage() {
    	const title = document.getElementById("noteInputTitle").value.trim();
    	const body = document.getElementById("noteInputBody").value.trim();
    	let notes = getAllNotes();
    	
    	if (date) {   // ← ここで date が undefined でないことを確認
    		 if (title || body) {
    			notes[date] = { title, body };
    		  } else {
    			delete notes[date];
    		  }
    	}
    	saveNotes(notes); // ←これで完了!
    	closeModal();
    	renderCalendar();
    }

    最後に不要になったinitUser();を削除すれば完了です。
    これで、data/cal-default.json に日付ごとの予定が保存されるようになります。
    試しに予定を記入してjsonファイルを開いてみると、こんな感じの JSON が記録されています。

    {
      "2025-09-01": {
        "title": "会議",
        "body": "14:00からオンラインミーティング"
      }
    }
    
    https://studio-happyvalley.com/wp/wp-content/uploads/fig4-7.png

第二章
まとめ

予定を入れられるデスクトップカレンダー、アプリ版の完成
  • Electronへ移植できました。

    ここまでで、ブラウザ版で使っていた「予定を書いて保存する」仕組みを、Electron アプリに移行できました。
    保存した内容は JSON ファイルとして残るので、アプリを閉じても再起動しても、同じ予定を呼び出せます。
    これで 「予定を書き込めるデスクトップカレンダー」 のアプリ版が完成です。
    次回以降は、いよいよ通知機能やタスクバーアイコン、そしてアプリのビルドについて解説していきます。

    ※注意 今回の方法は素早く動かすための「開発用設定」です。
    nodeIntegration: true は便利ですが、外部コンテンツを扱うアプリでそのままにすると危険です。
    本番アプリとして使用する際はpreload.js+IPC 方式でファイル操作だけを限定的に許可する安全構成に切り替えましょう(最終章で詳述します)。
    動作確認後はただちにアプリを終了していただくことをお勧めします。

この記事の続きは『リマインダー開発Ⅲ』でご覧になれます。

『リマインダー開発Ⅱ.エレクトロンを召喚する』関連のお薦め

このサイトで紹介しているコード、プログラムなどは個人の学習目的で作成されたものであり、いかなる保証も行いません。
利用はすべて自己責任でお願いします。
ただし、このページで紹介しているプログラムやビジュアルなどはご依頼いただければ実装を賜ります。
お問い合わせはこちら