Aurelia CLI with electron

thanks, I’ll look into the transpile task next time I loop back to this boilerplate – I’m dating a half-dozen different ones to see which one fits my project the best.

1 Like

Yep I guess what’s missing in your config is the scripts/**/* part for the renderer stuff, but aside that it should be fine. Also check out the latest update with the github actions build, which will now build on a multitarget matrix. Github actions is really a solid piece of work for that.

1 Like

Cool, let me know if you’re experiencing any issues and what your choice was in the end :wink:

1 Like

I don’t use the scripts folder. I just dump it all to dist. I personally think it’s dirty to have it go to two folders. I know it’s defaulted to scripts though

2 Likes

Thank you for this — But how do I share state between Aurelia and the rest of the Electron application?

For example, suppose I have file greeter.js in backend/ folder.

It seems I cannot do this from Aurelia:

 [@ src/main.ts]:

import { Greeter } from './backend/dist/greeter.js'

Greeter.greet('hi')

This doesn’t work — It seems the Aurelia and Electron codebases are separate.

1 Like

Electron can be quite a complex beast.

It has a main process and one renderer process for each window that you open. The main process is a node.js process and is generally used to launch “renderer” windows and to help send messages between multiple windows. The render processes are essentially Chromium browser processes which you can run Aurelia in just like you would a webpage from a file:// url.

The docs here do a better job at explaining that than I can.

Eventually you’re going to want to allow your Aurelia app to access node.js features, otherwise why would you be using Electron.

If you are not loading remote content, you can enable full node.js support in the renderer processes too. This is by far the easiest way to use Electron but can result in some serious security vulnerabilities. This is because a cross-site-scripting attack or a vulnerability in any of your dependencies could result in full access to node.js and everything that allows (ie. file system, network, native code, etc :scream:). It’s worth reading the security recommendations before you do this. It’s also worth remembering that “remote content” doesn’t necessarily mean “loading a remote web page”. If your app is a markdown editor and a security vulnerability is discovered in your markdown parser, somebody could create a malicious markdown file that could execute node.js code on users machines! Years ago, we found a serious security issue in our production Electron app due to an erroneous use of innerHtml which would have allowed a specially crafted file to inject and run node.js JavaScript when it was opened!

If enabling node.js in the renderer is off the table, one option which became common was to treat the main process as a backend and do all the node.js stuff there and treat the renderer as a front end, much like a standard website with a server. You can then use Electron IPC modules (Inter Process Communication) to send messages back and forth between main and renderer rather than HTTP messages.

There are however some pain points with the above which you should be aware of. The most important is that you should not block the main process. It is NOT an isolated background thread for you to run long running blocking tasks on. As well as running your supplied main process script, it is also in charge of marshaling messages between the renderer and GPU processes. Block the main process for more than a few 10s of milliseconds and you’ll notice freezing in the front end rendering. If you do any file IO in the main process, ensure you use async calls so node awaits these on another thread for you.

There is another option which has become the recommended means to access node.js functionality from your web frontend. The Electron renderer processes can be passed a “preload” script which has access to node.js which can then expose certain functionality for your isolated web page to use later once it’s loaded. Exposing certain functionality from a node.js object without exposing a huge gaping hole is also fraught with danger so it’s recommended that you use the contextBridge module to help you do this safely.

So yeah… there are many ways to skin this cat and they all involve footguns along the way.

Once things have calmed down at my end and I’m not so swamped, I want to collaborate with a few people and create a fully featured open source app with Electron + Aurelia using best practices. Ideally something that showcases the power of both.

4 Likes

@timfish

Wow thank you for that thorough and very patient response. You put the entire Aurelia/Electron challenge in context. Here I was thinking a few lines on webpack would solve everything but now I see it’s a more difficult architectural barrier.

Well, here’s some of my quagmire:

  1. My Aurelia app is a folder sitting nested in my Electron project ( :man_shrugging: OK).

  2. My Aurelia and Electron compile independently and I always have to run two different build scripts during dev ( :man_shrugging: Semi-OK)

  3. Long story short: I ended up with TWO node-modules in my project — one for Aurelia and the other for the Electron “backend”. (Probably not OK)

Question: Is this normal? Two node_module folder in one project?

What’s worse, many packages appear in both folders; so that means I am bundling them twice in the finished product…

Oh, and here’s something else: the Aurelia part uses webpack, but the Electron part uses Gulp. (Too hard to figure own how to do it all with webpack only).

It’s such a Byzantine experience this whole thing. Or maybe Kafka-esque is more precise…

1 Like

A few lines of Webpack config will almost certainly get everything building, just be aware that at some point you’re going to have to consider the Electron architecture to do node.js stuff.

We keep all our source in a single directory and let Webpack build both main and renderer. Webpack resolves which code goes in each process which means you can share code between them.

If a Webpack config returns an array of configurations, it will build each configuration one at a time.

webpack.config.js:

module.exports = [ // <- an array here rather than an object
  // This builds the main process code
  {
    mode: 'production',
    entry: './src/main.js',
    target: 'electron-main',
    output: {
      libraryTarget: 'commonjs2', 
      filename: 'main.js',
    },
    plugins: [
      // you might need stuff here
    ],
    // Tells Webpack not to attempt to bundle Electron internal code 
    externals: ['electron'], 
  },
  // This builds your renderer process code
  {
    mode: 'production',
    entry: './src/renderer.js',
    target: 'electron-renderer',
    output: {
      filename: 'renderer.js',
    },
    plugins: [
       new HtmlWebpackPlugin(),
       // Add everything else here from your Aurelia webpack config
    ],
  },
];
2 Likes

I’ve updated my Aurelia Electron boilerplate to use electron-forge and Aurelia v2 so it’s now much simpler.

To run in dev mode:

yarn start

To build packages for distribution:

yarn make
4 Likes

Thanks. This is one of many projects of mine that has went on the backburner for more important tasks. I should try electron forge to see if i an even get a distribution package to build in an offline environment.

2 Likes

oh excellent, thank you immensely. Truly one of the better aurelia community projects out there

2 Likes