electron-builder icon indicating copy to clipboard operation
electron-builder copied to clipboard

Preload.ts

Open JeroenMBooij opened this issue 1 year ago • 3 comments

  • electron-builder version: 25.1.7
  • Node version: 20
  • Electron version: 32.1.2
  • Target: Windows

I have an Angular app in Electron. When I add a preload.js file for inter process communication (IPC) the production build of the app is stuck on the index.html and the electron app is stuck and doesn't start up. For the development build I serve the electron app with a URL, localhost:4200 and it works without issues.

File structure (simplified for conciseness)

|-app
  |-main.ts
  |-preload.ts
|-src
  |-app
  |-main.ts
  |-index.html
|-package.json
|-dist
  |-index.html

I have an electron app that is wrapping a Angular app and I use electron-build to build the app

package.json

{
  "version": "2.2.10",
  "keywords": [
    "angular",
    "dotnet",
    "electron",
    "eslint",
    "kestrel",
    "scss",
    "typescript"
  ],
  "main": "app/main.js",
  "private": true,
  "scripts": {
    "start:mac": "npm-run-all -p server:serve:mac electron:serve web:serve",
    "start:win": "npm-run-all -p server:serve:win electron:serve web:serve",
    "start:test:win": "npm-run-all -p server:serve:win web:serve",
    "start:test:mac": "npm-run-all -p server:serve:mac web:serve",
    "start:electron": "npm-run-all -p electron:serve web:serve",
    "web:serve": "ng serve -c web",
    "web:build": "npm run build -- -c web-production",
    "build": "npm run electron:serve-tsc && ng build --base-href ./",
    "build:dev": "npm run build -- -c dev",
    "build:prod": "npm run build -- -c production",
    "electron:serve-tsc": "tsc -p tsconfig.serve.json",
    "electron:serve": "wait-on tcp:4200 && npm run electron:serve-tsc && electron . --serve",
    "electron:local": "npm run build:prod && electron .",
    "electron:build": "npm run build:prod && electron-builder build --publish=never",
    "win": "electron-builder --win --config ./electron-builder.json",
    "postinstall": "electron-builder install-app-deps",
    "post-build": "copyfiles -f ./src/assets/* ./dist/assets",
    "test": "ng test --watch=false",
    "test:e2e": "npx playwright test",
    "test:watch": "ng test",
    "lint": "ng lint",
  },
  "dependencies": {
    "@angular/animations": "^16.2.11",
    "@angular/cdk": "^16.2.10",
    "@angular/common": "^16.2.11",
    "@angular/compiler": "^16.2.11",
    "@angular/core": "^16.2.11",
    "@angular/forms": "16.2.11",
    "@angular/language-service": "16.2.11",
    "@angular/material": "16.2.10",
    "@angular/platform-browser": "^16.2.11",
    "@angular/platform-browser-dynamic": "16.2.11",
    "@angular/router": "16.2.11",
    "@ngbracket/ngx-layout": "16.1.3",
    "ag-grid-angular": "29.0.0",
    "ag-grid-community": "29.0.0",
    "dotenv": "16.3.1",
    "electron-log": "5.2.0",
    "ngx-translate-testing": "6.1.0",
    "rxjs": "7.8.0",
    "three": "0.137.5",
    "tslib": "2.5.3",
    "zone.js": "0.13.3"
  },
  "devDependencies": {
    "@angular-builders/custom-webpack": "16.0.1",
    "@angular-devkit/build-angular": "16.2.8",
    "@angular-eslint/builder": "16.2.0",
    "@angular-eslint/eslint-plugin": "^16.2.0",
    "@angular-eslint/eslint-plugin-template": "^16.2.0",
    "@angular-eslint/schematics": "16.2.0",
    "@angular-eslint/template-parser": "16.2.0",
    "@angular/cli": "16.2.8",
    "@angular/compiler-cli": "16.2.11",
    "@ngx-translate/core": "14.0.0",
    "@ngx-translate/http-loader": "7.0.0",
    "@playwright/test": "1.38.1",
    "@types/jasmine": "4.0.3",
    "@types/jasminewd2": "2.0.10",
    "@types/node": "18.16.18",
    "@typescript-eslint/eslint-plugin": "^5.59.2",
    "@typescript-eslint/parser": "^5.59.2",
    "concurrently": "8.2.1",
    "conventional-changelog-cli": "2.2.2",
    "electron": "32.1.2",
    "electron-builder": "23.6.0",
    "electron-debug": "3.2.0",
    "electron-reloader": "1.2.3",
    "eslint": "^8.39.0",
    "eslint-config-prettier": "8.5.0",
    "eslint-plugin-import": "2.26.0",
    "eslint-plugin-jsdoc": "39.3.25",
    "eslint-plugin-prefer-arrow": "1.2.3",
    "eslint-plugin-prettier": "4.2.1",
    "jasmine-core": "4.2.0",
    "jasmine-spec-reporter": "7.0.0",
    "karma": "6.4.2",
    "karma-coverage-istanbul-reporter": "3.0.3",
    "karma-electron": "7.2.0",
    "karma-jasmine": "5.1.0",
    "karma-jasmine-html-reporter": "2.0.0",
    "ng-mocks": "14.11.0",
    "node-polyfill-webpack-plugin": "2.0.1",
    "npm-run-all": "4.1.5",
    "playwright": "1.38.1",
    "prettier": "2.8.8",
    "prettier-eslint": "15.0.1",
    "ts-node": "10.9.1",
    "typescript": "4.9.5",
    "wait-on": "6.0.1",
    "webdriver-manager": "12.1.9"
  },
  "engines": {
    "node": ">= 20.16.0"
  }
}

electron-builder.json

{
  "directories": {
  "output": "../output"
  },
  "files": [
  "**/*",
  "!**/*.ts",
  "!**/.vscode",
  "!*.map",
  "!package.json",
  "!package-lock.json",
  "!tsconfig.json",
  "!tslint.json",
  {
  "from": "../dist",
  "filter": ["**/*"]
  }
  ],
  "extraFiles": {
  "from": "../bin/dist/",
  "to": "bin/dist/",
  "filter": ["**/*"]
  },
  "win": {
  "icon": "dist/assets/icons/icon.ico",
  "target": ["nsis"],
  "forceCodeSigning": true,
  "sign": "../CodeSigning/customSign.js"
  },
  "portable": {
  "splashImage": "dist/assets/icons/icon.ico"
  },
  "nsis": {
  "deleteAppDataOnUninstall": true,
  "createDesktopShortcut": true,
  "createStartMenuShortcut": true,
  "shortcutName": "App",
  "artifactName": "App.Setup.${version}.${ext}",
  "uninstallDisplayName": "Uninstall App (${version})",
  "oneClick": false,
  "runAfterFinish": false
  }
}

JeroenMBooij avatar Oct 12 '24 13:10 JeroenMBooij

Have you tried running your app with --inspect or --inspect-brk command line args and debugging breakpoints from there? I'd imagine some error is being thrown in the packaged app as this sounds like a local environment setup issue.

mmaietta avatar Oct 12 '24 14:10 mmaietta

@mmaietta yes, when I run the production executable with devtools enabled I get an error in the console saying Cannot find module 'mypath\win-unpacked\resources\app.asar\preload.js

I probably have to use some electron-builder configuration to properly use the preload.js, but I don't know how.

My win-unpacked folder does have a folder resources and a file app.asar, but since this is a file it cannot contain preload.js

JeroenMBooij avatar Oct 12 '24 18:10 JeroenMBooij

Can you share a minimum reproducible repo for this? Sounds like the wrong path is being passed to a require/import

Additional note: app.asar is an archive that can be extracted with npx asar e <asar path> <extract folder> to check the resources. preload.js should be in there and the asar is the root from which to require the preload.js from.

mmaietta avatar Oct 12 '24 19:10 mmaietta

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Dec 16 '24 00:12 github-actions[bot]

This issue was closed because it has been stalled for 30 days with no activity.

github-actions[bot] avatar Jan 16 '25 00:01 github-actions[bot]

@mmaietta Really appreciate you' re follow ups on many other issues over here.. This one was closed due to being stale but I have a similar issue, But would like to reopen and get to the bottom of it.

I have a Next.js ts project.

src-electron /
-index.ts  (is electron app)
-preload.mjs (colocated and works in dev)

build yaml:

directories:
  output: dist
  buildResources: assets

asarUnpack:
    - '.next/standalone/.next/cache/**/*'
asar: true

files:
  - build
  - '.next/standalone/**/*'
  - '.next/standalone/**/node_modules/**'

mac:
  category: public.app-category.developer-tools
  target:
    target: dir
    arch:
      - x64

index.ts:

        webPreferences: {
            preload: path.join(appPath,'./src-electron/preload.mjs'),  <--  using mjs works when and loaded in dev
            sandbox: false,
            nodeIntegration: true,
            contextIsolation: true,
            devTools: false,
        },

I can also use a preload.ts if needed but really need help on where yaml config file.

Thank You 🙏🏼.

gavan-x avatar Mar 23 '25 04:03 gavan-x

Can you share a minimum reproducible repo for this? Sounds like the wrong path is being passed to a require/import

mmaietta avatar Mar 23 '25 07:03 mmaietta

@mmaietta I've got it correctly loaded and your tips on unpacking asar and seeing if the path mached really helped. Apologies repo is not up and shareable yet but I will eventually do so.

I added below to my yaml which basically copied the folder in a directory above the asar.app .

extraResources:
  - from: './src-electron'
    to: src-electron
    filter:
      - '**/*' 

in my index.ts:

        webPreferences: {
            preload: path.join(appPath,'../src-electron/preload.mjs'),
            sandbox: false,
            nodeIntegration: true,
            contextIsolation: true, // protect against prototype pollution
            devTools: false,
        },

I do have a question I noticed a build folder in my asar with the index.js and preload.js

app.asar/build
 - index.js
 - preload.js

which I assume is my ts compiler, picking up the preload.ts and compiling correctly into a preload.js into the build dir.

but when I tried

preload: path.join(appPath,'./build/preload.js'),

I get the following error: "Error [ERR_REQUIRE_ESM]: require() of ES Module .../Next Electron RSC.app/Contents/Resources/app.asar/build/preload.js not supported. Instead change the require of preload.js in null to a dynamic import() which is available in all CommonJS modules."

I' de rather have this correctly working with a .ts file as opposed to using a .mjs file.

preload.ts

import { contextBridge, ipcRenderer } from "electron";

contextBridge.exposeInMainWorld('electronAPI', {
    toggleTrafficLights: (state) => {
        ipcRenderer.send('toggle-traffic-lights', state)
    }
})

gavan-x avatar Mar 23 '25 07:03 gavan-x

ESM, Commons js , ts configs .... Perfecting your tooling is a losing game... Kid yourself not it will eventually brake.

Loading the preload.mjs works... Sticking with that.

My module resolutions are "node" for the tsconfig.json and "bundler" for "tsconfig-electron.json" For anyone who does find the poorly documented and unsupported starter I've been building off and gets to this stage... and finds this thread my comments should help.

@mmaietta FYI I have decided not to spend effort to get the preload.ts working. I do think this should stay open as electron.js site points to the preload.ts but they are using electron forge.

My comments and errors are going to help anyone who does want to fix it.

gavan-x avatar Mar 23 '25 08:03 gavan-x

So you're preload is in a different folder structure when packaged? Can probably try something simple like this

preload: app.isPackaged ? path.join(appPath, "build/preload.js") : path.join(appPath,'../src-electron/preload.mjs'),

mmaietta avatar Mar 23 '25 13:03 mmaietta

No it’s not a matter of directory pointing it’s an issue with esm and commonjs bundling.

gavan-x avatar Mar 23 '25 21:03 gavan-x