Abstractions, Abstractions every where...
I decided to create a small application with two components: the Chrome extension to trap all the xhr calls and the desktop that opens a web socket to which the Chrome extension sends all the details.
I wrote the desktop app using Electronjs. For reasons unknown, I decided to use Typescript with Electronjs. Well, that’s what pushed me down the rabbit hole of transpiling with Typescript, to using bundlers such as Webpack, and finally electron-forge using template webpack-typescript.
Let’s deal with each issue at a time.
Plain Typescript, nothing fancy!!
When Electronjs loads the renderer process, it throws an error -
Uncaught ReferenceError: exports is not defined at renderer.js:5:23 (anonymous) @ renderer.js:5
On inspection of renderer.js, the culprit is the first line, which tries to define a property on object exports.
Object.defineProperty(exports, "__esModule", { value: true });
Check more about Object.defineProperty
Why is this line generated?, well ChatGpt says to provide ‘interoperability’. This line is trying to set the renderer process as esm, but there is no object defined at the top called exports
Adding a line var exports = {} at the top in index.js, resolves the issue.
The discussion around this issue on Typescript & Electronjs forum mentioned two solution, one mentioned above and second to comment the offending line
What happens if it’s just plain old typescript?, In that case, the line Object.defineProperty is also present, but then the keyword exports is defined and is an empty object. What’s the issue that Electronjs has? Electronjs is a web application encapsulated and presented as a desktop app. It loads the renderer process with a script tag. When a node process executes in a debug mode, vs code will show under local variables exports

The global Object on a browser is called window. Object.keys will not list anything that is exports.
The github discussion (click here) was very helpful.
Good’ol Webpack
Well, here’s the twist, when I started writing the article, I didn’t face any issue. In fact, I feel webpack could be the best option for making an electronjs app. As long as webpack.config.ts is correctly written, the app works like a charm.
webpackhas a config key calledelectron-mainandelectron-renderer. The entry points and target should be correct, and then electron works like a charm.
Electron Forge, the swiss knife
webpack has toooo… many features, such as webpack dev or hot reloading, configuring it for the first time is no joke, but electron forge makes it easy. What’s difficult is, to understand how electron-forge works. The template that is generated for index.ts has a comment which says -
// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
Initially, I felt it was entirely magic, untill I started peeling onion.
Few things that I learnt during the whole process -
- To enable debugging, execute the command
electron-forge start -l - Electron-forge uses
[debug](https://www.npmjs.com/package/debug)module to list the messages while building. The messages can be read athttp://localhost:9000 - The renderer process is served using webserver. So, the electronjs’s renderer html can be viewed at
http://localhost:3000. Electron-Forge is usingexpressjsto serve the artifacts related to rederer process. - The standard
index.htmldoesn’t have therendererfile path mentioned. Similarly, the main process javascipt doesn’t havehtmlfile path hard coded. These are generated using webpack. Check out@electron-forge\webpack-plugin - The
index.jsof main process is everything but english. To understand the code, changedevtooloption to false. It can be done by navigating to\node_modules\@electron-forge\plugin-webpack\src\WebpackConfig.ts - Debug can be enabled by setting environment variable
DEBUG=*, (source from readme of debug package) - In
forge.config.ts, renderer option hasentryPoints. This is an array. So, one can include multiple renderer process by defining multiple items in the array.
entryPoints: [
{
html: './src/index.html',
js: './src/renderer.ts',
name: 'some_window',
preload: {
js: './src/preload.ts',
},
},
{
html: './src/another.html',
js: './src/another.ts',
name: 'another_window',
preload: {
js: './src/preload.ts',
},
},
],
The magic constants, would be -
SOME_WINDOW_WEBPACK_ENTRY
ANOTHER_WINDOWS_WEBPACK_ENTRY