HB's Thoughts

I try to think really hard.

26.08.2024 г.

Electron, TypeScript и Parcel

Документацията на Electron е изцяло за JavaScript, но това не пречи да си използвате TypeScript, с който да генерира този JavaScript. Трябва да се спазват няколко простички правила, и то основно в пътищата на зареждане на файловете. Подготвил съм и малко допълнение за front-end частта. Вместо стандартната за Electron HTML страница ще направя малка компилация с Parcel.

Проекта

Най-напред ще си организираме проекта. В него ще има 2 подпапки - едната за Electron частта, др. за Browser частта. Създаваме папка electron-typescript-parcel и я отваряме във VSCode - или който редактор ползвате. Отваряме вградения във VSCode терминал (или друг ако не ползвате VSCode) и изпълнявате:

npm init -y

Това ще създаде package.json файл в папката electron-typescript-parcel. Отваряте файла и редактирате полето author. Като начало е достатъчно. Следва да добавим Electron модула в проекта.

npm install --save-dev electron

Ако ще ползвате GIT, сега е добре да изпълните:

git init

и да добавите .gitignore файл. В него добавете като начало добавяме node_modules.

След това добавяме 2 папки - electron и browser в папката на проекта. Както подсказват имената - в първата ще живее Electron, а във втората - front-end частта за браузъра.

Electron

През терминала влизаме в папката electron и изпълняваме:

npm init -y

и веднага след това добавяме и TypeScript модула:

npm install --save-dev typescript

Зареждаме package.json. В script частта добавяме "build": "tsc" и махаме "main" атрибута.

// electron/package.json
{
  "name": "electron",
  "version": "1.0.0",
  "scripts": {
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "typescript": "^5.5.4"
  }
}

В конзолата изпълняваме:

npx tsc --init

Това ще създаде tsconfig.json файл. В него ще трябва да намерите "outDir", да премахнете коментираната час на реда и задавате "outDir": "../dist".

От тук следваме стандартните за Electron стъпки за създаване на базово приложение, като изпускаме частта за създаване на index.html файла и renderer.js файла, които ще добавим чрез Parcel.

Добавяме main.ts файл в папката electron и в него пишем:

// electron/main.ts
import { app, BrowserWindow, ipcMain, nativeTheme } from "electron";
import path from "node:path";

/**
 * Creates a new window and loads an HTML file.
 */
const createWindow = (): void => {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, "./preload.js"),
    },
  });

  mainWindow.loadFile("./dist/index.html");

  ipcMain.handle("dark-mode:toggle", () => {
    if (nativeTheme.shouldUseDarkColors) {
      nativeTheme.themeSource = "light";
    } else {
      nativeTheme.themeSource = "dark";
    }

    return nativeTheme.shouldUseDarkColors;
  });
};

app.whenReady().then(() => {
  createWindow();

  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});

Това е примера от Electron документацията, който сменя тъмна или светла темата на приложението.

След това добавяме preload.ts файл в папката electron и в него пишем:

// electron/preload.ts
import { contextBridge, ipcRenderer } from "electron/renderer";

contextBridge.exposeInMainWorld("electronAPI", {
  toggle: () => ipcRenderer.invoke("dark-mode:toggle"),
});

Browser

През терминала влизаме в папката browser и изпълняваме:

npm init -y

след което инсталираме Parcel:

npm install --save-dev parcel

Зареждаме package.json. В script частта добавяме "build": "parcel build index.html --dist-dir ../dist --no-source-maps --public-url ./ --no-optimize"и махаме "main" атрибута.

Създаваме index.html файл в папката browser и в него пишем:

<!-- browser/index.html -->
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Hello World!</title>
  <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
  <link rel="stylesheet" type="text/css" href="./styles.css">
</head>

<body>
  <h1>Hello World!</h1>
  <p>Current theme source: <strong id="theme-source">System</strong></p>
  <p><button id="toggle-dark-mode">Toggle Theme Color</button></p>

  <script src="./render.ts"></script>
</body>

</html>

Създаваме styles.css файл в папката browser и в него пишем:

/* browser/styles.css */
@media (prefers-color-scheme: dark) {
  body { background: #333; color: white; }
}

@media (prefers-color-scheme: light) {
  body { background: #ddd; color: black; }
}

Добавяме render.ts файл в папката browser и в него пишем:

const toggleDarkMode = document.getElementById("toggle-dark-mode");
const themeSource = document.getElementById("theme-source");

if (themeSource && toggleDarkMode) {
  toggleDarkMode.addEventListener("click", async () => {
    // @ts-expect-error
    const isDarkMode = await window.electronAPI.toggle();

    themeSource.innerHTML = isDarkMode ? "Dark" : "Light";
    toggleDarkMode.innerHTML = `Toggle ${!isDarkMode ? "Dark" : "Light"} Mode`;
  });
}

За да 'компилираме' typescript файла с Parcel ще добавим в папката .parcelrc файл и в него ще напишем:

// browser/.parcelrc
{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.ts": ["@parcel/transformer-typescript-tsc"]
  }
}

Старт

Връщаме се обратно в папката на проекта и редактираме в package.json файла полетата scripts и main :

// package.json
{
  "main": "./dist/main.js",
  "scripts": {
    "start": "npm run build --prefix ./electron && npm run build --prefix ./browser && electron ."
  }
}

В .gitignore файла добавяме dist и .parcel-cache папките и в командния ред изпълняваме:

npm start

След старта на приложението, ще се появи папка dist в папката на проекта и в нея ще е целия код на Electron приложението.

Направил съм репозитори за този проект в GitHub.


Ако имате мнение или въпроси за статията, не се колебайте да ги споделите.

Влезте в отворената дискусия в GitHub.


Компетентност: Базова