Aurelia CLI with electron

Hey there,

yep the issue was that the electron-connect - the tool that will auto-restart (like browserSync) your electron app both on backend/frontend changes - isn’t started in the case of npm run electron, as the start happens within the run task. I’ve added now a env var to check for so now it should work properly.

With regards to electron-packager, I guess that was the same issue. Since the process of creating a packaged app is very specific to your project I didn’t add an example, since I’m personally preferring electron-build due to the included updater and binary build targets for all 3 major platforms including NSIS support.
Anyways, what I’d typically use to strip out code that shouldn’t land in the resulting build is gulp-preprocess. I’ve now also wrapped that electron-connect client part in a !=prod section as a demo.

1 Like

I’ve also updated the sample to use the latest version of Electron since it was locked into v3, plus added some descriptions on how you can use Electrons require in the renderer process. While you shouldn’t use nodeIntegration if you can avoid it, the sample is still showing it so in the case you have to it’s already properly setup

EDIT: also added a sample on how to handle native OS theme preferences in CSS, due to proper support in v7

2 Likes

This is awesome. This should be the aurelia way.

So regarding a packaged build. Did you mean electron-builder? My requirement to build the executable (installer or zip of files) requires me to be able to build on an offline network. I haven’t really touched this end to much yet, but to fully flesh out a plan I’m just trying to get these basics working. Getting things working in my environment is not easy sometimes.

1 Like

oh wow, that escalated quickly :slight_smile:

Yeah I meant electron-builder, sorry for the typo. That one should allow you to define whatever output you want per OS, be it zip, binary or OS specific packages such as AppImage on Linux. Sure your situation is quite an uncommon scenario to build in isolation. So there are a few things you need to keep in mind when doing so:

  • You need to get the initial set of dependencies. Either you connect once to get them and cache or you use an internal dependency management system like Artifactory or whatever else. As for caching see these instructions to build from cache
  • Codesigning might bite you in an offline mode. More specifically the communication with the timeserver. Now that said it depends on how you want to achieve this, since you could technically host that by yourself OR do the code-signing in a phase two from a remote capabale of connecting to the internet OR adding the timeserver to your DMZ. Again this really heavily depends on your infrastructure/use case but there are ways to solve that.
2 Likes

Updated the repo to include E2E tests with Spectron, since this is quite often a nightmare to get right together with Typescript and all the proper typings for the various versions of Spectron/WebdriverIO.

2 Likes

Leaving this here for history, but just using scss in the app is the way to go, there are no negatives as far as I can see. I used to think scss compile was slow though.

Original:
The only issue I ran into so far is I tried to require another css file into the app.html and that is failing

I’m just doing this. the material kit css is copied local so I can remove the font import since I’ll be offline. It appears that actual css files cannot be loaded this way. anyway to allow both css and scss from the app? If i use the scss version it’s okay. As long as I have control of the file, i don’t have a problem just changing the extension. Loading css from a node_module is no problem

  <require from="./styles/material-kit/assets/css/material-kit.css"></require>
  <require from="./app.css"></require>
1 Like

Would you mind sharing electron-builder basic config. I keep getting an error “Not allowed to load local resource”

Solved that with this config

"build": {
    "appId": "Aurelia",
    "files": [
      "dist/**/*",
      "package.json",
      "index.html"
    ]
  }
1 Like

I’ll try to get into that tomorrow

2 Likes

this is great!!

How does one access the aurelia_project/environments files from the backend?

1 Like

hey there, I’ve updated the sample to include electron-builder configured for Mac builds.

EDIT: now including a multi-target build and configuration for Linux/Windows. Note though, that I haven’t checked the later two since I’m currently running on Mac.

2 Likes

Ehm, dunno I haven’t used those as generally I try to stick to NODE_ENV vars. but what prohibits you from simply importing it?

EDIT: If you need to change the place where the file is generated, so e.g you can access it both in renderer and main from a common shared place, just adapt this transpile task. Also make sure to tweak both tsconfig/_backend.json to include the new destination.

1 Like

Thank you. I’m glad I did something right with the build! I don’t use macs and don’t have access to one so that one is out, but I trust that it is correct.

1 Like

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.

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.

3 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…

A few lines of Webpack config will almost certainly get everything building, just be aware that’s 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
    ],
  },
];
1 Like