Electron Adventures: Episode 70: CoffeeScript
History lesson! Once upon a time, not so long ago, JavaScript was trash. Among many things it lacked were:
- lexical variable scoping
- classes
- template literals
- multiline strings
- looping over arrays without explicit indexes
- any sanity with regards to what
this
referred to - concise syntax for declaring and using small functions
- array or object destructuring
...
spread operator- and a lot more
It was crazy why anyone would code this way. People were so desperate they were even cross-compiling Java to JavaScript, or coding things in Flash. Or just used jQuery
for everything.
The problem was that while JavaScript was terrible, cross-compiling another existing language like Ruby or Python (or Java for some crazy reason) would cause massive issues with interoperability with browser APIs, as they were all designed for JavaScript.
CoffeeScript tried something else - it fixed as much as it could on syntactic level, while keeping mostly to JavaScript-like semantics. It pretty much single-handedly saved JavaScript. I'm pretty sure it it wasn't for CoffeeScript we'd all be coding in Flash today.
Then the mainstream JavaScript incorporated 80% of CoffeeScript features in a way that would be backwards compatible and run natively in the browsers. This put CoffeeScript in an awkward position - not only it lost its main purpose, but ES6 implemented many things in ways that were not quite compatible with how CoffeeScript did it, so targetting ES6 would cause serious issues with existing CoffeeScript codebases.
CoffeeScript also arguably went overboard a bit - there's really no reason why foo is off
needs to be a synonym for foo == false
(that is foo === false
in JavaScript, CoffeeScript intentionally doesn't have sloppy equals).
CoffeeScript 2 attempted to continue CoffeeScript in the post-ES6 world. For sake of nostalgia, let's give it a try.
Let's get started!
The first I discovered is that js2coffee I used years ago no longer works. At least it doesn't support any modern JavaScript features, so I had to write all my CoffeeScript by hand. Oh well, I might still remember some of that.
npm install --save-dev coffeescript electron
package.json
As we're not using any pre-configured template, we need to decide how to structure our source, and write our own package.json
.
I decided to put all the source in src
, and output it all to public/build
.
This results in the following package.json
:
{
"scripts": {
"build": "coffee -o public/build/ -c src",
"watch": "coffee -o public/build/ -cw src",
"electron": "electron ."
},
"devDependencies": {
"coffeescript": "^2.6.0",
"electron": "^15.1.0"
},
"main": "public/build/backend.js"
}
src/backend.coffee
It's a little cleane, but same as before - just open a window with index.html
and preload.js
. Then quit the app when the window closes.
{app, BrowserWindow} = require("electron")
createWindow = ->
win = new BrowserWindow
webPreferences:
preload: "#{__dirname}/preload.js"
win.loadFile "#{__dirname}/../index.html"
app.on "ready", createWindow
app.on "window-all-closed", =>
app.quit()
public/index.html
All we need to do is refer the CSS and compiled JS:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="app.css">
</head>
<body>
<h1>Hello, World!</h1>
<script src="./build/app.js"></script>
</body>
</html>
public/app.css
I thought about making it SASS to better match CoffeeScript theme, but it will do like this.
body {
background-color: #444;
color: #fff;
}
src/preload.coffee
As a placeholder for something more useful, it just sends a dictionary of version numbers to the frontend:
{ contextBridge } = require("electron")
contextBridge.exposeInMainWorld(
"api",
versions: process.versions
)
src/app.coffee
And finally we use browser DOM APIs to print those version numbers:
body = document.querySelector "body"
ul = document.createElement "ul"
body.append ul
for key, val of window.api.versions
li = document.createElement "li"
li.append "#{key}: #{val}"
ul.append li
Run it!
Then we can run it with these two commands:
$ npm run watch
$ npm run electron
Results
Here's the results:
In the next episode we'll write some games.
As usual, all the code for the episode is here.