Michael Labbé
Michael Labbé

Reputation: 12296

How do I use require() when building a webpack-based svelte app with Electron?

I am attempting to call require('electron') from a .svelte file. It is not defined. I am using webpack with target: 'electron-renderer.

How do I include or require node modules from my electron renderer thread?

Upvotes: 1

Views: 650

Answers (2)

reZach
reZach

Reputation: 9479

I've written about this in other questions, so you may want to look there. The answer is not using svelte, but can be ported.

Brief summary

Electron apps used to include and recommend using require in the renderer process. This, however leads to security problems so the team has been slowly transitioning away from this. The new and better way is to require everything in your main process and whenever the renderer process needs a require'd module, it communicates to the main process via IPC. The response from the main process is then sent to the renderer process back via IPC.

This is a rough example of how this works

main.js

const {
  app,
  BrowserWindow,
  ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

async function createWindow() {

  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false, // is default value after Electron v5
      contextIsolation: true, // protect against prototype pollution
      enableRemoteModule: false, // turn off remote
      preload: path.join(__dirname, "preload.js") // use a preload script
    }
  });

  // Load app
  win.loadFile(path.join(__dirname, "dist/index.html"));

  // rest of code..
}

app.on("ready", createWindow);

ipcMain.on("toMain", (event, args) => {
  fs.readFile("path/to/file", (error, data) => {
    // Do something with file contents

    // Send result back to renderer process
    win.webContents.send("fromMain", responseObj);
  });
});

preload.js

const {
    contextBridge,
    ipcRenderer
} = require("electron");

// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
    "api", {
        send: (channel, data) => {
            // whitelist channels
            let validChannels = ["toMain"];
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, data);
            }
        },
        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => func(...args));
            }
        }
    }
);

index.html

<!doctype html>
<html lang="en-US">
<head>
    <meta charset="utf-8"/>
    <title>Title</title>
</head>
<body>
    <script>
        window.api.receive("fromMain", (data) => {
            console.log(`Received ${data} from main process`);
        });
        window.api.send("toMain", "some data");
    </script>
</body>
</html>

Disclaimer

I'm the author of secure-electron-template which has this pattern built out into a nice starter template, with security practices built in. (This sample was derived from this template).

Upvotes: 2

Tonton-Blax
Tonton-Blax

Reputation: 515

To require electron this way from a renderer file, you need to double-check the webPreferences options of BrowserWindow from within the main process js file, it should have those props set to true :

const mainWindow = new BrowserWindow({
//...
    webPreferences: {
      enableRemoteModule: true, // <-- You probably want this but note this is going through a severe deprecation process
      nodeIntegration: true,
      contextIsolation: false,
      // ...
    }
});

Upvotes: 0

Related Questions