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.