Upgrade guide on Electron

Upgrading Electron

When you add the Electron mode in a Quasar project for the first time you will get the latest version of the Electron package. At some point in time, you will want to upgrade the Electron version.

Before upgrading Electron, please consult its release notes. Are there breaking changes?

# from the root of your Quasar project
$ yarn upgrade electron@latest
# or: npm install electron@latest

Upgrading from Quasar v1

The Electron mode for Quasar v2 is an almost complete overhaul of the previous version, significantly improving the developer experience. Some of the changes here are required in order to ensure compatibility with the latest developments in the Electron world (so bulletproofing for upcoming upstream changes).

High overview of the improvements

  • Out of the box support for Typescript. Just rename electron-main.js and electron-preload.js to electron-main.ts and electron-preload.ts.
  • Support for Electron 11 and preparing out of the box support for upcoming changes in Electron 12+ (without you needing to change anything in the future). One of changes are that we’ll be using contextIsolation instead of the deprecated Node Integration.
  • The preload script no longer has the old limitations. You can import other js files with a relative path because the script is now bundled and passed through Babel (so you can use the import X from Y syntax too). You can also enable linting for it.
  • You can enable linting for the main thread and the preload script too.
  • We’ve removed the default electron-main.dev.js support as it seems that it’s not needed anymore. However, you can add it back by creating it and referencing it from electron-main (it’s no longer detected by Quasar CLI automatically – because we don’t need to; more on this later).

The /src-electron folder

# old structure
.
└── src-electron/
    ├── icons/                 # Icons of your app for all platforms
    |   ├── icon.icns             # Icon file for Darwin (MacOS) platform
    |   ├── icon.ico              # Icon file for win32 (Windows) platform
    |   └── linux-512x512.png     # Icon file for Linux platform (when using electron-builder)
    └── main-process/
        ├── electron-preload.js   # Electron preload script (injects Node.js stuff into renderer thread)
        ├── electron-main.dev.js  # Main thread code (preceded by dev code only)
        └── electron-main.js      # Main thread code (production code)

# NEW structure
.
└── src-electron/
    ├── icons/                 # Icons of your app for all platforms
    |   ├── icon.icns             # Icon file for Darwin (MacOS) platform
    |   ├── icon.ico              # Icon file for win32 (Windows) platform
    |   └── linux-512x512.png     # Icon file for Linux platform (when using electron-builder)
    ├── electron-preload.js   # (or .ts) Electron preload script (injects Node.js stuff into renderer thread)
    └── electron-main.js      # (or .ts) Main thread code

Notice that there’s no electron-main.dev.js file anymore (not needed anymore) and that the electron-preload/main.js files need to be moved directly under /src-electron.

The electron-main.js file

In order for us to be forward compatible with future versions of Electron, you’ll need to do some small (but important!) changes:

// OLD way
mainWindow = new BrowserWindow({
  // ...
  webPreferences: {
    nodeIntegration: process.env.QUASAR_NODE_INTEGRATION,
    nodeIntegrationInWorker: process.env.QUASAR_NODE_INTEGRATION,
    // preload: path.resolve(__dirname, 'electron-preload.js')
  }
})

// NEW way
mainWindow = new BrowserWindow({
  // ...
  webPreferences: {
    // we enable contextIsolation (Electron 12+ has it enabled by default anyway)
    contextIsolation: true,
    // we use a new way to reference the preload script
    // (it's going to be needed, so add it and create the file if it's not there already)
    preload: path.resolve(__dirname, process.env.QUASAR_ELECTRON_PRELOAD)
  }
})

The electron-preload.js file

You will need this file if you don’t have it already. So create it if it’s missing. Without it you won’t be able to use the power of Node.js in your renderer thread.

More info: preload script.

WARNING

You will need to transfer all the Node.js stuff away from your renderer thread (the UI code from /src) and into the preload script. Provide the same functionality through the contextBridge as seen below.

This is the default content of electron-preload.js:

/**
 * This file is used specifically for security reasons.
 * Here you can access Nodejs stuff and inject functionality into
 * the renderer thread (accessible there through the "window" object)
 *
 * WARNING!
 * If you import anything from node_modules, then make sure that the package is specified
 * in package.json > dependencies and NOT in devDependencies
 *
 * Example (injects window.myAPI.doAThing() into renderer thread):
 *
 *   const { contextBridge } = require('electron')
 *
 *   contextBridge.exposeInMainWorld('myAPI', {
 *     doAThing: () => {}
 *   })
 */

Quasar.conf changes

// OLD way
electron: {
  // it's gone now (upcoming upstream breaking change)
  // replaced by a change in electron-main.js documented earlier
  nodeIntegration: true, // remove me!

  // renamed to chainWebpackMain
  chainWebpack (chain) { /* ... */ },

  // renamed to extendWebpackMain
  extendWebpack (cfg) { /* ... */ }
}

// NEW way
electron: {
  // was renamed from chainWebpack()
  chainWebpackMain (chain) {
    // example for its content (adds linting)
    chain.plugin('eslint-webpack-plugin')
      .use(ESLintPlugin, [{ extensions: ['js'] }])
  },

  // was renamed from extendWebpack()
  extendWebpackMain (cfg) { /* ... */ },

  // New!
  chainWebpackPreload (chain) {
    // example (adds linting)
    chain.plugin('eslint-webpack-plugin')
      .use(ESLintPlugin, [{ extensions: ['js'] }])
  }

  // New!
  extendWebpackPreload (cfg) { /* ... */ }
}

Renderer thread (/src)

The $q object no longer contains the electron property. You will need to use the preload script to access it and provide it to the renderer thread.

Furthermore, the openURL util can no longer tap into Electron to open a new window. You will need to provide your own util from the preload script.

WARNING

You will need to transfer all the Node.js stuff away from your renderer thread (the UI code from /src) and into the preload script. Provide the same functionality through the contextBridge as seen in the preload script section above.

Browser Devtools

You may also want the following code in your electron-main.js to auto-open devtools while on dev mode (or prod with debugging enabled) and to disable devtools on production builds (without debugging enabled):

function createWindow () {
  mainWindow = new BrowserWindow({ /* ... */ })

  if (process.env.DEBUGGING) {
    // if on DEV or Production with debug enabled
    mainWindow.webContents.openDevTools()
  }
  else {
    // we're on production; no access to devtools pls
    mainWindow.webContents.on('devtools-opened', () => {
      mainWindow.webContents.closeDevTools()
    })
  }
}