8/26/2024
Electron, TypeScript & Parcel
Documentation for Electron is entirely in JavaScript, but that doesn't stop you from using TypeScript to generate that JavaScript. A few simple rules must be followed, mainly in the file loading paths. I have also prepared a small addition for the front-end part. Instead of the standard Electron HTML page, I will make a small compilation with Parcel.
The Project
First, we will organize our project. It will have 2 subfolders - one for the Electron part, the other for the Browser part. We create a folder electron-typescript-parcel
and open it in VSCode - or whichever editor you use. Open the built-in terminal in VSCode (or another if you don't use VSCode) and execute:
npm init -y
This will create a package.json
file in the electron-typescript-parcel
folder. Open the file and edit the author
field. As a start, it is enough. Next, we need to add the Electron module to the project.
npm install --save-dev electron
If you are going to use GIT, now is a good time to execute:
git init
and add a .gitignore
file. Add node_modules
as a start.
Then add 2 folders - electron
and browser
in the project folder. As the names suggest - Electron will live in the first, and the front-end part for the browser will live in the second.
Electron
Through the terminal, enter the electron
folder and execute:
npm init -y
and immediately after that add the TypeScript module:
npm install --save-dev typescript
Load package.json
. In the script part, add "build": "tsc"
and remove the "main"
attribute.
// electron/package.json
{
"name": "electron",
"version": "1.0.0",
"scripts": {
"build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"typescript": "^5.5.4"
}
}
In the console execute:
npx tsc --init
This will create a tsconfig.json
file. In it, you will need to find "outDir"
, uncomment the line, and set "outDir": "../dist"
.
From here, we follow the standard steps for creating a basic Electron application, skipping the part about creating the index.html
file and renderer.js
file, which we will add through Parcel.
Add a main.ts
file to the electron
folder and write the following code:
// 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();
});
This is an example from the Electron documentation that changes the dark or light theme of the application.
Next, we add a preload.ts
file to the electron
folder and write the following code:
// electron/preload.ts
import { contextBridge, ipcRenderer } from "electron/renderer";
contextBridge.exposeInMainWorld("electronAPI", {
toggle: () => ipcRenderer.invoke("dark-mode:toggle"),
});
Browser
Through the terminal, we navigate to the browser
folder and execute:
npm init -y
After that, we install Parcel:
npm install --save-dev parcel
Loading package.json
. In the script section, we add "build": "parcel build index.html --dist-dir ../dist --no-source-maps --public-url ./ --no-optimize"
and remove the "main"
attribute.
Create an index.html
file in the browser
folder and write the following code:
<!-- 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>
Creating a styles.css
file in the browser
folder and writing the following code in it:
/* browser/styles.css */
@media (prefers-color-scheme: dark) {
body { background: #333; color: white; }
}
@media (prefers-color-scheme: light) {
body { background: #ddd; color: black; }
}
Adding a render.ts
file to the browser
folder and writing the following code in it:
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`;
});
}
To 'compile' the TypeScript file with Parcel, we will add a .parcelrc
file in the folder and write the following:
// browser/.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.ts": ["@parcel/transformer-typescript-tsc"]
}
}
Start
We go back to the project folder and edit the scripts and main fields in the package.json
file:
// package.json
{
"main": "./dist/main.js",
"scripts": {
"start": "npm run build --prefix ./electron && npm run build --prefix ./browser && electron ."
}
}
In the .gitignore
file, we add the dist
and .parcel-cache
folders, and in the command line, we execute:
npm start
After starting the application, a dist
folder will appear in the project folder, containing all the code of the Electron application.
I have created repositories for this project on GitHub.
If you have an opinion or questions about the article, don't hesitate to share them.
Log info with Web Workers from Vue 3 to the server
Web Workers are small, powerful scripts that run in the browser's background. And because they don't interfere with the rendering of your application, you can load them with various tasks to perform.
Vue Router & Main file
When starting a new Vue project, I use the Quick Start section of the Vue website. Then, I make a few small changes before adding the project to the Source Control bank.