Electron Adventures: Episode 70: CoffeeScript
Tags: javascript, electron, coffeescript
Published: 2021-10-02 15:50:43

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](https://coffeescript.org/announcing-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](http://js2.coffee/) 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.

```shell
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`:

```javascript
{
  "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.

```coffee
{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:

```html
<!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.

```css
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:

```coffee
{ contextBridge } = require("electron")

contextBridge.exposeInMainWorld(
  "api",
  versions: process.versions
)
```

### `src/app.coffee`

And finally we use browser DOM APIs to print those version numbers:

```coffee
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:

```shell
$ npm run watch
$ npm run electron
```

### Results

Here's the results:

![Episode 70 Screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3u7f4qj0b19pe5yd0zlc.png)

In the next episode we'll write some games.

As usual, [all the code for the episode is here](https://github.com/taw/electron-adventures/tree/master/episode-70-coffeescript).