electron / forge

:electron: A complete tool for building and publishing Electron applications
https://electronforge.io
MIT License
6.42k stars 509 forks source link

Unable to load preload script #2931

Open mikesongming opened 2 years ago

mikesongming commented 2 years ago

Pre-flight checklist

Electron Forge version

6.0.0-beta.65

Electron version

20.1.0

Operating system

macOS12.5.1

Last known working Electron Forge version

No response

Expected behavior

use contextBridge to expose API in src/preload.js

Actual behavior

chome console shows error: unable to load preload script

Screen Shot 2022-08-31 at 15 41 11

Steps to reproduce

check the code at https://github.com/mikesongming/electron-forge-demo.git

Additional information

No response

MrRobz commented 2 years ago

What worked for me was setting nodeIntegration: true, for mainWindow.

  const mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
      nodeIntegration: true,
    },
  });
mikesongming commented 2 years ago

by setting nodeIntegration: true, context isolation is compromised. why nodeIntegration: false not supported?

There is an example of secure electron at: https://www.sitepoint.com/electron-forge-react-build-secure-desktop-app/

linonetwo commented 2 years ago

Electron 20 is enable sandbox by default, I have to remove all npm package that uses deps that not in the sandbox.

See https://www.electronjs.org/docs/latest/tutorial/sandbox#preload-scripts for what we can use.

Downmoto commented 2 years ago

Electron 20 is enable sandbox by default, I have to remove all npm package that uses deps that not in the sandbox.

See https://www.electronjs.org/docs/latest/tutorial/sandbox#preload-scripts for what we can use.

it says renderer process modules are available, however, when I use contextBridge, I get the error above.

linonetwo commented 2 years ago

__dirname is not available too, it is out of the white list.

My solution is using IPC to get most of the value and methods

https://github.com/tiddly-gittly/TidGi-Desktop/blob/22f79c13ed1a498bd8767eecfe571a4e8b5e187d/src/preload/common/services.ts#L28

https://github.com/tiddly-gittly/TidGi-Desktop/blob/22f79c13ed1a498bd8767eecfe571a4e8b5e187d/src/services/native/index.ts#L182-L196

And use https://github.com/linonetwo/electron-ipc-cat

not-manu commented 2 years ago

The following seems to work for me (in development mode). However, in a packaged application I am still having the same issue.

const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true
    },
});
georgexu99 commented 2 years ago

Hey! The maintainers are taking a look at this, but we're unable to reproduce this on MacOS12.5.1- this is what we are seeing:

image

can you double check that the demo leads us to the correct error message? 🥂

georgexu99 commented 2 years ago

https://github.com/electron/electron/blob/main/docs/breaking-changes.md#default-changed-renderers-without-nodeintegration-true-are-sandboxed-by-default

I suspect this error, as individuals above have mentioned stems from the electron 20 sandbox change. This means that you are unable to use __dirname in the renderer preload by default. For a quick fix, you can set sandbox: false in your browserWindow, but keep in mind that this goes against security best practices

not-manu commented 2 years ago

Seems to me I am not getting the error on Ubuntu 22.04

image

Weird... Could be a windows bug?

handyzhang commented 2 years ago

contextIsolation: true, // must be set to true when contextBridge is enabled nodeIntegrationInWorker: true, // must be set to true when contextBridge is enabled preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, // preload script enable contextBridge

----- I did this and it works using

DELEPaps commented 2 years ago

looks like preload is loaded before it's done build, when i save package.json everything works normally.

without contextIsolation & nodeIntegrationInWorker enabled

@georgexu99

mitchemmc commented 1 year ago

I was able to reproduce this with the release build of v6.0.0 on MacOS 12.5 by trying to use a Web Worker.

Specifically by using worker-loader and adding this rule to the webpack.renderer.config.js

rules.push({
  test: /\.worker\.js$/,
  use: { loader: "worker-loader", options: { inline: "fallback" } },
});

Here is my preload script:

import Worker from "./test.worker";

const worker = new Worker();

worker.postMessage("Hello from worker!");

And here is my worker code:

onmessage = function (e) {
  console.log(e.data);
};

Setting sandbox to false prevents the issue but it would be good to avoid that.

erickzhao commented 1 year ago

@mitchemmc Are you able to reproduce this issue without worker-loader? I see that package was deprecated in Webpack 5, which is the major version used in Forge 6.

https://webpack.js.org/guides/web-workers/

mitchemmc commented 1 year ago

@mitchemmc Are you able to reproduce this issue without worker-loader? I see that package was deprecated in Webpack 5, which is the major version used in Forge 6.

https://webpack.js.org/guides/web-workers/

Unfortunately the new method doesn't work with Electron Forge.

Specifically when using:

new Worker(new URL('./worker.js', import.meta.url));

It will give a 404 error

Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:3000/Users/mitch/Documents/Tests/forge-preload-webpack/.webpack/renderer/main_window/native_modules/worker.js
mitchemmc commented 1 year ago

@erickzhao

Ok I was able to get the new worker method to work. If you create a new url with the import.meta.url then webpack will pick up the worker.js file and include it in the bundle. Then if I hard code the worker path into the worker creation like this:

new URL("./worker.js", import.meta.url);
const worker = new Worker("/main_window/native_modules/worker.js");

This will make use of the local dev server so we can test it (although this method won't work in a production build).

Unfortunately after this I get the same issue as a worker-loader plugin.

It works with sandbox set to false but I still get a ReferenceError: __dirname is not defined error with sandbox enabled.

erickzhao commented 1 year ago

The native_modules folder path is suspect.

It seems like the @vercel/webpack-asset-relocator-loader loader is causing this problem. Commenting it out makes the minimal example from the webpack docs work both in dev mode and packaged mode.

See repro: https://github.com/erickzhao/ericktron-forge/commit/ea27cf9018f7163eb76c4355bb0a9113f59a7f96

I think I'll open a separate issue for this!

mitchemmc commented 1 year ago

Interesting, it seems that removing the webpack-asset-relocator-loader I was able to get workers to work in the renderer but it still seems to run in to issues if I try and create the worker in a preload script.

MarshallOfSound commented 1 year ago

Workers don't work in preload scripts as they run before a document context is initialized (this is an electron limitation). If we register the worker in the preload inside an window.onload event handler it will also work.

mitchemmc commented 1 year ago

@MarshallOfSound hmm, I've been using workers in preload scripts for a while now and haven't had many issues 🤔

The main issue I'm seeing is with sandboxing. Having it enabled seems to run in to these __dirname issues.

Here's a repro that incorporates what @erickzhao found with the webpack-asset-relocator-loader plugin.

https://github.com/mitchemmc/forge-worker-sandbox

If I disable sandboxing then it seems to work.

GreenBattery commented 1 year ago

I suspect this error, as individuals above have mentioned stems from the electron 20 sandbox change. This means that you are unable to use __dirname in the renderer preload by default. For a quick fix, you can set sandbox: false in your browserWindow, but keep in mind that this goes against security best practices

Converting to a relative URL for my preload script works!

aarowman commented 1 year ago

+1

I also am having this same issue now with Electron 20, using the Webpack plugin (https://www.electronforge.io/config/plugins/webpack). My local preload script combines with webpack to a new preload.js which uses __dirname. If I go outside webpack and add my own preload ONLY, then I get an error that I cannot import outside of modules (so I can't set up the IPC context bridge correctly).

I don't see a solution here besides enabling nodeIntegration / disabling sandbox (less secure), or simply downgrading electron :-/

MikeJerred commented 1 year ago

+1

I also am having this same issue now with Electron 20, using the Webpack plugin (https://www.electronforge.io/config/plugins/webpack). My local preload script combines with webpack to a new preload.js which uses __dirname. If I go outside webpack and add my own preload ONLY, then I get an error that I cannot import outside of modules (so I can't set up the IPC context bridge correctly).

I don't see a solution here besides enabling nodeIntegration / disabling sandbox (less secure), or simply downgrading electron :-/

Using my own preload.js is working for me. I think you may need to write it using require instead of import.

aarowman commented 1 year ago

@MikeJerred We could look into that, but trying to avoid using require if possible. I thought it was still failing for me, but haven't checked lately.

Ultimately we need to be able to use the Webpack preload.js anyway, so the first issue is still a huge blocker.

bdlb77 commented 1 year ago

+1

I also am having this same issue now with Electron 20, using the Webpack plugin (https://www.electronforge.io/config/plugins/webpack). My local preload script combines with webpack to a new preload.js which uses __dirname. If I go outside webpack and add my own preload ONLY, then I get an error that I cannot import outside of modules (so I can't set up the IPC context bridge correctly).

I don't see a solution here besides enabling nodeIntegration / disabling sandbox (less secure), or simply downgrading electron :-/

Checking in to see if anyone was able to get preload.js working without enabling nodeIntegration due to security concerns

fetsorn commented 1 year ago

+1 I also am having this same issue now with Electron 20, using the Webpack plugin (https://www.electronforge.io/config/plugins/webpack). My local preload script combines with webpack to a new preload.js which uses __dirname. If I go outside webpack and add my own preload ONLY, then I get an error that I cannot import outside of modules (so I can't set up the IPC context bridge correctly). I don't see a solution here besides enabling nodeIntegration / disabling sandbox (less secure), or simply downgrading electron :-/

Using my own preload.js is working for me. I think you may need to write it using require instead of import.

Had this issue in a pure ESM electron+forge+webpack project. For me as well, the issue was resolved by using my own preload.js in commonjs syntax instead of the preload.js bundled by webpack-plugin. nodeIntegration is disabled, sandbox and context isolation are enabled.

yesFighting commented 1 year ago

你好朋友! 我是win10+vue 在electron 使用preload的时候我也出现了这个问题。 我开始使用preload.js也是和主线程平级的然后就发生了错误 :“Unable to load preload script” 后来我发现需要放入生成的执行文件dist_electron里才能运行成功,目前我是这样处理的。 https://blog.csdn.net/qq_31460469/article/details/129231964?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129231964%22%2C%22source%22%3A%22qq_31460469%22%7D

VishnuRachagani commented 1 year ago

@bdlb77 did you find any solution for this : Checking in to see if anyone was able to get preload.js working without enabling nodeIntegration due to security concerns?

ferminc commented 1 year ago

I'm getting the same error, but as soon as I change a file and reload the error goes away

the problem only happens when you first run electron forge with "npm start or yarn start", it seems electron is trying to load the preload.js file before webpack is finished building it or something like that like someone implied earlier, once it starts you need to force a reload for it to be able to load it

Jbithell commented 1 year ago

I have been able to narrow this down to webpack placing a __dirname in the preload.js script that is compiled from typescript, and that by removing the plugin webpack-asset-relocator-loader its resolved - I had put it in following this comment

justjake commented 1 year ago

I just hit this case with a newly created forge app.

My package.json:

{
  "name": "tlshot",
  "productName": "tlshot",
  "version": "1.0.0",
  "description": "My Electron application description",
  "main": ".webpack/main",
  "scripts": {
    "dev": "./dev.sh",
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make",
    "publish": "electron-forge publish",
    "lint": "eslint .",
    "fix": "yarn lint --fix; prettier -w .",
    "postinstall": "patch-package"
  },
  "keywords": [],
  "author": {
    "name": "Jake Teton-Landis",
    "email": "just.1.jake@gmail.com"
  },
  "license": "MIT",
  "devDependencies": {
    "@electron-forge/cli": "^6.1.1",
    "@electron-forge/maker-deb": "^6.1.1",
    "@electron-forge/maker-rpm": "^6.1.1",
    "@electron-forge/maker-squirrel": "^6.1.1",
    "@electron-forge/maker-zip": "^6.1.1",
    "@electron-forge/plugin-webpack": "^6.1.1",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0",
    "@vercel/webpack-asset-relocator-loader": "1.7.3",
    "css-loader": "^6.0.0",
    "electron": "24.0.0",
    "electron-extension-installer": "^1.0.4",
    "eslint": "^8.0.1",
    "eslint-import-resolver-typescript": "^3.5.5",
    "eslint-plugin-import": "^2.25.0",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-unused-imports": "^2.0.0",
    "fork-ts-checker-webpack-plugin": "^7.2.13",
    "node-loader": "^2.0.0",
    "patch-package": "^6.5.1",
    "prettier": "^2.8.7",
    "style-loader": "^3.0.0",
    "ts-loader": "^9.2.2",
    "ts-node": "^10.0.0",
    "typescript": "5.0.4"
  },
  "dependencies": {
    "@tldraw/assets": "^2.0.0-canary.e59200e42",
    "@tldraw/editor": "^2.0.0-canary.e59200e42",
    "@tldraw/tldraw": "^2.0.0-canary.e59200e42",
    "@tldraw/tlstore": "^2.0.0-canary.e59200e42",
    "@tldraw/ui": "^2.0.0-canary.e59200e42",
    "@types/lodash": "^4.14.194",
    "electron-squirrel-startup": "^1.0.0",
    "electron-store": "^8.1.0",
    "execa": "^7.1.1",
    "fs-extra": "^11.1.1",
    "lodash": "^4.17.21",
    "nanoid": "^4.0.2",
    "path-browserify": "^1.0.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-new-window": "^1.0.1",
    "signia": "^0.1.2-canary.0094a5d",
    "signia-react": "^0.1.2-canary.0094a5d"
  }
}

Git repo: https://github.com/justjake/tlshot

justjake commented 1 year ago

Here's a fresh repro of the error in a newly-created electron-forge project:

image

I created this project via: yarn create electron-app preload-error-repro --template=webpack-typescript Then, I added some imports to preload.ts:

diff --git a/forge.config.ts b/forge.config.ts
index e356488..75f684a 100644
--- a/forge.config.ts
+++ b/forge.config.ts
@@ -15,6 +15,7 @@ const config: ForgeConfig = {
   plugins: [
     new WebpackPlugin({
       mainConfig,
+      port: 5001,
       renderer: {
         config: rendererConfig,
         entryPoints: [
diff --git a/src/preload.ts b/src/preload.ts
index 5e9d369..93aeaad 100644
--- a/src/preload.ts
+++ b/src/preload.ts
@@ -1,2 +1,26 @@
 // See the Electron documentation for details on how to use preload scripts:
 // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
+
+try {
+  console.log("preload.ts can access __dirname:", __dirname);
+} catch (error) {
+  console.error("preload.ts cannot access __dirname:", error);
+}
+
+import { contextBridge } from "electron";
+import child_process from "child_process";
+import path from "path";
+console.log("preload.ts can import node modules:", { child_process, path }});
+
+const preloadStuff = {
+  child_process,
+  join: path.join,
+} as const;
+
+contextBridge.exposeInMainWorld("fromPreload", preloadStuff);
+
+declare global {
+  interface Window {
+    fromPreload: typeof preloadStuff;
+  }
+}
diff --git a/src/renderer.ts b/src/renderer.ts
index a91994f..5754360 100644
--- a/src/renderer.ts
+++ b/src/renderer.ts
@@ -26,6 +26,13 @@
  * ```
  */

-import './index.css';
+import "./index.css";

-console.log('👋 This message is being logged by "renderer.js", included via webpack');
+console.log(
+  '👋 This message is being logged by "renderer.js", included via webpack'
+);
+console.log(
+  "fromPreload:",
+  window.fromPreload,
+  window.fromPreload.join("a", "b", "c")
+);

Repo: https://github.com/justjake/electron-forge-webpack-preload-error-repro

justjake commented 1 year ago

Ah, I think for __dirname and node modules to work in preload, you need to set sandbox: false (and probably then contextIsolation: true to maintain security while allowing complete node integration in the preload?

navdeepm20 commented 1 year ago

Can we expect an update from the team? Or any way to securely do it. Currently I am enabling the nodeIntegrationInWorker to resolve this "__dirname is not defined" and to make contextBridge and ipc work correctly.

PascalPixel commented 1 year ago

I 'fixed' it by patching @electron-forge/plugin-webpack so it also strips __dirname from preloads

(I do not know if this is safe)

diff --git a/dist/WebpackConfig.js b/dist/WebpackConfig.js
index a0e876ea373009554472cada07f1b80a38444ed7..c82403b9cd36d4ea67238648289a3062459bea30 100644
--- a/dist/WebpackConfig.js
+++ b/dist/WebpackConfig.js
@@ -142,6 +142,7 @@ class WebpackConfigGenerator {
                 __dirname: false,
                 __filename: false,
             },
+            plugins: [new AssetRelocatorPatch_1.default(this.isProd, !!this.pluginConfig.renderer.nodeIntegration)],
         }, rendererConfig || {}, { target: 'electron-preload' });
     }
     async getRendererConfig(entryPoints) {
promatik commented 7 months ago

Does anyone have a solution for this? It's 2024 and I'm still stuck on Electron 19.

PascalPixel commented 6 months ago

Does anyone have a solution for this? It's 2024 and I'm still stuck on Electron 19.

Yes it was fixed in the forge webpack plugin, this issue should be resolved

promatik commented 6 months ago

Yes it was fixed in the forge webpack plugin, this issue should be resolved

I just updated my project to the latest electron but I still get the error; VM4 sandbox_bundle:2 Unable to load preload script: C:\Users\...\server\src\js\preload.js

What should I do to fix it?

aaronculp-droneup commented 5 months ago

I'm seeing this error --

  1. unable to load preload script: path/to/file/.webpack/renderer/main_window/preload.js
  2. Error: Can not find module path/to/file/.webpack/renderer/main_window/preload.js

"devDependencies": { "@babel/core": "^7.24.4", "@babel/preset-react": "^7.24.1", "@electron-forge/cli": "^7.4.0", "@electron-forge/maker-deb": "^7.4.0", "@electron-forge/maker-rpm": "^7.4.0", "@electron-forge/maker-squirrel": "^7.4.0", "@electron-forge/maker-zip": "^7.4.0", "@electron-forge/plugin-auto-unpack-natives": "^7.4.0", "@electron-forge/plugin-fuses": "^7.4.0", "@electron-forge/plugin-webpack": "^7.4.0", "@electron/fuses": "^1.8.0", "@types/react": "^18.2.79", "@types/react-dom": "^18.2.25", "@vercel/webpack-asset-relocator-loader": "^1.7.3", "babel-loader": "^9.1.3", "css-loader": "^6.11.0", "electron": "30.0.0", "node-loader": "^2.0.0", "style-loader": "^3.3.4" },

sathyasanles commented 3 months ago

Tenía el mismo problema, lo resolví usando la extensión ".mjs" -> "preload.mjs". No fué necesario tocar nada más.

I had the same problem, I solved it by using the extension ".mjs" -> "preload.mjs". It was not necessary to touch anything else.

HFTSxRiDeR commented 2 months ago

Maybe it is not exactly this issue, however I had similar error after updating electron/forge from 6 to 7.4 forge.config.js stopped to generate preload.js file inside .webpack folder and I was struggling with it for a day... I am using loadUrl that is why I didn't have html file in my entryPoints, after I created html file and added it to forge.config.js, my preload.js file starts generating in .webpack/renderer/main_window/preload.js... Yes, right now I have useless html file, but it works!

  plugins: [
    {
      name: '@electron-forge/plugin-webpack',
      config: {
        mainConfig: path.resolve(__dirname, 'webpack.main.config.js'),
        renderer: {
          config: path.resolve(__dirname, 'webpack.renderer.config.js'),
          entryPoints: [
            {
              html: './src/index.html', // << you had to include this line!
              js: path.resolve(__dirname, 'src/renderer.ts'),
              name: 'main_window',
              preload: {
                js: path.resolve(__dirname, 'src/preload.ts'),
              }
            }
          ]
        },
      }
    }
  ],