Let's do something that frontend code can't do on its own - run shell commands.
Run a single command
Electron on the backend is basically node process, with some extras for communicating with the browser.
Now it would much prefer you to use complicated async system with proper error handling and so on, but we don't need this complexity - we'll just use child_process.execSync
to run a command and capture its output.
We need to do two small things with the result. First we need to convert Buffer
into String
- this isn't done automatically, as the output could be some binary like an image, not a valid UTF-8 String. And then we'll trim extra newline.
let child_process = require("child_process")
let runCommand = (command) => {
return child_process.execSync(command).toString().trim()
}
Gather information about operating system
Let's run a bunch of random commands to get system information
let sysInfo = {
os: runCommand("uname -s"),
cpu: runCommand("uname -m"),
hostname: runCommand("hostname -s"),
ip: runCommand("ipconfig getifaddr en0"),
}
Ship this information to the frontend
We can now ship this information to the frontend. There are many more complex ways, and we'll absolutely get there, but for now, let's jut use the simplest one and pass a query string.
Weirdly Javascript still doesn't have a way to turn an Object
into a query string, so we'll need to roll our own!
let toQueryString = (obj) => {
let q = []
for (let key in obj) {
q.push(`${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)
}
return q.join("&")
}
We used loadFile
function before, but that doesn't have any easy way of passing an query string. But we can do a little trick, and use loadURL
with file:
protocol instead.
let { app, BrowserWindow } = require("electron")
function createWindow() {
let win = new BrowserWindow({})
win.maximize()
win.loadURL(`file:${__dirname}/index.html?${toQueryString(sysInfo)}`)
}
app.on("ready", createWindow)
app.on("window-all-closed", () => {
app.quit()
})
Parse this on the frontend
Javascript has a way to parse a query string, but it's not very convenient. But let's give it a go the hard way with just browser APIs:
let data = new URLSearchParams(document.location.search)
let info = document.querySelector("#info")
for (const [key, value] of data) {
let p = document.createElement("p")
p.append(`${key} = ${value}`)
info.append(p)
}
We absolutely do not want to be writing bigger apps this way of course, so we'll get to using libraries soon.
For now let's just add this simple HTML, and we have a simple app:
<!DOCTYPE html>
<html>
<body>
<h1>System information!</h1>
<div id="info"></div>
<script src="app.js"></script>
</body>
</html>
The result
And here's what we got:
All the code for the episode is here.
See you in the next episode!
Top comments (2)
RE: Query strings
You can use
URLSearchParams
.I have this for one of my current projects:
I use this because there is special handling by the
URLSearchParams
objects (e.g. the question mark in the URL). Also, it allows other operations on the Params and URL if necessary.You could loop over the param object's entries manually, instead creating a
URLSearchParams
object and usingforEach
.Using it seems more foolproof, so it's probably a good idea unless you need high performance.
Code:
The check on
params
is needed in because it is an optional parameter. I use a fuller form of this function for an internal API library, so a query string is not always required.In JS, this check should be done anyway, in theory. In TS, simply make it a mandatory parameter.
Note:
URL.searchParams
is a read only instance ofURLSearchParams
.MDN Docs:
URL
URLSearchParams
Thanks, I didn't know about this.