Getting ReferenceError: require is not defined when passing contextIsolation: true with preload script

Viewed 300

I'm trying to add select folder popup functionality and wrote the below code for the same.

in the create-window.ts file, I've passed these browserOptions.webPreferences

webPreferences: {
   nodeIntegration: true,
  contextIsolation: true,
  preload: path.join(__dirname, '..', 'preload.js'),
  ...options.webPreferences,
},

in the same file, I've added this bit of logic right after invoking new BrowserWindow(browserOptions)

ipcMain.handle('select-folder-popup', (handler, args) => {
      return new Promise((resolve, reject) => {
         dialog
            .showOpenDialog(win, {
               properties: ['openDirectory'],
               title: 'Select folder',
            })
            .then(paths => {
               if (paths) {
                  resolve(paths[0])
               } else {
                  reject()
               }
            })
            .catch(error => {
               console.log('open folder', error)
               reject(error)
            })
      })
})

in the preload.js file, I've wrote

const { ipcRenderer, contextBridge } = require('electron')

contextBridge.exposeInMainWorld('api', {
   selectFolderPopup: () => ipcRenderer.invoke('select-folder-popup', true),
})

this bit of code resulted in the error

ReferenceError: global is not defined

which was solved by adding this line in next.config.js

config.output.globalObject = 'this'

with this much implemented, now I get the following error:

ReferenceError: require is not defined

after researching for a while, I've found that one of the solution was setting nodeIntegration: true which is already set to true in my case.

setting contextIsolation: false, isnt an option since that lead to security issues and I cant use contextBridge with it set to false.

Dependencies:

"dependencies": {
      "@emotion/css": "^11.1.3",
      "@emotion/react": "^11.4.1",
      "@emotion/server": "^11.4.0",
      "@emotion/styled": "^11.3.0",
      "axios": "^0.21.4",
      "electron-serve": "^1.1.0",
      "electron-store": "^8.0.0",
      "react-modal": "^3.14.3",
      "react-select": "^4.3.1"
   },
   "devDependencies": {
      "@emotion/babel-plugin": "^11.3.0",
      "@types/node": "^14.14.31",
      "@types/react": "^17.0.0",
      "@types/react-modal": "^3.12.1",
      "@types/react-select": "^4.0.17",
      "babel-plugin-macros": "^3.1.0",
      "electron": "^13.1.7",
      "electron-builder": "^22.11.7",
      "eslint": "^7.32.0",
      "eslint-config-next": "^11.1.2",
      "next": "^11.0.1",
      "nextron": "^7.0.0",
      "react": "^17.0.2",
      "react-dom": "^17.0.2",
      "tailwindcss": "^2.2.7",
      "twin.macro": "^2.7.0",
      "typescript": "^4.3.5"
   },
1 Answers

Update: I've settled to use an alternative solution that is somewhat derivative of this logic.

in the background.ts file, I did the following:

let mainWindow
;(async () => {
   ...
   mainWindow = createWindow('main', {
      width: 1000,
      height: 600,
   })
  ...
})()

...

ipcMain.handle('select-folder-popup', () => {
   return new Promise((resolve, reject) => {
      dialog
         .showOpenDialog(mainWindow, {
            title: 'Select folder',
            properties: ['openDirectory'],
         })
         .then(({ filePaths: paths }) => {
            if (paths) {
               resolve(paths[0])
            } else {
               reject()
            }
         })
         .catch(error => {
            reject(error.message)
         })
   })
})

and in the renderer, doing the following does the job.

import { ipcRenderer } from 'electron'

function Home () {
  return <button
    onClick={() => {
        ipcRenderer
        .invoke('select-folder-popup')
        .then(console.log)
        .catch(console.log)
    }}
  >
    Select Folder
  </button>
}
Related